refactor: improve typing

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/803/head
Varun Patil 2023-08-25 14:47:57 -07:00
parent fbcec52de4
commit 65cd1952ff
22 changed files with 95 additions and 69 deletions

View File

@ -40,8 +40,10 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import config from '../services/static-config'; import type { Component } from 'vue';
import axios from '@nextcloud/axios'; import axios from '@nextcloud/axios';
import { translate as t } from '@nextcloud/l10n';
import ClusterHList from './ClusterHList.vue'; import ClusterHList from './ClusterHList.vue';
@ -55,9 +57,10 @@ import CalendarIcon from 'vue-material-design-icons/Calendar.vue';
import MapIcon from 'vue-material-design-icons/Map.vue'; import MapIcon from 'vue-material-design-icons/Map.vue';
import CogIcon from 'vue-material-design-icons/Cog.vue'; import CogIcon from 'vue-material-design-icons/Cog.vue';
import type { ICluster, IConfig } from '../types'; import config from '../services/static-config';
import { API } from '../services/API'; import { API } from '../services/API';
import { translate as t } from '@nextcloud/l10n';
import type { ICluster, IConfig } from '../types';
export default defineComponent({ export default defineComponent({
name: 'Explore', name: 'Explore',
@ -110,7 +113,7 @@ export default defineComponent({
}, },
] as { ] as {
name: string; name: string;
icon: any; icon: Component;
link?: string; link?: string;
click?: () => void; click?: () => void;
}[], }[],

View File

@ -67,6 +67,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import type { Component } from 'vue';
import NcActions from '@nextcloud/vue/dist/Components/NcActions'; import NcActions from '@nextcloud/vue/dist/Components/NcActions';
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton'; import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton';
@ -97,7 +98,7 @@ interface TopField {
id?: string; id?: string;
title: string; title: string;
subtitle: string[]; subtitle: string[];
icon: any; icon: Component;
href?: string; href?: string;
edit?: () => void; edit?: () => void;
} }
@ -117,7 +118,7 @@ export default defineComponent({
data: () => ({ data: () => ({
fileid: null as number | null, fileid: null as number | null,
filename: '', filename: '',
exif: {} as { [prop: string]: any }, exif: {} as NonNullable<IImageInfo['exif']>,
baseInfo: {} as IImageInfo, baseInfo: {} as IImageInfo,
loading: 0, loading: 0,
@ -210,8 +211,8 @@ export default defineComponent({
// The fallback to datetaken can be eventually removed // The fallback to datetaken can be eventually removed
// and then this can be discarded // and then this can be discarded
if (this.exif.DateTimeEpoch) { if (this.exif.DateTimeEpoch) {
const tzOffset: string = this.exif.OffsetTimeOriginal || this.exif.OffsetTime; // e.g. -05:00 const tzOffset = this.exif.OffsetTimeOriginal || this.exif.OffsetTime; // e.g. -05:00
const tzId: string = this.exif.LocationTZID; // e.g. America/New_York const tzId = this.exif.LocationTZID; // e.g. America/New_York
let dateWithTz: DateTime | undefined = undefined; let dateWithTz: DateTime | undefined = undefined;

View File

@ -90,6 +90,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import type { Route } from 'vue-router';
import axios from '@nextcloud/axios'; import axios from '@nextcloud/axios';
import { showError } from '@nextcloud/dialogs'; import { showError } from '@nextcloud/dialogs';
@ -182,7 +183,7 @@ export default defineComponent({
}, },
watch: { watch: {
async $route(to: any, from?: any) { async $route(to: Route, from?: Route) {
await this.routeChange(to, from); await this.routeChange(to, from);
}, },
}, },
@ -228,7 +229,7 @@ export default defineComponent({
}, },
methods: { methods: {
async routeChange(to: any, from?: any) { async routeChange(to: Route, from?: Route) {
// Always do a hard refresh if the path changes // Always do a hard refresh if the path changes
if (from?.path !== to.path) { if (from?.path !== to.path) {
await this.refresh(); await this.refresh();
@ -444,7 +445,7 @@ export default defineComponent({
* This does NOT indicate the items have changed, only that * This does NOT indicate the items have changed, only that
* the pixel position of the recycler has changed. * the pixel position of the recycler has changed.
*/ */
scrollPositionChange(event?: any) { scrollPositionChange(event?: Event) {
this.scrollerManager().recyclerScrolled(event); this.scrollerManager().recyclerScrolled(event);
}, },
@ -549,40 +550,41 @@ export default defineComponent({
/** Get query string for API calls */ /** Get query string for API calls */
getQuery() { getQuery() {
const query: { [key: string]: string } = {}; const query: { [key in DaysFilterType]?: string } = {};
const set = (filter: DaysFilterType, value: string = '1') => (query[filter] = value);
// Favorites // Favorites
if (this.$route.name === 'favorites') { if (this.routeIsFavorites) {
API.DAYS_FILTER(query, DaysFilterType.FAVORITES); set(DaysFilterType.FAVORITES);
} }
// Videos // Videos
if (this.$route.name === 'videos') { if (this.routeIsVideos) {
API.DAYS_FILTER(query, DaysFilterType.VIDEOS); set(DaysFilterType.VIDEOS);
} }
// Folder // Folder
if (this.$route.name === 'folders') { if (this.routeIsFolders) {
const path = utils.getFolderRoutePath(this.config.folders_path); const path = utils.getFolderRoutePath(this.config.folders_path);
API.DAYS_FILTER(query, DaysFilterType.FOLDER, path); set(DaysFilterType.FOLDER, path);
if (this.$route.query.recursive) { if (this.$route.query.recursive) {
API.DAYS_FILTER(query, DaysFilterType.RECURSIVE); set(DaysFilterType.RECURSIVE);
} }
} }
// Archive // Archive
if (this.$route.name === 'archive') { if (this.routeIsArchive) {
API.DAYS_FILTER(query, DaysFilterType.ARCHIVE); set(DaysFilterType.ARCHIVE);
} }
// Albums // Albums
const user = <string>this.$route.params.user; const user = <string>this.$route.params.user;
const name = <string>this.$route.params.name; const name = <string>this.$route.params.name;
if (this.$route.name === 'albums') { if (this.routeIsAlbums) {
if (!user || !name) { if (!user || !name) {
throw new Error('Invalid album route'); throw new Error('Invalid album route');
} }
API.DAYS_FILTER(query, DaysFilterType.ALBUM, `${user}/${name}`); set(DaysFilterType.ALBUM, `${user}/${name}`);
} }
// People // People
@ -591,47 +593,48 @@ export default defineComponent({
throw new Error('Invalid album route'); throw new Error('Invalid album route');
} }
// name is "recognize" or "facerecognition"
const filter = <DaysFilterType>this.$route.name; const filter = <DaysFilterType>this.$route.name;
API.DAYS_FILTER(query, filter, `${user}/${name}`); set(filter, `${user}/${name}`);
// Face rect // Face rect
if (this.config.show_face_rect || this.routeIsRecognizeUnassigned) { if (this.config.show_face_rect || this.routeIsRecognizeUnassigned) {
API.DAYS_FILTER(query, DaysFilterType.FACE_RECT); set(DaysFilterType.FACE_RECT);
} }
} }
// Places // Places
if (this.$route.name === 'places') { if (this.routeIsPlaces) {
if (!name || !name.includes('-')) { if (!name || !name.includes('-')) {
throw new Error('Invalid place route'); throw new Error('Invalid place route');
} }
const id = <string>name.split('-', 1)[0]; const id = <string>name.split('-', 1)[0];
API.DAYS_FILTER(query, DaysFilterType.PLACE, id); set(DaysFilterType.PLACE, id);
} }
// Tags // Tags
if (this.$route.name === 'tags') { if (this.routeIsTags) {
if (!name) { if (!name) {
throw new Error('Invalid tag route'); throw new Error('Invalid tag route');
} }
API.DAYS_FILTER(query, DaysFilterType.TAG, name); set(DaysFilterType.TAG, name);
} }
// Map Bounds // Map Bounds
if (this.$route.name === 'map') { if (this.routeIsMap) {
const bounds = <string>this.$route.query.b; const bounds = <string>this.$route.query.b;
if (!bounds) { if (!bounds) {
throw new Error('Missing map bounds'); throw new Error('Missing map bounds');
} }
API.DAYS_FILTER(query, DaysFilterType.MAP_BOUNDS, bounds); set(DaysFilterType.MAP_BOUNDS, bounds);
} }
// Month view // Month view
if (this.isMonthView) { if (this.isMonthView) {
API.DAYS_FILTER(query, DaysFilterType.MONTH_VIEW); set(DaysFilterType.MONTH_VIEW);
API.DAYS_FILTER(query, DaysFilterType.REVERSE); set(DaysFilterType.REVERSE);
} }
return query; return query;

View File

@ -226,7 +226,7 @@ export default defineComponent({
}, },
/** Error in loading image */ /** Error in loading image */
error(e: any) { error(e: Event) {
this.data.flag |= this.c.FLAG_LOAD_FAIL; this.data.flag |= this.c.FLAG_LOAD_FAIL;
}, },

View File

@ -37,7 +37,7 @@ const expirationManager = new CacheExpiration(cacheName, {
}); });
// Start fetching with multipreview // Start fetching with multipreview
let fetchPreviewTimer: any; let fetchPreviewTimer: number;
/** Flushes the queue of preview image requests */ /** Flushes the queue of preview image requests */
async function flushPreviewQueue() { async function flushPreviewQueue() {

View File

@ -62,7 +62,7 @@ export default defineComponent({
this.$emit('close'); this.$emit('close');
}, },
done({ album }: any) { done({ album }: { album: { basename: string; filename: string } }) {
if (!this.album || album.basename !== this.album.basename) { if (!this.album || album.basename !== this.album.basename) {
const user = album.filename.split('/')[2]; const user = album.filename.split('/')[2];
const name = album.basename; const name = album.basename;

View File

@ -41,7 +41,7 @@ export default defineComponent({
}), }),
watch: { watch: {
$route: async function (from: any, to: any) { $route() {
this.refreshParams(); this.refreshParams();
}, },
}, },

View File

@ -163,7 +163,7 @@ export default defineComponent({
}, },
methods: { methods: {
submit(collaborators: any = []) { submit(collaborators: any[] = []) {
if (this.albumName === '' || this.loading) { if (this.albumName === '' || this.loading) {
return; return;
} }
@ -181,7 +181,7 @@ export default defineComponent({
} }
}, },
async handleCreateAlbum(collaborators = []) { async handleCreateAlbum(collaborators: any[] = []) {
try { try {
this.loading = true; this.loading = true;
let album = { let album = {

View File

@ -64,7 +64,7 @@ export default defineComponent({
}, },
methods: { methods: {
click($event: any, album: IAlbum) { click($event: Event, album: IAlbum) {
if (!this.link) { if (!this.link) {
$event.preventDefault(); $event.preventDefault();
} }

View File

@ -28,6 +28,11 @@ const NcTextField = () => import('@nextcloud/vue/dist/Components/NcTextField');
import { translate as t } from '@nextcloud/l10n'; import { translate as t } from '@nextcloud/l10n';
interface IField {
field: string;
label: string;
}
export default defineComponent({ export default defineComponent({
components: { components: {
NcTextField, NcTextField,
@ -41,8 +46,8 @@ export default defineComponent({
}, },
data: () => ({ data: () => ({
exif: null as any, exif: null! as Record<string, string>,
dirty: {}, dirty: {} as Record<string, boolean>,
fields: [ fields: [
{ {
@ -73,7 +78,7 @@ export default defineComponent({
field: 'Copyright', field: 'Copyright',
label: t('memories', 'Copyright'), label: t('memories', 'Copyright'),
}, },
], ] as IField[],
}), }),
mounted() { mounted() {
@ -95,7 +100,7 @@ export default defineComponent({
if (ePhoto && (eCurr === null || ePhoto === eCurr)) { if (ePhoto && (eCurr === null || ePhoto === eCurr)) {
exif[field.field] = String(ePhoto); exif[field.field] = String(ePhoto);
} else { } else {
exif[field.field] = ''; exif[field.field] = String();
} }
} }
} }
@ -114,15 +119,15 @@ export default defineComponent({
return diff; return diff;
}, },
label(field: any) { label(field: IField) {
return field.label + (this.dirty[field.field] ? '*' : ''); return field.label + (this.dirty[field.field] ? '*' : '');
}, },
placeholder(field: any) { placeholder(field: IField) {
return this.dirty[field.field] ? t('memories', 'Empty') : t('memories', 'Unchanged'); return this.dirty[field.field] ? t('memories', 'Empty') : t('memories', 'Unchanged');
}, },
reset(field: any) { reset(field: IField) {
this.exif[field.field] = ''; this.exif[field.field] = '';
this.dirty[field.field] = false; this.dirty[field.field] = false;
}, },

View File

@ -44,7 +44,7 @@ export default defineComponent({
}, },
watch: { watch: {
$route: async function (from: any, to: any) { $route() {
this.refreshParams(); this.refreshParams();
}, },
}, },

View File

@ -55,7 +55,7 @@ export default defineComponent({
}, },
watch: { watch: {
$route: async function (from: any, to: any) { $route() {
this.refreshParams(); this.refreshParams();
}, },
}, },

View File

@ -63,7 +63,7 @@ export default defineComponent({
}), }),
watch: { watch: {
$route: async function (from: any, to: any) { $route() {
this.refreshParams(); this.refreshParams();
}, },
}, },

View File

@ -135,7 +135,7 @@ export default defineComponent({
}, },
isLocal(): boolean { isLocal(): boolean {
return utils.isLocalPhoto(this.photo); return utils.isLocalPhoto(this.photo!);
}, },
}, },

View File

@ -7,6 +7,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { Component } from 'vue';
import UserMixin from '../../mixins/UserConfig'; import UserMixin from '../../mixins/UserConfig';
@ -23,7 +24,7 @@ export default defineComponent({
mixins: [UserMixin], mixins: [UserMixin],
computed: { computed: {
currentmatter(): any { currentmatter(): Component | null {
if (this.routeIsFolders) { if (this.routeIsFolders) {
return FolderDynamicTopMatter; return FolderDynamicTopMatter;
} else if (this.routeIsPlaces) { } else if (this.routeIsPlaces) {

View File

@ -340,7 +340,7 @@ export default defineComponent({
/** Is the current slide a local photo */ /** Is the current slide a local photo */
isLocal(): boolean { isLocal(): boolean {
return utils.isLocalPhoto(this.currentPhoto); return utils.isLocalPhoto(this.currentPhoto!);
}, },
/** Show bottom bar info such as date taken */ /** Show bottom bar info such as date taken */
@ -418,16 +418,16 @@ export default defineComponent({
}, },
/** User interacted with the page with mouse */ /** User interacted with the page with mouse */
setUiVisible(evt: any) { setUiVisible(event: PointerEvent | false) {
clearTimeout(this.activityTimer); clearTimeout(this.activityTimer);
if (evt) { if (event) {
// If directly triggered, always update ui visibility // If directly triggered, always update ui visibility
// If triggered through a pointer event, only update if this is not // If triggered through a pointer event, only update if this is not
// a touch event (i.e. a mouse move). // a touch event (i.e. a mouse move).
// On touch devices, tapAction directly handles the ui visibility // On touch devices, tapAction directly handles the ui visibility
// through Photoswipe. // through Photoswipe.
const isPointer = evt instanceof PointerEvent; const isPointer = event instanceof PointerEvent;
const isMouse = isPointer && evt.pointerType !== 'touch'; const isMouse = isPointer && event.pointerType !== 'touch';
if (this.isOpen && (!isPointer || isMouse)) { if (this.isOpen && (!isPointer || isMouse)) {
this.photoswipe?.template?.classList.add('pswp--ui-visible'); this.photoswipe?.template?.classList.add('pswp--ui-visible');

View File

@ -16,6 +16,12 @@ export default defineComponent({
routeIsBase(): boolean { routeIsBase(): boolean {
return this.$route.name === 'timeline'; return this.$route.name === 'timeline';
}, },
routeIsFavorites(): boolean {
return this.$route.name === 'favorites';
},
routeIsVideos(): boolean {
return this.$route.name === 'videos';
},
routeIsFolders(): boolean { routeIsFolders(): boolean {
return this.$route.name === 'folders'; return this.$route.name === 'folders';
}, },

View File

@ -76,10 +76,6 @@ export class API {
return tok(gen(`${BASE}/days/{id}`, { id })); return tok(gen(`${BASE}/days/{id}`, { id }));
} }
static DAYS_FILTER(query: any, filter: DaysFilterType, value: string = '1') {
query[filter] = value;
}
static FOLDERS_SUB() { static FOLDERS_SUB() {
return tok(gen(`${BASE}/folders/sub`)); return tok(gen(`${BASE}/folders/sub`));
} }

View File

@ -6,7 +6,7 @@
* @param elem Element to search for * @param elem Element to search for
* @param key Key to use for comparison * @param key Key to use for comparison
*/ */
export function binarySearch(arr: any, elem: any, key?: string) { export function binarySearch<T, K extends keyof T>(arr: T[], elem: T | T[K], key?: K): number {
if (arr.length === 0) return 0; if (arr.length === 0) return 0;
const desc = key ? arr[0][key] > arr[arr.length - 1][key] : arr[0] > arr[arr.length - 1]; const desc = key ? arr[0][key] > arr[arr.length - 1][key] : arr[0] > arr[arr.length - 1];
@ -56,7 +56,7 @@ export function roundHalf(num: number) {
} }
/** Choose a random element from an array */ /** Choose a random element from an array */
export function randomChoice(arr: any[]) { export function randomChoice<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)]; return arr[Math.floor(Math.random() * arr.length)];
} }
@ -64,7 +64,7 @@ export function randomChoice(arr: any[]) {
* Choose a random sub array from an array * Choose a random sub array from an array
* https://stackoverflow.com/a/11935263/4745239 * https://stackoverflow.com/a/11935263/4745239
*/ */
export function randomSubarray(arr: any[], size: number) { export function randomSubarray<T>(arr: T[], size: number): T[] {
if (arr.length <= size) return arr; if (arr.length <= size) return arr;
var shuffled = arr.slice(0), var shuffled = arr.slice(0),
i = arr.length, i = arr.length,
@ -81,7 +81,7 @@ export function randomSubarray(arr: any[], size: number) {
} }
/** Set a timer that renews if existing */ /** Set a timer that renews if existing */
export function setRenewingTimeout(ctx: any, name: string, callback: (() => void) | null, delay: number) { export function setRenewingTimeout<T>(ctx: T, name: string, callback: (() => void) | null, delay: number): void {
if (ctx[name]) window.clearTimeout(ctx[name]); if (ctx[name]) window.clearTimeout(ctx[name]);
ctx[name] = window.setTimeout(() => { ctx[name] = window.setTimeout(() => {
ctx[name] = 0; ctx[name] = 0;
@ -90,7 +90,7 @@ export function setRenewingTimeout(ctx: any, name: string, callback: (() => void
} }
/** Checks if a object is numeric */ /** Checks if a object is numeric */
export function isNumber(num: any): boolean { export function isNumber<T>(num: T): boolean {
const cast = Number(num); const cast = Number(num);
return !isNaN(cast) && isFinite(cast); return !isNaN(cast) && isFinite(cast);
} }

View File

@ -90,8 +90,8 @@ export function getPreviewUrl(opts: PreviewOptsSize | PreviewOptsMsize | Preview
* Check if the object is a local photo * Check if the object is a local photo
* @param photo Photo object * @param photo Photo object
*/ */
export function isLocalPhoto(photo: any): boolean { export function isLocalPhoto(photo: IPhoto): boolean {
return typeof photo === 'object' && photo?.fileid && Boolean((photo?.flag ?? 0) & constants.c.FLAG_IS_LOCAL); return Boolean(photo?.fileid) && Boolean((photo?.flag ?? 0) & constants.c.FLAG_IS_LOCAL);
} }
/** /**
@ -102,7 +102,7 @@ export function isLocalPhoto(photo: any): boolean {
export function getImageInfoUrl(photo: IPhoto | number): string { export function getImageInfoUrl(photo: IPhoto | number): string {
const fileid = typeof photo === 'number' ? photo : photo.fileid; const fileid = typeof photo === 'number' ? photo : photo.fileid;
if (isLocalPhoto(photo)) { if (typeof photo === 'object' && isLocalPhoto(photo)) {
return nativex.API.IMAGE_INFO(fileid); return nativex.API.IMAGE_INFO(fileid);
} }

View File

@ -111,8 +111,17 @@ export interface IImageInfo {
Orientation?: number; Orientation?: number;
ImageWidth?: number; ImageWidth?: number;
ImageHeight?: number; ImageHeight?: number;
Title?: string; Title?: string;
Description?: string; Description?: string;
Make?: string;
Model?: string;
DateTimeEpoch?: number;
OffsetTimeOriginal?: string;
OffsetTime?: string;
LocationTZID?: string;
[other: string]: unknown; [other: string]: unknown;
}; };

View File

@ -12,6 +12,8 @@ declare module 'vue' {
state_noDownload: boolean; state_noDownload: boolean;
routeIsBase: boolean; routeIsBase: boolean;
routeIsFavorites: boolean;
routeIsVideos: boolean;
routeIsFolders: boolean; routeIsFolders: boolean;
routeIsAlbums: boolean; routeIsAlbums: boolean;
routeIsPeople: boolean; routeIsPeople: boolean;