refactor: Fix eslint warnings (#3698)

* Update dependencies and remove typescript version pinning (fixed upstream)

* Fix esling warnings (disabled any and script lang checks)
Rewrote clipboard copy (Fixes #3407)
Run prettier

---------

Co-authored-by: Oleg Lobanov <oleg@lobanov.me>
pull/3712/head
kloon15 2025-01-30 10:18:48 +01:00 committed by GitHub
parent cc331383fb
commit 0201f9c5c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 1085 additions and 1026 deletions

View File

@ -1,2 +1,3 @@
# Ignore artifacts: # Ignore artifacts:
dist dist
pnpm-lock.yaml

View File

@ -19,14 +19,13 @@ export default [
{ {
rules: { rules: {
"no-var": "warn",
"prefer-const": "warn",
// Note: you must disable the base rule as it can report incorrect errors // Note: you must disable the base rule as it can report incorrect errors
"no-unused-expressions": "off", "no-unused-expressions": "off",
"@typescript-eslint/no-unused-expressions": "warn", "@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-explicit-any": "warn", // TODO: theres too many of these from before ts
"@typescript-eslint/ban-ts-comment": "warn", "@typescript-eslint/no-explicit-any": "off",
"vue/block-lang": "warn", // TODO: finish the ts conversion
"vue/block-lang": "off",
"vue/multi-word-component-names": "off", "vue/multi-word-component-names": "off",
"vue/no-mutating-props": [ "vue/no-mutating-props": [
"error", "error",

View File

@ -19,64 +19,59 @@
}, },
"dependencies": { "dependencies": {
"@chenfengyuan/vue-number-input": "^2.0.1", "@chenfengyuan/vue-number-input": "^2.0.1",
"@vueuse/core": "^12.0.0", "@vueuse/core": "^12.5.0",
"@vueuse/integrations": "^12.0.0", "@vueuse/integrations": "^12.5.0",
"ace-builds": "^1.32.9", "ace-builds": "^1.37.5",
"core-js": "^3.36.1", "core-js": "^3.40.0",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"epubjs": "^0.3.93", "epubjs": "^0.3.93",
"filesize": "^10.1.1", "filesize": "^10.1.1",
"js-base64": "^3.7.7", "js-base64": "^3.7.7",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"marked": "^15.0.3", "marked": "^15.0.6",
"material-icons": "^1.13.12", "material-icons": "^1.13.13",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"pinia": "^2.1.7", "pinia": "^2.3.1",
"pretty-bytes": "^6.1.1", "pretty-bytes": "^6.1.1",
"qrcode.vue": "^3.4.1", "qrcode.vue": "^3.4.1",
"tus-js-client": "^4.1.0", "tus-js-client": "^4.3.1",
"utif": "^3.1.0", "utif": "^3.1.0",
"video.js": "^8.10.0", "video.js": "^8.21.0",
"videojs-hotkeys": "^0.2.28", "videojs-hotkeys": "^0.2.28",
"videojs-mobile-ui": "^1.1.1", "videojs-mobile-ui": "^1.1.1",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-final-modal": "^4.5.4", "vue-final-modal": "^4.5.4",
"vue-i18n": "^10.0.5", "vue-i18n": "^11.0.1",
"vue-lazyload": "^3.0.0", "vue-lazyload": "^3.0.0",
"vue-reader": "^1.2.14", "vue-reader": "^1.2.17",
"vue-router": "^4.3.0", "vue-router": "^4.3.0",
"vue-toastification": "^2.0.0-rc.5" "vue-toastification": "^2.0.0-rc.5"
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "^6.0.0", "@intlify/unplugin-vue-i18n": "^6.0.3",
"@playwright/test": "^1.42.1", "@playwright/test": "^1.50.0",
"@tsconfig/node22": "^22.0.0", "@tsconfig/node22": "^22.0.0",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^22.10.1", "@types/node": "^22.10.10",
"@typescript-eslint/eslint-plugin": "^8.17.0", "@typescript-eslint/eslint-plugin": "^8.21.0",
"@vitejs/plugin-legacy": "^6.0.0", "@vitejs/plugin-legacy": "^6.0.0",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-prettier": "^10.1.0", "@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.1.4", "@vue/eslint-config-typescript": "^14.3.0",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"concurrently": "^9.1.0", "concurrently": "^9.1.2",
"eslint": "^9.16.0", "eslint": "^9.19.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-vue": "^9.24.0", "eslint-plugin-vue": "^9.24.0",
"jsdom": "^25.0.1", "jsdom": "^26.0.0",
"postcss": "^8.4.38", "postcss": "^8.5.1",
"prettier": "^3.2.5", "prettier": "^3.4.2",
"terser": "^5.30.0", "terser": "^5.37.0",
"vite": "^6.0.2", "vite": "^6.0.11",
"vite-plugin-compression2": "^1.0.0", "vite-plugin-compression2": "^1.0.0",
"vue-tsc": "^2.0.7" "vue-tsc": "^2.2.0"
}, },
"pnpm": { "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0"
"overrides": {
"typescript": "~5.6.3"
}
},
"packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab"
} }

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ export async function create(user: IUser) {
throw new StatusError(await res.text(), res.status); throw new StatusError(await res.text(), res.status);
} }
export async function update(user: IUser, which = ["all"]) { export async function update(user: Partial<IUser>, which = ["all"]) {
await fetchURL(`/api/users/${user.id}`, { await fetchURL(`/api/users/${user.id}`, {
method: "PUT", method: "PUT",
body: JSON.stringify({ body: JSON.stringify({

View File

@ -34,7 +34,7 @@ const props = defineProps<{
const items = computed(() => { const items = computed(() => {
const relativePath = route.path.replace(props.base, ""); const relativePath = route.path.replace(props.base, "");
let parts = relativePath.split("/"); const parts = relativePath.split("/");
if (parts[0] === "") { if (parts[0] === "") {
parts.shift(); parts.shift();
@ -44,7 +44,7 @@ const items = computed(() => {
parts.pop(); parts.pop();
} }
let breadcrumbs: BreadCrumb[] = []; const breadcrumbs: BreadCrumb[] = [];
for (let i = 0; i < parts.length; i++) { for (let i = 0; i < parts.length; i++) {
if (i === 0) { if (i === 0) {

View File

@ -46,7 +46,7 @@ https://raw.githubusercontent.com/dzwillia/vue-simple-progress/master/src/compon
<script> <script>
// We're leaving this untouched as you can read in the beginning // We're leaving this untouched as you can read in the beginning
var isNumber = function (n) { const isNumber = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n); return !isNaN(parseFloat(n)) && isFinite(n);
}; };
@ -107,7 +107,7 @@ export default {
}, },
computed: { computed: {
pct() { pct() {
var pct = (this.val / this.max) * 100; let pct = (this.val / this.max) * 100;
pct = pct.toFixed(2); pct = pct.toFixed(2);
return Math.min(pct, this.max); return Math.min(pct, this.max);
}, },
@ -160,7 +160,7 @@ export default {
return isNumber(this.fontSize) ? this.fontSize : 13; return isNumber(this.fontSize) ? this.fontSize : 13;
}, },
progress_style() { progress_style() {
var style = { const style = {
background: this.bgColor, background: this.bgColor,
}; };
@ -177,7 +177,7 @@ export default {
return style; return style;
}, },
bar_style() { bar_style() {
var style = { const style = {
background: this.barColor, background: this.barColor,
width: this.pct + "%", width: this.pct + "%",
height: this.size_px + "px", height: this.size_px + "px",
@ -198,7 +198,7 @@ export default {
return style; return style;
}, },
text_style() { text_style() {
var style = { const style = {
color: this.textFgColor, color: this.textFgColor,
"font-size": this.text_font_size + "px", "font-size": this.text_font_size + "px",
"text-align": this.textAlign, "text-align": this.textAlign,

View File

@ -163,7 +163,7 @@ export default {
this.canInput = false; this.canInput = false;
event.target.innerHTML = ""; event.target.innerHTML = "";
let results = { const results = {
text: `${cmd}\n\n`, text: `${cmd}\n\n`,
}; };
@ -180,7 +180,7 @@ export default {
}, },
() => { () => {
results.text = results.text results.text = results.text
// eslint-disable-next-line no-control-regex
.replace(/\u001b\[[0-9;]+m/g, "") // Filter ANSI color for now .replace(/\u001b\[[0-9;]+m/g, "") // Filter ANSI color for now
.trimEnd(); .trimEnd();
this.canInput = true; this.canInput = true;

View File

@ -158,7 +158,7 @@ export default {
methods: { methods: {
...mapActions(useLayoutStore, ["closeHovers", "showHover"]), ...mapActions(useLayoutStore, ["closeHovers", "showHover"]),
async fetchUsage() { async fetchUsage() {
let path = this.$route.path.endsWith("/") const path = this.$route.path.endsWith("/")
? this.$route.path ? this.$route.path
: this.$route.path + "/"; : this.$route.path + "/";
let usageStats = USAGE_DEFAULT; let usageStats = USAGE_DEFAULT;
@ -166,7 +166,7 @@ export default {
return Object.assign(this.usage, usageStats); return Object.assign(this.usage, usageStats);
} }
try { try {
let usage = await api.usage(path); const usage = await api.usage(path);
usageStats = { usageStats = {
used: prettyBytes(usage.used, { binary: true }), used: prettyBytes(usage.used, { binary: true }),
total: prettyBytes(usage.total, { binary: true }), total: prettyBytes(usage.total, { binary: true }),

View File

@ -102,10 +102,11 @@ const decodeUTIF = () => {
if (document?.location?.pathname === undefined) { if (document?.location?.pathname === undefined) {
return; return;
} }
let suff = document.location.pathname.split(".")?.pop()?.toLowerCase() ?? ""; const suff =
document.location.pathname.split(".")?.pop()?.toLowerCase() ?? "";
if (sufs.indexOf(suff) == -1) return false; if (sufs.indexOf(suff) == -1) return false;
let xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
UTIF._xhrs.push(xhr); UTIF._xhrs.push(xhr);
UTIF._imgs.push(imgex.value); UTIF._imgs.push(imgex.value);
xhr.open("GET", props.src); xhr.open("GET", props.src);
@ -230,7 +231,7 @@ const touchMove = (event: TouchEvent) => {
if (imgex.value === null) { if (imgex.value === null) {
return; return;
} }
let step = imgex.value.width / 5; const step = imgex.value.width / 5;
if (event.targetTouches.length === 2) { if (event.targetTouches.length === 2) {
moveDisabled.value = true; moveDisabled.value = true;
if (disabledTimer.value) clearTimeout(disabledTimer.value); if (disabledTimer.value) clearTimeout(disabledTimer.value);
@ -239,9 +240,9 @@ const touchMove = (event: TouchEvent) => {
props.moveDisabledTime props.moveDisabledTime
); );
let p1 = event.targetTouches[0]; const p1 = event.targetTouches[0];
let p2 = event.targetTouches[1]; const p2 = event.targetTouches[1];
let touchDistance = Math.sqrt( const touchDistance = Math.sqrt(
Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2) Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)
); );
if (!lastTouchDistance.value) { if (!lastTouchDistance.value) {
@ -253,8 +254,8 @@ const touchMove = (event: TouchEvent) => {
setZoom(); setZoom();
} else if (event.targetTouches.length === 1) { } else if (event.targetTouches.length === 1) {
if (moveDisabled.value) return; if (moveDisabled.value) return;
let x = event.targetTouches[0].pageX - (lastX.value ?? 0); const x = event.targetTouches[0].pageX - (lastX.value ?? 0);
let y = event.targetTouches[0].pageY - (lastY.value ?? 0); const y = event.targetTouches[0].pageY - (lastY.value ?? 0);
if (Math.abs(x) >= step && Math.abs(y) >= step) return; if (Math.abs(x) >= step && Math.abs(y) >= step) return;
lastX.value = event.targetTouches[0].pageX; lastX.value = event.targetTouches[0].pageX;
lastY.value = event.targetTouches[0].pageY; lastY.value = event.targetTouches[0].pageY;
@ -268,8 +269,8 @@ const doMove = (x: number, y: number) => {
} }
const style = imgex.value.style; const style = imgex.value.style;
let posX = pxStringToNumber(style.left) + x; const posX = pxStringToNumber(style.left) + x;
let posY = pxStringToNumber(style.top) + y; const posY = pxStringToNumber(style.top) + y;
style.left = posX + "px"; style.left = posX + "px";
style.top = posY + "px"; style.top = posY + "px";

View File

@ -82,7 +82,7 @@ const isDraggable = computed(
const canDrop = computed(() => { const canDrop = computed(() => {
if (!props.isDir || props.readOnly) return false; if (!props.isDir || props.readOnly) return false;
for (let i of fileStore.selected) { for (const i of fileStore.selected) {
if (fileStore.req?.items[i].url === props.url) { if (fileStore.req?.items[i].url === props.url) {
return false; return false;
} }
@ -156,9 +156,9 @@ const drop = async (event: Event) => {
} }
} }
let items: any[] = []; const items: any[] = [];
for (let i of fileStore.selected) { for (const i of fileStore.selected) {
if (fileStore.req) { if (fileStore.req) {
items.push({ items.push({
from: fileStore.req?.items[i].url, from: fileStore.req?.items[i].url,
@ -172,10 +172,10 @@ const drop = async (event: Event) => {
if (el === null) { if (el === null) {
return; return;
} }
let path = el.__vue__.url; const path = el.__vue__.url;
let baseItems = (await api.fetch(path)).items; const baseItems = (await api.fetch(path)).items;
let action = (overwrite: boolean, rename: boolean) => { const action = (overwrite: boolean, rename: boolean) => {
api api
.move(items, overwrite, rename) .move(items, overwrite, rename)
.then(() => { .then(() => {
@ -184,7 +184,7 @@ const drop = async (event: Event) => {
.catch($showError); .catch($showError);
}; };
let conflict = upload.checkConflict(items, baseItems); const conflict = upload.checkConflict(items, baseItems);
let overwrite = false; let overwrite = false;
let rename = false; let rename = false;

View File

@ -73,11 +73,16 @@ const initVideoPlayer = async () => {
const langOpt = { language: "videoPlayerLocal" }; const langOpt = { language: "videoPlayerLocal" };
// support for playback at different speeds. // support for playback at different speeds.
const playbackRatesOpt = { playbackRates: [0.5, 1, 1.5, 2, 2.5, 3] }; const playbackRatesOpt = { playbackRates: [0.5, 1, 1.5, 2, 2.5, 3] };
let options = getOptions(props.options, langOpt, srcOpt, playbackRatesOpt); const options = getOptions(
props.options,
langOpt,
srcOpt,
playbackRatesOpt
);
player.value = videojs(videoPlayer.value!, options, () => {}); player.value = videojs(videoPlayer.value!, options, () => {});
// TODO: need to test on mobile // TODO: need to test on mobile
// @ts-ignore // @ts-expect-error no ts definition for mobileUi
player.value!.mobileUi(); player.value!.mobileUi();
} catch (error) { } catch (error) {
console.error("Error initializing video player:", error); console.error("Error initializing video player:", error);

View File

@ -82,10 +82,10 @@ export default {
...mapActions(useLayoutStore, ["showHover", "closeHovers"]), ...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
copy: async function (event) { copy: async function (event) {
event.preventDefault(); event.preventDefault();
let items = []; const items = [];
// Create a new promise for each file. // Create a new promise for each file.
for (let item of this.selected) { for (const item of this.selected) {
items.push({ items.push({
from: this.req.items[item].url, from: this.req.items[item].url,
to: this.dest + encodeURIComponent(this.req.items[item].name), to: this.dest + encodeURIComponent(this.req.items[item].name),
@ -93,7 +93,7 @@ export default {
}); });
} }
let action = async (overwrite, rename) => { const action = async (overwrite, rename) => {
buttons.loading("copy"); buttons.loading("copy");
await api await api
@ -122,8 +122,8 @@ export default {
return; return;
} }
let dstItems = (await api.fetch(this.dest)).items; const dstItems = (await api.fetch(this.dest)).items;
let conflict = upload.checkConflict(items, dstItems); const conflict = upload.checkConflict(items, dstItems);
let overwrite = false; let overwrite = false;
let rename = false; let rename = false;

View File

@ -74,8 +74,8 @@ export default {
return; return;
} }
let promises = []; const promises = [];
for (let index of this.selected) { for (const index of this.selected) {
promises.push(api.remove(this.req.items[index].url)); promises.push(api.remove(this.req.items[index].url));
} }

View File

@ -43,7 +43,7 @@ export default {
submit: async function () { submit: async function () {
this.updateRequest(null); this.updateRequest(null);
let uri = url.removeLastDir(this.$route.path) + "/"; const uri = url.removeLastDir(this.$route.path) + "/";
this.$router.push({ path: uri }); this.$router.push({ path: uri });
}, },
}, },

View File

@ -80,7 +80,7 @@ export default {
// Otherwise we add every directory to the // Otherwise we add every directory to the
// move options. // move options.
for (let item of req.items) { for (const item of req.items) {
if (!item.isDir) continue; if (!item.isDir) continue;
this.items.push({ this.items.push({
@ -93,12 +93,12 @@ export default {
// Retrieves the URL of the directory the user // Retrieves the URL of the directory the user
// just clicked in and fill the options with its // just clicked in and fill the options with its
// content. // content.
let uri = event.currentTarget.dataset.url; const uri = event.currentTarget.dataset.url;
files.fetch(uri).then(this.fillOptions).catch(this.$showError); files.fetch(uri).then(this.fillOptions).catch(this.$showError);
}, },
touchstart(event) { touchstart(event) {
let url = event.currentTarget.dataset.url; const url = event.currentTarget.dataset.url;
// In 300 milliseconds, we shall reset the count. // In 300 milliseconds, we shall reset the count.
setTimeout(() => { setTimeout(() => {

View File

@ -124,7 +124,7 @@ export default {
let sum = 0; let sum = 0;
for (let selected of this.selected) { for (const selected of this.selected) {
sum += this.req.items[selected].size; sum += this.req.items[selected].size;
} }

View File

@ -81,9 +81,9 @@ export default {
...mapActions(useLayoutStore, ["showHover", "closeHovers"]), ...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
move: async function (event) { move: async function (event) {
event.preventDefault(); event.preventDefault();
let items = []; const items = [];
for (let item of this.selected) { for (const item of this.selected) {
items.push({ items.push({
from: this.req.items[item].url, from: this.req.items[item].url,
to: this.dest + encodeURIComponent(this.req.items[item].name), to: this.dest + encodeURIComponent(this.req.items[item].name),
@ -91,7 +91,7 @@ export default {
}); });
} }
let action = async (overwrite, rename) => { const action = async (overwrite, rename) => {
buttons.loading("move"); buttons.loading("move");
await api await api
@ -106,8 +106,8 @@ export default {
}); });
}; };
let dstItems = (await api.fetch(this.dest)).items; const dstItems = (await api.fetch(this.dest)).items;
let conflict = upload.checkConflict(items, dstItems); const conflict = upload.checkConflict(items, dstItems);
let overwrite = false; let overwrite = false;
let rename = false; let rename = false;

View File

@ -196,13 +196,23 @@ export default {
methods: { methods: {
...mapActions(useLayoutStore, ["closeHovers"]), ...mapActions(useLayoutStore, ["closeHovers"]),
copyToClipboard: function (text) { copyToClipboard: function (text) {
copy(text).then( copy({ text }).then(
() => { () => {
// clipboard successfully set // clipboard successfully set
this.$showSuccess(this.$t("success.linkCopied")); this.$showSuccess(this.$t("success.linkCopied"));
}, },
() => { () => {
// clipboard write failed // clipboard write failed
copy({ text }, { permission: true }).then(
() => {
// clipboard successfully set
this.$showSuccess(this.$t("success.linkCopied"));
},
(e) => {
// clipboard write failed
this.$showError(e);
}
);
} }
); );
}, },

View File

@ -48,10 +48,10 @@ const layoutStore = useLayoutStore();
// TODO: this is a copy of the same function in FileListing.vue // TODO: this is a copy of the same function in FileListing.vue
const uploadInput = (event: Event) => { const uploadInput = (event: Event) => {
let files = (event.currentTarget as HTMLInputElement)?.files; const files = (event.currentTarget as HTMLInputElement)?.files;
if (files === null) return; if (files === null) return;
let folder_upload = !!files[0].webkitRelativePath; const folder_upload = !!files[0].webkitRelativePath;
const uploadFiles: UploadList = []; const uploadFiles: UploadList = [];
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
@ -66,8 +66,8 @@ const uploadInput = (event: Event) => {
}); });
} }
let path = route.path.endsWith("/") ? route.path : route.path + "/"; const path = route.path.endsWith("/") ? route.path : route.path + "/";
let conflict = upload.checkConflict(uploadFiles, fileStore.req!.items); const conflict = upload.checkConflict(uploadFiles, fileStore.req!.items);
if (conflict) { if (conflict) {
layoutStore.showHover({ layoutStore.showHover({

View File

@ -13,7 +13,7 @@ export default {
name: "languages", name: "languages",
props: ["locale"], props: ["locale"],
data() { data() {
let dataObj = {}; const dataObj = {};
const locales = { const locales = {
he: "עברית", he: "עברית",
hu: "Magyar", hu: "Magyar",

View File

@ -39,7 +39,7 @@ export default {
methods: { methods: {
remove(event, index) { remove(event, index) {
event.preventDefault(); event.preventDefault();
let rules = [...this.rules]; const rules = [...this.rules];
rules.splice(index, 1); rules.splice(index, 1);
this.$emit("update:rules", [...rules]); this.$emit("update:rules", [...rules]);
}, },

View File

@ -17,7 +17,6 @@ defineProps<{
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(e: "update:theme", val: string | null): void; (e: "update:theme", val: string | null): void;
}>(); }>();

View File

@ -116,7 +116,7 @@ watch(createUserDirData, () => {
if (props.user?.scope) { if (props.user?.scope) {
props.user.scope = createUserDirData.value props.user.scope = createUserDirData.value
? "" ? ""
: originalUserScope.value ?? ""; : (originalUserScope.value ?? "");
} }
}); });
</script> </script>

View File

@ -63,8 +63,8 @@
local("Roboto"), local("Roboto"),
local("Roboto-Regular"), local("Roboto-Regular"),
url(../assets/fonts/roboto/normal-latin-ext.woff2) format("woff2"); url(../assets/fonts/roboto/normal-latin-ext.woff2) format("woff2");
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF,
U+A720-A7FF; U+2C60-2C7F, U+A720-A7FF;
} }
@font-face { @font-face {
@ -142,8 +142,8 @@
local("Roboto Medium"), local("Roboto Medium"),
local("Roboto-Medium"), local("Roboto-Medium"),
url(../assets/fonts/roboto/medium-latin-ext.woff2) format("woff2"); url(../assets/fonts/roboto/medium-latin-ext.woff2) format("woff2");
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF,
U+A720-A7FF; U+2C60-2C7F, U+A720-A7FF;
} }
@font-face { @font-face {
@ -221,8 +221,8 @@
local("Roboto Bold"), local("Roboto Bold"),
local("Roboto-Bold"), local("Roboto-Bold"),
url(../assets/fonts/roboto/bold-latin-ext.woff2) format("woff2"); url(../assets/fonts/roboto/bold-latin-ext.woff2) format("woff2");
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF,
U+A720-A7FF; U+2C60-2C7F, U+A720-A7FF;
} }
@font-face { @font-face {

View File

@ -142,7 +142,7 @@ export const i18n = createI18n({
export const isRtl = (locale?: string) => { export const isRtl = (locale?: string) => {
// see below // see below
// @ts-ignore // @ts-expect-error incorrect type when legacy
return rtlLanguages.includes(locale || i18n.global.locale.value); return rtlLanguages.includes(locale || i18n.global.locale.value);
}; };
@ -150,7 +150,7 @@ export function setLocale(locale: string) {
dayjs.locale(locale); dayjs.locale(locale);
// according to doc u only need .value if legacy: false but they lied // according to doc u only need .value if legacy: false but they lied
// https://vue-i18n.intlify.dev/guide/essentials/scope.html#local-scope-1 // https://vue-i18n.intlify.dev/guide/essentials/scope.html#local-scope-1
//@ts-ignore // @ts-expect-error incorrect type when legacy
i18n.global.locale.value = locale; i18n.global.locale.value = locale;
} }

View File

@ -24,7 +24,7 @@ export async function validateLogin() {
await renew(<string>localStorage.getItem("jwt")); await renew(<string>localStorage.getItem("jwt"));
} }
} catch (error) { } catch (error) {
console.warn("Invalid JWT token in storage"); // eslint-disable-line console.warn("Invalid JWT token in storage");
throw error; throw error;
} }
} }

View File

@ -4,7 +4,7 @@ function loading(button: string) {
); );
if (el === undefined || el === null) { if (el === undefined || el === null) {
console.log("Error getting button " + button); // eslint-disable-line console.log("Error getting button " + button);
return; return;
} }
@ -30,7 +30,7 @@ function done(button: string) {
); );
if (el === undefined || el === null) { if (el === undefined || el === null) {
console.log("Error getting button " + button); // eslint-disable-line console.log("Error getting button " + button);
return; return;
} }
@ -51,7 +51,7 @@ function success(button: string) {
); );
if (el === undefined || el === null) { if (el === undefined || el === null) {
console.log("Error getting button " + button); // eslint-disable-line console.log("Error getting button " + button);
return; return;
} }

View File

@ -1,39 +1,36 @@
// Based on code by the following links: // Based on code by the following links:
// https://stackoverflow.com/a/74528564 // https://stackoverflow.com/a/74528564
// https://web.dev/articles/async-clipboard // https://web.dev/articles/async-clipboard
export function copy(text: string) {
interface ClipboardArgs {
text?: string;
data?: ClipboardItems;
}
interface ClipboardOpts {
permission?: boolean;
}
export function copy(data: ClipboardArgs, opts?: ClipboardOpts) {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
if ( if (
// Clipboard API requires secure context // Clipboard API requires secure context
window.isSecureContext && window.isSecureContext &&
typeof navigator.clipboard !== "undefined" && typeof navigator.clipboard !== "undefined"
// @ts-ignore
navigator.permissions !== "undefined"
) { ) {
navigator.permissions if (opts?.permission) {
// @ts-ignore getPermission("clipboard-write")
.query({ name: "clipboard-write" }) .then(() => writeToClipboard(data).then(resolve).catch(reject))
.then((permission) => { .catch(reject);
if (permission.state === "granted" || permission.state === "prompt") {
// simple writeText should work for all modern browsers
navigator.clipboard.writeText(text).then(resolve).catch(reject);
} else { } else {
reject(new Error("Permission not granted!")); writeToClipboard(data).then(resolve).catch(reject);
} }
})
.catch((e) => {
// Firefox doesn't support clipboard-write permission
if (navigator.userAgent.indexOf("Firefox") != -1) {
navigator.clipboard.writeText(text).then(resolve).catch(reject);
} else {
reject(e);
}
});
} else if ( } else if (
document.queryCommandSupported && document.queryCommandSupported &&
document.queryCommandSupported("copy") document.queryCommandSupported("copy") &&
data.text // old method only supports text
) { ) {
const textarea = createTemporaryTextarea(text); const textarea = createTemporaryTextarea(data.text);
const body = document.activeElement || document.body; const body = document.activeElement || document.body;
try { try {
body.appendChild(textarea); body.appendChild(textarea);
@ -54,6 +51,35 @@ export function copy(text: string) {
}); });
} }
function getPermission(name: string) {
return new Promise<void>((resolve, reject) => {
typeof navigator.permissions !== "undefined" &&
navigator.permissions
// @ts-expect-error chrome specific api
.query({ name })
.then((permission) => {
if (permission.state === "granted" || permission.state === "prompt") {
resolve();
} else {
reject(new Error("Permission denied!"));
}
});
});
}
function writeToClipboard(data: ClipboardArgs) {
if (data.text) {
return navigator.clipboard.writeText(data.text);
}
if (data.data) {
return navigator.clipboard.write(data.data);
}
return new Promise<void>((resolve, reject) => {
reject(new Error("No data was supplied!"));
});
}
const styles = { const styles = {
fontSize: "12pt", fontSize: "12pt",
position: "fixed", position: "fixed",
@ -69,10 +95,10 @@ const styles = {
background: "transparent", background: "transparent",
}; };
const createTemporaryTextarea = (text: string) => { function createTemporaryTextarea(text: string) {
const textarea = document.createElement("textarea"); const textarea = document.createElement("textarea");
textarea.value = text; textarea.value = text;
textarea.setAttribute("readonly", ""); textarea.setAttribute("readonly", "");
Object.assign(textarea.style, styles); Object.assign(textarea.style, styles);
return textarea; return textarea;
}; }

View File

@ -6,13 +6,16 @@ export default function getRule(rules: string[]) {
let result = null; let result = null;
const find = Array.prototype.find; const find = Array.prototype.find;
find.call(document.styleSheets, (styleSheet) => { find.call(document.styleSheets, (styleSheet: CSSStyleSheet) => {
result = find.call(styleSheet.cssRules, (cssRule) => { result = find.call(styleSheet.cssRules, (cssRule: CSSRule) => {
let found = false; let found = false;
if (cssRule instanceof window.CSSStyleRule) { // faster than checking instanceof for every element
if (cssRule.constructor.name === "CSSStyleRule") {
for (let i = 0; i < rules.length; i++) { for (let i = 0; i < rules.length; i++) {
if (cssRule.selectorText.toLowerCase() === rules[i]) { if (
(cssRule as CSSStyleRule).selectorText.toLowerCase() === rules[i]
) {
found = true; found = true;
} }
} }
@ -24,5 +27,5 @@ export default function getRule(rules: string[]) {
return result != null; return result != null;
}); });
return result; return result as CSSStyleRule | null;
} }

View File

@ -325,6 +325,7 @@ const token = ref<string>("");
const audio = ref<HTMLAudioElement>(); const audio = ref<HTMLAudioElement>();
const tag = ref<boolean>(false); const tag = ref<boolean>(false);
const $showError = inject<IToastError>("$showError")!;
const $showSuccess = inject<IToastSuccess>("$showSuccess")!; const $showSuccess = inject<IToastSuccess>("$showSuccess")!;
const { t } = useI18n({}); const { t } = useI18n({});
@ -463,9 +464,9 @@ const download = () => {
if (req.value === null) return false; if (req.value === null) return false;
layoutStore.closeHovers(); layoutStore.closeHovers();
let files: string[] = []; const files: string[] = [];
for (let i of fileStore.selected) { for (const i of fileStore.selected) {
files.push(req.value.items[i].path); files.push(req.value.items[i].path);
} }
@ -488,13 +489,23 @@ const linkSelected = () => {
}; };
const copyToClipboard = (text: string) => { const copyToClipboard = (text: string) => {
copy(text).then( copy({ text }).then(
() => { () => {
// clipboard successfully set // clipboard successfully set
$showSuccess(t("success.linkCopied")); $showSuccess(t("success.linkCopied"));
}, },
() => { () => {
// clipboard write failed // clipboard write failed
copy({ text }, { permission: true }).then(
() => {
// clipboard successfully set
$showSuccess(t("success.linkCopied"));
},
(e) => {
// clipboard write failed
$showError(e);
}
);
} }
); );
}; };

View File

@ -108,7 +108,7 @@ onMounted(() => {
showPrintMargin: false, showPrintMargin: false,
readOnly: fileStore.req?.type === "textImmutable", readOnly: fileStore.req?.type === "textImmutable",
theme: "ace/theme/chrome", theme: "ace/theme/chrome",
mode: modelist.getModeForPath(fileStore.req?.name).mode, mode: modelist.getModeForPath(fileStore.req!.name).mode,
wrap: true, wrap: true,
enableBasicAutocompletion: true, enableBasicAutocompletion: true,
enableLiveAutocompletion: true, enableLiveAutocompletion: true,
@ -173,7 +173,7 @@ const close = () => {
fileStore.updateRequest(null); fileStore.updateRequest(null);
let uri = url.removeLastDir(route.path) + "/"; const uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri }); router.push({ path: uri });
}; };

View File

@ -523,12 +523,12 @@ const keyEvent = (event: KeyboardEvent) => {
break; break;
case "a": case "a":
event.preventDefault(); event.preventDefault();
for (let file of items.value.files) { for (const file of items.value.files) {
if (fileStore.selected.indexOf(file.index) === -1) { if (fileStore.selected.indexOf(file.index) === -1) {
fileStore.selected.push(file.index); fileStore.selected.push(file.index);
} }
} }
for (let dir of items.value.dirs) { for (const dir of items.value.dirs) {
if (fileStore.selected.indexOf(dir.index) === -1) { if (fileStore.selected.indexOf(dir.index) === -1) {
fileStore.selected.push(dir.index); fileStore.selected.push(dir.index);
} }
@ -551,9 +551,9 @@ const copyCut = (event: Event | KeyboardEvent): void => {
if (fileStore.req === null) return; if (fileStore.req === null) return;
let items = []; const items = [];
for (let i of fileStore.selected) { for (const i of fileStore.selected) {
items.push({ items.push({
from: fileStore.req.items[i].url, from: fileStore.req.items[i].url,
name: fileStore.req.items[i].name, name: fileStore.req.items[i].name,
@ -575,9 +575,9 @@ const paste = (event: Event) => {
if ((event.target as HTMLElement).tagName?.toLowerCase() === "input") return; if ((event.target as HTMLElement).tagName?.toLowerCase() === "input") return;
// TODO router location should it be // TODO router location should it be
let items: any[] = []; const items: any[] = [];
for (let item of clipboardStore.items) { for (const item of clipboardStore.items) {
const from = item.from.endsWith("/") ? item.from.slice(0, -1) : item.from; const from = item.from.endsWith("/") ? item.from.slice(0, -1) : item.from;
const to = route.path + encodeURIComponent(item.name); const to = route.path + encodeURIComponent(item.name);
items.push({ from, to, name: item.name }); items.push({ from, to, name: item.name });
@ -614,7 +614,7 @@ const paste = (event: Event) => {
return; return;
} }
let conflict = upload.checkConflict(items, fileStore.req!.items); const conflict = upload.checkConflict(items, fileStore.req!.items);
let overwrite = false; let overwrite = false;
let rename = false; let rename = false;
@ -640,14 +640,13 @@ const paste = (event: Event) => {
const colunmsResize = () => { const colunmsResize = () => {
// Update the columns size based on the window width. // Update the columns size based on the window width.
let items_ = css(["#listing.mosaic .item", ".mosaic#listing .item"]); const items_ = css(["#listing.mosaic .item", ".mosaic#listing .item"]);
if (items_ === null) return; if (items_ === null) return;
let columns = Math.floor( let columns = Math.floor(
(document.querySelector("main")?.offsetWidth ?? 0) / columnWidth.value (document.querySelector("main")?.offsetWidth ?? 0) / columnWidth.value
); );
if (columns === 0) columns = 1; if (columns === 0) columns = 1;
// @ts-ignore never type error
items_.style.width = `calc(${100 / columns}% - 1em)`; items_.style.width = `calc(${100 / columns}% - 1em)`;
}; };
@ -677,11 +676,10 @@ const dragEnter = () => {
// When the user starts dragging an item, put every // When the user starts dragging an item, put every
// file on the listing with 50% opacity. // file on the listing with 50% opacity.
let items = document.getElementsByClassName("item"); const items = document.getElementsByClassName("item");
// @ts-ignore Array.from(items).forEach((file: Element) => {
Array.from(items).forEach((file: HTMLElement) => { (file as HTMLElement).style.opacity = "0.5";
file.style.opacity = "0.5";
}); });
}; };
@ -698,7 +696,7 @@ const drop = async (event: DragEvent) => {
dragCounter.value = 0; dragCounter.value = 0;
resetOpacity(); resetOpacity();
let dt = event.dataTransfer; const dt = event.dataTransfer;
let el: HTMLElement | null = event.target as HTMLElement; let el: HTMLElement | null = event.target as HTMLElement;
if (fileStore.req === null || dt === null || dt.files.length <= 0) return; if (fileStore.req === null || dt === null || dt.files.length <= 0) return;
@ -709,7 +707,7 @@ const drop = async (event: DragEvent) => {
} }
} }
let files: UploadList = (await upload.scanFiles(dt)) as UploadList; const files: UploadList = (await upload.scanFiles(dt)) as UploadList;
let items = fileStore.req.items; let items = fileStore.req.items;
let path = route.path.endsWith("/") ? route.path : route.path + "/"; let path = route.path.endsWith("/") ? route.path : route.path + "/";
@ -729,7 +727,7 @@ const drop = async (event: DragEvent) => {
} }
} }
let conflict = upload.checkConflict(files, items); const conflict = upload.checkConflict(files, items);
if (conflict) { if (conflict) {
layoutStore.showHover({ layoutStore.showHover({
@ -753,10 +751,10 @@ const drop = async (event: DragEvent) => {
}; };
const uploadInput = (event: Event) => { const uploadInput = (event: Event) => {
let files = (event.currentTarget as HTMLInputElement)?.files; const files = (event.currentTarget as HTMLInputElement)?.files;
if (files === null) return; if (files === null) return;
let folder_upload = !!files[0].webkitRelativePath; const folder_upload = !!files[0].webkitRelativePath;
const uploadFiles: UploadList = []; const uploadFiles: UploadList = [];
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
@ -771,8 +769,8 @@ const uploadInput = (event: Event) => {
}); });
} }
let path = route.path.endsWith("/") ? route.path : route.path + "/"; const path = route.path.endsWith("/") ? route.path : route.path + "/";
let conflict = upload.checkConflict(uploadFiles, fileStore.req!.items); const conflict = upload.checkConflict(uploadFiles, fileStore.req!.items);
if (conflict) { if (conflict) {
layoutStore.showHover({ layoutStore.showHover({
@ -796,7 +794,7 @@ const uploadInput = (event: Event) => {
}; };
const resetOpacity = () => { const resetOpacity = () => {
let items = document.getElementsByClassName("item"); const items = document.getElementsByClassName("item");
Array.from(items).forEach((file: Element) => { Array.from(items).forEach((file: Element) => {
(file as HTMLElement).style.opacity = "1"; (file as HTMLElement).style.opacity = "1";
@ -822,7 +820,6 @@ const sort = async (by: string) => {
try { try {
if (authStore.user?.id) { if (authStore.user?.id) {
// @ts-ignore
await users.update({ id: authStore.user?.id, sorting: { by, asc } }, [ await users.update({ id: authStore.user?.id, sorting: { by, asc } }, [
"sorting", "sorting",
]); ]);
@ -873,10 +870,10 @@ const download = () => {
confirm: (format: any) => { confirm: (format: any) => {
layoutStore.closeHovers(); layoutStore.closeHovers();
let files = []; const files = [];
if (fileStore.selectedCount > 0 && fileStore.req !== null) { if (fileStore.selectedCount > 0 && fileStore.req !== null) {
for (let i of fileStore.selected) { for (const i of fileStore.selected) {
files.push(fileStore.req.items[i].url); files.push(fileStore.req.items[i].url);
} }
} else { } else {
@ -899,13 +896,12 @@ const switchView = async () => {
const data = { const data = {
id: authStore.user?.id, id: authStore.user?.id,
viewMode: modes[authStore.user?.viewMode ?? "list"] || "list", viewMode: (modes[authStore.user?.viewMode ?? "list"] ||
"list") as ViewModeType,
}; };
// @ts-ignore
users.update(data, ["viewMode"]).catch($showError); users.update(data, ["viewMode"]).catch($showError);
// @ts-ignore
authStore.updateUser(data); authStore.updateUser(data);
setItemWeight(); setItemWeight();

View File

@ -353,7 +353,7 @@ const updatePreview = async () => {
autoPlay.value = false; autoPlay.value = false;
} }
let dirs = route.fullPath.split("/"); const dirs = route.fullPath.split("/");
name.value = decodeURIComponent(dirs[dirs.length - 1]); name.value = decodeURIComponent(dirs[dirs.length - 1]);
if (!listing.value) { if (!listing.value) {
@ -422,7 +422,7 @@ const toggleNavigation = throttle(function () {
const close = () => { const close = () => {
fileStore.updateRequest(null); fileStore.updateRequest(null);
let uri = url.removeLastDir(route.path) + "/"; const uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri }); router.push({ path: uri });
}; };

View File

@ -282,7 +282,7 @@ const formattedChunkSize = computed({
// Define funcs // Define funcs
const capitalize = (name: string, where: string | RegExp = "_") => { const capitalize = (name: string, where: string | RegExp = "_") => {
if (where === "caps") where = /(?=[A-Z])/; if (where === "caps") where = /(?=[A-Z])/;
let split = name.split(where); const split = name.split(where);
name = ""; name = "";
for (let i = 0; i < split.length; i++) { for (let i = 0; i < split.length; i++) {
@ -294,7 +294,7 @@ const capitalize = (name: string, where: string | RegExp = "_") => {
const save = async () => { const save = async () => {
if (settings.value === null) return false; if (settings.value === null) return false;
let newSettings: ISettings = { const newSettings: ISettings = {
...settings.value, ...settings.value,
shell: shell:
settings.value?.shell settings.value?.shell
@ -376,7 +376,7 @@ onMounted(async () => {
try { try {
layoutStore.loading = true; layoutStore.loading = true;
const original: ISettings = await api.get(); const original: ISettings = await api.get();
let newSettings: ISettings = { ...original, commands: {} }; const newSettings: ISettings = { ...original, commands: {} };
const keys = Object.keys(original.commands) as Array<keyof SettingsCommand>; const keys = Object.keys(original.commands) as Array<keyof SettingsCommand>;
for (const key of keys) { for (const key of keys) {

View File

@ -87,12 +87,12 @@ onMounted(async () => {
layoutStore.loading = true; layoutStore.loading = true;
try { try {
let newLinks = await api.list(); const newLinks = await api.list();
if (authStore.user?.perm.admin) { if (authStore.user?.perm.admin) {
let userMap = new Map<number, string>(); const userMap = new Map<number, string>();
for (let user of await users.getAll()) for (const user of await users.getAll())
userMap.set(user.id, user.username); userMap.set(user.id, user.username);
for (let link of newLinks) { for (const link of newLinks) {
if (link.userID && userMap.has(link.userID)) if (link.userID && userMap.has(link.userID))
link.username = userMap.get(link.userID); link.username = userMap.get(link.userID);
} }
@ -108,13 +108,23 @@ onMounted(async () => {
}); });
const copyToClipboard = (text: string) => { const copyToClipboard = (text: string) => {
copy(text).then( copy({ text }).then(
() => { () => {
// clipboard successfully set // clipboard successfully set
$showSuccess(t("success.linkCopied")); $showSuccess(t("success.linkCopied"));
}, },
() => { () => {
// clipboard write failed // clipboard write failed
copy({ text }, { permission: true }).then(
() => {
// clipboard successfully set
$showSuccess(t("success.linkCopied"));
},
(e) => {
// clipboard write failed
$showError(e);
}
);
} }
); );
}; };

View File

@ -90,7 +90,7 @@ const fetchData = async () => {
try { try {
if (isNew.value) { if (isNew.value) {
let { defaults, createUserDir: _createUserDir } = await settings.get(); const { defaults, createUserDir: _createUserDir } = await settings.get();
createUserDir.value = _createUserDir; createUserDir.value = _createUserDir;
user.value = { user.value = {
...defaults, ...defaults,