Skip to content

Commit

Permalink
Merge branch 'jovanbulck-albums-metadata'
Browse files Browse the repository at this point in the history
  • Loading branch information
pulsejet committed Sep 20, 2024
2 parents 05d68e8 + 8f379b9 commit 0390817
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 30 deletions.
6 changes: 6 additions & 0 deletions lib/ClustersBackend/AlbumsBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,14 @@ public function getClustersInternal(int $fileid = 0): array
);
};

// Transformation to select the shared flag
$sharedFlag = function (IQueryBuilder &$query): void {
$this->albumsQuery->transformSharedFlag($query);
};

// Transformations to apply to own albums
$transformOwned = [
$sharedFlag,
$materialize, $ownCover,
$materialize, $etag('last_added_photo'), $etag('cover'),
];
Expand Down
14 changes: 14 additions & 0 deletions lib/Db/AlbumsQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,20 @@ public function getAlbumPhotos(int $albumId, ?int $limit, ?int $fileid): array
return $result;
}

/**
* Query transformation to add a "shared" flag to the list
* of albums (whether the album has any shared collaborators).
*/
public function transformSharedFlag(IQueryBuilder &$query): void
{
$sSq = $query->getConnection()->getQueryBuilder();
$sSq->select($sSq->expr()->literal(1))
->from($this->collaboratorsTable(), 'pc')
->where($sSq->expr()->eq('pc.album_id', 'pa.album_id'))
;
$query->selectAlias(SQL::exists($query, $sSq), 'shared');
}

/**
* Get the various collaborator IDs that a user has.
* This includes the groups the user is in and the user itself.
Expand Down
16 changes: 1 addition & 15 deletions src/components/frame/Cluster.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,7 @@ export default defineComponent({
subtitle() {
if (dav.clusterIs.album(this.data)) {
let text: string;
if (this.data.count === 0) {
text = this.t('memories', 'No items');
} else {
text = this.n('memories', '{n} item', '{n} items', this.data.count, { n: this.data.count });
}
if (this.data.user !== utils.uid) {
const sharer = this.t('memories', 'Shared by {user}', {
user: this.data.user_display || this.data.user,
});
text = `${text} / ${sharer}`;
}
return text;
return dav.getAlbumSubtitle(this.data);
}
return String();
Expand Down
4 changes: 4 additions & 0 deletions src/components/modal/AlbumCreateModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Modal from './Modal.vue';
import ModalMixin from './ModalMixin';
import AlbumForm from './AlbumForm.vue';
import * as utils from '@services/utils';
import * as dav from '@services/dav';
export default defineComponent({
Expand Down Expand Up @@ -81,6 +82,9 @@ export default defineComponent({
} else {
await this.$router.replace(route);
}
} else {
// refresh timeline for metadata changes
utils.bus.emit('memories:timeline:soft-refresh', null);
}
},
},
Expand Down
4 changes: 4 additions & 0 deletions src/components/modal/AlbumShareModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import Modal from './Modal.vue';
import ModalMixin from './ModalMixin';
import AlbumCollaborators from './AlbumCollaborators.vue';
import * as utils from '@services/utils';
import * as dav from '@services/dav';
export default defineComponent({
Expand Down Expand Up @@ -151,6 +152,9 @@ export default defineComponent({
}
}
// Refresh timeline for metadata changes
utils.bus.emit('memories:timeline:soft-refresh', null);
// Close modal
await this.close();
} catch (error) {
Expand Down
13 changes: 2 additions & 11 deletions src/components/modal/AlbumsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js';
const NcListItem = () => import('@nextcloud/vue/dist/Components/NcListItem.js');
import * as utils from '@services/utils';
import * as dav from '@services/dav';
import type { IAlbum, IPhoto } from '@typings';
Expand Down Expand Up @@ -107,17 +108,7 @@ export default defineComponent({
},
getSubtitle(album: IAlbum) {
let text = this.n('memories', '%n item', '%n items', album.count);
if (album.user !== utils.uid) {
text +=
' / ' +
this.t('memories', 'Shared by {user}', {
user: album.user_display || album.user,
});
}
return text;
return dav.getAlbumSubtitle(album);
},
},
});
Expand Down
100 changes: 100 additions & 0 deletions src/components/top-matter/AlbumDynamicTopMatter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<template>
<div class="album-dtm">
<div v-if="album?.location" class="subtitle">
<MapMarkerOutlineIcon class="icon" :size="18" />
<span>{{ album.location }}</span>
</div>

<div class="avatars" v-if="album && (album?.collaborators.length ?? 0 > 1)">
<!-- Show own user only if we have other collaborators -->
<NcAvatar :user="utils.uid" :showUserStatus="false" />

<!-- Other collaborators -->
<template v-for="c of album.collaborators">
<!-- Links -->
<NcAvatar v-if="c.type === 3" :isNoUser="true">
<template #icon>
<LinkIcon :size="20" />
</template>
</NcAvatar>

<!-- Users and groups -->
<NcAvatar v-else :key="c.id" :user="c.id" :showUserStatus="false" />
</template>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import * as utils from '@services/utils';
import * as dav from '@services/dav';
const NcAvatar = () => import('@nextcloud/vue/dist/Components/NcAvatar.js');
import MapMarkerOutlineIcon from 'vue-material-design-icons/MapMarkerOutline.vue';
import LinkIcon from 'vue-material-design-icons/Link.vue';
export default defineComponent({
name: 'AlbumDynamicTopMatter',
components: {
NcAvatar,
MapMarkerOutlineIcon,
LinkIcon,
},
data: () => ({
album: null as dav.IDavAlbum | null,
utils: utils,
}),
methods: {
async refresh(): Promise<boolean> {
// Skip everything if user is not logged in
if (!utils.uid) return false;
// Skip if we are not on an album (e.g. on the list)
const { user, name } = this.$route.params;
if (!user || !name) return false;
// Get DAV album for collaborators
try {
this.album = await dav.getAlbum(user, name);
return true;
} catch (e) {
return false;
}
},
},
});
</script>

<style lang="scss" scoped>
.album-dtm {
> .subtitle {
font-size: 1.1em;
line-height: 1.2em;
margin-top: 0.5em;
color: var(--color-text-lighter);
display: flex;
padding-left: 10px;
.icon {
margin-right: 5px;
}
}
> .avatars {
line-height: 1.2em;
margin-top: 0.5em;
padding-left: 10px;
:deep .avatardiv {
margin-right: 2px;
vertical-align: bottom;
}
}
}
</style>
4 changes: 3 additions & 1 deletion src/components/top-matter/DynamicTopMatter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { defineComponent, type Component } from 'vue';
import UserMixin from '@mixins/UserConfig';
import AlbumDynamicTopMatter from './AlbumDynamicTopMatter.vue';
import FolderDynamicTopMatter from './FolderDynamicTopMatter.vue';
import PlacesDynamicTopMatterVue from './PlacesDynamicTopMatter.vue';
import OnThisDay from './OnThisDay.vue';
import * as strings from '@services/strings';
// Auto-hide top header on public shares if redundant
Expand All @@ -40,6 +40,8 @@ export default defineComponent({
return FolderDynamicTopMatter;
} else if (this.routeIsPlaces) {
return PlacesDynamicTopMatterVue;
} else if (this.routeIsAlbums) {
return AlbumDynamicTopMatter;
} else if (this.routeIsBase && this.config.enable_top_memories) {
return OnThisDay;
}
Expand Down
42 changes: 39 additions & 3 deletions src/services/dav/albums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@ import axios from '@nextcloud/axios';
import { showError } from '@nextcloud/dialogs';
import { getLanguage } from '@nextcloud/l10n';

import { translate as t } from '@services/l10n';
import { translate as t, translatePlural as n } from '@services/l10n';
import { API } from '@services/API';
import client from '@services/dav/client';
import staticConfig from '@services/static-config';
import * as utils from '@services/utils';

import type { IAlbum, IFileInfo, IPhoto } from '@typings';

export type IDavAlbum = {
location: string;
collaborators: {
id: string;
label: string;
type: number;
}[];
};

/**
* Get DAV path for album
*/
Expand Down Expand Up @@ -190,7 +199,7 @@ export async function updateAlbum(album: any, { albumName, properties }: any) {
* @param user Owner of album
* @param name Name of album (or ID)
*/
export async function getAlbum(user: string, name: string, extraProps = {}) {
export async function getAlbum(user: string, name: string, extraProps = {}): Promise<IDavAlbum> {
const req = `<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:"
xmlns:oc="http://owncloud.org/ns"
Expand All @@ -205,7 +214,7 @@ export async function getAlbum(user: string, name: string, extraProps = {}) {
${extraProps}
</d:prop>
</d:propfind>`;
let album = (await client.stat(`/photos/${user}/albums/${name}`, {
let album = (await client.stat(getAlbumPath(user, name), {
data: req,
details: true,
})) as any;
Expand All @@ -217,6 +226,12 @@ export async function getAlbum(user: string, name: string, extraProps = {}) {
};
const c = album?.collaborators?.collaborator;
album.collaborators = c ? (Array.isArray(c) ? c : [c]) : [];

// Sort collaborators by type
album.collaborators.sort((a: any, b: any) => {
return (a.type ?? -1) - (b.type ?? -1);
});

return album;
}

Expand Down Expand Up @@ -258,3 +273,24 @@ export function getAlbumFileInfos(photos: IPhoto[], albumUser: string, albumName
} as IFileInfo;
});
}

export function getAlbumSubtitle(album: IAlbum) {
let text: string;
if (album.count === 0) {
text = t('memories', 'No items');
} else {
text = n('memories', '{n} item', '{n} items', album.count, { n: album.count });
}

if (album.user !== utils.uid) {
const sharer = t('memories', 'Shared by {user}', {
user: album.user_display || album.user,
});
text = `${text} | ${sharer}`;
} else if (album.shared) {
const shared = t('memories', 'Shared Album');
text = `${text} | ${shared}`;
}

return text;
}
2 changes: 2 additions & 0 deletions src/typings/cluster.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ declare module '@typings' {
last_added_photo_etag: string;
/** Record ID of the latest update */
update_id: number;
/** Album is shared with other users */
shared: boolean;
}

export interface IFace extends ICluster {
Expand Down

0 comments on commit 0390817

Please sign in to comment.