albums: add list
parent
294b3b8a0c
commit
50bb55536f
|
@ -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'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)),
|
||||||
|
}]
|
||||||
}
|
}
|
|
@ -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,
|
||||||
|
|
15
src/types.ts
15
src/types.ts
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue