From 00510820a2794efcadbc83f7f8b54318fe198ecb Mon Sep 17 00:00:00 2001
From: Zéro~Informatique
Date: Tue, 26 Jul 2022 08:44:34 +0200
Subject: viewer: migrate to vue 3, general refactoring and cleanup
Non-exhaustive list of fixes and improvements done at the same time:
- html default background to grey (avoids white flash during init)
- unified links behavior
- added more theme variables
- removed the flex-expand transition (it wasn't working) and replaced it
with a slide
- fixed LdLoading not centered on the content
- title on removable tags
- fixed an issue with encoded URI from vue-router
- unified Item resource URLs
- removed the iframe for PlainTextViewer (it wasn't working properly)
and replaced it with a pre
- fixed clear and search buttons tabindex
- fixed the information panel bumping up during the fade animation of
tag's dropdown
- fixed some focus outlines not appearing correctly
- moved CSS variables to the :root context
- Code cleaning
GitHub: closes #217
GitHub: closes #300
GitHub: closes #297
GitHub: closes #105
GitHub: closes #267
GitHub: closes #275
GitHub: closes #228
GitHub: closes #215
GitHub: closes #112
---
viewer/src/store/galleryStore.ts | 201 ++++++++++++++++-----------------------
viewer/src/store/index.ts | 46 ---------
viewer/src/store/uiStore.ts | 116 +++++++++-------------
3 files changed, 129 insertions(+), 234 deletions(-)
delete mode 100644 viewer/src/store/index.ts
(limited to 'viewer/src/store')
diff --git a/viewer/src/store/galleryStore.ts b/viewer/src/store/galleryStore.ts
index e2adf18..7ee660a 100644
--- a/viewer/src/store/galleryStore.ts
+++ b/viewer/src/store/galleryStore.ts
@@ -1,7 +1,7 @@
/* ldgallery - A static generator which turns a collection of tagged
-- pictures into a searchable web gallery.
--
--- Copyright (C) 2019-2020 Guillaume FOUET
+-- Copyright (C) 2019-2022 Guillaume FOUET
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
@@ -17,124 +17,89 @@
-- along with this program. If not, see .
*/
-import { Config, Index, Item } from "@/@types/gallery";
-import { TagCategory, TagIndex, TagSearch } from "@/@types/tag";
-import IndexFactory from "@/services/indexfactory";
-import Navigation from "@/services/navigation";
-import { action, createModule, mutation } from "vuex-class-component";
+import { Config, Index, Item } from '@/@types/gallery';
+import { TagCategory, TagIndex, TagSearch } from '@/@types/tag';
+import { useIndexFactory } from '@/services/indexFactory';
+import { useNavigation } from '@/services/navigation';
+import { defineStore } from 'pinia';
-const VuexModule = createModule({
- namespaced: "galleryStore",
- strict: true,
-});
-
-export default class GalleryStore extends VuexModule {
- config: Config | null = null;
- galleryIndex: Index | null = null;
- tagsIndex: TagIndex = {};
- tagsCategories: TagCategory[] = [];
- currentPath: string | null = null;
- currentSearch: TagSearch[] = [];
-
- // ---
-
- @mutation private setConfig(config: Config) {
- this.config = config;
- }
-
- @mutation setGalleryIndex(galleryIndex: Index) {
- this.galleryIndex = Object.freeze(galleryIndex);
- }
-
- @mutation private setTagsIndex(tagsIndex: TagIndex) {
- this.tagsIndex = Object.freeze(tagsIndex);
- }
-
- @mutation private setTagsCategories(tagsCategories: TagCategory[]) {
- this.tagsCategories = tagsCategories;
- }
-
- @mutation setCurrentPath(currentPath: string) {
- this.currentPath = currentPath;
- }
-
- @mutation setCurrentSearch(currentSearch: TagSearch[]) {
- this.currentSearch = currentSearch;
- }
-
- // ---
-
- get currentItemPath(): Item[] {
- const root = this.galleryIndex?.tree;
- if (root && this.currentPath) return Navigation.searchCurrentItemPath(root, this.currentPath);
- return [];
- }
-
- get currentItem(): Item | null {
- const path = this.currentItemPath;
- return path.length > 0 ? path[path.length - 1] : null;
- }
+const navigation = useNavigation();
+const indexFactory = useIndexFactory();
- get galleryTitle(): string {
- return this.galleryIndex?.properties.galleryTitle ?? "ldgallery";
- }
-
- get resourceRoot(): string {
- return process.env.VUE_APP_DATA_URL + this.config!.galleryRoot;
- }
-
- // ---
-
- // Fetches the gallery's JSON config
- @action async fetchConfig() {
- await fetch(`${process.env.VUE_APP_DATA_URL}${GalleryStore.getUrlConfig()}`, { cache: "no-cache" })
- .then(GalleryStore.responseToJson)
- .then(this.setConfig);
- return this.config!;
- }
-
- // Fetches the gallery's JSON metadata
- @action async fetchGalleryItems() {
- const root = this.config?.galleryRoot ?? "";
- const index = this.config?.galleryIndex ?? "index.json";
- await fetch(`${process.env.VUE_APP_DATA_URL}${root}${index}`, { cache: "no-cache" })
- .then(GalleryStore.responseToJson)
- .then(this.setGalleryIndex)
- .then(this.indexTags)
- .then(this.indexTagCategories);
- return this.galleryIndex!;
- }
-
- // Indexes the gallery
- @action async indexTags() {
- const root = this.galleryIndex?.tree ?? null;
- const index = IndexFactory.generateTags(root);
- this.setTagsIndex(index);
- return index;
- }
-
- // Indexes the proposed categories
- @action async indexTagCategories() {
- const categories = IndexFactory.generateCategories(this.tagsIndex, this.galleryIndex?.properties.tagCategories);
- this.setTagsCategories(categories);
- return categories;
- }
-
- // Searches for tags
- @action async search(filters: string[]) {
- const results = filters.flatMap(filter => IndexFactory.searchTags(this.tagsIndex, filter, true));
- this.setCurrentSearch(results);
- return results;
- }
-
- private static getUrlConfig() {
- const search = window.location.search;
- if (search.length > 1) return search.substr(1) + ".json";
- return "config.json";
- }
+function getUrlConfig() {
+ const search = window.location.search;
+ if (search.length > 1) return search.substring(1) + '.json';
+ return 'config.json';
+}
- private static responseToJson(response: Response) {
- if (!response.ok) throw new Error(`${response.status}: ${response.statusText}`);
- return response.json();
- }
+function responseToJson(response: Response) {
+ if (!response.ok) throw new Error(`${response.status}: ${response.statusText}`);
+ return response.json();
}
+
+export const useGalleryStore = defineStore('gallery', {
+ state: () => ({
+ config: null as Config | null,
+ galleryIndex: null as Index | null,
+ tagsIndex: {} as TagIndex,
+ tagsCategories: [] as TagCategory[],
+ currentPath: null as string | null,
+ currentSearch: [] as TagSearch[],
+ }),
+ getters: {
+ currentItemPath(): Item[] {
+ const root = this.galleryIndex?.tree;
+ if (root && this.currentPath) return navigation.searchCurrentItemPath(root, this.currentPath);
+ return [];
+ },
+ currentItem(): Item | null {
+ const path = this.currentItemPath;
+ return path.length > 0 ? path[path.length - 1] : null;
+ },
+ galleryTitle(): string {
+ return this.galleryIndex?.properties.galleryTitle ?? 'ldgallery';
+ },
+ resourceRoot(): string {
+ return process.env.VUE_APP_DATA_URL + (this.config?.galleryRoot ?? '');
+ },
+ },
+ actions: {
+ // Fetches the gallery's JSON config
+ async fetchConfig() {
+ await fetch(`${process.env.VUE_APP_DATA_URL}${getUrlConfig()}`, { cache: 'no-cache' })
+ .then(responseToJson)
+ .then(v => (this.config = v));
+ return this.config as Config;
+ },
+ // Fetches the gallery's JSON metadata
+ async fetchGalleryItems() {
+ const root = this.config?.galleryRoot ?? '';
+ const index = this.config?.galleryIndex ?? 'index.json';
+ await fetch(`${process.env.VUE_APP_DATA_URL}${root}${index}`, { cache: 'no-cache' })
+ .then(responseToJson)
+ .then(v => (this.galleryIndex = v))
+ .then(this.indexTags)
+ .then(this.indexTagCategories);
+ return this.galleryIndex;
+ },
+ // Indexes the gallery
+ async indexTags() {
+ const root = this.galleryIndex?.tree ?? null;
+ const index = indexFactory.generateTags(root);
+ this.tagsIndex = index;
+ return index;
+ },
+ // Indexes the proposed categories
+ async indexTagCategories() {
+ const categories = indexFactory.generateCategories(this.tagsIndex, this.galleryIndex?.properties.tagCategories);
+ this.tagsCategories = categories;
+ return categories;
+ },
+ // Searches for tags
+ async search(filters: string[]) {
+ const results = filters.flatMap(filter => indexFactory.searchTags(this.tagsIndex, filter, true));
+ this.currentSearch = results;
+ return results;
+ },
+ },
+});
diff --git a/viewer/src/store/index.ts b/viewer/src/store/index.ts
deleted file mode 100644
index 1f49589..0000000
--- a/viewer/src/store/index.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/* ldgallery - A static generator which turns a collection of tagged
--- pictures into a searchable web gallery.
---
--- Copyright (C) 2019-2020 Guillaume FOUET
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--- GNU Affero General Public License for more details.
---
--- You should have received a copy of the GNU Affero General Public License
--- along with this program. If not, see .
-*/
-
-import GalleryStore from "@/store/galleryStore";
-import UIStore from "@/store/uiStore";
-import Vue from "vue";
-import Vuex from "vuex";
-import { createProxy, extractVuexModule } from "vuex-class-component";
-
-Vue.use(Vuex);
-
-const store = new Vuex.Store({
- modules: {
- ...extractVuexModule(UIStore),
- ...extractVuexModule(GalleryStore),
- },
- strict: process.env.NODE_ENV !== "production",
-});
-
-Vue.use(vue => (vue.prototype.$uiStore = createProxy(store, UIStore)));
-Vue.use(vue => (vue.prototype.$galleryStore = createProxy(store, GalleryStore)));
-
-declare module "vue/types/vue" {
- interface Vue {
- $uiStore: UIStore;
- $galleryStore: GalleryStore;
- }
-}
-
-export default store;
diff --git a/viewer/src/store/uiStore.ts b/viewer/src/store/uiStore.ts
index 520fcf4..df8dacc 100644
--- a/viewer/src/store/uiStore.ts
+++ b/viewer/src/store/uiStore.ts
@@ -1,7 +1,7 @@
/* ldgallery - A static generator which turns a collection of tagged
-- pictures into a searchable web gallery.
--
--- Copyright (C) 2019-2020 Guillaume FOUET
+-- Copyright (C) 2019-2022 Guillaume FOUET
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
@@ -17,73 +17,49 @@
-- along with this program. If not, see .
*/
-import { Config } from "@/@types/gallery";
-import { SplashScreenConfig } from "@/@types/splashscreen";
-import ItemComparators, { ItemSort } from "@/services/itemComparators";
-import { action, createModule, mutation } from "vuex-class-component";
-
-const VuexModule = createModule({
- namespaced: "uiStore",
- strict: true,
+import { Config } from '@/@types/gallery';
+import { SplashScreenConfig } from '@/@types/splashscreen';
+import { ItemSort, useItemComparator } from '@/services/itemComparator';
+import { useLocalStorage } from '@vueuse/core';
+import { defineStore } from 'pinia';
+
+const itemComparator = useItemComparator();
+const splashScreenAcknowledgment = useLocalStorage('splashScreenAcknowledgment', '');
+
+export const useUiStore = defineStore('ui', {
+ state: () => ({
+ fullscreen: false,
+ fullWidth: window.innerWidth < Number(process.env.VUE_APP_FULLWIDTH_LIMIT),
+ searchMode: false,
+ sort: itemComparator.DEFAULT as ItemSort,
+
+ splashScreenConfig: null as SplashScreenConfig | null,
+ splashScreenEnabled: false,
+ }),
+ getters: {
+ },
+ actions: {
+ toggleFullscreen(value?: boolean) {
+ this.fullscreen = value ?? !this.fullscreen;
+ },
+ toggleFullWidth(value?: boolean) {
+ this.fullWidth = value ?? !this.fullWidth;
+ },
+ validateSpashScreen() {
+ this.splashScreenEnabled = false;
+ splashScreenAcknowledgment.value = this.splashScreenConfig?.acknowledgmentKey ?? '';
+ },
+ async initFromConfig(config: Config) {
+ if (config.initialItemSort) {
+ const itemSort = itemComparator.ITEM_SORTS.find(sort => sort.name === config.initialItemSort);
+ if (itemSort) this.sort = itemSort;
+ else throw new Error('Unknown sort type: ' + config.initialItemSort);
+ }
+ if (config.splashScreen) {
+ this.splashScreenConfig = config.splashScreen;
+ const uid = config.splashScreen.acknowledgmentKey;
+ this.splashScreenEnabled = !uid || splashScreenAcknowledgment.value !== uid;
+ }
+ },
+ },
});
-
-const STORAGE_SPLASHSCREEN_ACKNOWLEDGMENT = "splashScreenAcknowledgment";
-
-export default class UIStore extends VuexModule {
- fullscreen: boolean = false;
- fullWidth: boolean = window.innerWidth < Number(process.env.VUE_APP_FULLWIDTH_LIMIT);
- searchMode: boolean = false;
- sort: ItemSort = ItemComparators.DEFAULT;
-
- splashScreenConfig: SplashScreenConfig | null = null;
- splashScreenEnabled: boolean = false;
-
- // ---
-
- @mutation toggleFullscreen(value?: boolean) {
- this.fullscreen = value ?? !this.fullscreen;
- }
-
- @mutation toggleFullWidth(value?: boolean) {
- this.fullWidth = value ?? !this.fullWidth;
- }
-
- @mutation toggleSearchMode(value?: boolean) {
- this.searchMode = value ?? !this.searchMode;
- }
-
- @mutation setSort(sort: ItemSort) {
- this.sort = sort;
- }
-
- @mutation setSplashScreenConfig(splashScreenConfig: SplashScreenConfig) {
- this.splashScreenConfig = splashScreenConfig;
- }
-
- @mutation setSplashScreenEnabled(enabled: boolean) {
- this.splashScreenEnabled = enabled;
- }
-
- // ---
-
- @action async initFromConfig(config: Config) {
- if (config.initialItemSort) {
- const itemSort = ItemComparators.ITEM_SORTS[config.initialItemSort];
- if (itemSort) this.setSort(itemSort);
- else throw new Error("Unknown sort type: " + config.initialItemSort);
- }
- if (config.splashScreen) {
- this.setSplashScreenConfig(config.splashScreen);
- const uid = config.splashScreen.acknowledgmentKey;
- this.setSplashScreenEnabled(!uid || localStorage.getItem(STORAGE_SPLASHSCREEN_ACKNOWLEDGMENT) !== uid);
- }
- }
-
- // ---
-
- @action async validateSpashScreen() {
- this.setSplashScreenEnabled(false);
- const uid = this.splashScreenConfig?.acknowledgmentKey;
- if (uid) localStorage.setItem(STORAGE_SPLASHSCREEN_ACKNOWLEDGMENT, String(uid));
- }
-}
--
cgit v1.2.3