This commit is contained in:
skidoodle 2024-03-13 00:30:45 +01:00
commit d761a10bf7
102 changed files with 4761 additions and 0 deletions

452
lib/stores/editor.ts Normal file
View file

@ -0,0 +1,452 @@
import { SocialLink } from '$lib/models/socials';
import type Sortable from 'sortablejs';
import { writable } from 'svelte/store';
interface EditorData {
loadTime: number;
editMode: boolean;
draggingWidget: boolean;
edits: EditorPublish;
previews: EmbedCacheFetched['widgets'];
widget?: BioSiteWidget | null;
widgetPreview?: WidgetPreview | null;
avatar?: string | null;
banner?: string | null;
sortables?: {
left?: Sortable;
right?: Sortable;
};
socialSortables?: {
links?: Sortable;
texts?: Sortable;
};
updatedSortable?: boolean;
}
interface EditorStore extends EditorData {
toggleEditMode: () => void;
setDraggingWidget: (draggingWidget: boolean) => void;
setActive: (widget?: BioSiteWidget) => void;
setPreview: (preview?: WidgetPreview) => void;
editWidget: (widget: BioSiteWidget) => void;
deleteWidget: (id: string | number) => void;
applyIndex: () => void;
setSortables: (left?: Sortable, right?: Sortable) => void;
setDisplayName: (displayName: string) => void;
setUsername: (username: string) => void;
setDescription: (description: string) => void;
setLocation: (location: string) => void;
setSchool: (school: string) => void;
setWorkplace: (workplace: string) => void;
setAvatar: (file: File) => void;
deleteAvatar: () => void;
setBanner: (file: File) => void;
deleteBanner: () => void;
setSocials: (
createSocialLinks: BioSiteSocialLink[],
createSocialTexts: BioSiteSocialText[],
indexSocialLinks: IndexSocialLink[],
indexSocialTexts: IndexSocialText[],
deleteSocialLinks: number[],
deleteSocialTexts: number[],
) => void;
toggleUid: (uid: boolean) => void;
reset: () => void;
}
export const generateResetState = (): EditorData => {
return {
loadTime: Date.now(),
editMode: false,
widget: null,
widgetPreview: null,
avatar: null,
banner: null,
edits: {
edits: {
createWidgets: [],
updateWidgets: [],
deleteWidgets: [],
indexWidgets: [],
alignWidgets: [],
imageWidgets: [],
updateDisplayName: null,
updateUsername: null,
updateDescription: null,
updateLocation: null,
updateSchool: null,
updateWorkplace: null,
deleteBanner: null,
deleteAvatar: null,
toggleUid: null,
createSocialLinks: [],
indexSocialLinks: [],
deleteSocialLinks: [],
createSocialTexts: [],
indexSocialTexts: [],
deleteSocialTexts: [],
},
avatar: null,
banner: null,
},
previews: {},
updatedSortable: false,
draggingWidget: false,
};
};
export const editorStore = writable({
...generateResetState(),
toggleEditMode: () => {
editorStore.update((store) => ({ ...store, editMode: !store.editMode }));
},
setDraggingWidget: (draggingWidget) => {
editorStore.update((store) => ({ ...store, draggingWidget, updatedSortable: true }));
},
setActive: (widget) => {
editorStore.update((store) => ({ ...store, widget }));
},
setPreview(preview) {
editorStore.update((store) => ({ ...store, widgetPreview: preview }));
},
editWidget: (widget) => {
editorStore.update((store) => {
const edits = store.edits;
if (widget.id.toString().startsWith('new+')) {
const index = edits.edits.createWidgets.findIndex((edit) => edit.widgetId === widget.id);
if (index !== -1) {
edits.edits.createWidgets[index] = {
...edits.edits.createWidgets[index],
...widget,
align: edits.edits.createWidgets[index].align,
};
} else {
edits.edits.createWidgets.push({
widgetId: widget.id,
type: widget.type,
visible: widget.visible,
align: 2,
index: -1,
data: widget.data,
});
}
} else {
const index = edits.edits.updateWidgets.findIndex((edit) => edit.widgetId === widget.id);
if (index !== -1) {
edits.edits.updateWidgets[index] = { ...edits.edits.updateWidgets[index], ...widget };
} else {
edits.edits.updateWidgets.push({
widgetId: widget.id,
align: 2,
data: widget.data,
});
}
}
if (!store.widgetPreview || !store.widget) return { ...store };
const previews = store.previews;
previews[store.widget.id] = store.widgetPreview;
return { ...store };
});
},
deleteWidget: (id) => {
editorStore.update((store) => {
const edits = store.edits;
if (!id.toString().startsWith('new+') && typeof id === 'number') edits.edits.deleteWidgets.push({ widgetId: id });
return {
...store,
edits: {
...edits,
edits: {
...edits.edits,
createWidgets: edits.edits.createWidgets.filter(function (edit) {
return edit.widgetId !== id;
}),
updateWidgets: edits.edits.updateWidgets.filter(function (edit) {
return edit.widgetId !== id;
}),
},
},
};
});
},
applyIndex: () => {
editorStore.update((store) => {
if (!store.sortables?.left || !store.sortables?.right) return store;
const sortables = {
left: store.sortables.left.toArray(),
right: store.sortables.right.toArray(),
};
const map = (id: string, index: number) => {
if (id.toString().startsWith('new+')) {
const edits = store.edits;
const createIndex = edits.edits.createWidgets.findIndex((edit) => edit.widgetId === id);
if (createIndex !== -1) {
const align = sortables.right.findIndex((sortableId) => sortableId === id) !== -1 ? 2 : 0;
edits.edits.createWidgets[createIndex] = { ...edits.edits.createWidgets[createIndex], index, align };
}
return null;
}
return {
widgetId: Number.parseInt(id),
index,
} as IndexWidget;
};
const sorted = {
left: sortables.left.map(map).filter(Boolean) as IndexWidget[],
right: sortables.right.map(map).filter(Boolean) as IndexWidget[],
};
const aligned = {
left: sorted.left.map((widget) => ({ ...widget, align: 0 } as AlignWidget)) as AlignWidget[],
right: sorted.right.map((widget) => ({ ...widget, align: 2 } as AlignWidget)) as AlignWidget[],
};
return {
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
indexWidgets: [...sorted.left, ...sorted.right],
alignWidgets: [...aligned.left, ...aligned.right],
},
} as EditorPublish,
};
});
},
setSortables: (left, right) => {
editorStore.update((store) => ({
...store,
sortables: { left: left ? left : store.sortables?.left, right: right ? right : store.sortables?.right },
}));
},
setDisplayName: (displayName) => {
editorStore.update((store) => ({
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
updateDisplayName: { newDisplayName: displayName },
},
},
}));
},
setUsername: (username) => {
editorStore.update((store) => ({
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
updateUsername: { newUsername: username },
},
},
}));
},
setDescription: (description) => {
editorStore.update((store) => ({
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
updateDescription: { newDescription: description },
},
},
}));
},
setLocation: (location) => {
editorStore.update((store) => ({
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
updateLocation: { newLocation: location },
},
},
}));
},
setSchool: (school) => {
editorStore.update((store) => ({
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
updateSchool: { newSchool: school },
},
},
}));
},
setWorkplace: (workplace) => {
editorStore.update((store) => ({
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
updateWorkplace: { newWorkplace: workplace },
},
},
}));
},
setAvatar: (file) => {
const url = URL.createObjectURL(file);
editorStore.update((store) => ({
...store,
avatar: url,
edits: {
...store.edits,
edits: {
...store.edits.edits,
deleteAvatar: null,
},
avatar: file,
},
}));
},
deleteAvatar: () => {
editorStore.update((store) => ({
...store,
avatar: null,
edits: {
...store.edits,
edits: {
...store.edits.edits,
deleteAvatar: {},
},
avatar: null,
},
}));
},
setBanner: (file) => {
const url = URL.createObjectURL(file);
editorStore.update((store) => ({
...store,
banner: url,
edits: {
...store.edits,
edits: {
...store.edits.edits,
deleteBanner: null,
},
banner: file,
},
}));
},
deleteBanner: () => {
editorStore.update((store) => ({
...store,
banner: null,
edits: {
...store.edits,
edits: {
...store.edits.edits,
deleteBanner: {},
},
banner: null,
},
}));
},
setSocials: (
createSocialLinks,
createSocialTexts,
indexSocialLinks,
indexSocialTexts,
deleteSocialLinks,
deleteSocialTexts,
) => {
editorStore.update((store) => {
return {
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
createSocialLinks: createSocialLinks
.filter((x) => x.type != 'UNKNOWN' && x.value)
.map(
(x) =>
({
id: x.id,
index: x.index,
type: Object.values(SocialLink).indexOf(x.type),
value: x.value,
} as CreateSocialLink),
),
createSocialTexts: createSocialTexts
.filter((x) => x.value)
.map((x) => ({ id: x.id, index: x.index, title: x.title, value: x.value })),
indexSocialLinks,
indexSocialTexts,
deleteSocialLinks: deleteSocialLinks.map((x) => ({ id: x })),
deleteSocialTexts: deleteSocialTexts.map((x) => ({ id: x })),
},
},
};
});
},
toggleUid: (visible) => {
editorStore.update((store) => ({
...store,
edits: {
...store.edits,
edits: {
...store.edits.edits,
toggleUid: { visible },
},
},
}));
},
reset: () => {
editorStore.update((store) => {
return {
...store,
...generateResetState(),
};
});
},
} as EditorStore);
export const hasEdits = (edits: EditorStore['edits']) => {
return (
[
...edits.edits.createWidgets,
...edits.edits.updateWidgets,
...edits.edits.deleteWidgets,
...edits.edits.indexWidgets,
...edits.edits.alignWidgets,
...edits.edits.imageWidgets,
...edits.edits.createSocialTexts,
...edits.edits.indexSocialTexts,
...edits.edits.deleteSocialTexts,
...edits.edits.createSocialLinks,
...edits.edits.indexSocialLinks,
...edits.edits.deleteSocialLinks,
].length !== 0 ||
edits.edits.deleteAvatar ||
edits.edits.deleteBanner ||
edits.edits.updateDescription ||
edits.edits.updateDisplayName ||
edits.edits.updateLocation ||
edits.edits.updateUsername ||
edits.edits.updateSchool ||
edits.edits.updateWorkplace ||
edits.edits.toggleUid ||
edits.avatar ||
edits.banner
);
};

39
lib/stores/modal.ts Normal file
View file

@ -0,0 +1,39 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { browser } from '$app/environment';
import Modal from '$lib/models/modal';
import { toast } from '@zerodevx/svelte-toast';
import { writable } from 'svelte/store';
interface ModalStore {
visible: boolean;
type?: Modal | null;
data?: any;
warn?: boolean | null;
showModal: (type: Modal, data?: any) => void;
hideModal: (noConfirm?: boolean | null) => void;
disableWarn: () => void;
}
export const modalStore = browser
? writable({
visible: false,
type: null,
data: null,
warn: null,
showModal: (type: Modal, data?: any) => {
modalStore?.update((store) => ({ ...store, type, data, visible: true, warn: true }));
},
hideModal: (noConfirm) => {
modalStore?.update((store) => {
if (!noConfirm && store.type === Modal.EditWidget && store.warn) {
toast.push('Click again to dismiss.');
return { ...store, warn: false };
}
return { ...store, type: null, data: null, visible: false, warn: false };
});
},
disableWarn: () => {
modalStore?.update((store) => ({ ...store, warn: false }));
},
} as ModalStore)
: null;