refactor(large): disallow implicit any

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/877/head
Varun Patil 2023-10-11 22:56:53 -07:00
parent bcd16a9cb0
commit 313ba6c3c2
21 changed files with 119 additions and 87 deletions

13
package-lock.json generated
View File

@ -38,6 +38,7 @@
"@nextcloud/webpack-vue-config": "^6.0.0", "@nextcloud/webpack-vue-config": "^6.0.0",
"@playwright/test": "^1.39.0", "@playwright/test": "^1.39.0",
"@types/hammerjs": "^2.0.42", "@types/hammerjs": "^2.0.42",
"@types/justified-layout": "^4.1.1",
"@types/luxon": "^3.3.2", "@types/luxon": "^3.3.2",
"@types/url-parse": "^1.4.9", "@types/url-parse": "^1.4.9",
"@types/videojs-contrib-quality-levels": "^2.0.2", "@types/videojs-contrib-quality-levels": "^2.0.2",
@ -2534,6 +2535,12 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"peer": true "peer": true
}, },
"node_modules/@types/justified-layout": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/justified-layout/-/justified-layout-4.1.1.tgz",
"integrity": "sha512-/ZJGVeDif6EHRzK3kUifyOekGJcBXD1s/eRYAYgkJHI4QAkohz62E0PSMbFrGpOdTulPWRgOAh1mFZbYw9a9iQ==",
"dev": true
},
"node_modules/@types/leaflet": { "node_modules/@types/leaflet": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.0.tgz",
@ -13688,6 +13695,12 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"peer": true "peer": true
}, },
"@types/justified-layout": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/justified-layout/-/justified-layout-4.1.1.tgz",
"integrity": "sha512-/ZJGVeDif6EHRzK3kUifyOekGJcBXD1s/eRYAYgkJHI4QAkohz62E0PSMbFrGpOdTulPWRgOAh1mFZbYw9a9iQ==",
"dev": true
},
"@types/leaflet": { "@types/leaflet": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.0.tgz",

View File

@ -66,6 +66,7 @@
"@nextcloud/webpack-vue-config": "^6.0.0", "@nextcloud/webpack-vue-config": "^6.0.0",
"@playwright/test": "^1.39.0", "@playwright/test": "^1.39.0",
"@types/hammerjs": "^2.0.42", "@types/hammerjs": "^2.0.42",
"@types/justified-layout": "^4.1.1",
"@types/luxon": "^3.3.2", "@types/luxon": "^3.3.2",
"@types/url-parse": "^1.4.9", "@types/url-parse": "^1.4.9",
"@types/videojs-contrib-quality-levels": "^2.0.2", "@types/videojs-contrib-quality-levels": "^2.0.2",

View File

@ -278,7 +278,7 @@ export default defineComponent({
name: this.t('memories', 'Info'), name: this.t('memories', 'Info'),
icon: 'icon-details', icon: 'icon-details',
mount(el, fileInfo, context) { mount(el: HTMLElement, fileInfo: { id: string | number }, context: any) {
this.metadataComponent?.$destroy?.(); this.metadataComponent?.$destroy?.();
this.metadataComponent = new Vue({ this.metadataComponent = new Vue({
render: (h) => h(Metadata), render: (h) => h(Metadata),
@ -287,7 +287,7 @@ export default defineComponent({
this.metadataComponent.$mount(el); this.metadataComponent.$mount(el);
this.metadataComponent.$children[0].update(Number(fileInfo.id)); this.metadataComponent.$children[0].update(Number(fileInfo.id));
}, },
update(fileInfo) { update(fileInfo: { id: string | number }) {
this.metadataComponent.$children[0].update(Number(fileInfo.id)); this.metadataComponent.$children[0].update(Number(fileInfo.id));
}, },
destroy() { destroy() {

View File

@ -27,11 +27,24 @@ import './styles/global.scss';
// Global exposed variables // Global exposed variables
declare global { declare global {
var mode: 'admin' | 'user'; var __webpack_nonce__: string;
var __webpack_public_path__: string;
var vueroute: () => Route;
var OC: Nextcloud.Common.OC; var OC: Nextcloud.Common.OC;
var OCP: Nextcloud.Common.OCP; var OCP: Nextcloud.Common.OCP;
var OCA: {
Files?: {
Sidebar?: any;
App?: any;
};
Theming?: {
name: string;
enabledThemes: any[];
};
};
var mode: 'admin' | 'user';
var vueroute: () => Route;
var mModals: { var mModals: {
editMetadata: (photos: IPhoto[], sections?: number[]) => void; editMetadata: (photos: IPhoto[], sections?: number[]) => void;
@ -62,13 +75,12 @@ declare global {
var windowInnerWidth: number; // cache var windowInnerWidth: number; // cache
var windowInnerHeight: number; // cache var windowInnerHeight: number; // cache
var __webpack_nonce__: string;
var __webpack_public_path__: string;
var vidjs: typeof videojsType; var vidjs: typeof videojsType;
var Plyr: typeof PlyrType; var Plyr: typeof PlyrType;
var videoClientId: string; var videoClientId: string;
var videoClientIdPersistent: string; var videoClientIdPersistent: string;
var photoswipe: unknown;
} }
// Allow global access to the router // Allow global access to the router

View File

@ -113,7 +113,7 @@ export default defineComponent({
} }
}, },
async update(key: keyof ISystemConfig, value: any = null) { async update<K extends keyof ISystemConfig>(key: K, value: ISystemConfig[K] | null = null) {
if (!this.config?.hasOwnProperty(key)) { if (!this.config?.hasOwnProperty(key)) {
console.error('Unknown setting', key); console.error('Unknown setting', key);
return; return;
@ -121,7 +121,7 @@ export default defineComponent({
// Get final value // Get final value
value ??= this.config[key]; value ??= this.config[key];
this.config[key as string] = value; this.config[key] = value;
try { try {
this.loading++; this.loading++;

View File

@ -265,7 +265,7 @@ export default defineComponent({
}, },
}); });
this.currentSearchResults = response.data.ocs.data.map((collaborator) => { this.currentSearchResults = response.data.ocs.data.map((collaborator: any) => {
switch (collaborator.source) { switch (collaborator.source) {
case 'users': case 'users':
return { return {
@ -394,7 +394,7 @@ export default defineComponent({
}, 10000); }, 10000);
}, },
selectEntity(collaboratorKey) { selectEntity(collaboratorKey: string) {
if (this.selectedCollaboratorsKeys.includes(collaboratorKey)) { if (this.selectedCollaboratorsKeys.includes(collaboratorKey)) {
return; return;
} }
@ -403,7 +403,7 @@ export default defineComponent({
this.selectedCollaboratorsKeys.push(collaboratorKey); this.selectedCollaboratorsKeys.push(collaboratorKey);
}, },
unselectEntity(collaboratorKey) { unselectEntity(collaboratorKey: string) {
const index = this.selectedCollaboratorsKeys.indexOf(collaboratorKey); const index = this.selectedCollaboratorsKeys.indexOf(collaboratorKey);
if (index === -1) { if (index === -1) {

View File

@ -216,10 +216,7 @@ export default defineComponent({
this.loading = true; this.loading = true;
let album = { ...this.album }; let album = { ...this.album };
if (this.album.basename !== this.albumName) { if (this.album.basename !== this.albumName) {
album = await dav.renameAlbum(this.album, { album = await dav.renameAlbum(this.album, this.album.basename, this.albumName);
currentAlbumName: this.album.basename,
newAlbumName: this.albumName,
});
} }
if (this.album.location !== this.albumLocation) { if (this.album.location !== this.albumLocation) {
album.location = await dav.updateAlbum(this.album, { album.location = await dav.updateAlbum(this.album, {

View File

@ -23,14 +23,14 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { IPhoto } from '../../types'; import { IExif, IPhoto } from '../../types';
const NcTextField = () => import('@nextcloud/vue/dist/Components/NcTextField'); const NcTextField = () => import('@nextcloud/vue/dist/Components/NcTextField');
import { translate as t } from '@nextcloud/l10n'; import { translate as t } from '@nextcloud/l10n';
interface IField { interface IField {
field: string; field: keyof IExif;
label: string; label: string;
} }
@ -51,8 +51,8 @@ export default defineComponent({
}, },
data: () => ({ data: () => ({
exif: null! as Record<string, string>, exif: null as Record<keyof IExif, string> | null,
dirty: {} as Record<string, boolean>, dirty: {} as Record<keyof IExif, boolean>,
fields: [ fields: [
{ {
@ -87,26 +87,17 @@ export default defineComponent({
}), }),
mounted() { mounted() {
let exif = {}; const exif = {} as NonNullable<typeof this.exif>;
for (const field of this.fields) { for (const field of this.fields) {
exif[field.field] = null;
this.dirty[field.field] = false; this.dirty[field.field] = false;
}
const photos = this.photos as IPhoto[]; // Check if all photos have the same value for this field
for (const photo of photos) { const first = this.photos[0]?.imageInfo?.exif?.[field.field];
if (!photo.imageInfo?.exif) { if (this.photos.every((p) => p.imageInfo?.exif?.[field.field] === first)) {
continue; exif[field.field] = String(first ?? String());
} } else {
exif[field.field] = String();
for (const field of this.fields) {
const ePhoto = photo.imageInfo?.exif[field.field];
const eCurr = exif[field.field];
if (ePhoto && (eCurr === null || ePhoto === eCurr)) {
exif[field.field] = String(ePhoto);
} else {
exif[field.field] = String();
}
} }
} }
@ -115,10 +106,10 @@ export default defineComponent({
methods: { methods: {
result() { result() {
const diff = {}; const diff = {} as Record<keyof IExif, string>;
for (const field of this.fields) { for (const field of this.fields) {
if (this.dirty[field.field]) { if (this.dirty[field.field]) {
diff[field.field] = this.exif[field.field]; diff[field.field] = this.exif![field.field];
} }
} }
return diff; return diff;
@ -133,7 +124,7 @@ export default defineComponent({
}, },
reset(field: IField) { reset(field: IField) {
this.exif[field.field] = ''; this.exif![field.field] = '';
this.dirty[field.field] = false; this.dirty[field.field] = false;
}, },
}, },

View File

@ -23,7 +23,7 @@ import LandscapeIcon from '@scaleflex/icons/landscape';
import PortraitIcon from '@scaleflex/icons/portrait'; import PortraitIcon from '@scaleflex/icons/portrait';
import SquareIcon from '@scaleflex/icons/square'; import SquareIcon from '@scaleflex/icons/square';
let TABS, TOOLS: any; let TABS: any, TOOLS: any;
type FilerobotImageEditor = import('filerobot-image-editor').default; type FilerobotImageEditor = import('filerobot-image-editor').default;
let FilerobotImageEditor: typeof import('filerobot-image-editor').default; let FilerobotImageEditor: typeof import('filerobot-image-editor').default;
@ -151,7 +151,7 @@ export default defineComponent({
hasHighContrastEnabled(): boolean { hasHighContrastEnabled(): boolean {
const themes = globalThis.OCA?.Theming?.enabledThemes || []; const themes = globalThis.OCA?.Theming?.enabledThemes || [];
return themes.find((theme) => theme.indexOf('highcontrast') !== -1); return themes.find((theme: any) => theme.indexOf('highcontrast') !== -1);
}, },
themeDataAttr(): Record<string, boolean> { themeDataAttr(): Record<string, boolean> {
@ -209,7 +209,7 @@ export default defineComponent({
return img; return img;
}, },
onClose(closingReason, haveNotSavedChanges) { onClose(closingReason: any, haveNotSavedChanges: boolean) {
if (haveNotSavedChanges) { if (haveNotSavedChanges) {
this.onExitWithoutSaving(); this.onExitWithoutSaving();
return; return;
@ -303,7 +303,7 @@ export default defineComponent({
}, },
// Key Handlers, override default Viewer arrow and escape key // Key Handlers, override default Viewer arrow and escape key
handleKeydown(event) { handleKeydown(event: KeyboardEvent) {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
// escape key // escape key
if (event.key === 'Escape') { if (event.key === 'Escape') {

View File

@ -30,7 +30,7 @@ class LivePhotoContentSetup {
} }
} }
onContentLoad(e) { onContentLoad(e: PsEvent) {
const content: PsContent = e.content; const content: PsContent = e.content;
if (!isLiveContent(content)) return; if (!isLiveContent(content)) return;

View File

@ -30,7 +30,7 @@ type PsVideoEvent = PsEvent & {
* Check if slide has video content * Check if slide has video content
*/ */
export function isVideoContent(content: unknown): content is VideoContent { export function isVideoContent(content: unknown): content is VideoContent {
return typeof content === 'object' && content?.['data']?.['type'] === 'video'; return typeof content === 'object' && (<any>content)?.['data']?.['type'] === 'video';
} }
class VideoContentSetup { class VideoContentSetup {
@ -235,7 +235,7 @@ class VideoContentSetup {
playWithDelay(); playWithDelay();
}); });
content.videojs.qualityLevels?.()?.on('addqualitylevel', (e) => { content.videojs.qualityLevels?.()?.on('addqualitylevel', (e: any) => {
if (e.qualityLevel?.label?.includes('max.m3u8')) { if (e.qualityLevel?.label?.includes('max.m3u8')) {
// This is the highest quality level // This is the highest quality level
// and guaranteed to be the last one // and guaranteed to be the last one
@ -448,7 +448,7 @@ class VideoContentSetup {
this.destroyVideo(content); this.destroyVideo(content);
} }
onContentResize(e) { onContentResize(e: PsVideoEvent & { width: number; height: number }) {
if (isVideoContent(e.content)) { if (isVideoContent(e.content)) {
e.preventDefault(); e.preventDefault();
@ -461,12 +461,12 @@ class VideoContentSetup {
content.element.style.height = height + 'px'; content.element.style.height = height + 'px';
} }
if (content.slide && content.slide.placeholder) { // override placeholder size, so it more accurately matches the video
// override placeholder size, so it more accurately matches the video const phStyle = content.placeholder?.element?.style;
const placeholderElStyle = content.slide.placeholder.element.style; if (phStyle) {
placeholderElStyle.transform = 'none'; phStyle.transform = 'none';
placeholderElStyle.width = width + 'px'; phStyle.width = width + 'px';
placeholderElStyle.height = height + 'px'; phStyle.height = height + 'px';
} }
} }
} }

View File

@ -487,8 +487,10 @@ export default defineComponent({
globalThis.photoswipe = this.photoswipe; globalThis.photoswipe = this.photoswipe;
// Monkey patch for focus trapping in sidebar // Monkey patch for focus trapping in sidebar
const _onFocusIn = this.photoswipe.keyboard['_onFocusIn']; const psKeyboard = this.photoswipe.keyboard as any;
this.photoswipe.keyboard['_onFocusIn'] = (e: FocusEvent) => { const _onFocusIn = psKeyboard['_onFocusIn'];
console.assert(_onFocusIn, 'Missing _onFocusIn for monkey patch');
psKeyboard['_onFocusIn'] = (e: FocusEvent) => {
if ( if (
e.target instanceof HTMLElement && e.target instanceof HTMLElement &&
e.target.closest(['#app-sidebar-vue', '.v-popper__popper', '.modal-mask', '.oc-dialog'].join(',')) e.target.closest(['#app-sidebar-vue', '.v-popper__popper', '.modal-mask', '.oc-dialog'].join(','))

View File

@ -31,10 +31,10 @@ export default defineComponent({
methods: { methods: {
async refreshFromConfig() { async refreshFromConfig() {
const config = await staticConfig.getAll(); const config = await staticConfig.getAll();
const changed = Object.keys(config).filter((key) => config[key] !== this.config[key]); const changed = Object.keys(config).filter(<K extends keyof IConfig>(key: K) => config[key] !== this.config[key]);
if (changed.length === 0) return; if (changed.length === 0) return;
changed.forEach((key) => (this.config[key] = config[key])); changed.forEach(<K extends keyof IConfig>(key: K) => (this.config[key] = config[key]));
utils.bus.emit(eventName, null); utils.bus.emit(eventName, null);
}, },

View File

@ -190,4 +190,4 @@ export type NativeX = {
}; };
/** The native interface is a global object that is injected by the native app. */ /** The native interface is a global object that is injected by the native app. */
export const nativex: NativeX = globalThis.nativex; export const nativex: NativeX = (<any>globalThis).nativex;

View File

@ -212,7 +212,7 @@ export async function getAlbum(user: string, name: string, extraProps = {}) {
} }
/** Rename an album */ /** Rename an album */
export async function renameAlbum(album: any, { currentAlbumName, newAlbumName }) { export async function renameAlbum(album: any, currentAlbumName: string, newAlbumName: string) {
const newAlbum = { ...album, basename: newAlbumName }; const newAlbum = { ...album, basename: newAlbumName };
try { try {
await client.moveFile( await client.moveFile(

View File

@ -28,7 +28,7 @@ import { generateRemoteUrl } from '@nextcloud/router';
// Monkey business // Monkey business
import * as rq from 'webdav/dist/node/request'; import * as rq from 'webdav/dist/node/request';
const prepareRequestOptionsOld = rq.prepareRequestOptions.bind(rq); const prepareRequestOptionsOld = rq.prepareRequestOptions.bind(rq);
(<any>rq).prepareRequestOptions = (requestOptions, context, userOptions) => { (<any>rq).prepareRequestOptions = (requestOptions: any, context: any, userOptions: any) => {
requestOptions.method = userOptions.method || requestOptions.method; requestOptions.method = userOptions.method || requestOptions.method;
return prepareRequestOptionsOld(requestOptions, context, userOptions); return prepareRequestOptionsOld(requestOptions, context, userOptions);
}; };

View File

@ -48,9 +48,9 @@ class StaticConfig {
} }
// Assign to existing default // Assign to existing default
for (const key in this.config) { for (const k in this.config) {
this.default![key] = this.config[key]; const key = k as keyof IConfig;
this.setLs(key as keyof IConfig, this.config[key]); this.setLs(key, this.config[key]);
} }
// Resolve all promises // Resolve all promises
@ -141,17 +141,20 @@ class StaticConfig {
album_list_sort: 1, album_list_sort: 1,
}; };
for (const key in config) { const set = <K extends keyof IConfig, V extends IConfig[K]>(key: K, value: string | null) => {
const val = this.storage.getItem(`memories_${key}`); if (value == null) return;
if (val !== null) {
if (typeof config[key] === 'boolean') { if (typeof config[key] === 'boolean') {
config[key] = val === 'true'; config[key] = (value === 'true') as V;
} else if (typeof config[key] === 'number') { } else if (typeof config[key] === 'number') {
config[key] = Number(val); config[key] = Number(value) as V;
} else { } else {
config[key] = val; config[key] = value as V;
}
} }
};
for (const key in config) {
set(key as keyof IConfig, this.storage.getItem(`memories_${key}`));
} }
this.default = config; this.default = config;

View File

@ -81,7 +81,7 @@ export function randomSubarray<T>(arr: T[], size: number): T[] {
} }
/** Set a timer that renews if existing */ /** Set a timer that renews if existing */
export function setRenewingTimeout<T>(ctx: T, name: string, callback: (() => void) | null, delay: number): void { export function setRenewingTimeout(ctx: any, 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;

19
src/shims.d.ts vendored 100644
View File

@ -0,0 +1,19 @@
declare module '*.svg' {
const content: string;
export default content;
}
declare module '*.vue' {
import type { defineComponent } from 'vue';
const Component: ReturnType<typeof defineComponent>;
export default Component;
}
// External components cannot be imported with .vue extension
declare module '@nextcloud/vue/dist/Components/*' {
import type { defineComponent } from 'vue';
const Component: ReturnType<typeof defineComponent>;
export default Component;
}
declare module 'vue-virtual-scroller';

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

@ -1,10 +0,0 @@
declare module '*.vue' {
import type { defineComponent } from 'vue';
const Component: ReturnType<typeof defineComponent>;
export default Component;
}
declare module '*.svg' {
const content: string;
export default content;
}

View File

@ -1,6 +1,9 @@
{ {
"compilerOptions": { "compilerOptions": {
"lib": ["dom", "es2017"], "lib": [
"dom",
"es2017"
],
"target": "ES2017", "target": "ES2017",
"module": "es2020", "module": "es2020",
"moduleResolution": "node", "moduleResolution": "node",
@ -9,10 +12,11 @@
"jsx": "preserve", "jsx": "preserve",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true,
"esModuleInterop": true, "esModuleInterop": true,
"strictNullChecks": true "strictNullChecks": true
}, },
"vueCompilerOptions": { "vueCompilerOptions": {
"target": 2.7 "target": 2.7
} }
} }