Fix esling warnings (disabled any and script lang checks)

Rewrote clipboard copy (Fixes #3407)
Run prettier
pull/3698/head
Kloon ImKloon 2025-01-26 12:40:44 +01:00
parent b65716777d
commit 74dd8b71ce
No known key found for this signature in database
GPG Key ID: CCF1C86A995C5B6A
36 changed files with 225 additions and 167 deletions

View File

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

View File

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

View File

@ -4,9 +4,6 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
typescript: ~5.6.3
importers:
.:
@ -1174,14 +1171,14 @@ packages:
peerDependencies:
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
eslint: ^8.57.0 || ^9.0.0
typescript: ~5.6.3
typescript: '>=4.8.4 <5.8.0'
'@typescript-eslint/parser@8.21.0':
resolution: {integrity: sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ~5.6.3
typescript: '>=4.8.4 <5.8.0'
'@typescript-eslint/scope-manager@8.21.0':
resolution: {integrity: sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==}
@ -1192,7 +1189,7 @@ packages:
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ~5.6.3
typescript: '>=4.8.4 <5.8.0'
'@typescript-eslint/types@8.21.0':
resolution: {integrity: sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==}
@ -1202,14 +1199,14 @@ packages:
resolution: {integrity: sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: ~5.6.3
typescript: '>=4.8.4 <5.8.0'
'@typescript-eslint/utils@8.21.0':
resolution: {integrity: sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ~5.6.3
typescript: '>=4.8.4 <5.8.0'
'@typescript-eslint/visitor-keys@8.21.0':
resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==}
@ -1281,7 +1278,7 @@ packages:
peerDependencies:
eslint: ^9.10.0
eslint-plugin-vue: ^9.28.0
typescript: ~5.6.3
typescript: '>=4.8.4'
peerDependenciesMeta:
typescript:
optional: true
@ -1289,7 +1286,7 @@ packages:
'@vue/language-core@2.2.0':
resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==}
peerDependencies:
typescript: ~5.6.3
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
@ -1314,7 +1311,7 @@ packages:
'@vue/tsconfig@0.7.0':
resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==}
peerDependencies:
typescript: ~5.6.3
typescript: 5.x
vue: ^3.4.0
peerDependenciesMeta:
typescript:
@ -2175,7 +2172,7 @@ packages:
pinia@2.3.1:
resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==}
peerDependencies:
typescript: ~5.6.3
typescript: '>=4.4.4'
vue: ^2.7.0 || ^3.5.11
peerDependenciesMeta:
typescript:
@ -2438,7 +2435,7 @@ packages:
resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==}
engines: {node: '>=18.12'}
peerDependencies:
typescript: ~5.6.3
typescript: '>=4.8.4'
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@ -2463,7 +2460,7 @@ packages:
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ~5.6.3
typescript: '>=4.8.4 <5.8.0'
typescript@5.6.3:
resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
@ -2643,12 +2640,12 @@ packages:
resolution: {integrity: sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==}
hasBin: true
peerDependencies:
typescript: ~5.6.3
typescript: '>=5.0.0'
vue@3.5.13:
resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
peerDependencies:
typescript: ~5.6.3
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true

View File

@ -25,7 +25,7 @@ export async function create(user: IUser) {
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}`, {
method: "PUT",
body: JSON.stringify({

View File

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

View File

@ -46,7 +46,7 @@ https://raw.githubusercontent.com/dzwillia/vue-simple-progress/master/src/compon
<script>
// 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);
};
@ -107,7 +107,7 @@ export default {
},
computed: {
pct() {
var pct = (this.val / this.max) * 100;
let pct = (this.val / this.max) * 100;
pct = pct.toFixed(2);
return Math.min(pct, this.max);
},
@ -160,7 +160,7 @@ export default {
return isNumber(this.fontSize) ? this.fontSize : 13;
},
progress_style() {
var style = {
const style = {
background: this.bgColor,
};
@ -177,7 +177,7 @@ export default {
return style;
},
bar_style() {
var style = {
const style = {
background: this.barColor,
width: this.pct + "%",
height: this.size_px + "px",
@ -198,7 +198,7 @@ export default {
return style;
},
text_style() {
var style = {
const style = {
color: this.textFgColor,
"font-size": this.text_font_size + "px",
"text-align": this.textAlign,

View File

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

View File

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

View File

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

View File

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

View File

@ -73,11 +73,16 @@ const initVideoPlayer = async () => {
const langOpt = { language: "videoPlayerLocal" };
// support for playback at different speeds.
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, () => {});
// TODO: need to test on mobile
// @ts-ignore
// @ts-expect-error no ts definition for mobileUi
player.value!.mobileUi();
} catch (error) {
console.error("Error initializing video player:", error);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -196,13 +196,23 @@ export default {
methods: {
...mapActions(useLayoutStore, ["closeHovers"]),
copyToClipboard: function (text) {
copy(text).then(
copy({ text }).then(
() => {
// clipboard successfully set
this.$showSuccess(this.$t("success.linkCopied"));
},
() => {
// 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
const uploadInput = (event: Event) => {
let files = (event.currentTarget as HTMLInputElement)?.files;
const files = (event.currentTarget as HTMLInputElement)?.files;
if (files === null) return;
let folder_upload = !!files[0].webkitRelativePath;
const folder_upload = !!files[0].webkitRelativePath;
const uploadFiles: UploadList = [];
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 + "/";
let conflict = upload.checkConflict(uploadFiles, fileStore.req!.items);
const path = route.path.endsWith("/") ? route.path : route.path + "/";
const conflict = upload.checkConflict(uploadFiles, fileStore.req!.items);
if (conflict) {
layoutStore.showHover({

View File

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

View File

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

View File

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

View File

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

View File

@ -63,8 +63,8 @@
local("Roboto"),
local("Roboto-Regular"),
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,
U+A720-A7FF;
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF,
U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
@ -142,8 +142,8 @@
local("Roboto Medium"),
local("Roboto-Medium"),
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,
U+A720-A7FF;
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF,
U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
@ -221,8 +221,8 @@
local("Roboto Bold"),
local("Roboto-Bold"),
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,
U+A720-A7FF;
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF,
U+2C60-2C7F, U+A720-A7FF;
}
@font-face {

View File

@ -142,7 +142,7 @@ export const i18n = createI18n({
export const isRtl = (locale?: string) => {
// see below
// @ts-ignore
// @ts-expect-error incorrect type when legacy
return rtlLanguages.includes(locale || i18n.global.locale.value);
};
@ -150,7 +150,7 @@ export function setLocale(locale: string) {
dayjs.locale(locale);
// 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
//@ts-ignore
// @ts-expect-error incorrect type when legacy
i18n.global.locale.value = locale;
}

View File

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

View File

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

View File

@ -1,39 +1,36 @@
// Based on code by the following links:
// https://stackoverflow.com/a/74528564
// 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) => {
if (
// Clipboard API requires secure context
window.isSecureContext &&
typeof navigator.clipboard !== "undefined" &&
// @ts-ignore
navigator.permissions !== "undefined"
typeof navigator.clipboard !== "undefined"
) {
navigator.permissions
// @ts-ignore
.query({ name: "clipboard-write" })
.then((permission) => {
if (permission.state === "granted" || permission.state === "prompt") {
// simple writeText should work for all modern browsers
navigator.clipboard.writeText(text).then(resolve).catch(reject);
} else {
reject(new Error("Permission not granted!"));
}
})
.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);
}
});
if (opts?.permission) {
getPermission("clipboard-write")
.then(() => writeToClipboard(data).then(resolve).catch(reject))
.catch(reject);
} else {
writeToClipboard(data).then(resolve).catch(reject);
}
} else if (
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;
try {
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 = {
fontSize: "12pt",
position: "fixed",
@ -69,10 +95,10 @@ const styles = {
background: "transparent",
};
const createTemporaryTextarea = (text: string) => {
function createTemporaryTextarea(text: string) {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.setAttribute("readonly", "");
Object.assign(textarea.style, styles);
return textarea;
};
}

View File

@ -6,13 +6,16 @@ export default function getRule(rules: string[]) {
let result = null;
const find = Array.prototype.find;
find.call(document.styleSheets, (styleSheet) => {
result = find.call(styleSheet.cssRules, (cssRule) => {
find.call(document.styleSheets, (styleSheet: CSSStyleSheet) => {
result = find.call(styleSheet.cssRules, (cssRule: CSSRule) => {
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++) {
if (cssRule.selectorText.toLowerCase() === rules[i]) {
if (
(cssRule as CSSStyleRule).selectorText.toLowerCase() === rules[i]
) {
found = true;
}
}
@ -24,5 +27,5 @@ export default function getRule(rules: string[]) {
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 tag = ref<boolean>(false);
const $showError = inject<IToastError>("$showError")!;
const $showSuccess = inject<IToastSuccess>("$showSuccess")!;
const { t } = useI18n({});
@ -463,9 +464,9 @@ const download = () => {
if (req.value === null) return false;
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);
}
@ -488,13 +489,23 @@ const linkSelected = () => {
};
const copyToClipboard = (text: string) => {
copy(text).then(
copy({ text }).then(
() => {
// clipboard successfully set
$showSuccess(t("success.linkCopied"));
},
() => {
// 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,
readOnly: fileStore.req?.type === "textImmutable",
theme: "ace/theme/chrome",
mode: modelist.getModeForPath(fileStore.req?.name).mode,
mode: modelist.getModeForPath(fileStore.req!.name).mode,
wrap: true,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
@ -173,7 +173,7 @@ const close = () => {
fileStore.updateRequest(null);
let uri = url.removeLastDir(route.path) + "/";
const uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri });
};

View File

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

View File

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

View File

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

View File

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