File Extension Image for pack.js pack.js
6348 Chars | 273 Lines | 6.35 kb | Text File Parent Directory
import { create } from 'zustand'
import { produce } from 'immer'

import { v4 as uuidv4 } from 'uuid'

import { levenSort, shuffle } from '@/lib/utilities'

import { db, syncLocalStorage } from '@/lib/firebase'
import { doc, getDoc } from 'firebase/firestore'

import { db as localDb } from '@/lib/localstore'

// import { mountStoreDevtool } from 'simple-zustand-devtools'

export async function saveStarred(val, packUUID, cardUUID, uid, save = true) {
	if (val) {
		localStorage.setItem(`starred-${packUUID}-${cardUUID}`, true)
	} else {
		localStorage.removeItem(`starred-${packUUID}-${cardUUID}`)
	}
	if (uid !== undefined && save) {
		await syncLocalStorage(uid)
	}
}

export const usePack = create((set, get) => ({
	// Unfiltered Pack
	// Read-only!
	defaultPack: undefined,
	// Filtered Pack
	// See filterPackCategory
	pack: undefined,
	error: false,
	loading: true,
	canEdit: false,
	loadPack: async (user, id) => {
		if (user == 'me') {
			let ref = await localDb.packs.get(id)
			if (ref === undefined) {
				set({ pack: undefined, error: true, loading: false })
			} else {
				set({ defaultPack: ref, pack: ref, error: false, loading: false })
			}
		} else {
			var ref
			try {
				ref = await getDoc(doc(db, 'packs', user, 'packs', id))
			} catch (e) {
				set({ pack: undefined, error: e, loading: false })
				return
			}
			if (!ref.exists()) {
				set({ pack: undefined, error: true, loading: false })
			} else {
				set({ defaultPack: ref.data(), pack: ref.data(), error: false, loading: false })
			}
		}
		set({ loading: false })
	},
	filterPackCategory: (category) => {
		if (category === 0) {
			set({ pack: get().defaultPack })
		} else {
			set({
				pack: {
					...get().defaultPack,
					content: get().defaultPack.content.filter((c) => c.category == category),
				},
			})
		}
	},
	filterPackStarred: (starred) => {
		if (starred === 0) {
			set({ pack: get().defaultPack })
		} else {
			set({
				pack: {
					...get().defaultPack,
					content: get().defaultPack.content.filter((c) => {
						if (starred) {
							return localStorage.getItem(
								`starred-${get().defaultPack.uuid}-${c.uuid}`
							)
						} else {
							return (
								localStorage.getItem(
									`starred-${get().defaultPack.uuid}-${c.uuid}`
								) == false
							)
						}
					}),
				},
			})
		}
	},
	shufflePack: (doShuffle = true) => {
		if (doShuffle) {
			set({
				pack: {
					...get().defaultPack,
					content: shuffle(get().defaultPack.content),
				},
			})
		} else {
			set({
				pack: get().defaultPack,
			})
		}
	},
	addCards: (cards, usePreviousCategory = false) =>
		set(
			produce((state) => {
				state.pack.content = state.pack.content.concat({
					...cards,
					...{
						category: usePreviousCategory
							? // There's alwways a first card but just make sure
								state.pack.content[state.pack.content.length - 1].category ||
								'default'
							: cards.category,
					},
				})
			})
		),
	setCard: (i, newCard) =>
		set(
			produce((state) => {
				state.pack.content[i] = { ...state.pack.content[i], ...newCard }
			})
		),
	deleteCard: (i) =>
		set(
			produce((state) => {
				state.pack.content.splice(i, 1)
			})
		),
	// Utility
	addQuizletCards: (quizletContents, category = 'default') =>
		set(
			produce((state) => {
				let cards = []
				// Split pack into cards
				const arr = quizletContents.split('⭕')
				arr.forEach((e) => {
					// Split cards into term/defintion
					let a = e.split('🟢')
					cards.push({
						term: a[0],
						definition: a[1],
						category: category,
						uuid: uuidv4(),
					})
				})

				// Remove the last (empty) element
				cards.pop()

				// Add the cards to the pack
				state.pack.content = state.pack.content.concat(cards)
			})
		),
	getSimilarCards: (card, n = 4) => {
		let p = get().pack.content

		// Get the index of the card we want to use as a base
		let i = p.findIndex((c) => c.uuid === card.uuid)

		// Sort using the Levenshtein algorithm
		p = levenSort(p, i)

		// Only use the first nth elements
		p = p.slice(0, n)

		// Randomize it!
		p = shuffle(p)

		return p
	},
	// Metadata
	editClass: (newClass) =>
		set(
			produce((state) => {
				state.pack.class = newClass
			})
		),
	editName: (newName) =>
		set(
			produce((state) => {
				state.pack.name = newName
			})
		),
	togglePublish: (val) =>
		set(
			produce((state) => {
				state.pack.published = val
			})
		),
	setFolder: (folder) => {
		set(
			produce((state) => {
				if (folder) {
					state.pack.folder = folder
				} else {
					delete state.pack.folder
				}
			})
		)
	},
	// Categories
	addCategory: (newCategory, newColor, uuid = uuidv4()) =>
		set(
			produce((state) => {
				let categoryExists = false
				Object.entries(state.pack.categories).map((uuid, category) => {
					if (category.name == newCategory) categoryExists = true
				})
				if (!categoryExists) {
					state.pack.categories[uuid] = { name: newCategory, colors: newColor }
				}
			})
		),
	addPicture: (picture) =>
		set(
			produce((state) => {
				state.pack.pictures.push(picture)
			})
		),
	editCategory: (id, newCategory) =>
		set(
			produce((state) => {
				state.pack.categories[id] = { ...state.pack.categories[id], ...newCategory }
			})
		),
	setCardCategory: (i, categoryId) =>
		set(
			produce((state) => {
				state.pack.content[i]['category'] = categoryId
			})
		),
	setCardPicture: (i, pictureURL) => {
		set(
			produce((state) => {
				state.pack.content[i]['picture'] = pictureURL
			})
		)
	},
	removeCardCategory: (id) =>
		set(
			produce((state) => {
				state.pack.content.forEach((_e, i) => {
					if (state.pack.content[i].category == id) {
						state.pack.content[i].category = 'default'
					}
				})
				delete state.pack.categories[id]
			})
		),
	removeCardPicture: (i) => {
		set(
			produce((state) => {
				delete state.pack.content[i].picture
			})
		)
	},
	// Editor
	letMeEdit: (val) => set({ canEdit: val }),
	editing: false,
	setEditing: (val) => set({ editing: val }),
}))

// if (import.meta.env.DEV) {
// 	mountStoreDevtool('Pack', usePack)
// }