From 6c2782f18deffd1e9fef4d732e66982b491b2775 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Wed, 4 Oct 2017 13:39:59 +0200 Subject: [PATCH] Inventory implementation --- pages/inventory/inventory.go | 6 ++ pages/inventory/inventory.pixy | 12 ++- pages/inventory/inventory.scarlet | 35 ++++++++ pages/shop/shop.go | 11 ++- patches/add-shop-items/add-shop-items.go | 104 +++++++++++++++++++++++ scripts/AnimeNotifier.ts | 81 ++++++++++++++---- scripts/Utils.ts | 23 +++++ 7 files changed, 250 insertions(+), 22 deletions(-) create mode 100644 patches/add-shop-items/add-shop-items.go diff --git a/pages/inventory/inventory.go b/pages/inventory/inventory.go index bd91a8ea..473698ed 100644 --- a/pages/inventory/inventory.go +++ b/pages/inventory/inventory.go @@ -25,5 +25,11 @@ func Get(ctx *aero.Context) string { return ctx.Error(http.StatusInternalServerError, "Error fetching inventory data", err) } + // TEST + inventory.AddItem("anime-support-ticket", 35) + inventory.AddItem("pro-account-24", 20) + inventory.AddItem("anime-support-ticket", 15) + inventory.AddItem("pro-account-24", 10) + return ctx.HTML(components.Inventory(inventory)) } diff --git a/pages/inventory/inventory.pixy b/pages/inventory/inventory.pixy index fc92ddc4..11326778 100644 --- a/pages/inventory/inventory.pixy +++ b/pages/inventory/inventory.pixy @@ -1,7 +1,11 @@ component Inventory(inventory *arn.Inventory) ShopTabs .inventory - each slot in inventory.Slots - .inventory-slot - span= slot.ItemID - p Coming soon. \ No newline at end of file + for index, slot := range inventory.Slots + if slot.ItemID == "" + .inventory-slot.mountable(draggable="false", data-index=index) + else + .inventory-slot.mountable(title=slot.Item().Name, draggable="true", data-index=index) + Icon(slot.Item().Icon) + if slot.Quantity > 1 + .inventory-slot-quantity= slot.Quantity \ No newline at end of file diff --git a/pages/inventory/inventory.scarlet b/pages/inventory/inventory.scarlet index e69de29b..b4829920 100644 --- a/pages/inventory/inventory.scarlet +++ b/pages/inventory/inventory.scarlet @@ -0,0 +1,35 @@ +inventory-slot-size = 64px + +.inventory + display grid + grid-gap 0.25rem + grid-template-columns repeat(auto-fit, inventory-slot-size) + grid-auto-rows inventory-slot-size + justify-content center + width 100% + max-width 450px + margin 0 auto + +.inventory-slot + ui-element + position relative + display flex + align-items center + justify-content center + font-size 2rem + + .icon + margin 0 + pointer-events none + +.inventory-slot-quantity + position absolute + bottom 0.25rem + right 0.25rem + font-size 0.8rem + line-height 1em + opacity 0.5 + pointer-events none + +.drag-enter + border-style dashed \ No newline at end of file diff --git a/pages/shop/shop.go b/pages/shop/shop.go index 1ef04e3d..16ec90a1 100644 --- a/pages/shop/shop.go +++ b/pages/shop/shop.go @@ -2,6 +2,7 @@ package shop import ( "net/http" + "sort" "github.com/animenotifier/arn" @@ -18,7 +19,15 @@ func Get(ctx *aero.Context) string { return ctx.Error(http.StatusUnauthorized, "Not logged in", nil) } - items := arn.AllItems() + items, err := arn.AllItems() + + if err != nil { + return ctx.Error(http.StatusInternalServerError, "Error fetching shop item data", err) + } + + sort.Slice(items, func(i, j int) bool { + return items[i].Order < items[j].Order + }) return ctx.HTML(components.Shop(user, items)) } diff --git a/patches/add-shop-items/add-shop-items.go b/patches/add-shop-items/add-shop-items.go new file mode 100644 index 00000000..57592e09 --- /dev/null +++ b/patches/add-shop-items/add-shop-items.go @@ -0,0 +1,104 @@ +package main + +import "github.com/animenotifier/arn" + +var items = []*arn.Item{ + &arn.Item{ + ID: "pro-account-3", + Name: "PRO Account (1 season)", + Price: 900, + Description: `PRO account for 1 anime season (3 months). + +Includes: + +* Special highlight on the forums +* Customizable cover image for your profile +* Custom title for profile and forums +* Your suggestions will have a high priority +* Access to the VIP channel on Discord`, + Icon: "star", + Rarity: arn.ItemRaritySuperior, + Order: 1, + Consumable: true, + }, + &arn.Item{ + ID: "pro-account-6", + Name: "PRO Account (2 seasons)", + Price: 1600, + Description: `PRO account for 2 anime seasons (6 months). + +Includes: + +* Special highlight on the forums +* Customizable cover image for your profile +* Custom title for profile and forums +* Your suggestions will have a high priority +* Access to the VIP channel on Discord`, + Icon: "star", + Rarity: arn.ItemRarityRare, + Order: 2, + Consumable: true, + }, + &arn.Item{ + ID: "pro-account-12", + Name: "PRO Account (4 seasons)", + Price: 3000, + Description: `PRO account for 4 anime seasons (12 months). + +Includes: + +* Special highlight on the forums +* Customizable cover image for your profile +* Custom title for profile and forums +* Your suggestions will have a high priority +* Access to the VIP channel on Discord`, + Icon: "star", + Rarity: arn.ItemRarityUnique, + Order: 3, + Consumable: true, + }, + &arn.Item{ + ID: "pro-account-24", + Name: "PRO Account (8 seasons)", + Price: 5900, + Description: `PRO account for 8 anime seasons (24 months). + +Includes: + +* Special highlight on the forums +* Customizable cover image for your profile +* Custom title for profile and forums +* Your suggestions will have a high priority +* Access to the VIP channel on Discord`, + Icon: "star", + Rarity: arn.ItemRarityLegendary, + Order: 4, + Consumable: true, + }, + &arn.Item{ + ID: "anime-support-ticket", + Name: "Anime Support Ticket", + Price: 100, + Description: `Support the makers of your favourite anime by using an anime support ticket. +Anime Notifier uses 8% of the money to handle the transaction fees while the remaining 92% go directly +to the studios involved in the creation of your favourite anime.`, + Icon: "ticket", + Rarity: arn.ItemRarityRare, + Order: 5, + Consumable: false, + }, +} + +func main() { + for _, item := range items { + item.Save() + } +} + +//- ShopItem("PRO Account", "6 months", "1600", "star", strings.Replace(strings.Replace(proAccountMarkdown, "3 months", "6 months", 1), "1 anime season", "2 anime seasons", 1)) +//- ShopItem("PRO Account", "1 year", "3000", "star", strings.Replace(strings.Replace(proAccountMarkdown, "3 months", "12 months", 1), "1 anime season", "4 anime seasons", 1)) +//- ShopItem("PRO Account", "2 years", "5900", "star", strings.Replace(strings.Replace(proAccountMarkdown, "3 months", "24 months", 1), "1 anime season", "8 anime seasons", 1)) +//- ShopItem("Anime Support Ticket", "", "100", "ticket", "Support the makers of your favourite anime by using an anime support ticket. Anime Notifier uses 8% of the money to handle the transaction fees while the remaining 92% go directly to the studios involved in the creation of your favourite anime.") +//- ShopItem("Artwork Support Ticket", "", "100", "ticket", "Support the makers of your favourite artwork by using an artwork support ticket. Anime Notifier uses 8% of the money to handle the transaction fees while the remaining 92% go directly to the creator.") +//- ShopItem("Soundtrack Support Ticket", "", "100", "ticket", "Support the makers of your favourite soundtrack by using a soundtrack support ticket. Anime Notifier uses 8% of the money to handle the transaction fees while the remaining 92% go directly to the creator.") +//- ShopItem("AMV Support Ticket", "", "100", "ticket", "Support the makers of your favourite AMV by using an AMV support ticket. Anime Notifier uses 8% of the money to handle the transaction fees while the remaining 92% go directly to the creator.") diff --git a/scripts/AnimeNotifier.ts b/scripts/AnimeNotifier.ts index b9a78533..4a94eb7d 100644 --- a/scripts/AnimeNotifier.ts +++ b/scripts/AnimeNotifier.ts @@ -1,6 +1,6 @@ import * as actions from "./Actions" import { displayAiringDate, displayDate } from "./DateView" -import { findAll, delay, canUseWebP } from "./Utils" +import { findAll, delay, canUseWebP, swapElements } from "./Utils" import { Application } from "./Application" import { Diff } from "./Diff" import { MutationQueue } from "./MutationQueue" @@ -139,6 +139,7 @@ export class AnimeNotifier { Promise.resolve().then(() => this.setSelectBoxValue()), Promise.resolve().then(() => this.assignActions()), Promise.resolve().then(() => this.updatePushUI()), + Promise.resolve().then(() => this.dragAndDrop()), Promise.resolve().then(() => this.countUp()) ]) @@ -154,22 +155,6 @@ export class AnimeNotifier { } } - async updatePushUI() { - if(!this.pushManager.pushSupported || !this.app.currentPath.includes("/settings")) { - return - } - - let subscription = await this.pushManager.subscription() - - if(subscription) { - this.app.find("enable-notifications").style.display = "none" - this.app.find("disable-notifications").style.display = "flex" - } else { - this.app.find("enable-notifications").style.display = "flex" - this.app.find("disable-notifications").style.display = "none" - } - } - onIdle() { // Service worker this.registerServiceWorker() @@ -261,6 +246,68 @@ export class AnimeNotifier { } } + dragAndDrop() { + for(let element of findAll("inventory-slot")) { + if(element.draggable) { + element.addEventListener("dragstart", e => { + let element = e.target as HTMLElement + e.dataTransfer.setData("text", element.dataset.index) + }, false) + } + + element.addEventListener("dragenter", e => { + let element = e.target as HTMLElement + element.classList.add("drag-enter") + }, false) + + element.addEventListener("dragleave", e => { + let element = e.target as HTMLElement + element.classList.remove("drag-enter") + }, false) + + element.addEventListener("dragover", e => { + e.preventDefault() + }, false) + + element.addEventListener("drop", e => { + let inventory = e.toElement.parentElement + let fromIndex = e.dataTransfer.getData("text") + let fromElement = inventory.childNodes[fromIndex] as HTMLElement + let toElement = e.toElement as HTMLElement + let toIndex = toElement.dataset.index + + e.stopPropagation() + e.preventDefault() + + if(fromElement === toElement || fromIndex === toIndex) { + return + } + + toElement.classList.remove("drag-enter") + swapElements(fromElement, toElement) + + fromElement.dataset.index = toIndex + toElement.dataset.index = fromIndex + }, false) + } + } + + async updatePushUI() { + if(!this.pushManager.pushSupported || !this.app.currentPath.includes("/settings")) { + return + } + + let subscription = await this.pushManager.subscription() + + if(subscription) { + this.app.find("enable-notifications").style.display = "none" + this.app.find("disable-notifications").style.display = "flex" + } else { + this.app.find("enable-notifications").style.display = "flex" + this.app.find("disable-notifications").style.display = "none" + } + } + countUp() { for(let element of findAll("count-up")) { let final = parseInt(element.innerText) diff --git a/scripts/Utils.ts b/scripts/Utils.ts index 17ad8d01..cc164528 100644 --- a/scripts/Utils.ts +++ b/scripts/Utils.ts @@ -24,4 +24,27 @@ export function canUseWebP(): boolean { // In very old browsers (IE 8) canvas is not supported return false } +} + +export function swapElements(a: Node, b: Node) { + let parent = b.parentNode + let bNext = b.nextSibling + + // Special case for when a is the next sibling of b + if(bNext === a) { + // Just put a before b + parent.insertBefore(a, b) + } else { + // Insert b right before a + a.parentNode.insertBefore(b, a) + + // Now insert a where b was + if(bNext) { + // If there was an element after b, then insert a right before that + parent.insertBefore(a, bNext) + } else { + // Otherwise just append it as the last child + parent.appendChild(a) + } + } } \ No newline at end of file