Refactor getFiles to use photos
parent
06b5c2c29a
commit
49bf43e1f1
|
@ -276,7 +276,7 @@ export default class SelectionHandler extends Mixins(GlobalMixin, UserConfig) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
await dav.downloadFilesByIds(Array.from(selection.keys()));
|
||||
await dav.downloadFilesByIds(Array.from(selection.values()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -294,7 +294,7 @@ export default class SelectionHandler extends Mixins(GlobalMixin, UserConfig) {
|
|||
private async favoriteSelection(selection: Selection) {
|
||||
const val = !this.allSelectedFavorites(selection);
|
||||
for await (const favIds of dav.favoriteFilesByIds(
|
||||
Array.from(selection.keys()),
|
||||
Array.from(selection.values()),
|
||||
val
|
||||
)) {
|
||||
favIds.forEach((id) => {
|
||||
|
@ -330,8 +330,8 @@ export default class SelectionHandler extends Mixins(GlobalMixin, UserConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
for await (const delIds of dav.deleteFilesByIds(
|
||||
Array.from(selection.keys())
|
||||
for await (const delIds of dav.deletePhotos(
|
||||
Array.from(selection.values())
|
||||
)) {
|
||||
const delPhotos = delIds.map((id) => selection.get(id));
|
||||
this.deletePhotos(delPhotos);
|
||||
|
@ -353,7 +353,7 @@ export default class SelectionHandler extends Mixins(GlobalMixin, UserConfig) {
|
|||
if (selection.size !== 1) return;
|
||||
|
||||
const photo: IPhoto = selection.values().next().value;
|
||||
const f = await dav.getFiles([photo.fileid]);
|
||||
const f = await dav.getFiles([photo]);
|
||||
if (f.length === 0) return;
|
||||
|
||||
const file = f[0];
|
||||
|
@ -424,7 +424,11 @@ export default class SelectionHandler extends Mixins(GlobalMixin, UserConfig) {
|
|||
this.updateLoading(1);
|
||||
const user = this.$route.params.user;
|
||||
const name = this.$route.params.name;
|
||||
const gen = dav.removeFromAlbum(user, name, Array.from(selection.keys()));
|
||||
const gen = dav.removeFromAlbum(
|
||||
user,
|
||||
name,
|
||||
Array.from(selection.values())
|
||||
);
|
||||
for await (const delIds of gen) {
|
||||
const delPhotos = delIds
|
||||
.filter((p) => p)
|
||||
|
@ -481,7 +485,7 @@ export default class SelectionHandler extends Mixins(GlobalMixin, UserConfig) {
|
|||
for await (let delIds of dav.removeFaceImages(
|
||||
user,
|
||||
name,
|
||||
Array.from(selection.keys())
|
||||
Array.from(selection.values())
|
||||
)) {
|
||||
const delPhotos = delIds.filter((x) => x).map((id) => selection.get(id));
|
||||
this.deletePhotos(delPhotos);
|
||||
|
|
|
@ -125,32 +125,30 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Watch, Mixins } from "vue-property-decorator";
|
||||
import { IDay, IFolder, IHeadRow, IPhoto, IRow, IRowType } from "../types";
|
||||
import { generateUrl } from "@nextcloud/router";
|
||||
import axios from "@nextcloud/axios";
|
||||
import { showError } from "@nextcloud/dialogs";
|
||||
import { NcEmptyContent } from "@nextcloud/vue";
|
||||
import { subscribe, unsubscribe } from "@nextcloud/event-bus";
|
||||
import { generateUrl } from "@nextcloud/router";
|
||||
import { NcEmptyContent } from "@nextcloud/vue";
|
||||
import PeopleIcon from "vue-material-design-icons/AccountMultiple.vue";
|
||||
import CheckCircle from "vue-material-design-icons/CheckCircle.vue";
|
||||
import ImageMultipleIcon from "vue-material-design-icons/ImageMultiple.vue";
|
||||
import ArchiveIcon from "vue-material-design-icons/PackageDown.vue";
|
||||
import { Component, Mixins, Watch } from "vue-property-decorator";
|
||||
import GlobalMixin from "../mixins/GlobalMixin";
|
||||
import UserConfig from "../mixins/UserConfig";
|
||||
|
||||
import { ViewerManager } from "../services/Viewer";
|
||||
import { getLayout } from "../services/Layout";
|
||||
import * as dav from "../services/DavRequests";
|
||||
import { getLayout } from "../services/Layout";
|
||||
import * as utils from "../services/Utils";
|
||||
import axios from "@nextcloud/axios";
|
||||
import { ViewerManager } from "../services/Viewer";
|
||||
import { IDay, IFolder, IHeadRow, IPhoto, IRow, IRowType } from "../types";
|
||||
import Folder from "./frame/Folder.vue";
|
||||
import Tag from "./frame/Tag.vue";
|
||||
import Photo from "./frame/Photo.vue";
|
||||
import TopMatter from "./top-matter/TopMatter.vue";
|
||||
import OnThisDay from "./top-matter/OnThisDay.vue";
|
||||
import SelectionManager from "./SelectionManager.vue";
|
||||
import Tag from "./frame/Tag.vue";
|
||||
import ScrollerManager from "./ScrollerManager.vue";
|
||||
|
||||
import ArchiveIcon from "vue-material-design-icons/PackageDown.vue";
|
||||
import CheckCircle from "vue-material-design-icons/CheckCircle.vue";
|
||||
import PeopleIcon from "vue-material-design-icons/AccountMultiple.vue";
|
||||
import ImageMultipleIcon from "vue-material-design-icons/ImageMultiple.vue";
|
||||
import SelectionManager from "./SelectionManager.vue";
|
||||
import OnThisDay from "./top-matter/OnThisDay.vue";
|
||||
import TopMatter from "./top-matter/TopMatter.vue";
|
||||
|
||||
const SCROLL_LOAD_DELAY = 100; // Delay in loading data when scrolling
|
||||
const DESKTOP_ROW_HEIGHT = 200; // Height of row on desktop
|
||||
|
|
|
@ -61,11 +61,7 @@ export default class AddToAlbumModal extends Mixins(GlobalMixin) {
|
|||
|
||||
public async selectAlbum(album: IAlbum) {
|
||||
const name = album.name || album.album_id.toString();
|
||||
const gen = dav.addToAlbum(
|
||||
album.user,
|
||||
name,
|
||||
this.photos.map((p) => p.fileid)
|
||||
);
|
||||
const gen = dav.addToAlbum(album.user, name, this.photos);
|
||||
this.processing = true;
|
||||
|
||||
for await (const fids of gen) {
|
||||
|
|
|
@ -105,7 +105,7 @@ export default class FaceMoveModal extends Mixins(GlobalMixin) {
|
|||
photoMap.set(photo.fileid, photo);
|
||||
}
|
||||
|
||||
let data = await dav.getFiles(this.photos.map((p) => p.fileid));
|
||||
let data = await dav.getFiles(this.photos);
|
||||
|
||||
// Create move calls
|
||||
const calls = data.map((p) => async () => {
|
||||
|
|
|
@ -27,6 +27,12 @@ import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
|
|||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
|
||||
// Global exposed variables
|
||||
declare global {
|
||||
var vuerouter: typeof router;
|
||||
}
|
||||
globalThis.vuerouter = router;
|
||||
|
||||
Vue.use(VueVirtualScroller);
|
||||
|
||||
// https://github.com/nextcloud/photos/blob/156f280c0476c483cb9ce81769ccb0c1c6500a4e/src/main.js
|
||||
|
|
|
@ -35,17 +35,9 @@ export class ViewerManager {
|
|||
|
||||
// Get file infos
|
||||
let fileInfos: IFileInfo[];
|
||||
const ids = list.map((p) => p.fileid);
|
||||
try {
|
||||
this.updateLoading(1);
|
||||
|
||||
if (this.$route.name === "albums") {
|
||||
const user = this.$route.params.user;
|
||||
const name = this.$route.params.name;
|
||||
fileInfos = dav.getAlbumFileInfos(list, user, name);
|
||||
} else {
|
||||
fileInfos = await dav.getFiles(ids);
|
||||
}
|
||||
fileInfos = await dav.getFiles(list);
|
||||
} catch (e) {
|
||||
console.error("Failed to load fileInfos", e);
|
||||
showError("Failed to load fileInfos");
|
||||
|
@ -59,8 +51,8 @@ export class ViewerManager {
|
|||
|
||||
// Fix sorting of the fileInfos
|
||||
const itemPositions = {};
|
||||
for (const [index, id] of ids.entries()) {
|
||||
itemPositions[id] = index;
|
||||
for (const [index, p] of list.entries()) {
|
||||
itemPositions[p.fileid] = index;
|
||||
}
|
||||
fileInfos.sort(function (a, b) {
|
||||
return itemPositions[a.fileid] - itemPositions[b.fileid];
|
||||
|
|
|
@ -3,7 +3,14 @@ import { getCurrentUser } from "@nextcloud/auth";
|
|||
import { generateUrl } from "@nextcloud/router";
|
||||
import { showError } from "@nextcloud/dialogs";
|
||||
import { translate as t, translatePlural as n } from "@nextcloud/l10n";
|
||||
import { IAlbum, IDay, IFileInfo, IPhoto, ITag } from "../../types";
|
||||
import {
|
||||
IAlbum,
|
||||
IDay,
|
||||
IExtendedPhoto,
|
||||
IFileInfo,
|
||||
IPhoto,
|
||||
ITag,
|
||||
} from "../../types";
|
||||
import { constants } from "../Utils";
|
||||
import axios from "@nextcloud/axios";
|
||||
import client from "../DavClient";
|
||||
|
@ -60,16 +67,16 @@ export async function getAlbumsData(type: "1" | "2" | "3"): Promise<IDay[]> {
|
|||
*
|
||||
* @param user User ID of album
|
||||
* @param name Name of album (or ID)
|
||||
* @param fileIds List of file IDs to add
|
||||
* @param photos List of photos to add
|
||||
* @returns Generator
|
||||
*/
|
||||
export async function* addToAlbum(
|
||||
user: string,
|
||||
name: string,
|
||||
fileIds: number[]
|
||||
photos: IPhoto[]
|
||||
) {
|
||||
// Get files data
|
||||
let fileInfos = await base.getFiles(fileIds.filter((f) => f));
|
||||
let fileInfos = await base.getFiles(photos);
|
||||
|
||||
const albumPath = getAlbumPath(user, name);
|
||||
|
||||
|
@ -101,16 +108,16 @@ export async function* addToAlbum(
|
|||
*
|
||||
* @param user Owner of album
|
||||
* @param name Name of album (or ID)
|
||||
* @param fileIds List of file IDs to remove
|
||||
* @param photos List of photos to remove
|
||||
* @returns Generator
|
||||
*/
|
||||
export async function* removeFromAlbum(
|
||||
user: string,
|
||||
name: string,
|
||||
fileIds: number[]
|
||||
photos: IPhoto[]
|
||||
) {
|
||||
// Get files data
|
||||
let fileInfos = await base.getFiles(fileIds.filter((f) => f));
|
||||
let fileInfos = await base.getFiles(photos);
|
||||
|
||||
// Add each file
|
||||
const calls = fileInfos.map((f) => async () => {
|
||||
|
@ -263,25 +270,27 @@ export function getAlbumFileInfos(
|
|||
albumUser: string,
|
||||
albumName: string
|
||||
): IFileInfo[] {
|
||||
const ephotos = photos as IExtendedPhoto[];
|
||||
|
||||
const uid = getCurrentUser()?.uid;
|
||||
const collection =
|
||||
albumUser === uid
|
||||
? `/photos/${uid}/albums/${albumName}`
|
||||
: `/photos/${uid}/sharedalbums/${albumName} (${albumUser})`;
|
||||
|
||||
return photos.map((photo) => {
|
||||
return ephotos.map((photo) => {
|
||||
const basename =
|
||||
albumUser === uid
|
||||
? `${photo.fileid}-${(<any>photo).basename}`
|
||||
? `${photo.fileid}-${photo.basename}`
|
||||
: `${photo.fileid}-${albumName} (${albumUser})`;
|
||||
|
||||
return {
|
||||
fileid: photo.fileid,
|
||||
filename: `${collection}/${basename}`,
|
||||
basename: basename,
|
||||
mime: (<any>photo).mimetype,
|
||||
mime: photo.mimetype,
|
||||
hasPreview: true,
|
||||
etag: photo.etag,
|
||||
};
|
||||
} as IFileInfo;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { getCurrentUser } from "@nextcloud/auth";
|
||||
import { showError } from "@nextcloud/dialogs";
|
||||
import { translate as t } from "@nextcloud/l10n";
|
||||
import { IFileInfo } from "../../types";
|
||||
import { IFileInfo, IPhoto } from "../../types";
|
||||
import client from "../DavClient";
|
||||
import { genFileInfo } from "../FileUtils";
|
||||
import { getAlbumFileInfos } from "./albums";
|
||||
|
||||
export const props = `
|
||||
<oc:fileid />
|
||||
|
@ -35,10 +36,19 @@ const GET_FILE_CHUNK_SIZE = 50;
|
|||
|
||||
/**
|
||||
* Get file infos for list of files given Ids
|
||||
* @param fileIds list of file ids
|
||||
* @param photos list of photos
|
||||
* @returns list of file infos
|
||||
*/
|
||||
export async function getFiles(fileIds: number[]): Promise<IFileInfo[]> {
|
||||
export async function getFiles(photos: IPhoto[]): Promise<IFileInfo[]> {
|
||||
// Check if albums
|
||||
const route = vuerouter.currentRoute;
|
||||
if (route.name === "albums") {
|
||||
return getAlbumFileInfos(photos, route.params.user, route.params.name);
|
||||
}
|
||||
|
||||
// Get file IDs array
|
||||
const fileIds = photos.map((photo) => photo.fileid);
|
||||
|
||||
// Divide fileIds into chunks of GET_FILE_CHUNK_SIZE
|
||||
const chunks = [];
|
||||
for (let i = 0; i < fileIds.length; i += GET_FILE_CHUNK_SIZE) {
|
||||
|
@ -155,22 +165,22 @@ export async function deleteFile(path: string) {
|
|||
/**
|
||||
* Delete all files in a given list of Ids
|
||||
*
|
||||
* @param fileIds list of file ids
|
||||
* @param photos list of photos to delete
|
||||
* @returns list of file ids that were deleted
|
||||
*/
|
||||
export async function* deleteFilesByIds(fileIds: number[]) {
|
||||
const fileIdsSet = new Set(fileIds);
|
||||
|
||||
if (fileIds.length === 0) {
|
||||
export async function* deletePhotos(photos: IPhoto[]) {
|
||||
if (photos.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileIdsSet = new Set(photos.map((p) => p.fileid));
|
||||
|
||||
// Get files data
|
||||
let fileInfos: any[] = [];
|
||||
let fileInfos: IFileInfo[] = [];
|
||||
try {
|
||||
fileInfos = await getFiles(fileIds.filter((f) => f));
|
||||
fileInfos = await getFiles(photos);
|
||||
} catch (e) {
|
||||
console.error("Failed to get file info for files to delete", fileIds, e);
|
||||
console.error("Failed to get file info for files to delete", photos, e);
|
||||
showError(t("memories", "Failed to delete files."));
|
||||
return;
|
||||
}
|
||||
|
@ -180,7 +190,7 @@ export async function* deleteFilesByIds(fileIds: number[]) {
|
|||
const calls = fileInfos.map((fileInfo) => async () => {
|
||||
try {
|
||||
await deleteFile(fileInfo.filename);
|
||||
return fileInfo.fileid as number;
|
||||
return fileInfo.fileid;
|
||||
} catch (error) {
|
||||
console.error("Failed to delete", fileInfo, error);
|
||||
showError(t("memories", "Failed to delete {fileName}.", fileInfo));
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as base from "./base";
|
|||
import { generateUrl } from "@nextcloud/router";
|
||||
import { showError } from "@nextcloud/dialogs";
|
||||
import { translate as t } from "@nextcloud/l10n";
|
||||
import { IPhoto } from "../../types";
|
||||
|
||||
/**
|
||||
* Download a file
|
||||
|
@ -39,16 +40,16 @@ export async function downloadFiles(fileNames: string[]): Promise<boolean> {
|
|||
|
||||
/**
|
||||
* Download the files given by the fileIds
|
||||
* @param fileIds list of file ids
|
||||
* @param photos list of photos
|
||||
*/
|
||||
export async function downloadFilesByIds(fileIds: number[]) {
|
||||
if (fileIds.length === 0) {
|
||||
export async function downloadFilesByIds(photos: IPhoto[]) {
|
||||
if (photos.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get files to download
|
||||
const fileInfos = await base.getFiles(fileIds);
|
||||
if (fileInfos.length !== fileIds.length) {
|
||||
const fileInfos = await base.getFiles(photos);
|
||||
if (fileInfos.length !== photos.length) {
|
||||
showError(t("memories", "Failed to download some files."));
|
||||
}
|
||||
if (fileInfos.length === 0) {
|
||||
|
|
|
@ -53,16 +53,16 @@ export async function getPeopleData(): Promise<IDay[]> {
|
|||
*
|
||||
* @param user User ID of face
|
||||
* @param name Name of face (or ID)
|
||||
* @param fileIds List of file IDs to remove
|
||||
* @param photos List of photos to remove
|
||||
* @returns Generator
|
||||
*/
|
||||
export async function* removeFaceImages(
|
||||
user: string,
|
||||
name: string,
|
||||
fileIds: number[]
|
||||
photos: IPhoto[]
|
||||
) {
|
||||
// Get files data
|
||||
let fileInfos = await base.getFiles(fileIds.filter((f) => f));
|
||||
let fileInfos = await base.getFiles(photos);
|
||||
|
||||
// Remove each file
|
||||
const calls = fileInfos.map((f) => async () => {
|
||||
|
|
|
@ -4,6 +4,7 @@ import { encodePath } from "@nextcloud/paths";
|
|||
import { showError } from "@nextcloud/dialogs";
|
||||
import { translate as t, translatePlural as n } from "@nextcloud/l10n";
|
||||
import axios from "@nextcloud/axios";
|
||||
import { IPhoto } from "../../types";
|
||||
|
||||
/**
|
||||
* Favorite a file
|
||||
|
@ -34,32 +35,33 @@ export async function favoriteFile(fileName: string, favoriteState: boolean) {
|
|||
/**
|
||||
* Favorite all files in a given list of Ids
|
||||
*
|
||||
* @param fileIds list of file ids
|
||||
* @param photos list of photos
|
||||
* @param favoriteState the new favorite state
|
||||
* @returns generator of lists of file ids that were state-changed
|
||||
*/
|
||||
export async function* favoriteFilesByIds(
|
||||
fileIds: number[],
|
||||
photos: IPhoto[],
|
||||
favoriteState: boolean
|
||||
) {
|
||||
const fileIdsSet = new Set(fileIds);
|
||||
|
||||
if (fileIds.length === 0) {
|
||||
if (photos.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get files data
|
||||
let fileInfos: any[] = [];
|
||||
try {
|
||||
fileInfos = await base.getFiles(fileIds.filter((f) => f));
|
||||
fileInfos = await base.getFiles(photos);
|
||||
} catch (e) {
|
||||
console.error("Failed to get file info", fileIds, e);
|
||||
console.error("Failed to get file info", photos, e);
|
||||
showError(t("memories", "Failed to favorite files."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileInfos.length !== photos.length) {
|
||||
showError(t("memories", "Failed to favorite some files."));
|
||||
}
|
||||
|
||||
// Favorite each file
|
||||
fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid));
|
||||
const calls = fileInfos.map((fileInfo) => async () => {
|
||||
try {
|
||||
await favoriteFile(fileInfo.filename, favoriteState);
|
||||
|
|
|
@ -17,6 +17,8 @@ export type IFileInfo = {
|
|||
favorite?: boolean;
|
||||
/** Vue flags */
|
||||
flag?: number;
|
||||
/** MIME type of file */
|
||||
mime?: string;
|
||||
};
|
||||
|
||||
export type IDay = {
|
||||
|
@ -77,6 +79,13 @@ export type IPhoto = {
|
|||
datetaken?: number;
|
||||
};
|
||||
|
||||
export interface IExtendedPhoto extends IPhoto {
|
||||
/** Base name of file */
|
||||
basename: string;
|
||||
/** Mime type of file */
|
||||
mimetype: string;
|
||||
}
|
||||
|
||||
export interface IFolder extends IPhoto {
|
||||
/** Path to folder */
|
||||
path: string;
|
||||
|
|
Loading…
Reference in New Issue