Skip to content

Commit

Permalink
favourites
Browse files Browse the repository at this point in the history
  • Loading branch information
ildyria committed Mar 1, 2025
1 parent 7e30847 commit 1ac4375
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 18 deletions.
2 changes: 2 additions & 0 deletions resources/js/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Aura from "@primevue/themes/aura";
import { i18nVue } from "laravel-vue-i18n";
import ToastService from "primevue/toastservice";
import AxiosConfig from "@/config/axios-config";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import AppComponent from "@/views/App.vue";
import { definePreset } from "@primevue/themes";
import LycheePrimeVueConfig from "./style/preset";
Expand All @@ -38,6 +39,7 @@ const router = createRouter({
const LycheePreset = definePreset(Aura, LycheePrimeVueConfig);

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

/**
* Next, we will create a fresh Vue application instance. You may then begin
Expand Down
14 changes: 13 additions & 1 deletion resources/js/components/gallery/albumModule/AlbumPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@
:photo-layout="config.photo_layout"
:selected-photos="selectedPhotosIds"
@clicked="photoClick"
@selected="photoSelect"
@contexted="photoMenuOpen"
:is-timeline="config.is_photo_timeline_enabled"
:with-control="true"
/>
<GalleryFooter v-once />
<ScrollTop v-if="!props.isPhotoOpen" target="parent" />
Expand Down Expand Up @@ -119,6 +121,10 @@ import Button from "primevue/button";
import GalleryFooter from "@/components/footers/GalleryFooter.vue";
import AlbumStatistics from "@/components/drawers/AlbumStatistics.vue";
import { useTogglablesStateStore } from "@/stores/ModalsState";
import { usePhotoRoute } from "@/composables/photo/photoRoute";
import { useRouter } from "vue-router";
const router = useRouter();
const props = defineProps<{
modelAlbum: App.Http.Resources.Models.AlbumResource | undefined;
Expand Down Expand Up @@ -170,10 +176,16 @@ const {
selectedAlbums,
selectedPhotosIds,
selectedAlbumsIds,
photoClick,
photoSelect,
albumClick,
} = useSelection(photos, children, togglableStore);
const { photoRoute } = usePhotoRoute(togglableStore);
function photoClick(idx: number, e: MouseEvent) {
router.push(photoRoute(album.value?.id, photos.value[idx].id));
}
const areStatisticsOpen = ref(false);
function toggleStatistics() {
if (is_se_enabled) {
Expand Down
15 changes: 12 additions & 3 deletions resources/js/components/gallery/albumModule/PhotoThumbPanel.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<Panel id="lychee_view_content" :header="$t(props.header)" class="w-full border-0">
<template #icons>
<PhotoThumbPanelControl v-model:layout="layout" />
<PhotoThumbPanelControl v-if="withControl" v-model:layout="layout" />
</template>
<PhotoThumbPanelList
v-if="isTimeline === false"
Expand All @@ -12,6 +12,7 @@
:selectedPhotos="props.selectedPhotos"
:iter="0"
@clicked="propagateClicked"
@selected="propagateSelected"
@contexted="propagateMenuOpen"
:isTimeline="isTimeline"
/>
Expand All @@ -28,8 +29,9 @@
:selectedPhotos="props.selectedPhotos"
:iter="slotProps.item.iter"
:isTimeline="isTimeline"
@clicked="propagateClicked"
@contexted="propagateMenuOpen"
@selected="propagateSelected"
@clicked="propagateClicked"
/>
</div>
</template>
Expand All @@ -46,8 +48,9 @@
:selectedPhotos="props.selectedPhotos"
:iter="photoTimeline.iter"
:isTimeline="isTimeline"
@clicked="propagateClicked"
@contexted="propagateMenuOpen"
@selected="propagateSelected"
@clicked="propagateClicked"
/>
</div>
</template>
Expand Down Expand Up @@ -80,6 +83,7 @@ const props = defineProps<{
galleryConfig: App.Http.Resources.GalleryConfigs.PhotoLayoutConfig;
selectedPhotos: string[];
isTimeline: boolean;
withControl: boolean;
}>();
const layout = ref(props.photoLayout);
Expand All @@ -88,9 +92,14 @@ const isTimeline = ref(props.isTimeline);
// bubble up.
const emits = defineEmits<{
clicked: [idx: number, event: MouseEvent];
selected: [idx: number, event: MouseEvent];
contexted: [idx: number, event: MouseEvent];
}>();
const propagateSelected = (idx: number, e: MouseEvent) => {
emits("selected", idx, e);
};
const propagateClicked = (idx: number, e: MouseEvent) => {
emits("clicked", idx, e);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { onMounted, onUpdated, Ref } from "vue";
import PhotoThumb from "./thumbs/PhotoThumb.vue";
import { useLycheeStateStore } from "@/stores/LycheeState";
import { storeToRefs } from "pinia";
import { ctrlKeyState, metaKeyState, shiftKeyState } from "@/utils/keybindings-utils";
const props = defineProps<{
photos: { [key: number]: App.Http.Resources.Models.PhotoResource };
Expand All @@ -43,9 +44,16 @@ const timelineData: TimelineData = {
const emits = defineEmits<{
clicked: [idx: number, event: MouseEvent];
selected: [idx: number, event: MouseEvent];
contexted: [idx: number, event: MouseEvent];
}>();
const maySelect = (idx: number, e: MouseEvent) => emits("clicked", idx, e);
const maySelect = (idx: number, e: MouseEvent) => {
if (ctrlKeyState.value || metaKeyState.value || shiftKeyState.value) {
emits("selected", idx, e);
return;
}
emits("clicked", idx, e);
};
const menuOpen = (idx: number, e: MouseEvent) => emits("contexted", idx, e);
// Layouts stuff
Expand Down
21 changes: 12 additions & 9 deletions resources/js/components/gallery/albumModule/thumbs/PhotoThumb.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<template>
<router-link
:to="photoRoute(props.album?.id, props.photo.id)"
<a
:class="{
'photo group shadow-md shadow-black/25 animate-zoomIn transition-all ease-in duration-200 block absolute': true,
'outline outline-1.5 outline-primary-500': props.isSelected,
Expand Down Expand Up @@ -61,13 +60,14 @@
>
<img class="absolute aspect-square w-fit h-fit" alt="play" :src="srcPlay" />
</div>
<ThumbFavourite v-if="is_favourite_enabled" :is-favourite="isFavourite" @click="toggleFavourite" />
<!-- TODO: make me an option. -->
<div v-if="user?.id" class="badges absolute mt-[-1px] ml-1 top-0 left-0 flex">
<ThumbBadge v-if="props.photo.is_starred" class="bg-yellow-500" icon="star" />
<ThumbBadge v-if="is_cover_id" class="bg-yellow-500" icon="folder-cover" />
<ThumbBadge v-if="is_header_id" class="bg-slate-400 hidden sm:block" pi="image" />
</div>
</router-link>
</a>
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue";
Expand All @@ -77,8 +77,8 @@ import ThumbBadge from "@/components/gallery/albumModule/thumbs/ThumbBadge.vue";
import { useLycheeStateStore } from "@/stores/LycheeState";
import { storeToRefs } from "pinia";
import { useImageHelpers } from "@/utils/Helpers";
import { useTogglablesStateStore } from "@/stores/ModalsState";
import { usePhotoRoute } from "@/composables/photo/photoRoute";
import { useFavouriteStore } from "@/stores/FavouriteState";
import ThumbFavourite from "./ThumbFavourite.vue";
const { getNoImageIcon, getPlayIcon } = useImageHelpers();
Expand All @@ -95,16 +95,18 @@ const props = defineProps<{
const auth = useAuthStore();
const { user } = storeToRefs(auth);
const favourites = useFavouriteStore();
const lycheeStore = useLycheeStateStore();
const { display_thumb_photo_overlay } = storeToRefs(lycheeStore);
const togglableStore = useTogglablesStateStore();
const { photoRoute } = usePhotoRoute(togglableStore);
const { is_favourite_enabled, display_thumb_photo_overlay } = storeToRefs(lycheeStore);
const srcPlay = ref(getPlayIcon());
const srcNoImage = ref(getNoImageIcon());
const isImageLoaded = ref(false);
function toggleFavourite() {
favourites.toggle(props.photo);
}
function onImageLoad() {
isImageLoaded.value = true;
}
Expand All @@ -113,6 +115,7 @@ function onImageLoad() {
const is_cover_id = computed(() => props.album?.cover_id === props.photo.id);
// @ts-expect-error
const is_header_id = computed(() => props.album?.header_id === props.photo.id);
const isFavourite = computed(() => favourites.getPhotoIds.includes(props.photo.id));
watch(
() => props.photo.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<div
:class="{
'absolute top-0 right-0 flex justify-center items-center h-8 w-8 text-surface-0 text-2xl filter-shadow': true,
'md:opacity-0 md:group-hover:opacity-50 md:transition-all md:ease-out': !props.isFavourite,
}"
>
<span
:class="{
pi: true,
'pi-heart-fill': props.isFavourite,
'pi-heart': !props.isFavourite,
}"
></span>
</div>
<div
:class="{
'absolute top-0 right-0 flex justify-center items-center h-8 w-8 text-surface-0 text-2xl cursor-pointer': true,
'opacity-0 md:opacity-0 md:hover:opacity-100 md:transition-all md:ease-out': !props.isFavourite,
}"
@click="propagateClick"
>
<span
:class="{
pi: true,
'pi-heart': props.isFavourite,
'pi-heart-fill': !props.isFavourite,
}"
></span>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
isFavourite: boolean;
}>();
const emits = defineEmits<{
click: [];
}>();
function propagateClick(e: MouseEvent) {
e.stopPropagation();
emits("click");
}
console.log("ThumbFavourite.vue");
</script>
5 changes: 3 additions & 2 deletions resources/js/components/gallery/searchModule/ResultPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
:album="undefined"
:gallery-config="props.layoutConfig"
:selected-photos="selectedPhotosIds"
@clicked="photoClick"
@selected="photoSelect"
@contexted="photoMenuOpen"
:is-timeline="props.isPhotoTimelineEnabled"
:with-control="true"
/>
<div class="flex justify-center w-full" v-if="photos.length > 0">
<Paginator :total-records="total" :rows="rows" v-model:first="first" @update:first="emits('refresh')" :always-show="false" />
Expand Down Expand Up @@ -93,7 +94,7 @@ const emits = defineEmits<{
const photos = ref(props.photos);
const children = ref(props.albums);
const { selectedPhotosIds, selectedAlbumsIds, photoClick, albumClick } = useSelection(photos, children, togglableStore);
const { selectedPhotosIds, selectedAlbumsIds, photoSelect, albumClick } = useSelection(photos, children, togglableStore);
const { menu, Menu, photoMenuOpen, albumMenuOpen } = useContextMenu(props.selectors, props.photoCallbacks, props.albumCallbacks);
</script>
10 changes: 10 additions & 0 deletions resources/js/components/headers/AlbumHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
</template>

<template #end>
<router-link
:to="{ name: 'favourites' }"
v-if="(favourites.photos?.length ?? 0) > 0"
class="hidden sm:block"
v-tooltip.bottom="'Favourites'"
>
<Button icon="pi pi-heart" class="border-none" severity="secondary" text />
</router-link>
<Button
v-tooltip.bottom="'Start slideshow'"
icon="pi pi-play"
Expand Down Expand Up @@ -84,6 +92,7 @@ import { storeToRefs } from "pinia";
import AlbumService from "@/services/album-service";
import DropBox from "../modals/DropBox.vue";
import { useTogglablesStateStore } from "@/stores/ModalsState";
import { useFavouriteStore } from "@/stores/FavouriteState";
const props = defineProps<{
config: App.Http.Resources.GalleryConfigs.AlbumConfig;
Expand All @@ -94,6 +103,7 @@ const props = defineProps<{
const togglableStore = useTogglablesStateStore();
const lycheeStore = useLycheeStateStore();
lycheeStore.init();
const favourites = useFavouriteStore();
const { dropbox_api_key } = storeToRefs(lycheeStore);
const { is_album_edit_open } = storeToRefs(togglableStore);
Expand Down
4 changes: 2 additions & 2 deletions resources/js/composables/selections/selections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function useSelection(
selectedAlbumsIdx.value = selectedAlbumsIdx.value.filter((i) => i !== idx);
}

function photoClick(idx: number, e: Event): void {
function photoSelect(idx: number, e: Event): void {
// clear the Album selection.
selectedAlbumsIdx.value = [];

Expand Down Expand Up @@ -215,7 +215,7 @@ export function useSelection(
selectedAlbums,
selectedPhotosIds,
selectedAlbumsIds,
photoClick,
photoSelect,
albumClick,
selectEverything,
unselect,
Expand Down
1 change: 1 addition & 0 deletions resources/js/lychee.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ declare namespace App.Http.Resources.GalleryConfigs {
can_rotate: boolean;
can_autoplay: boolean;
is_exif_disabled: boolean;
is_favourite_enabled: boolean;
display_thumb_album_overlay: App.Enum.ThumbOverlayVisibilityType;
display_thumb_photo_overlay: App.Enum.ThumbOverlayVisibilityType;
album_subtitle_type: App.Enum.ThumbAlbumSubtitleType;
Expand Down
6 changes: 6 additions & 0 deletions resources/js/router/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Album from "@/views/gallery-panels/Album.vue";
import Albums from "@/views/gallery-panels/Albums.vue";

const Landing = () => import("@/views/Landing.vue");
const Favourites = () => import("@/views/gallery-panels/Favourites.vue");
const Frame = () => import("@/views/gallery-panels/Frame.vue");
const Search = () => import("@/views/gallery-panels/Search.vue");
const MapView = () => import("@/views/gallery-panels/Map.vue");
Expand All @@ -23,6 +24,11 @@ const routes_ = [
path: "/",
component: Landing,
},
{
name: "favourites",
path: "/gallery/favourites",
component: Favourites,
},
{
name: "photo",
path: "/gallery/:albumid/:photoid",
Expand Down
Loading

0 comments on commit 1ac4375

Please sign in to comment.