Skip to content

Commit e6e1208

Browse files
committed
feat(List): groups list item by date
1 parent fad8a36 commit e6e1208

File tree

6 files changed

+95
-48
lines changed

6 files changed

+95
-48
lines changed

src/components/common/list/ListItem.vue

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
<script setup lang="ts">
22
import { NFlex, NImage, NTimelineItem } from 'naive-ui';
33
4-
import { computed, type PropType, toRefs } from 'vue';
4+
import { type PropType } from 'vue';
5+
6+
import type { ListScrollItem } from '~/components/common/list/ListScroll.model';
57
68
import PosterPlaceholder from '~/assets/images/poster-placholder.webp';
9+
import ListItemPanel from '~/components/common/list/ListItemPanel.vue';
710
import { Colors } from '~/styles/colors.style';
811
912
const props = defineProps({
10-
id: {
11-
type: Number,
13+
item: {
14+
type: Object as PropType<ListScrollItem>,
1215
required: true,
1316
},
1417
index: {
@@ -30,27 +33,23 @@ const props = defineProps({
3033
required: false,
3134
default: 'default',
3235
},
33-
tag: {
36+
noTag: {
3437
type: Boolean,
3538
required: false,
36-
default: true,
3739
},
3840
});
39-
40-
const { id, index } = toRefs(props);
41-
42-
const isLoading = computed(() => id?.value < 0);
4341
</script>
4442

4543
<template>
4644
<NTimelineItem
47-
:key="id"
45+
:key="item.id"
4846
class="timeline-item"
49-
:class="{ 'no-tag': !tag }"
50-
:data-key="id"
47+
:data-tag="JSON.stringify(item.date)"
48+
:class="{ 'no-tag': noTag || item.date?.sameDay }"
49+
:data-key="item.id"
5150
:data-index="index"
52-
:line-type="isLoading ? 'dashed' : lineType"
53-
:color="isLoading ? 'grey' : color"
51+
:line-type="item.loading ? 'dashed' : lineType"
52+
:color="item.loading ? 'grey' : color"
5453
>
5554
<template #icon>
5655
<slot name="tag" />
@@ -59,7 +58,7 @@ const isLoading = computed(() => id?.value < 0);
5958
<slot name="before" />
6059
</template>
6160
<template #default>
62-
<NFlex class="content">
61+
<NFlex class="content" :class="{ 'no-border': noTag || item.date?.sameDay }">
6362
<NImage
6463
alt="poster-image"
6564
class="poster"
@@ -69,7 +68,9 @@ const isLoading = computed(() => id?.value < 0);
6968
:preview-src="poster"
7069
:fallback-src="PosterPlaceholder"
7170
/>
72-
<slot :id="id" :index="index" :loading="isLoading" />
71+
<ListItemPanel :item="item" :loading="item.loading">
72+
<slot :item="item" :index="index" :loading="item.loading" />
73+
</ListItemPanel>
7374
</NFlex>
7475
</template>
7576
<template #footer>
@@ -92,7 +93,10 @@ const isLoading = computed(() => id?.value < 0);
9293
);
9394
9495
padding: 0.5rem;
95-
border-top: 1px solid rgba(255 255 255 / 10%);
96+
97+
&:not(.no-border) {
98+
border-top: 1px solid rgba(255 255 255 / 10%);
99+
}
96100
}
97101
98102
.poster {

src/components/views/history/HistoryItem.vue src/components/common/list/ListItemPanel.vue

+8-10
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import { NFlex, NSkeleton } from 'naive-ui';
33
44
import { computed, type PropType, toRefs } from 'vue';
55
6-
import type { TraktHistory } from '~/models/trakt/trakt-history.model';
6+
import type { ListScrollItem } from '~/components/common/list/ListScroll.model';
77
88
import PosterPlaceholder from '~/assets/images/poster-placholder.webp';
99
1010
const props = defineProps({
1111
item: {
12-
type: Object as PropType<TraktHistory>,
12+
type: Object as PropType<ListScrollItem>,
1313
required: true,
1414
},
1515
loading: {
1616
type: Boolean,
17-
required: true,
17+
required: false,
1818
},
1919
poster: {
2020
type: String,
@@ -27,24 +27,22 @@ const { item } = toRefs(props);
2727
2828
const title = computed(() => {
2929
const media = item.value;
30-
if ('movie' in media) return media.movie.title;
30+
if (media.movie) return media.movie.title;
3131
if (!media.episode) return media.show?.title;
32-
const number = media.episode?.number?.toString().padStart(2, '0');
33-
return `${media.episode?.season}x${number} - ${media?.episode?.title}`;
32+
const number = media.episode.number?.toString().padStart(2, '0');
33+
return `${media.episode.season}x${number} - ${media.episode.title}`;
3434
});
3535
3636
const content = computed(() => {
3737
const media = item.value;
38-
if ('movie' in media) return media.movie.year;
38+
if (media.movie) return media.movie.year;
3939
if (!media.episode) return media.show?.year;
4040
return media.show?.title;
4141
});
4242
4343
const date = computed(() => {
4444
const media = item.value;
45-
return media.watched_at
46-
? new Date(media.watched_at).toLocaleString()
47-
: media.watched_at;
45+
return media.date?.current?.toLocaleString();
4846
});
4947
5048
const type = computed(() => {

src/components/common/list/ListScroll.model.ts

+17
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,20 @@ export type VirtualListProps = {
1212

1313
export type OnScroll = (listRef: Ref<VirtualListRef | undefined>) => void;
1414
export type OnUpdated = (listRef: Ref<VirtualListRef | undefined>) => void;
15+
16+
export type ListScrollItem = {
17+
id: string | number;
18+
index: number;
19+
20+
movie?: { title: string; year: number };
21+
show?: { title: string; year: number };
22+
episode?: { title: string; season: number; number: number };
23+
24+
loading?: boolean;
25+
date?: {
26+
previous?: Date;
27+
current: Date;
28+
next?: Date;
29+
sameDay?: boolean;
30+
};
31+
};

src/components/common/list/ListScroll.vue

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ref, toRefs } from 'vue';
66
import type { PropType, Ref, Transition } from 'vue';
77
88
import type {
9+
ListScrollItem,
910
VirtualListProps,
1011
VirtualListRef,
1112
} from '~/components/common/list/ListScroll.model';
@@ -19,7 +20,7 @@ const virtualList = ref<VirtualListRef>();
1920
2021
const props = defineProps({
2122
items: {
22-
type: Array as PropType<Record<string, unknown>[]>,
23+
type: Array as PropType<ListScrollItem[]>,
2324
required: true,
2425
},
2526
loading: {
@@ -87,8 +88,8 @@ const debounceLog = debounce(e => console.info('top', e), 100);
8788
@vue:updated="onUpdatedHandler"
8889
>
8990
<template #default="{ item, index }">
90-
<ListItem :id="item.id" :index="index">
91-
<slot :id="item.id" :item="item" :index="index" :loading="item.id < 0" />
91+
<ListItem :item="item" :index="index">
92+
<slot :item="item" :index="index" :loading="item.loading" />
9293
</ListItem>
9394
</template>
9495
</NVirtualList>

src/components/views/history/HistoryComponent.vue

+30-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
<script lang="ts" setup>
2-
import { onActivated, onDeactivated, onMounted, ref, watch } from 'vue';
2+
import { computed, onActivated, onDeactivated, onMounted, ref, watch } from 'vue';
33
4-
import type { OnScroll, OnUpdated } from '~/components/common/list/ListScroll.model';
4+
import type {
5+
ListScrollItem,
6+
OnScroll,
7+
OnUpdated,
8+
} from '~/components/common/list/ListScroll.model';
59
610
import ListScroll from '~/components/common/list/ListScroll.vue';
7-
import HistoryItem from '~/components/views/history/HistoryItem.vue';
811
912
import { useHistoryStore, useHistoryStoreRefs } from '~/stores/data/history.store';
1013
import { useUserSettingsStoreRefs } from '~/stores/settings/user.store';
1114
12-
const {
13-
filteredHistory: history,
14-
pagination,
15-
loading,
16-
pageSize,
17-
belowThreshold,
18-
} = useHistoryStoreRefs();
15+
const { filteredHistory, pagination, loading, pageSize, belowThreshold } =
16+
useHistoryStoreRefs();
1917
const { fetchHistory, clearState } = useHistoryStore();
2018
2119
const { user } = useUserSettingsStoreRefs();
@@ -41,6 +39,26 @@ onMounted(() => {
4139
});
4240
});
4341
42+
const history = computed<ListScrollItem[]>(() => {
43+
const array = filteredHistory.value;
44+
if (!array.length) return [];
45+
return array.map((item, index) => {
46+
const _item: ListScrollItem = { ...item, index, loading: item.id < 0 };
47+
if (!item.watched_at) return _item;
48+
49+
const date: ListScrollItem['date'] = { current: new Date(item.watched_at) };
50+
if (index > 1 && array[index - 1]?.watched_at) {
51+
date.previous = new Date(array[index - 1]?.watched_at);
52+
}
53+
if (array[index + 1]?.watched_at) {
54+
date.next = new Date(array[index + 1]?.watched_at);
55+
}
56+
date.sameDay =
57+
date.previous?.toLocaleDateString() === date.current?.toLocaleDateString();
58+
return { ..._item, date };
59+
});
60+
});
61+
4462
const onScroll: OnScroll = async listRef => {
4563
const key = history.value[history.value.length - 1].id;
4664
await fetchHistory({
@@ -72,8 +90,8 @@ const onUpdated: OnUpdated = listRef => {
7290
@on-scroll-top="() => console.info('Scrolled to top')"
7391
@on-updated="onUpdated"
7492
>
75-
<template #default="{ item, loading: itemLoading }">
76-
<HistoryItem :item="item" :loading="itemLoading" />
93+
<template #default>
94+
<!-- TODO buttons here-->
7795
</template>
7896
</ListScroll>
7997
</template>
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
<script lang="ts" setup>
2-
import { onMounted, ref } from 'vue';
2+
import { computed, onMounted, ref } from 'vue';
33
4+
import type { ListScrollItem } from '~/components/common/list/ListScroll.model';
45
import type { TraktClientPagination } from '~/models/trakt/trakt-client.model';
56
import type { TraktWatchlist } from '~/models/trakt/trakt-watchlist.model';
67
78
import ListScroll from '~/components/common/list/ListScroll.vue';
8-
import HistoryItem from '~/components/views/history/HistoryItem.vue';
99
1010
import { TraktService } from '~/services/trakt.service';
1111
import { useUserSettingsStoreRefs } from '~/stores/settings/user.store';
1212
1313
const { user } = useUserSettingsStoreRefs();
1414
15-
const list = ref<TraktWatchlist[]>([]);
15+
const filteredList = ref<TraktWatchlist[]>([]);
1616
const pagination = ref<TraktClientPagination>();
1717
const loading = ref(true);
1818
const pageSize = 100;
@@ -22,13 +22,22 @@ onMounted(() => {
2222
TraktService.traktClient.sync.collection.get.cached({ type: 'movies' });
2323
TraktService.traktClient.sync.collection.get.cached({ type: 'shows' });
2424
TraktService.traktClient.sync.watchlist.get.cached().then(async res => {
25-
list.value = await res.json();
25+
filteredList.value = await res.json();
2626
pagination.value = res.pagination;
2727
loading.value = false;
2828
});
2929
TraktService.traktClient.users.lists.get.cached({ id: user.value });
3030
TraktService.traktClient.users.lists.collaborations.cached({ id: user.value });
3131
});
32+
33+
const list = computed<ListScrollItem[]>(() => {
34+
const array = filteredList.value;
35+
if (!array.length) return [];
36+
return array.map((item, index) => {
37+
const _item: ListScrollItem = { ...item, index, loading: item.id < 0 };
38+
return _item;
39+
});
40+
});
3241
</script>
3342

3443
<template>
@@ -38,8 +47,8 @@ onMounted(() => {
3847
:pagination="pagination"
3948
:page-size="pageSize"
4049
>
41-
<template #default="{ item, loading: itemLoading }">
42-
<HistoryItem :item="item" :loading="itemLoading" />
50+
<template #default>
51+
<!-- TODO buttons here-->
4352
</template>
4453
</ListScroll>
4554
</template>

0 commit comments

Comments
 (0)