From 0f9225cce4adcaaebfedc1bdf5080f1e676b9837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Viguier?= Date: Wed, 30 Oct 2024 23:14:51 +0100 Subject: [PATCH] Prepare ground work for timeline (#2631) * Prepare ground work for timeline * add condition back --- .../Resources/GalleryConfigs/InitConfig.php | 103 +++++++++++++----- .../js/components/gallery/AlbumThumbPanel.vue | 10 +- .../js/composables/album/albumsRefresher.ts | 38 ++----- resources/js/composables/album/splitter.ts | 31 ++++++ resources/js/lychee.d.ts | 9 +- resources/js/stores/LycheeState.ts | 76 +++++++------ resources/js/views/gallery-panels/Album.vue | 1 - resources/js/views/gallery-panels/Albums.vue | 7 +- 8 files changed, 171 insertions(+), 104 deletions(-) create mode 100644 resources/js/composables/album/splitter.ts diff --git a/app/Http/Resources/GalleryConfigs/InitConfig.php b/app/Http/Resources/GalleryConfigs/InitConfig.php index 164fbab4290..19e988e9469 100644 --- a/app/Http/Resources/GalleryConfigs/InitConfig.php +++ b/app/Http/Resources/GalleryConfigs/InitConfig.php @@ -17,25 +17,39 @@ #[TypeScript()] class InitConfig extends Data { + // ! This will make the error on http requests or front-end being displayed very visibly. public bool $is_debug_enabled; + + // NSFW settings public bool $are_nsfw_visible; - public bool $is_nsfw_warning_visible; public bool $is_nsfw_background_blurred; public string $nsfw_banner_override; public bool $is_nsfw_banner_backdrop_blurred; + + // Keybinding help popup public bool $show_keybinding_help_popup; + + // Image overlay settings public ImageOverlayType $image_overlay_type; + public bool $can_rotate; + public bool $can_autoplay; + + // Thumbs configuration public ThumbOverlayVisibilityType $display_thumb_album_overlay; public ThumbOverlayVisibilityType $display_thumb_photo_overlay; - public ?string $clockwork_url; public ThumbAlbumSubtitleType $album_subtitle_type; - public bool $can_rotate; - public bool $can_autoplay; public AlbumDecorationType $album_decoration; public AlbumDecorationOrientation $album_decoration_orientation; + + // Clockwork + public ?string $clockwork_url; + + // Slideshow setting + public int $slideshow_timeout; + + // Site title & dropbox key if logged in as admin. public string $title; public string $dropbox_api_key; - public int $slideshow_timeout; // Lychee SE is available. public bool $is_se_enabled; @@ -47,48 +61,79 @@ class InitConfig extends Data public function __construct() { + // Debug mode $this->is_debug_enabled = config('app.debug'); + + // NSFW settings $this->are_nsfw_visible = Configs::getValueAsBool('nsfw_visible'); - $this->is_nsfw_background_blurred = Configs::getValueAsBool('nsfw_blur'); - $this->nsfw_banner_override = Configs::getValueAsString('nsfw_banner_override'); - $this->is_nsfw_banner_backdrop_blurred = Configs::getValueAsBool('nsfw_banner_blur_backdrop'); - $this->image_overlay_type = Configs::getValueAsEnum('image_overlay_type', ImageOverlayType::class); - $this->display_thumb_album_overlay = Configs::getValueAsEnum('display_thumb_album_overlay', ThumbOverlayVisibilityType::class); - $this->display_thumb_photo_overlay = Configs::getValueAsEnum('display_thumb_photo_overlay', ThumbOverlayVisibilityType::class); + $this->is_nsfw_background_blurred = Configs::getValueAsBool('nsfw_blur'); // blur the thumbnails + $this->nsfw_banner_override = Configs::getValueAsString('nsfw_banner_override'); // override the banner text. + $this->is_nsfw_banner_backdrop_blurred = Configs::getValueAsBool('nsfw_banner_blur_backdrop'); // blur the backdrop of the warning banner. + + // keybinding help popup $this->show_keybinding_help_popup = Configs::getValueAsBool('show_keybinding_help_popup'); - $this->clockwork_url = $this->has_clockwork_in_menu(); - $this->album_subtitle_type = Configs::getValueAsEnum('album_subtitle_type', ThumbAlbumSubtitleType::class); + // Image overlay settings + $this->image_overlay_type = Configs::getValueAsEnum('image_overlay_type', ImageOverlayType::class); $this->can_rotate = Configs::getValueAsBool('editor_enabled'); $this->can_autoplay = Configs::getValueAsBool('autoplay_enabled'); - $this->slideshow_timeout = Configs::getValueAsInt('slideshow_timeout'); + // Thumbs configuration + $this->display_thumb_album_overlay = Configs::getValueAsEnum('display_thumb_album_overlay', ThumbOverlayVisibilityType::class); + $this->display_thumb_photo_overlay = Configs::getValueAsEnum('display_thumb_photo_overlay', ThumbOverlayVisibilityType::class); + $this->album_subtitle_type = Configs::getValueAsEnum('album_subtitle_type', ThumbAlbumSubtitleType::class); $this->album_decoration = Configs::getValueAsEnum('album_decoration', AlbumDecorationType::class); $this->album_decoration_orientation = Configs::getValueAsEnum('album_decoration_orientation', AlbumDecorationOrientation::class); - $this->title = Configs::getValueAsString('site_title'); + // Clockwork + $this->has_clockwork_in_menu(); - $verify = resolve(Verify::class); - $is_supporter = $verify->is_supporter(); - $this->is_se_enabled = $verify->validate() && $is_supporter; - $this->is_se_preview_enabled = !$is_supporter && Configs::getValueAsBool('enable_se_preview'); - $this->is_se_info_hidden = $is_supporter || Configs::getValueAsBool('disable_se_call_for_actions'); + // Slideshow settings + $this->slideshow_timeout = Configs::getValueAsInt('slideshow_timeout'); + // Site title & dropbox key if logged in as admin. + $this->title = Configs::getValueAsString('site_title'); $this->dropbox_api_key = Auth::user()?->may_administrate === true ? Configs::getValueAsString('dropbox_key') : 'disabled'; + + $this->set_supporter_properties(); } - private function has_clockwork_in_menu(): string|null + /** + * For clockwork we need to check that it is enabled or that we are in debug mode. + * Furthermore we need to check if the web interface is enabled. + * + * @return void + */ + private function has_clockwork_in_menu(): void { // Defining clockwork URL $clockWorkEnabled = config('clockwork.enable') === true || (config('app.debug') === true && config('clockwork.enable') === null); $clockWorkWeb = config('clockwork.web'); - if ($clockWorkEnabled && $clockWorkWeb === true) { - return URL::asset('clockwork/app'); - } - if (is_string($clockWorkWeb)) { - return $clockWorkWeb . '/app'; - } - - return null; + + $this->clockwork_url = match (true) { + $clockWorkEnabled && ($clockWorkWeb === true) => URL::asset('clockwork/app'), + is_string($clockWorkWeb) => $clockWorkWeb . '/app', + default => null, + }; + } + + /** + * We set the properties related to Lychee SE. + * + * @return void + */ + private function set_supporter_properties() + { + $verify = resolve(Verify::class); + $is_supporter = $verify->is_supporter(); + + // We enable Lychee SE if the user is a supporter. + $this->is_se_enabled = $verify->validate() && $is_supporter; + + // We disable preview if we are already a supporter. + $this->is_se_preview_enabled = !$is_supporter && Configs::getValueAsBool('enable_se_preview'); + + // We hide the info if we are already a supporter (or the user requests it). + $this->is_se_info_hidden = $is_supporter || Configs::getValueAsBool('disable_se_call_for_actions'); } } \ No newline at end of file diff --git a/resources/js/components/gallery/AlbumThumbPanel.vue b/resources/js/components/gallery/AlbumThumbPanel.vue index a07e529df88..2d8395a4f60 100644 --- a/resources/js/components/gallery/AlbumThumbPanel.vue +++ b/resources/js/components/gallery/AlbumThumbPanel.vue @@ -8,7 +8,7 @@ :album="album" :cover_id="null" :config="props.config" - v-if="!album.is_nsfw || props.areNsfwVisible" + v-if="!album.is_nsfw || are_nsfw_visible" :is-selected="props.selectedAlbums.includes(album.id)" /> @@ -19,9 +19,13 @@ import Panel from "primevue/panel"; import AlbumThumb, { AlbumThumbConfig } from "@/components/gallery/thumbs/AlbumThumb.vue"; import { computed } from "vue"; +import { useLycheeStateStore } from "@/stores/LycheeState"; +import { storeToRefs } from "pinia"; + +const lycheeStore = useLycheeStateStore(); +const { are_nsfw_visible } = storeToRefs(lycheeStore); const props = defineProps<{ - areNsfwVisible: boolean; header: string; album: App.Http.Resources.Models.AlbumResource | undefined | null; albums: { [key: number]: App.Http.Resources.Models.ThumbAlbumResource }; @@ -36,12 +40,14 @@ const emits = defineEmits<{ clicked: [idx: number, event: MouseEvent]; contexted: [idx: number, event: MouseEvent]; }>(); + const maySelect = (idx: number, e: MouseEvent) => { if (props.idxShift < 0) { return; } emits("clicked", idx, e); }; + const menuOpen = (idx: number, e: MouseEvent) => { if (props.idxShift < 0) { return; diff --git a/resources/js/composables/album/albumsRefresher.ts b/resources/js/composables/album/albumsRefresher.ts index 0a663f9e4fc..e78fa4f5228 100644 --- a/resources/js/composables/album/albumsRefresher.ts +++ b/resources/js/composables/album/albumsRefresher.ts @@ -2,22 +2,18 @@ import AlbumService from "@/services/album-service"; import { AuthStore } from "@/stores/Auth"; import { LycheeStateStore } from "@/stores/LycheeState"; import { computed, ref, Ref } from "vue"; - -export type SharedAlbums = { - owner: string; - albums: App.Http.Resources.Models.ThumbAlbumResource[]; - iter: number; -}; +import { SplitData, useSplitter } from "./splitter"; export function useAlbumsRefresher(auth: AuthStore, lycheeStore: LycheeStateStore, isLoginOpen: Ref) { + const { spliter } = useSplitter(); const user = ref(undefined) as Ref; const isKeybindingsHelpOpen = ref(false); const smartAlbums = ref([]) as Ref; const albums = ref([]) as Ref; - const sharedAlbums = ref([]) as Ref; + const sharedAlbums = ref([]) as Ref[]>; const rootConfig = ref(undefined) as Ref; const rootRights = ref(undefined) as Ref; - const selectableAlbums = computed(() => albums.value.concat(sharedAlbums.value.map((album) => album.albums).flat())); + const selectableAlbums = computed(() => albums.value.concat(sharedAlbums.value.map((album) => album.data).flat())); function refresh() { auth.getUser().then((data) => { @@ -35,9 +31,12 @@ export function useAlbumsRefresher(auth: AuthStore, lycheeStore: LycheeStateStor smartAlbums.value = (data.data.smart_albums as App.Http.Resources.Models.ThumbAlbumResource[]) ?? []; albums.value = data.data.albums as App.Http.Resources.Models.ThumbAlbumResource[]; smartAlbums.value = smartAlbums.value.concat(data.data.tag_albums as App.Http.Resources.Models.ThumbAlbumResource[]); - sharedAlbums.value = []; - - prepSharedAlbum((data.data.shared_albums as App.Http.Resources.Models.ThumbAlbumResource[]) ?? []); + sharedAlbums.value = spliter( + (data.data.shared_albums as App.Http.Resources.Models.ThumbAlbumResource[]) ?? [], + (d) => d.owner as string, // mapper + (d) => d.owner as string, // formatter + albums.value.length, + ); rootConfig.value = data.data.config; rootRights.value = data.data.rights; @@ -56,23 +55,6 @@ export function useAlbumsRefresher(auth: AuthStore, lycheeStore: LycheeStateStor }); } - function prepSharedAlbum(sharedAlbumsData: App.Http.Resources.Models.ThumbAlbumResource[]) { - // In this specific case, album owner is not null. - const sharedOwners: string[] = [...new Set(sharedAlbumsData.map((album) => album.owner as string))]; - sharedOwners.forEach((owner) => { - const albums = sharedAlbumsData.filter((album) => album.owner === owner); - sharedAlbums.value.push({ owner, albums, iter: 0 }); - }); - - // loop over all the shared albums to prep the indexes. - let idx = 0; - let sum = albums.value.length; - for (idx = 0; idx < sharedAlbums.value.length; idx++) { - sharedAlbums.value[idx].iter = sum; - sum += sharedAlbums.value[idx].albums.length; - } - } - return { user, isKeybindingsHelpOpen, diff --git a/resources/js/composables/album/splitter.ts b/resources/js/composables/album/splitter.ts new file mode 100644 index 00000000000..369418b3481 --- /dev/null +++ b/resources/js/composables/album/splitter.ts @@ -0,0 +1,31 @@ +export type SplitData = { + header: string; + data: T[]; + iter: number; +}; + +export function useSplitter() { + function spliter(data: T[], mapper: (d: T) => string, formatter: (d: T) => string, start: number = 0): SplitData[] { + const ret = [] as SplitData[]; + + const headers: string[] = [...new Set(data.map(mapper))]; + headers.forEach((h) => { + const headerData = data.filter((d) => mapper(d) === h); + ret.push({ header: formatter(headerData[0]), data: headerData, iter: 0 }); + }); + + // loop over all the shared albums to prep the indexes. + let idx = 0; + let sum = start; + for (idx = 0; idx < ret.length; idx++) { + ret[idx].iter = sum; + sum += ret[idx].data.length; + } + + return ret; + } + + return { + spliter, + }; +} diff --git a/resources/js/lychee.d.ts b/resources/js/lychee.d.ts index 9cf7c18592d..5dfce8d3ea2 100644 --- a/resources/js/lychee.d.ts +++ b/resources/js/lychee.d.ts @@ -196,23 +196,22 @@ declare namespace App.Http.Resources.GalleryConfigs { export type InitConfig = { is_debug_enabled: boolean; are_nsfw_visible: boolean; - is_nsfw_warning_visible: boolean; is_nsfw_background_blurred: boolean; nsfw_banner_override: string; is_nsfw_banner_backdrop_blurred: boolean; show_keybinding_help_popup: boolean; image_overlay_type: App.Enum.ImageOverlayType; + can_rotate: boolean; + can_autoplay: boolean; display_thumb_album_overlay: App.Enum.ThumbOverlayVisibilityType; display_thumb_photo_overlay: App.Enum.ThumbOverlayVisibilityType; - clockwork_url: string | null; album_subtitle_type: App.Enum.ThumbAlbumSubtitleType; - can_rotate: boolean; - can_autoplay: boolean; album_decoration: App.Enum.AlbumDecorationType; album_decoration_orientation: App.Enum.AlbumDecorationOrientation; + clockwork_url: string | null; + slideshow_timeout: number; title: string; dropbox_api_key: string; - slideshow_timeout: number; is_se_enabled: boolean; is_se_preview_enabled: boolean; is_se_info_hidden: boolean; diff --git a/resources/js/stores/LycheeState.ts b/resources/js/stores/LycheeState.ts index 5fa561270dd..7f5a929f6cd 100644 --- a/resources/js/stores/LycheeState.ts +++ b/resources/js/stores/LycheeState.ts @@ -6,6 +6,11 @@ export type LycheeStateStore = ReturnType; export const useLycheeStateStore = defineStore("lychee-store", { state: () => ({ + // flag to fetch data + is_init: false, + is_loading: false, + + // Debug mode (is default to true to see the first crash) is_debug_enabled: true, // togglables @@ -31,46 +36,39 @@ export const useLycheeStateStore = defineStore("lychee-store", { search_page: 1, // configs for nsfw + are_nsfw_visible: false, is_nsfw_background_blurred: false, is_nsfw_banner_backdrop_blurred: false, nsfw_banner_override: "", + nsfw_consented: [] as string[], + + // Image overlay settings + image_overlay_type: "exif" as App.Enum.ImageOverlayType, + can_rotate: false, + can_autoplay: false, + // keybinding help show_keybinding_help_popup: false, - // Lychee Supporter Edition - is_se_enabled: false, - is_se_preview_enabled: false, - is_se_info_hidden: false, - // album stuff - album_decoration: "LAYERS" as App.Enum.AlbumDecorationType, - album_decoration_orientation: "ROW" as App.Enum.AlbumDecorationOrientation, - album_subtitle_type: "OLDSTYLE" as App.Enum.ThumbAlbumSubtitleType, display_thumb_album_overlay: "always" as App.Enum.ThumbOverlayVisibilityType, display_thumb_photo_overlay: "always" as App.Enum.ThumbOverlayVisibilityType, - - can_rotate: false, - can_autoplay: false, + album_subtitle_type: "OLDSTYLE" as App.Enum.ThumbAlbumSubtitleType, + album_decoration: "LAYERS" as App.Enum.AlbumDecorationType, + album_decoration_orientation: "ROW" as App.Enum.AlbumDecorationOrientation, // menu stuff clockwork_url: "" as null | string, - // togglable with defaults - are_nsfw_visible: false, - image_overlay_type: "exif" as App.Enum.ImageOverlayType, - - // Site title + // Site title & Dropbox API key title: "lychee.GALLERY", - - // flag to fetch data - is_init: false, - is_loading: false, - - nsfw_consented: [] as string[], - - // Dropbox API key dropbox_api_key: "disabled", + + // Lychee Supporter Edition + is_se_enabled: false, + is_se_preview_enabled: false, + is_se_info_hidden: false, }), getters: { isSearchActive(): boolean { @@ -95,30 +93,40 @@ export const useLycheeStateStore = defineStore("lychee-store", { InitService.fetchInitData() .then((response) => { + this.is_init = true; + this.is_loading = false; + const data = response.data; + + this.is_debug_enabled = data.is_debug_enabled; + this.are_nsfw_visible = data.are_nsfw_visible; this.is_nsfw_background_blurred = data.is_nsfw_background_blurred; this.nsfw_banner_override = data.nsfw_banner_override; this.is_nsfw_banner_backdrop_blurred = data.is_nsfw_banner_backdrop_blurred; - this.image_overlay_type = data.image_overlay_type; - this.display_thumb_album_overlay = data.display_thumb_album_overlay; - this.display_thumb_photo_overlay = data.display_thumb_photo_overlay; - this.is_init = true; - this.is_loading = false; + this.show_keybinding_help_popup = data.show_keybinding_help_popup; - this.clockwork_url = data.clockwork_url; + + this.image_overlay_type = data.image_overlay_type; this.can_rotate = data.can_rotate; this.can_autoplay = data.can_autoplay; + + this.display_thumb_album_overlay = data.display_thumb_album_overlay; + this.display_thumb_photo_overlay = data.display_thumb_photo_overlay; + this.album_subtitle_type = data.album_subtitle_type; this.album_decoration = data.album_decoration; this.album_decoration_orientation = data.album_decoration_orientation; - this.album_subtitle_type = data.album_subtitle_type; + + this.clockwork_url = data.clockwork_url; + + this.slideshow_timeout = data.slideshow_timeout; + this.title = data.title; - this.is_debug_enabled = data.is_debug_enabled; + this.dropbox_api_key = data.dropbox_api_key; + this.is_se_enabled = data.is_se_enabled; this.is_se_preview_enabled = data.is_se_preview_enabled; this.is_se_info_hidden = data.is_se_info_hidden; - this.dropbox_api_key = data.dropbox_api_key; - this.slideshow_timeout = data.slideshow_timeout; }) .catch((error) => { // In this specific case, even though it has been possibly disabled, we really need to see the error. diff --git a/resources/js/views/gallery-panels/Album.vue b/resources/js/views/gallery-panels/Album.vue index 598cd733d4f..445cc7bc38e 100644 --- a/resources/js/views/gallery-panels/Album.vue +++ b/resources/js/views/gallery-panels/Album.vue @@ -49,7 +49,6 @@ :albums="children" :config="albumPanelConfig" :is-alone="!photos?.length" - :are-nsfw-visible="are_nsfw_visible" @clicked="albumClick" @contexted="albumMenuOpen" :idx-shift="0" diff --git a/resources/js/views/gallery-panels/Albums.vue b/resources/js/views/gallery-panels/Albums.vue index e5b9f3d8de2..7fd356e3255 100644 --- a/resources/js/views/gallery-panels/Albums.vue +++ b/resources/js/views/gallery-panels/Albums.vue @@ -20,7 +20,6 @@ :user="user" :config="albumPanelConfig" :is-alone="!albums.length" - :are-nsfw-visible="false" :idx-shift="-1" :selected-albums="[]" /> @@ -32,7 +31,6 @@ :user="user" :config="albumPanelConfig" :is-alone="!sharedAlbums.length && !smartAlbums.length" - :are-nsfw-visible="are_nsfw_visible" :idx-shift="0" :selected-albums="selectedAlbumsIds" @clicked="albumClick" @@ -41,13 +39,12 @@