backport vue3 branch

vue3-2
Varun Patil 2022-12-10 09:58:30 -08:00
parent 555697a404
commit 3b8571ce92
28 changed files with 150 additions and 141 deletions

View File

@ -95,16 +95,17 @@ export default defineComponent({
data() {
return {
navItems: [],
metadataComponent: null as Metadata,
metadataComponent: null as any,
};
},
computed: {
ncVersion() {
ncVersion(): number {
const version = (<any>window.OC).config.version.split(".");
return Number(version[0]);
},
recognize() {
recognize(): string | boolean {
if (!this.config_recognizeEnabled) {
return false;
}
@ -115,7 +116,8 @@ export default defineComponent({
return t("memories", "People");
},
facerecognition() {
facerecognition(): string | boolean {
if (!this.config_facerecognitionInstalled) {
return false;
}
@ -126,16 +128,20 @@ export default defineComponent({
return t("memories", "People");
},
isFirstStart() {
isFirstStart(): boolean {
return this.config_timelinePath === "EMPTY";
},
showAlbums() {
showAlbums(): boolean {
return this.config_albumsEnabled;
},
removeOuterGap() {
removeOuterGap(): boolean {
return this.ncVersion >= 25;
},
showNavigation() {
showNavigation(): boolean {
return this.$route.name !== "folder-share";
},
},
@ -283,7 +289,7 @@ export default defineComponent({
doRouteChecks() {
if (this.$route.name === "folder-share") {
this.putFolderShareToken(this.$route.params.token);
this.putFolderShareToken(<string>this.$route.params.token);
}
},

View File

@ -67,6 +67,14 @@ import InfoIcon from "vue-material-design-icons/InformationOutline.vue";
import LocationIcon from "vue-material-design-icons/MapMarker.vue";
import { API } from "../services/API";
interface TopField {
title: string;
subtitle: string[];
icon: any;
href?: string;
edit?: () => void;
}
export default defineComponent({
name: "Metadata",
components: {
@ -94,14 +102,8 @@ export default defineComponent({
},
computed: {
topFields() {
let list: {
title: string;
subtitle: string[];
icon: any;
href?: string;
edit?: () => void;
}[] = [];
topFields(): TopField[] {
let list: TopField[] = [];
if (this.dateOriginal) {
list.push({
@ -152,7 +154,7 @@ export default defineComponent({
},
/** Date taken info */
dateOriginal() {
dateOriginal(): moment.Moment | null {
const dt = this.exif["DateTimeOriginal"] || this.exif["CreateDate"];
if (!dt) return null;
@ -162,12 +164,12 @@ export default defineComponent({
return m;
},
dateOriginalStr() {
dateOriginalStr(): string | null {
if (!this.dateOriginal) return null;
return utils.getLongDateStr(this.dateOriginal.toDate(), true);
},
dateOriginalTime() {
dateOriginalTime(): string[] | null {
if (!this.dateOriginal) return null;
// Try to get timezone
@ -182,7 +184,7 @@ export default defineComponent({
},
/** Camera make and model info */
camera() {
camera(): string | null {
const make = this.exif["Make"];
const model = this.exif["Model"];
if (!make || !model) return null;
@ -190,7 +192,7 @@ export default defineComponent({
return `${make} ${model}`;
},
cameraSub() {
cameraSub(): string[] | null {
const f = this.exif["FNumber"] || this.exif["Aperture"];
const s = this.shutterSpeed;
const len = this.exif["FocalLength"];
@ -205,7 +207,7 @@ export default defineComponent({
},
/** Convert shutter speed decimal to 1/x format */
shutterSpeed() {
shutterSpeed(): string | null {
const speed = Number(
this.exif["ShutterSpeedValue"] ||
this.exif["ShutterSpeed"] ||
@ -221,11 +223,11 @@ export default defineComponent({
},
/** Image info */
imageInfo() {
imageInfo(): string | null {
return this.fileInfo.basename || (<any>this.fileInfo).name;
},
imageInfoSub() {
imageInfoSub(): string[] | null {
let parts = [];
let mp = Number(this.exif["Megapixels"]);
@ -244,7 +246,7 @@ export default defineComponent({
return parts;
},
address() {
address(): string | null {
if (!this.lat || !this.lon) return null;
if (!this.nominatim) return this.t("memories", "Loading …");
@ -263,15 +265,15 @@ export default defineComponent({
}
},
lat() {
lat(): number {
return this.exif["GPSLatitude"];
},
lon() {
lon(): number {
return this.exif["GPSLongitude"];
},
mapUrl() {
mapUrl(): string | null {
const boxSize = 0.0075;
const bbox = [
this.lon - boxSize,
@ -283,7 +285,7 @@ export default defineComponent({
return `https://www.openstreetmap.org/export/embed.html?bbox=${bbox.join()}&marker=${m}`;
},
mapFullUrl() {
mapFullUrl(): string | null {
return `https://www.openstreetmap.org/?mlat=${this.lat}&mlon=${this.lon}#map=18/${this.lat}/${this.lon}`;
},
},

View File

@ -152,7 +152,7 @@ export default defineComponent({
},
/** Recycler scroll event, must be called by timeline */
recyclerScrolled() {
recyclerScrolled(event: Event | null) {
// This isn't a renewing timer, it's a scheduled task
if (this.scrollingRecyclerUpdateTimer) return;
this.scrollingRecyclerUpdateTimer = window.setTimeout(() => {
@ -510,7 +510,7 @@ export default defineComponent({
interactend() {
this.interacting = false;
this.recyclerScrolled(); // make sure final position is correct
this.recyclerScrolled(null); // make sure final position is correct
},
/** Update scroller is being used to scroll recycler */

View File

@ -840,8 +840,8 @@ export default defineComponent({
// Run query
for await (let delIds of dav.removeFaceImages(
user,
name,
<string>user,
<string>name,
Array.from(selection.values())
)) {
const delPhotos = delIds

View File

@ -86,7 +86,7 @@ export default defineComponent({
},
computed: {
pathSelTitle() {
pathSelTitle(): string {
return this.t("memories", "Choose Timeline Paths");
},
},

View File

@ -81,23 +81,14 @@
transform: `translate(${photo.dispX}px, ${photo.dispY}px`,
}"
>
<Folder
v-if="photo.flag & c.FLAG_IS_FOLDER"
:data="photo"
:key="photo.fileid"
/>
<Folder v-if="photo.flag & c.FLAG_IS_FOLDER" :data="photo" />
<Tag
v-else-if="photo.flag & c.FLAG_IS_TAG"
:data="photo"
:key="photo.fileid"
/>
<Tag v-else-if="photo.flag & c.FLAG_IS_TAG" :data="photo" />
<Photo
v-else
:data="photo"
:day="item.day"
:key="photo.fileid"
@select="selectionManager.selectPhoto"
@pointerdown="selectionManager.clickPhoto(photo, $event, index)"
@touchstart="
@ -230,15 +221,15 @@ export default defineComponent({
state: Math.random(),
/** Selection manager component */
selectionManager: null as SelectionManager & any,
selectionManager: null as InstanceType<typeof SelectionManager>,
/** Scroller manager component */
scrollerManager: null as ScrollerManager & any,
scrollerManager: null as InstanceType<typeof ScrollerManager>,
};
},
mounted() {
this.selectionManager = this.$refs.selectionManager;
this.scrollerManager = this.$refs.scrollerManager;
this.selectionManager = <any>this.$refs.selectionManager;
this.scrollerManager = <any>this.$refs.scrollerManager;
this.routeChange(this.$route);
},
@ -265,20 +256,22 @@ export default defineComponent({
},
computed: {
routeIsBase() {
routeIsBase(): boolean {
return this.$route.name === "timeline";
},
routeIsPeople() {
return ["recognize", "facerecognition"].includes(this.$route.name);
routeIsPeople(): boolean {
return ["recognize", "facerecognition"].includes(
<string>this.$route.name
);
},
routeIsArchive() {
routeIsArchive(): boolean {
return this.$route.name === "archive";
},
isMonthView() {
isMonthView(): boolean {
return this.$route.name === "albums";
},
/** Get view name for dynamic top matter */
viewName() {
viewName(): string {
switch (this.$route.name) {
case "timeline":
return this.t("memories", "Your Timeline");
@ -301,7 +294,7 @@ export default defineComponent({
return "";
}
},
emptyViewDescription() {
emptyViewDescription(): string {
switch (this.$route.name) {
case "facerecognition":
if (this.config_facerecognitionEnabled)
@ -655,7 +648,7 @@ export default defineComponent({
this.$route.params.name
) {
query.set(
this.$route.name, // "recognize" or "facerecognition"
<string>this.$route.name, // "recognize" or "facerecognition"
`${this.$route.params.user}/${this.$route.params.name}`
);
@ -667,15 +660,14 @@ export default defineComponent({
// Tags
if (this.$route.name === "tags" && this.$route.params.name) {
query.set("tag", this.$route.params.name);
query.set("tag", <string>this.$route.params.name);
}
// Albums
if (this.$route.name === "albums" && this.$route.params.name) {
query.set(
"album",
`${this.$route.params.user}/${this.$route.params.name}`
);
const user = <string>this.$route.params.user;
const name = <string>this.$route.params.name;
query.set("album", `${user}/${name}`);
}
// Month view
@ -718,7 +710,7 @@ export default defineComponent({
/** Fetch timeline main call */
async fetchDays(noCache = false) {
const url = API.Q(API.DAYS(), this.getQuery());
const cacheUrl = this.$route.name + url;
const cacheUrl = <string>this.$route.name + url;
// Try cache first
let cache: IDay[];

View File

@ -52,8 +52,8 @@ export default defineComponent({
if (edit) {
try {
this.album = await dav.getAlbum(
this.$route.params.user,
this.$route.params.name
<string>this.$route.params.user,
<string>this.$route.params.name
);
} catch (e) {
console.error(e);

View File

@ -78,8 +78,8 @@ export default defineComponent({
},
refreshParams() {
this.user = this.$route.params.user || "";
this.name = this.$route.params.name || "";
this.user = <string>this.$route.params.user || "";
this.name = <string>this.$route.params.name || "";
},
async save() {

View File

@ -136,6 +136,7 @@ export default defineComponent({
data() {
return {
collaborators: [],
showCollaboratorView: false,
albumName: "",
albumLocation: "",
@ -176,7 +177,7 @@ export default defineComponent({
},
methods: {
submit(collaborators = []) {
submit(collaborators: any = []) {
if (this.albumName === "" || this.loading) {
return;
}

View File

@ -19,7 +19,7 @@
<img
v-if="album.last_added_photo !== -1"
class="album__image"
:src="album.last_added_photo | toCoverUrl"
:src="toCoverUrl(album.last_added_photo)"
/>
<div v-else class="album__image album__image--placeholder">
<ImageMultiple :size="32" />
@ -85,18 +85,6 @@ export default defineComponent({
NcLoadingIcon,
},
filters: {
toCoverUrl(fileId: string) {
return getPreviewUrl(
{
fileid: Number(fileId),
} as IPhoto,
true,
256
);
},
},
data() {
return {
showAlbumCreationForm: false,
@ -110,6 +98,16 @@ export default defineComponent({
},
methods: {
toCoverUrl(fileId: string | number) {
return getPreviewUrl(
{
fileid: Number(fileId),
} as IPhoto,
true,
256
);
},
albumCreatedHandler() {
this.showAlbumCreationForm = false;
this.loadAlbums();

View File

@ -52,6 +52,7 @@ export default defineComponent({
album: null as any,
show: false,
loadingAddCollaborators: false,
collaborators: [] as any[],
};
},
@ -65,8 +66,8 @@ export default defineComponent({
async open() {
this.show = true;
this.loadingAddCollaborators = true;
const user = this.$route.params.user || "";
const name = this.$route.params.name || "";
const user = <string>this.$route.params.user || "";
const name = <string>this.$route.params.name || "";
this.album = await dav.getAlbum(user, name);
this.loadingAddCollaborators = false;
},

View File

@ -74,8 +74,8 @@ export default defineComponent({
},
refreshParams() {
this.user = this.$route.params.user || "";
this.name = this.$route.params.name || "";
this.user = <string>this.$route.params.user || "";
this.name = <string>this.$route.params.name || "";
},
async save() {

View File

@ -82,9 +82,9 @@ export default defineComponent({
},
refreshParams() {
this.user = this.$route.params.user || "";
this.name = this.$route.params.name || "";
this.oldName = this.$route.params.name || "";
this.user = <string>this.$route.params.user || "";
this.name = <string>this.$route.params.name || "";
this.oldName = <string>this.$route.params.name || "";
},
async save() {

View File

@ -26,7 +26,7 @@ export default defineComponent({
return {
user: "",
name: "",
detail: null as IPhoto[] | null,
detail: null as ITag[] | null,
};
},
@ -46,8 +46,8 @@ export default defineComponent({
},
async refreshParams() {
this.user = this.$route.params.user || "";
this.name = this.$route.params.name || "";
this.user = <string>this.$route.params.user || "";
this.name = <string>this.$route.params.name || "";
this.detail = null;
let data = [];

View File

@ -69,7 +69,7 @@ export default defineComponent({
},
computed: {
isRoot() {
isRoot(): boolean {
return this.folderPath === "/" || this.folderPath === "";
},
},

View File

@ -114,11 +114,11 @@ export default defineComponent({
},
computed: {
isAlbumList() {
isAlbumList(): boolean {
return !Boolean(this.$route.params.name);
},
canEditAlbum() {
canEditAlbum(): boolean {
return (
!this.isAlbumList && this.$route.params.user === getCurrentUser()?.uid
);
@ -137,7 +137,8 @@ export default defineComponent({
methods: {
createMatter() {
this.name = this.$route.params.name || this.t("memories", "Albums");
this.name =
<string>this.$route.params.name || this.t("memories", "Albums");
},
back() {
@ -146,7 +147,10 @@ export default defineComponent({
async downloadAlbum() {
const res = await axios.post(
API.ALBUM_DOWNLOAD(this.$route.params.user, this.$route.params.name)
API.ALBUM_DOWNLOAD(
<string>this.$route.params.user,
<string>this.$route.params.name
)
);
if (res.status === 200 && res.data.handle) {
downloadWithHandle(res.data.handle);

View File

@ -99,7 +99,7 @@ export default defineComponent({
methods: {
createMatter() {
this.name = this.$route.params.name || "";
this.name = <string>this.$route.params.name || "";
},
back() {

View File

@ -44,7 +44,7 @@ export default defineComponent({
methods: {
createMatter() {
this.name = this.$route.params.name || "";
this.name = <string>this.$route.params.name || "";
},
back() {

View File

@ -270,7 +270,7 @@ export default defineComponent({
computed: {
/** Number of buttons to show inline */
numInlineActions() {
numInlineActions(): number {
let base = 3;
if (this.canShare) base++;
if (this.canEdit) base++;
@ -283,17 +283,17 @@ export default defineComponent({
},
/** Route is public */
routeIsPublic() {
routeIsPublic(): boolean {
return this.$route.name === "folder-share";
},
/** Route is album */
routeIsAlbum() {
routeIsAlbum(): boolean {
return this.$route.name === "albums";
},
/** Get the currently open photo */
currentPhoto() {
currentPhoto(): IPhoto | null {
if (!this.list.length || !this.photoswipe) {
return null;
}
@ -305,31 +305,35 @@ export default defineComponent({
},
/** Is the current slide a video */
isVideo() {
return this.currentPhoto?.flag & this.c.FLAG_IS_VIDEO;
isVideo(): boolean {
return Boolean(this.currentPhoto?.flag & this.c.FLAG_IS_VIDEO);
},
/** Show bottom bar info such as date taken */
showBottomBar() {
return !this.isVideo && this.fullyOpened && this.currentPhoto?.imageInfo;
showBottomBar(): boolean {
return (
!this.isVideo &&
this.fullyOpened &&
Boolean(this.currentPhoto?.imageInfo)
);
},
/** Get date taken string */
currentDateTaken() {
currentDateTaken(): string | null {
const date = this.currentPhoto?.imageInfo?.datetaken;
if (!date) return null;
return utils.getLongDateStr(new Date(date * 1000), false, true);
},
/** Get download link for current photo */
currentDownloadLink() {
currentDownloadLink(): string | null {
return this.currentPhoto
? window.location.origin + getDownloadLink(this.currentPhoto)
: null;
},
/** Allow opening editor */
canEdit() {
canEdit(): boolean {
return (
this.currentPhoto?.mimetype?.startsWith("image/") &&
!this.currentPhoto.liveid
@ -337,7 +341,7 @@ export default defineComponent({
},
/** Does the browser support native share API */
canShare() {
canShare(): boolean {
return "share" in navigator && this.currentPhoto && !this.isVideo;
},
},
@ -547,14 +551,14 @@ export default defineComponent({
});
// Video support
new PsVideo(this.photoswipe, {
new PsVideo(<any>this.photoswipe, {
videoAttributes: { controls: "", playsinline: "", preload: "none" },
autoplay: true,
preventDragOffset: 40,
});
// Live photo support
new PsLivePhoto(this.photoswipe, {});
new PsLivePhoto(<any>this.photoswipe, {});
// Patch the close button to stop the slideshow
const _close = this.photoswipe.close.bind(this.photoswipe);

View File

@ -9,13 +9,14 @@ import UserConfig from "./mixins/UserConfig";
import App from "./App.vue";
import router from "./router";
import { Route } from "vue-router";
import { generateFilePath } from "@nextcloud/router";
import { getRequestToken } from "@nextcloud/auth";
import { IPhoto } from "./types";
// Global exposed variables
declare global {
var vuerouter: typeof router;
var vueroute: () => Route;
var OC: Nextcloud.v24.OC;
var OCP: Nextcloud.v24.OCP;
@ -35,7 +36,7 @@ declare global {
}
// Allow global access to the router
globalThis.vuerouter = router;
globalThis.vueroute = () => router.currentRoute;
// Cache these for better performance
globalThis.windowInnerWidth = window.innerWidth;

View File

@ -6,8 +6,10 @@ const gen = generateUrl;
/** Add auth token to this URL */
function tok(url: string) {
if (vuerouter.currentRoute.name === "folder-share") {
url = API.Q(url, `folder_share=${vuerouter.currentRoute.params.token}`);
const route = vueroute();
if (route.name === "folder-share") {
const token = <string>route.params.token;
url = API.Q(url, `folder_share=${token}`);
}
return url;
}

View File

@ -27,15 +27,11 @@ import { generateRemoteUrl } from "@nextcloud/router";
// Monkey business
import * as rq from "webdav/dist/node/request";
(<any>rq).prepareRequestOptionsOld = rq.prepareRequestOptions.bind(rq);
(<any>rq).prepareRequestOptions = function (
requestOptions,
context,
userOptions
) {
const prepareRequestOptionsOld = rq.prepareRequestOptions.bind(rq);
(<any>rq).prepareRequestOptions = (requestOptions, context, userOptions) => {
requestOptions.method = userOptions.method || requestOptions.method;
return this.prepareRequestOptionsOld(requestOptions, context, userOptions);
}.bind(rq);
return prepareRequestOptionsOld(requestOptions, context, userOptions);
};
// force our axios
const patcher = webdav.getPatcher();

View File

@ -235,7 +235,7 @@ export function convertFlags(photo: IPhoto) {
* This function does not check if this is the folder route
*/
export function getFolderRoutePath(basePath: string) {
let path: any = vuerouter.currentRoute.params.path || "/";
let path: any = vueroute().params.path || "/";
path = typeof path === "string" ? path : path.join("/");
path = basePath + "/" + path;
path = path.replace(/\/\/+/, "/"); // Remove double slashes

View File

@ -44,9 +44,13 @@ const GET_FILE_CHUNK_SIZE = 50;
*/
export async function getFiles(photos: IPhoto[]): Promise<IFileInfo[]> {
// Check if albums
const route = vuerouter.currentRoute;
const route = vueroute();
if (route.name === "albums") {
return getAlbumFileInfos(photos, route.params.user, route.params.name);
return getAlbumFileInfos(
photos,
<string>route.params.user,
<string>route.params.name
);
}
// Get file infos

View File

@ -47,9 +47,11 @@ export async function downloadFilesByPhotos(photos: IPhoto[]) {
/** Get URL to download one file (e.g. for video streaming) */
export function getDownloadLink(photo: IPhoto) {
const route = vueroute();
// Check if public
if (vuerouter.currentRoute.name === "folder-share") {
const token = window.vuerouter.currentRoute.params.token;
if (route.name === "folder-share") {
const token = <string>route.params.token;
// TODO: allow proper dav access without the need of basic auth
// https://github.com/nextcloud/server/issues/19700
return generateUrl(`/s/${token}/download?path={dirname}&files={basename}`, {
@ -59,12 +61,11 @@ export function getDownloadLink(photo: IPhoto) {
}
// Check if albums
const route = vuerouter.currentRoute;
if (route.name === "albums") {
const fInfos = getAlbumFileInfos(
[photo],
route.params.user,
route.params.name
<string>route.params.user,
<string>route.params.name
);
if (fInfos.length) {
return generateUrl(`/remote.php/dav${fInfos[0].originalFilename}`);

View File

@ -1,5 +1,3 @@
import { VueConstructor } from "vue";
export type IFileInfo = {
/** Same as fileid */
id: number;
@ -229,7 +227,7 @@ export type ISelectionAction = {
/** Display text */
name: string;
/** Icon component */
icon: VueConstructor;
icon: any;
/** Action to perform */
callback: (selection: Map<number, IPhoto>) => Promise<void>;
/** Condition to check for including */

3
src/vue-shims.d.ts vendored
View File

@ -4,7 +4,6 @@ declare module "*.vue" {
}
declare module "*.svg" {
import Vue, { VueConstructor } from "vue";
const content: VueConstructor<Vue>;
const content: any;
export default content;
}

View File

@ -6,8 +6,8 @@
"moduleResolution": "node",
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"jsx": "preserve"
"jsx": "preserve",
"useDefineForClassFields": true,
"esModuleInterop": true
}
}