diff --git a/src/components/MobileHeader.vue b/src/components/MobileHeader.vue
index 16868e1f7..163c5deda 100644
--- a/src/components/MobileHeader.vue
+++ b/src/components/MobileHeader.vue
@@ -11,6 +11,7 @@
+
@@ -21,6 +22,7 @@
import { defineComponent } from 'vue';
import { generateUrl } from '@nextcloud/router';
+import GoToDateMenuItem from '@components/header/GoToDateMenuItem.vue';
import UploadMenuItem from '@components/header/UploadMenuItem.vue';
import SearchbarMenuItem from '@components/header/SearchbarMenuItem.vue';
@@ -31,6 +33,7 @@ import banner from '@assets/banner.svg';
export default defineComponent({
name: 'MobileHeader',
components: {
+ GoToDateMenuItem,
UploadMenuItem,
SearchbarMenuItem,
},
diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue
index 9e4811c4b..0ba79c5fe 100644
--- a/src/components/Timeline.vue
+++ b/src/components/Timeline.vue
@@ -236,6 +236,8 @@ export default defineComponent({
utils.bus.on('memories:timeline:deleted', this.deleteFromViewWithAnimation);
utils.bus.on('memories:timeline:soft-refresh', this.softRefresh);
utils.bus.on('memories:timeline:hard-refresh', this.refresh);
+ utils.bus.on('memories:timeline:scrollToDate', this.scrollToDate);
+ utils.bus.on('memories:timeline:getDateRange', this.getDateRange);
},
beforeDestroy() {
@@ -246,6 +248,8 @@ export default defineComponent({
utils.bus.off('memories:timeline:deleted', this.deleteFromViewWithAnimation);
utils.bus.off('memories:timeline:soft-refresh', this.softRefresh);
utils.bus.off('memories:timeline:hard-refresh', this.refresh);
+ utils.bus.off('memories:timeline:scrollToDate', this.scrollToDate);
+ utils.bus.off('memories:timeline:getDateRange', this.getDateRange);
this.resetState();
this.state = 0;
},
@@ -347,6 +351,49 @@ export default defineComponent({
return this.containerSize[0] <= 768;
},
+ /** Scroll to the nearest day matching the given date */
+ scrollToDate(date: Date) {
+ const targetDayId = utils.dateToDayId(date);
+
+ // Find the nearest dayId <= target (closest earlier or exact date)
+ let bestDayId: number | null = null;
+ for (const dayId of this.heads.keys()) {
+ if (dayId <= targetDayId) {
+ if (bestDayId === null || dayId > bestDayId) {
+ bestDayId = dayId;
+ }
+ }
+ }
+
+ // If no earlier date found, use the closest one overall
+ if (bestDayId === null) {
+ for (const dayId of this.heads.keys()) {
+ if (bestDayId === null || Math.abs(dayId - targetDayId) < Math.abs(bestDayId - targetDayId)) {
+ bestDayId = dayId;
+ }
+ }
+ }
+
+ if (bestDayId === null) return;
+
+ const index = this.list.findIndex((r) => r.type === 0 && r.dayId === bestDayId);
+ if (index !== -1) {
+ this.refs.recycler?.scrollToItem(index);
+ }
+ },
+
+ /** Provide the date range of the current view */
+ getDateRange(event: { result: { min: Date; max: Date } | null }) {
+ const dayIds = Array.from(this.heads.keys());
+ if (dayIds.length === 0) return;
+ const min = Math.min(...dayIds);
+ const max = Math.max(...dayIds);
+ event.result = {
+ min: utils.dayIdToDate(min),
+ max: utils.dayIdToDate(max),
+ };
+ },
+
isMobileLayout() {
return this.containerSize[0] <= 600;
},
diff --git a/src/components/header/GoToDateMenuItem.vue b/src/components/header/GoToDateMenuItem.vue
new file mode 100644
index 000000000..338ed7eb0
--- /dev/null
+++ b/src/components/header/GoToDateMenuItem.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/src/components/top-matter/FolderTopMatter.vue b/src/components/top-matter/FolderTopMatter.vue
index 36e3a10db..4f7a3a18c 100644
--- a/src/components/top-matter/FolderTopMatter.vue
+++ b/src/components/top-matter/FolderTopMatter.vue
@@ -54,6 +54,14 @@
+
+
@@ -67,6 +75,7 @@ const NcBreadcrumbs = () => import('@nextcloud/vue/dist/Components/NcBreadcrumbs
const NcBreadcrumb = () => import('@nextcloud/vue/dist/Components/NcBreadcrumb.js');
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js';
+import NcDateTimePicker from '@nextcloud/vue/dist/Components/NcDateTimePicker.js';
import PublicUploadHandler from '@components/upload/PublicUploadHandler.vue';
import * as utils from '@services/utils';
@@ -86,6 +95,7 @@ export default defineComponent({
NcBreadcrumb,
NcActions,
NcActionButton,
+ NcDateTimePicker,
PublicUploadHandler,
HomeIcon,
ShareIcon,
@@ -161,6 +171,11 @@ export default defineComponent({
uploadHandler(): InstanceType | null {
return (this.$refs.uploadHandler as InstanceType) || null;
},
+
+ onDateSelected(date: Date) {
+ if (!date) return;
+ utils.bus.emit('memories:timeline:scrollToDate', date);
+ },
},
});
diff --git a/src/components/top-matter/TopMatter.vue b/src/components/top-matter/TopMatter.vue
index e3ecde649..7b169db78 100644
--- a/src/components/top-matter/TopMatter.vue
+++ b/src/components/top-matter/TopMatter.vue
@@ -4,9 +4,14 @@
:class="{
'dynamic-visible': dynamicVisible,
}"
- v-if="currentmatter"
>
-
+
+
+
@@ -19,6 +24,8 @@ import FaceTopMatter from './FaceTopMatter.vue';
import AlbumTopMatter from './AlbumTopMatter.vue';
import PlacesTopMatter from './PlacesTopMatter.vue';
+import GoToDateMenuItem from '@components/header/GoToDateMenuItem.vue';
+
import * as utils from '@services/utils';
export default defineComponent({
@@ -28,6 +35,7 @@ export default defineComponent({
ClusterTopMatter,
FaceTopMatter,
AlbumTopMatter,
+ GoToDateMenuItem,
},
data: () => ({
@@ -134,6 +142,10 @@ export default defineComponent({
}
}
+ .top-matter-date-only {
+ justify-content: flex-end;
+ }
+
:deep button {
display: inline-block;
}
diff --git a/src/services/utils/event-bus.ts b/src/services/utils/event-bus.ts
index 7c26adfa8..d8e8fff5a 100644
--- a/src/services/utils/event-bus.ts
+++ b/src/services/utils/event-bus.ts
@@ -44,6 +44,10 @@ export type BusEvent = {
previous: number;
dynTopMatterVisible: boolean;
};
+ /** Scroll timeline to a specific date */
+ 'memories:timeline:scrollToDate': Date;
+ /** Request the date range of the current view (handler fills in result) */
+ 'memories:timeline:getDateRange': { result: { min: Date; max: Date } | null };
/** Albums were updated for these photos */
'memories:albums:update': IPhoto[];