Skip to content

Commit bbc1f93

Browse files
committed
improve UI/UX
1 parent 96845a4 commit bbc1f93

File tree

3 files changed

+86
-55
lines changed

3 files changed

+86
-55
lines changed

resources/js/components/gallery/photoModule/PhotoRatingWidget.vue

Lines changed: 5 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
:disabled="loading || props.rating.rating_user === 0"
2424
:class="{
2525
'px-2 py-1 text-sm rounded transition-colors': true,
26-
'bg-red-600 hover:bg-red-700 text-white': props.rating.rating_user > 0 && !loading,
26+
'bg-red-600 hover:bg-red-700 text-white hover:cursor-pointer': props.rating.rating_user > 0 && !loading,
2727
'bg-gray-500 text-gray-300 cursor-not-allowed': loading || props.rating.rating_user === 0,
2828
}"
29-
@click="handleRatingClick(0)"
29+
@click="handleRatingClick(props.photoId, 0)"
3030
>
3131
×
3232
</button>
@@ -43,7 +43,7 @@
4343
}"
4444
@mouseenter="handleMouseEnter(rating)"
4545
@mouseleave="handleMouseLeave()"
46-
@click="handleRatingClick(rating as 1 | 2 | 3 | 4 | 5)"
46+
@click="handleRatingClick(props.photoId, rating as 1 | 2 | 3 | 4 | 5)"
4747
>
4848
<i
4949
:class="{
@@ -63,21 +63,19 @@
6363
</template>
6464

6565
<script setup lang="ts">
66-
import { ref } from "vue";
6766
import { useLycheeStateStore } from "@/stores/LycheeState";
6867
import { usePhotoStore } from "@/stores/PhotoState";
6968
import { useToast } from "primevue/usetoast";
70-
import PhotoService from "@/services/photo-service";
7169
import { useUserStore } from "@/stores/UserState";
7270
import StarRow from "@/components/icons/StarRow.vue";
71+
import { useRating } from "@/composables/photo/useRating";
7372
7473
const lycheeStore = useLycheeStateStore();
7574
const photoStore = usePhotoStore();
7675
const userStore = useUserStore();
7776
const toast = useToast();
7877
79-
const loading = ref(false);
80-
const hoverRating = ref<number | null>(null);
78+
const { hoverRating, loading, handleRatingClick } = useRating(photoStore, toast, userStore);
8179
8280
const props = defineProps<{
8381
photoId: string;
@@ -93,52 +91,4 @@ function handleMouseEnter(rating: number) {
9391
function handleMouseLeave() {
9492
hoverRating.value = null;
9593
}
96-
97-
function handleRatingClick(rating: 0 | 1 | 2 | 3 | 4 | 5) {
98-
if (loading.value) {
99-
return;
100-
}
101-
102-
loading.value = true;
103-
104-
PhotoService.setRating(props.photoId, rating)
105-
.then((response) => {
106-
// Update photo store with new photo data (includes updated rating)
107-
photoStore.photo = response.data;
108-
109-
// Show success message
110-
const message = rating === 0 ? "gallery.photo.rating.removed" : "gallery.photo.rating.saved";
111-
toast.add({
112-
severity: "success",
113-
summary: "Success",
114-
detail: message,
115-
life: 3000,
116-
});
117-
})
118-
.catch((error) => {
119-
console.error("Failed to save rating:", error);
120-
121-
// Show error toast
122-
let errorMessage = "gallery.photo.rating.error";
123-
124-
if (error.response?.status === 401) {
125-
errorMessage = "gallery.photo.rating.error_unauthorized";
126-
} else if (error.response?.status === 403) {
127-
errorMessage = "gallery.photo.rating.error_forbidden";
128-
} else if (error.response?.status === 404) {
129-
errorMessage = "gallery.photo.rating.error_not_found";
130-
}
131-
132-
toast.add({
133-
severity: "error",
134-
summary: "Error",
135-
detail: errorMessage,
136-
life: 5000,
137-
});
138-
})
139-
.finally(() => {
140-
loading.value = false;
141-
hoverRating.value = null;
142-
});
143-
}
14494
</script>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import PhotoService from "@/services/photo-service";
2+
import { PhotoStore } from "@/stores/PhotoState";
3+
import { UserStore } from "@/stores/UserState";
4+
import { ToastServiceMethods } from "primevue/toastservice";
5+
import { ref } from "vue";
6+
7+
export function useRating(photoStore: PhotoStore, toast: ToastServiceMethods, userStore: UserStore) {
8+
const loading = ref(false);
9+
const hoverRating = ref<number | null>(null);
10+
11+
function handleRatingClick(photoId: string, rating: 0 | 1 | 2 | 3 | 4 | 5) {
12+
if (loading.value) {
13+
return;
14+
}
15+
if (!userStore.user?.id) {
16+
return;
17+
}
18+
if (!photoStore.photo?.rating) {
19+
return;
20+
}
21+
22+
loading.value = true;
23+
24+
PhotoService.setRating(photoId, rating)
25+
.then((response) => {
26+
if (photoStore.photo !== null && photoStore.photo !== undefined) {
27+
// Update photo store with new photo data (includes updated rating)
28+
photoStore.photo = response.data;
29+
}
30+
31+
// Show success message
32+
const message = rating === 0 ? "gallery.photo.rating.removed" : "gallery.photo.rating.saved";
33+
toast.add({
34+
severity: "success",
35+
summary: "Success",
36+
detail: message,
37+
life: 3000,
38+
});
39+
})
40+
.catch((error) => {
41+
console.error("Failed to save rating:", error);
42+
43+
// Show error toast
44+
let errorMessage = "gallery.photo.rating.error";
45+
46+
if (error.response?.status === 401) {
47+
errorMessage = "gallery.photo.rating.error_unauthorized";
48+
} else if (error.response?.status === 403) {
49+
errorMessage = "gallery.photo.rating.error_forbidden";
50+
} else if (error.response?.status === 404) {
51+
errorMessage = "gallery.photo.rating.error_not_found";
52+
}
53+
54+
toast.add({
55+
severity: "error",
56+
summary: "Error",
57+
detail: errorMessage,
58+
life: 5000,
59+
});
60+
})
61+
.finally(() => {
62+
loading.value = false;
63+
hoverRating.value = null;
64+
});
65+
}
66+
67+
return {
68+
hoverRating,
69+
loading,
70+
handleRatingClick,
71+
};
72+
}

resources/js/views/gallery-panels/Album.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ import { usePhotosStore } from "@/stores/PhotosState";
179179
import { useLayoutStore } from "@/stores/LayoutState";
180180
import { useAlbumsStore } from "@/stores/AlbumsState";
181181
import { useCatalogStore } from "@/stores/CatalogState";
182+
import { useRating } from "@/composables/photo/useRating";
182183
183184
const { isLTR } = useLtRorRtL();
184185
@@ -286,6 +287,8 @@ const { selectedPhoto, selectedAlbum, selectedPhotosIds, selectedAlbumsIds, sele
286287
togglableStore,
287288
);
288289
290+
const { handleRatingClick } = useRating(photoStore, toast, userStore);
291+
289292
function goBack() {
290293
if (is_slideshow_active.value) {
291294
stop();
@@ -375,6 +378,12 @@ onKeyStroke("m", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && photoS
375378
onKeyStroke("e", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && photoStore.rights?.can_edit && toggleEdit());
376379
onKeyStroke("s", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && photoStore.rights?.can_edit && toggleStar());
377380
onKeyStroke(["Delete", "Backspace"], () => !shouldIgnoreKeystroke() && photoStore.isLoaded && albumStore.rights?.can_delete && toggleDelete());
381+
onKeyStroke("0", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && handleRatingClick(photoStore.photo!.id, 0));
382+
onKeyStroke("1", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && handleRatingClick(photoStore.photo!.id, 1));
383+
onKeyStroke("2", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && handleRatingClick(photoStore.photo!.id, 2));
384+
onKeyStroke("3", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && handleRatingClick(photoStore.photo!.id, 3));
385+
onKeyStroke("4", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && handleRatingClick(photoStore.photo!.id, 4));
386+
onKeyStroke("5", () => !shouldIgnoreKeystroke() && photoStore.isLoaded && handleRatingClick(photoStore.photo!.id, 5));
378387
379388
// on key stroke escape:
380389
// 1. lose focus

0 commit comments

Comments
 (0)