452 lines
12 KiB
TypeScript
452 lines
12 KiB
TypeScript
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
|
|
);
|
|
};
|