albums: add list

old-stable24
Varun Patil 2022-10-26 15:48:46 -07:00
parent 294b3b8a0c
commit 50bb55536f
6 changed files with 75 additions and 14 deletions

View File

@ -15,11 +15,24 @@ trait TimelineQueryAlbums
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();
// SELECT everything from albums // SELECT everything from albums
$query->select('*')->from('photos_albums', 'pa')->where( $count = $query->func()->count($query->createFunction('DISTINCT m.fileid'), 'count');
$query->select('pa.*', $count)->from('photos_albums', 'pa')->where(
$query->expr()->eq('user', $query->createNamedParameter($uid)), $query->expr()->eq('user', $query->createNamedParameter($uid)),
); );
// WHERE there are items with this tag
$query->innerJoin('pa', 'photos_albums_files', 'paf', $query->expr()->andX(
$query->expr()->eq('paf.album_id', 'pa.album_id'),
));
// WHERE these items are memories indexed photos
$query->innerJoin('paf', 'memories', 'm', $query->expr()->eq('m.fileid', 'paf.file_id'));
// WHERE these photos are in the filecache
$query->innerJoin('m', 'filecache', 'f', $query->expr()->eq('m.fileid', 'f.fileid'),);
// GROUP and ORDER by // GROUP and ORDER by
$query->groupBy('pa.album_id');
$query->orderBy('pa.created', 'DESC'); $query->orderBy('pa.created', 'DESC');
$query->addOrderBy('pa.album_id', 'DESC'); // tie-breaker $query->addOrderBy('pa.album_id', 'DESC'); // tie-breaker
@ -28,8 +41,8 @@ trait TimelineQueryAlbums
// Post process // Post process
foreach ($albums as &$row) { foreach ($albums as &$row) {
$row['album_id'] = (int) $row['id']; $row['album_id'] = (int) $row['album_id'];
$row['created'] = (int) $row['count']; $row['created'] = (int) $row['created'];
$row['last_added_photo'] = (int) $row['last_added_photo']; $row['last_added_photo'] = (int) $row['last_added_photo'];
} }

View File

@ -52,7 +52,7 @@
{{ item.super }} {{ item.super }}
</div> </div>
<div class="main" @click="selectionManager.selectHead(item)"> <div class="main" @click="selectionManager.selectHead(item)">
<CheckCircle :size="18" class="select" /> <CheckCircle :size="18" class="select" v-if="item.name" />
<span class="name" > {{ item.name || getHeadName(item) }} </span> <span class="name" > {{ item.name || getHeadName(item) }} </span>
</div> </div>
</div> </div>
@ -516,9 +516,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
} }
// Special headers // Special headers
if (head.dayId === this.TagDayID.FOLDERS) { if (this.TagDayIDValueSet.has(head.dayId)) {
return (head.name = this.t("memories", "Folders"));
} else if (head.dayId === this.TagDayID.TAGS || head.dayId === this.TagDayID.FACES) {
return (head.name = ""); return (head.name = "");
} }
@ -637,8 +635,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
}; };
// Special headers // Special headers
if (day.dayid === this.TagDayID.TAGS || if (this.TagDayIDValueSet.has(day.dayid)) {
day.dayid === this.TagDayID.FACES) {
head.size = 10; head.size = 10;
} else if (this.$route.name === 'thisday' && (!prevDay || Math.abs(prevDay.dayid - day.dayid) > 30)) { } else if (this.$route.name === 'thisday' && (!prevDay || Math.abs(prevDay.dayid - day.dayid) > 30)) {
// thisday view with new year title // thisday view with new year title

View File

@ -26,7 +26,7 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Watch, Mixins, Emit } from 'vue-property-decorator'; import { Component, Prop, Watch, Mixins, Emit } from 'vue-property-decorator';
import { IPhoto, ITag } from '../../types'; import { IAlbum, IPhoto, ITag } from '../../types';
import { generateUrl } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
import { getPreviewUrl } from "../../services/FileUtils"; import { getPreviewUrl } from "../../services/FileUtils";
@ -77,6 +77,10 @@ export default class Tag extends Mixins(GlobalMixin) {
return this.data.flag & constants.c.FLAG_IS_FACE; return this.data.flag & constants.c.FLAG_IS_FACE;
} }
get isAlbum() {
return this.data.flag & constants.c.FLAG_IS_ALBUM;
}
async refreshPreviews() { async refreshPreviews() {
// Reset state // Reset state
this.error = false; this.error = false;
@ -87,6 +91,13 @@ export default class Tag extends Mixins(GlobalMixin) {
return; return;
} }
// Add preview from last photo if album
if (this.isAlbum) {
const album = this.data as IAlbum;
this.previews = [{ fileid: album.last_added_photo, etag: '', flag: 0 }];
return;
}
// Look for previews // Look for previews
if (!this.data.previews) return; if (!this.data.previews) return;

View File

@ -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 { IDay, IFileInfo, IPhoto, ITag } from '../types'; import { IAlbum, IDay, IFileInfo, IPhoto, ITag } from '../types';
import { constants, hashCode } from './Utils'; import { constants, hashCode } from './Utils';
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import client from './DavClient'; import client from './DavClient';
@ -557,5 +557,24 @@ export async function* removeFaceImages(user: string, name: string, fileIds: num
* Get list of albums and convert to Days response * Get list of albums and convert to Days response
*/ */
export async function getAlbumsData(): Promise<IDay[]> { export async function getAlbumsData(): Promise<IDay[]> {
return []; let data: IAlbum[] = [];
try {
const res = await axios.get<typeof data>(generateUrl('/apps/memories/api/albums'));
data = res.data;
} catch (e) {
throw e;
}
// Convert to days response
return [{
dayid: constants.TagDayID.ALBUMS,
count: data.length,
detail: data.map((album) => ({
...album,
fileid: album.album_id,
flag: constants.c.FLAG_IS_TAG & constants.c.FLAG_IS_ALBUM,
istag: true,
isalbum: true,
} as ITag)),
}]
} }

View File

@ -173,6 +173,10 @@ export function convertFlags(photo: IPhoto) {
photo.flag |= constants.c.FLAG_IS_TAG; photo.flag |= constants.c.FLAG_IS_TAG;
delete photo.istag; delete photo.istag;
} }
if (photo.isalbum) {
photo.flag |= constants.c.FLAG_IS_ALBUM;
delete photo.isalbum;
}
} }
// Outside for set // Outside for set
@ -181,6 +185,7 @@ const TagDayID = {
FOLDERS: -(1 << 30) + 1, FOLDERS: -(1 << 30) + 1,
TAGS: -(1 << 30) + 2, TAGS: -(1 << 30) + 2,
FACES: -(1 << 30) + 3, FACES: -(1 << 30) + 3,
ALBUMS: -(1 << 30) + 4,
} }
/** Global constants */ /** Global constants */
@ -193,8 +198,9 @@ export const constants = {
FLAG_IS_FOLDER: 1 << 4, FLAG_IS_FOLDER: 1 << 4,
FLAG_IS_TAG: 1 << 5, FLAG_IS_TAG: 1 << 5,
FLAG_IS_FACE: 1 << 6, FLAG_IS_FACE: 1 << 6,
FLAG_SELECTED: 1 << 7, FLAG_IS_ALBUM: 1 << 7,
FLAG_LEAVING: 1 << 8, FLAG_SELECTED: 1 << 8,
FLAG_LEAVING: 1 << 9,
}, },
TagDayID: TagDayID, TagDayID: TagDayID,

View File

@ -67,6 +67,8 @@ export type IPhoto = {
isfolder?: boolean; isfolder?: boolean;
/** Is this a tag */ /** Is this a tag */
istag?: boolean; istag?: boolean;
/** Is this an album */
isalbum?: boolean;
/** Is this a face */ /** Is this a face */
isface?: boolean; isface?: boolean;
/** Optional datetaken epoch */ /** Optional datetaken epoch */
@ -93,6 +95,19 @@ export interface ITag extends IPhoto {
previews?: IPhoto[]; previews?: IPhoto[];
} }
export interface IAlbum extends ITag {
/** ID of album */
album_id: number;
/** Owner of album */
user: string;
/** Created timestamp */
created: number;
/** Location string */
location: string;
/** File ID of last added photo */
last_added_photo: number;
}
export interface IFaceRect { export interface IFaceRect {
w: number; w: number;
h: number; h: number;