Add on this day tab (#41)
parent
efd9232c94
commit
5a250818a5
|
@ -14,6 +14,7 @@ return [
|
||||||
['name' => 'page#favorites', 'url' => '/favorites', 'verb' => 'GET'],
|
['name' => 'page#favorites', 'url' => '/favorites', 'verb' => 'GET'],
|
||||||
['name' => 'page#videos', 'url' => '/videos', 'verb' => 'GET'],
|
['name' => 'page#videos', 'url' => '/videos', 'verb' => 'GET'],
|
||||||
['name' => 'page#archive', 'url' => '/archive', 'verb' => 'GET'],
|
['name' => 'page#archive', 'url' => '/archive', 'verb' => 'GET'],
|
||||||
|
['name' => 'page#thisday', 'url' => '/thisday', 'verb' => 'GET'],
|
||||||
|
|
||||||
// API
|
// API
|
||||||
['name' => 'api#days', 'url' => '/api/days', 'verb' => 'GET'],
|
['name' => 'api#days', 'url' => '/api/days', 'verb' => 'GET'],
|
||||||
|
|
|
@ -96,4 +96,12 @@ class PageController extends Controller {
|
||||||
public function archive() {
|
public function archive() {
|
||||||
return $this->main();
|
return $this->main();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*/
|
||||||
|
public function thisday() {
|
||||||
|
return $this->main();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
:title="t('memories', 'Archive')">
|
:title="t('memories', 'Archive')">
|
||||||
<ArchiveIcon slot="icon" :size="20" />
|
<ArchiveIcon slot="icon" :size="20" />
|
||||||
</NcAppNavigationItem>
|
</NcAppNavigationItem>
|
||||||
|
<NcAppNavigationItem :to="{name: 'thisday'}"
|
||||||
|
:title="t('memories', 'On this day')">
|
||||||
|
<CalendarIcon slot="icon" :size="20" />
|
||||||
|
</NcAppNavigationItem>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<NcAppNavigationSettings :title="t('memories', 'Settings')">
|
<NcAppNavigationSettings :title="t('memories', 'Settings')">
|
||||||
|
@ -69,6 +73,7 @@ import FolderIcon from 'vue-material-design-icons/Folder.vue'
|
||||||
import Star from 'vue-material-design-icons/Star.vue'
|
import Star from 'vue-material-design-icons/Star.vue'
|
||||||
import Video from 'vue-material-design-icons/Video.vue'
|
import Video from 'vue-material-design-icons/Video.vue'
|
||||||
import ArchiveIcon from 'vue-material-design-icons/PackageDown.vue';
|
import ArchiveIcon from 'vue-material-design-icons/PackageDown.vue';
|
||||||
|
import CalendarIcon from 'vue-material-design-icons/Calendar.vue';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
|
@ -86,6 +91,7 @@ import ArchiveIcon from 'vue-material-design-icons/PackageDown.vue';
|
||||||
Star,
|
Star,
|
||||||
Video,
|
Video,
|
||||||
ArchiveIcon,
|
ArchiveIcon,
|
||||||
|
CalendarIcon,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class App extends Mixins(GlobalMixin) {
|
export default class App extends Mixins(GlobalMixin) {
|
||||||
|
|
|
@ -519,6 +519,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
||||||
return this.$route.name === 'timeline' ||
|
return this.$route.name === 'timeline' ||
|
||||||
this.$route.name === 'favorites' ||
|
this.$route.name === 'favorites' ||
|
||||||
this.$route.name === 'videos' ||
|
this.$route.name === 'videos' ||
|
||||||
|
this.$route.name === 'thisday' ||
|
||||||
this.$route.name === 'archive';
|
this.$route.name === 'archive';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,8 +560,14 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
||||||
try {
|
try {
|
||||||
this.loading++;
|
this.loading++;
|
||||||
const startState = this.state;
|
const startState = this.state;
|
||||||
const res = await axios.get<IDay[]>(generateUrl(this.appendQuery(url), params));
|
|
||||||
const data = res.data;
|
let data: IDay[] = [];
|
||||||
|
if (this.$route.name === 'thisday') {
|
||||||
|
data = await dav.getOnThisDayData();
|
||||||
|
} else {
|
||||||
|
data = (await axios.get<IDay[]>(generateUrl(this.appendQuery(url), params))).data;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state !== startState) return;
|
if (this.state !== startState) return;
|
||||||
await this.processDays(data);
|
await this.processDays(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -90,5 +90,14 @@
|
||||||
rootTitle: t('memories', 'Archive'),
|
rootTitle: t('memories', 'Archive'),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/thisday',
|
||||||
|
component: Timeline,
|
||||||
|
name: 'thisday',
|
||||||
|
props: route => ({
|
||||||
|
rootTitle: t('memories', 'On this day'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
|
@ -4,7 +4,7 @@ import { encodePath } from '@nextcloud/paths'
|
||||||
import { showError } from '@nextcloud/dialogs'
|
import { showError } from '@nextcloud/dialogs'
|
||||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||||
import { genFileInfo } from './FileUtils'
|
import { genFileInfo } from './FileUtils'
|
||||||
import { IFileInfo } from '../types';
|
import { IDay, IFileInfo, IPhoto } from '../types';
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import client from './DavClient';
|
import client from './DavClient';
|
||||||
|
|
||||||
|
@ -369,3 +369,59 @@ export async function downloadFilesByIds(fileIds: number[]) {
|
||||||
|
|
||||||
yield* runInParallel(calls, 10);
|
yield* runInParallel(calls, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the onThisDay data
|
||||||
|
* Query for last 120 years; should be enough
|
||||||
|
*/
|
||||||
|
export async function getOnThisDayData() {
|
||||||
|
const diffs: { [dayId: number]: number } = {};
|
||||||
|
const now = new Date();
|
||||||
|
const nowUTC = new Date(now.getTime() - now.getTimezoneOffset() * 60000);
|
||||||
|
|
||||||
|
// Populate dayIds
|
||||||
|
for (let i = 1; i <= 120; i++) {
|
||||||
|
// +- 3 days from this day
|
||||||
|
for (let j = -3; j <= 3; j++) {
|
||||||
|
const d = new Date(nowUTC);
|
||||||
|
d.setFullYear(d.getFullYear() - i);
|
||||||
|
d.setDate(d.getDate() + j);
|
||||||
|
const dayId = Math.floor(d.getTime() / 1000 / 86400)
|
||||||
|
diffs[dayId] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query for photos
|
||||||
|
let data: IPhoto[] = [];
|
||||||
|
try {
|
||||||
|
const res = await axios.post<IPhoto[]>(generateUrl('/apps/memories/api/days/BODY'), {
|
||||||
|
body_ids: Object.keys(diffs).join(','),
|
||||||
|
});
|
||||||
|
data = res.data;
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group photos by day
|
||||||
|
const ans: IDay[] = [];
|
||||||
|
const prevDayId = Number.MIN_SAFE_INTEGER;
|
||||||
|
for (const photo of data) {
|
||||||
|
if (!photo.dayid) continue;
|
||||||
|
|
||||||
|
// This works because the response is sorted by date taken
|
||||||
|
if (photo.dayid !== prevDayId) {
|
||||||
|
ans.push({
|
||||||
|
dayid: photo.dayid,
|
||||||
|
count: 0,
|
||||||
|
detail: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to last day
|
||||||
|
const day = ans[ans.length - 1];
|
||||||
|
day.detail.push(photo);
|
||||||
|
day.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ans;
|
||||||
|
}
|
Loading…
Reference in New Issue