From 0a1439aa610cbbe1e2f7b4eed13c1a0e52ccdcba Mon Sep 17 00:00:00 2001 From: Lorin Speybrouck Date: Wed, 28 Aug 2024 19:22:12 +0200 Subject: [PATCH 1/2] feat: added reordering for announcements --- .../announcements/AnnouncementRow.svelte | 2 +- ...Table.svelte => AnnouncementsTable.svelte} | 70 ++++++++--------- src/lib/domain/Announcement.ts | 8 ++ src/lib/stores/AnnouncementStore.ts | 76 +++++++++---------- src/lib/stores/SponsorStore.ts | 33 +++----- src/lib/stores/mocks/MockAnnouncementStore.ts | 7 +- src/lib/utils/Stores.ts | 10 +++ src/routes/announcements/+page.svelte | 12 +-- src/routes/sponsors/+page.svelte | 1 - 9 files changed, 111 insertions(+), 108 deletions(-) rename src/components/announcements/{AnnouncementTable.svelte => AnnouncementsTable.svelte} (74%) create mode 100644 src/lib/utils/Stores.ts diff --git a/src/components/announcements/AnnouncementRow.svelte b/src/components/announcements/AnnouncementRow.svelte index 446b912..5cc61f5 100644 --- a/src/components/announcements/AnnouncementRow.svelte +++ b/src/components/announcements/AnnouncementRow.svelte @@ -13,7 +13,6 @@ export let updateDismissibleHandler: ( announcement: Announcement, ) => Promise | any - export let dragDisabled: boolean export let dragFullyDisabled = false @@ -24,6 +23,7 @@ deleteHandler(announcement) } function updateVisibilityWrapper() { + console.log("updateVisibilityWrapper") updateVisibilityHandler(announcement) } function updateDismissibleWrapper() { diff --git a/src/components/announcements/AnnouncementTable.svelte b/src/components/announcements/AnnouncementsTable.svelte similarity index 74% rename from src/components/announcements/AnnouncementTable.svelte rename to src/components/announcements/AnnouncementsTable.svelte index 34e5a5e..2cf38fb 100644 --- a/src/components/announcements/AnnouncementTable.svelte +++ b/src/components/announcements/AnnouncementsTable.svelte @@ -6,9 +6,10 @@ import TableHeaderRow from "$components/table/TableHeaderRow.svelte" import type { Announcement } from "$lib/domain/Announcement" import { announcementStore } from "$lib/stores/AnnouncementStore" - + import { FLIP_DURATION } from "$lib/utils/Constants" import { handleFirebaseError } from "$lib/utils/Firebase" import { faSearch } from "@fortawesome/free-solid-svg-icons" + import { dndzone } from "svelte-dnd-action" import AnnouncementRow from "./AnnouncementRow.svelte" export let startEdit: (announcement: Announcement) => void @@ -22,21 +23,20 @@ let dragDisabled = true $: dragFullyDisabled = searchString.length > 0 - $: dragableAnnouncements = $announcementStore || [] - //.map((e) => e.toDragableItem()) + $: dragableAnnouncements = $announcementStore.map((e) => e.toDragableItem()) - // function handleConsider(event: CustomEvent>) { - // dragableSponsors = event.detail.items - // dragDisabled = true - // } - // async function handleFinalize(event: CustomEvent>) { - // savingNewOrder = true - // dragableSponsors = event.detail.items - // dragDisabled = true - // const newSortedIds = dragableSponsors.map((e) => e.value.id) - // await announcementStore.updateAnnouncementOrder(newSortedIds) - // savingNewOrder = false - // } + function handleConsider(event: CustomEvent>) { + dragableAnnouncements = event.detail.items + dragDisabled = true + } + async function handleFinalize(event: CustomEvent>) { + savingNewOrder = true + dragableAnnouncements = event.detail.items + dragDisabled = true + const newSortedIds = dragableAnnouncements.map((e) => e.value.id) + await announcementStore.updateAnnouncementsOrder(newSortedIds) + savingNewOrder = false + } // -- Edit articles -- let showModal = false @@ -69,12 +69,12 @@ // : dragableAnnouncements $: filteredDragableAnnouncements = dragableAnnouncements -// function filterAnnouncements(searchString: string) { -// return dragableAnnouncements.filter( -// (announcement) => true, -// //sponsor.value.matchesSearchString(searchString), -// ) -// } + // function filterAnnouncements(searchString: string) { + // return dragableAnnouncements.filter( + // (announcement) => true, + // //announcement.value.matchesSearchString(searchString), + // ) + // } // -- Util -- let saving = false @@ -125,24 +125,23 @@ ]} /> - - + {#each filteredDragableAnnouncements as dragable (dragable.id)} {}} + updateDismissibleHandler={() => {console.error("Not implemented")}} bind:dragDisabled {dragFullyDisabled} /> @@ -150,6 +149,9 @@ + {dragDisabled} + {dragFullyDisabled} + {filteredDragableAnnouncements.map(e => e.value.title)} + } + /** * Checks if Announcemnt matches a search string * - if searchString is undefined, matches all diff --git a/src/lib/stores/AnnouncementStore.ts b/src/lib/stores/AnnouncementStore.ts index fcf1529..95cbaef 100644 --- a/src/lib/stores/AnnouncementStore.ts +++ b/src/lib/stores/AnnouncementStore.ts @@ -2,29 +2,31 @@ import { browser } from '$app/environment' import { Announcement, announcementConverter, type AnnouncementJson } from '$lib/domain/Announcement' import { Collections } from '$lib/firebase/Firebase' import { convertStringToBool } from '$lib/utils/Utils' -import { writable } from 'svelte/store' +import { get, writable } from 'svelte/store' import { createMockAnnouncementStore } from './mocks/MockAnnouncementStore' +import type { OrderingJson } from '$lib/domain/Ordering' +import { sortWithOrdering } from '$lib/utils/Stores' function createAnnouncementStore() { const store = writable<(Announcement)[]>(undefined, set => { async function init() { if (!browser) return - // -- Load Announcement -- + // -- Load Announcements -- const { firebaseApp } = await import('$lib/firebase/Firebase') - const { getFirestore, getDocs, collection } = await import('firebase/firestore') + const { getFirestore, getDocs, getDoc, doc, collection } = await import('firebase/firestore') const firestore = getFirestore(firebaseApp) const announcementsRef = collection(firestore, Collections.ANNOUNCEMENTS) const announcementsSnap = await getDocs(announcementsRef) const announcements = announcementsSnap.docs.map(e => Announcement.fromJson(e.id, e.data() as AnnouncementJson)) - // // -- Sort Announcement -- - // const orderingRef = doc(firestore, Collections.ORDERINGS, Collections.ANNOUNCEMENTS) - // const orderingSnap = await getDoc(orderingRef) - // const ordering = orderingSnap.data() as OrderingJson | undefined - // if (!ordering) throw new Error(`Sort order ${Collections.ANNOUNCEMENTS} not found`) - // sortAnnouncements(announcements, ordering.ids) + // // -- Sort announcements -- + const orderingRef = doc(firestore, Collections.ORDERINGS, Collections.ANNOUNCEMENTS) + const orderingSnap = await getDoc(orderingRef) + const ordering = orderingSnap.data() as OrderingJson | undefined + if (!ordering) throw new Error(`Sort order ${Collections.ANNOUNCEMENTS} not found`) + sortWithOrdering(announcements, ordering.ids) // -- Set store -- set(announcements) @@ -34,7 +36,7 @@ function createAnnouncementStore() { const { subscribe, update } = store async function createAnnouncement(announcement: Announcement) { - // -- Upload announcement -- + // -- Upload document -- const { getFirestore, collection, doc, setDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -44,15 +46,15 @@ function createAnnouncementStore() { announcement.id = newDocRef.id // -- Update ordering -- - // const existingSortedIds = get(store).map((e) => e.id) - // await updateAnnouncementOrder([...existingSortedIds, newDocRef.id]) + const existingSortedIds = get(store).map((e) => e.id) + await updateAnnouncementsOrder([...existingSortedIds, newDocRef.id]) // -- Update store(new item last) -- update((anouncements) => ([...anouncements, announcement])) } async function updateAnnouncement(newTitle: string, newContent: string, newVisible: boolean, newDismissable: boolean, announcement: Announcement) { - // -- Update Announcement -- + // -- Update document -- const { getFirestore, doc, updateDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -76,7 +78,7 @@ function createAnnouncementStore() { } async function deleteAnnouncement(announcement: Announcement) { - // -- Remove announcement -- + // -- Delete document -- const { getFirestore, doc, deleteDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -84,31 +86,32 @@ function createAnnouncementStore() { const docRef = doc(firestore, Collections.ANNOUNCEMENTS, announcement.id) await deleteDoc(docRef) - // -- Remove from store -- + // -- Delete from store -- update((announcements) => announcements.filter((e) => e.id !== announcement.id)) // -- Update ordering -- - //const existingSortedIds = get(store).map((e) => e.id) - //await updateAnnouncementOrder(existingSortedIds) + const existingSortedIds = get(store).map((e) => e.id) + await updateAnnouncementsOrder(existingSortedIds) } - // async function updateAnnouncementOrder(newSortedIds: string[]) { - // // -- Update ordering -- - // const { firebaseApp } = await import('$lib/firebase/Firebase') - // const { getFirestore, doc, updateDoc } = await import('firebase/firestore') - // const firestore = getFirestore(firebaseApp) + async function updateAnnouncementsOrder(newSortedIds: string[]) { + // -- Update document -- + const { firebaseApp } = await import('$lib/firebase/Firebase') + const { getFirestore, doc, updateDoc } = await import('firebase/firestore') + const firestore = getFirestore(firebaseApp) - // const docRef = doc(firestore, Collections.ORDERINGS, Collections.ANNOUNCEMENTS) - // await updateDoc(docRef, { - // ids: newSortedIds, - // }) + const docRef = doc(firestore, Collections.ORDERINGS, Collections.ANNOUNCEMENTS) + await updateDoc(docRef, { + ids: newSortedIds, + }) - // // -- Update store -- - // update((announcements) => [...announcements]) - // } + // -- Update store -- + sortWithOrdering(get(store), newSortedIds) + update((announcements) => [...announcements]) + } async function updateAnnouncementVisibility(announcement: Announcement) { - // -- Update announcement -- + // -- Update document -- const { getFirestore, doc, updateDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -123,23 +126,12 @@ function createAnnouncementStore() { update((announcements) => [...announcements]) } - // function sortAnnouncements(announcements: Announcement[], sortedIds: string[]) { - // const sortMap = new Map(sortedIds.map((e, i) => [e, i] as [string, number])) - // announcements.sort((a, b) => { - // const first = sortMap.get(a.id) - // if (first === undefined) throw new Error(`Announcement ${a.id} not found in sort map`) - // const second = sortMap.get(b.id) - // if (second === undefined) throw new Error(`Announcement ${b.id} not found in sort map`) - // return first - second - // }) - // } - return { subscribe, createAnnouncement, updateAnnouncement, deleteAnnouncement, - // updateAnnouncementOrder, + updateAnnouncementsOrder, updateAnnouncementVisibility } } diff --git a/src/lib/stores/SponsorStore.ts b/src/lib/stores/SponsorStore.ts index 2e65901..586aa00 100644 --- a/src/lib/stores/SponsorStore.ts +++ b/src/lib/stores/SponsorStore.ts @@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid" import { blobToWebP } from 'webp-converter-browser' import { createMockSponsorStore } from './mocks/MockSponsorStore' import { convertStringToBool } from '$lib/utils/Utils' +import { sortWithOrdering } from '$lib/utils/Stores' function createSponsorStore() { const store = writable<(Sponsor)[]>(undefined, set => { @@ -28,7 +29,7 @@ function createSponsorStore() { const orderingSnap = await getDoc(orderingRef) const ordering = orderingSnap.data() as OrderingJson | undefined if (!ordering) throw new Error(`Sort order ${Collections.SPONSORS} not found`) - sortSponsors(sponsors, ordering.ids) + sortWithOrdering(sponsors, ordering.ids) // -- Set store -- set(sponsors) @@ -38,7 +39,7 @@ function createSponsorStore() { const { subscribe, update } = store async function createSponsor(sponsor: Sponsor, image: File) { - // -- Convert images -- + // -- Convert image -- const convertedImage = await blobToWebP(image, { quality: WEBP_IMAGE_QUALITY }) // -- Upload image -- @@ -50,7 +51,7 @@ function createSponsorStore() { const uploadedImage = await getDownloadURL(snapshot.ref) sponsor.imageUrl = uploadedImage - // -- Upload sponsor -- + // -- Upload document -- const { getFirestore, collection, doc, setDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -79,7 +80,8 @@ function createSponsorStore() { await uploadBytes(imageRef, convertedImage) newImageUrl = `${sponsor.imageUrl}#${new Date().getTime()}` // Force reload cache } - // -- Update sponsor -- + + // -- Update document -- const { getFirestore, doc, updateDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -102,7 +104,7 @@ function createSponsorStore() { } async function deleteSponsor(sponsor: Sponsor) { - // -- Remove image -- + // -- Delete image -- const { getStorage, ref, deleteObject } = await import('firebase/storage') const storage = getStorage() @@ -114,7 +116,7 @@ function createSponsorStore() { if (error.code !== 'storage/object-not-found') throw error } - // -- Remove sponsor -- + // -- Delete document -- const { getFirestore, doc, deleteDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -122,7 +124,7 @@ function createSponsorStore() { const docRef = doc(firestore, Collections.SPONSORS, sponsor.id) await deleteDoc(docRef) - // -- Remove from store -- + // -- Delete from store -- update((sponsors) => sponsors.filter((e) => e.id !== sponsor.id)) // -- Update ordering -- @@ -131,7 +133,7 @@ function createSponsorStore() { } async function updateSponsorsOrder(newSortedIds: string[]) { - // -- Update ordering -- + // -- Update document -- const { firebaseApp } = await import('$lib/firebase/Firebase') const { getFirestore, doc, updateDoc } = await import('firebase/firestore') const firestore = getFirestore(firebaseApp) @@ -142,12 +144,12 @@ function createSponsorStore() { }) // -- Update store -- - sortSponsors(get(store), newSortedIds) + sortWithOrdering(get(store), newSortedIds) update((sponsors) => [...sponsors]) } async function updateVisibility(sponsor: Sponsor) { - // -- Update sponsors -- + // -- Update document -- const { getFirestore, doc, updateDoc } = await import('firebase/firestore') const { firebaseApp } = await import('$lib/firebase/Firebase') const firestore = getFirestore(firebaseApp) @@ -163,17 +165,6 @@ function createSponsorStore() { update((sponsors) => [...sponsors]) } - function sortSponsors(sponsors: Sponsor[], sortedIds: string[]) { - const sortMap = new Map(sortedIds.map((e, i) => [e, i] as [string, number])) - sponsors.sort((a, b) => { - const first = sortMap.get(a.id) - if (first === undefined) throw new Error(`Sponsor ${a.id} not found in sort map`) - const second = sortMap.get(b.id) - if (second === undefined) throw new Error(`Sponsor ${b.id} not found in sort map`) - return first - second - }) - } - return { subscribe, createSponsor, diff --git a/src/lib/stores/mocks/MockAnnouncementStore.ts b/src/lib/stores/mocks/MockAnnouncementStore.ts index f852fc9..831b59e 100644 --- a/src/lib/stores/mocks/MockAnnouncementStore.ts +++ b/src/lib/stores/mocks/MockAnnouncementStore.ts @@ -24,9 +24,9 @@ export function createMockAnnouncementStore() { update((announcements) => announcements.filter((e) => e.id !== announcement.id)) } - // async function updateAnnouncementOrder(_newSortedIds: string[]) { - // update((announcements) => [...announcements]) - // } + async function updateAnnouncementsOrder(_newSortedIds: string[]) { + update((announcements) => [...announcements]) + } async function updateAnnouncementVisibility(announcement: Announcement) { announcement.updateSearchableString() @@ -38,6 +38,7 @@ export function createMockAnnouncementStore() { subscribe, createAnnouncement, updateAnnouncement, + updateAnnouncementsOrder, deleteAnnouncement, updateAnnouncementVisibility } diff --git a/src/lib/utils/Stores.ts b/src/lib/utils/Stores.ts new file mode 100644 index 0000000..4b12954 --- /dev/null +++ b/src/lib/utils/Stores.ts @@ -0,0 +1,10 @@ +export function sortWithOrdering(items: { id: string }[], sortedIds: string[]) { + const sortMap = new Map(sortedIds.map((e, i) => [e, i] as [string, number])) + items.sort((a, b) => { + const first = sortMap.get(a.id) + if (first === undefined) throw new Error(`${typeof(items)} ${a.id} not found in sort map`) + const second = sortMap.get(b.id) + if (second === undefined) throw new Error(`${typeof(items)} ${b.id} not found in sort map`) + return first - second + }) +} \ No newline at end of file diff --git a/src/routes/announcements/+page.svelte b/src/routes/announcements/+page.svelte index fc5bbdf..b44bcd0 100644 --- a/src/routes/announcements/+page.svelte +++ b/src/routes/announcements/+page.svelte @@ -1,11 +1,11 @@