Compare commits

..

10 Commits

Author SHA1 Message Date
Henrique Dias
7f559ffd07 chore(release): 2.42.0 2025-07-27 13:38:09 +02:00
Henrique Dias
619f6837b0 fix: norsk loading 2025-07-27 13:37:43 +02:00
Henrique Dias
d778c192ae Revert "chore(release): 2.42.0"
This reverts commit a290c6d7db.
2025-07-27 13:31:12 +02:00
Henrique Dias
a290c6d7db chore(release): 2.42.0 2025-07-27 13:14:20 +02:00
Henrique Dias
c1b0207800 build: bump to go 1.24 2025-07-27 13:14:03 +02:00
Christopher Campo
c7a5c7efee build: bump go version to 1.23.11 2025-07-27 13:09:05 +02:00
Ramires Viana
cbeec6d225 feat: select item on file list after navigating back (#5329) 2025-07-27 13:03:00 +02:00
Henrique Dias
25e47c3ce8 feat: add Norwegian support (#5332) 2025-07-26 07:35:46 +02:00
transifex-integration[bot]
5eb3bf4058 feat: Translate frontend/src/i18n/en.json in no 2025-07-26 07:30:10 +02:00
transifex-integration[bot]
07dfdce8e4 feat: Translate frontend/src/i18n/en.json in sk
91% of minimum 50% translated source file: 'frontend/src/i18n/en.json'
on 'sk'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format
2025-07-25 08:50:31 +02:00
21 changed files with 447 additions and 86 deletions

View File

@@ -29,7 +29,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.23.0
go-version: '1.24'
- run: make lint-backend
lint:
runs-on: ubuntu-latest
@@ -57,7 +57,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.23.0
go-version: '1.24'
- run: make test-backend
test:
runs-on: ubuntu-latest
@@ -76,7 +76,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: 1.23.0
go-version: '1.23'
- uses: pnpm/action-setup@v4
with:
package_json_file: "frontend/package.json"

View File

@@ -2,6 +2,32 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [2.42.0](https://github.com/filebrowser/filebrowser/compare/v2.41.0...v2.42.0) (2025-07-27)
### Features
* add Norwegian support ([#5332](https://github.com/filebrowser/filebrowser/issues/5332)) ([25e47c3](https://github.com/filebrowser/filebrowser/commit/25e47c3ce8b35b820b5370a4b8bfdf682bd5ae0b))
* select item on file list after navigating back ([#5329](https://github.com/filebrowser/filebrowser/issues/5329)) ([cbeec6d](https://github.com/filebrowser/filebrowser/commit/cbeec6d225691723c4750d7f84122ebb14d662bf))
* Translate frontend/src/i18n/en.json in no ([5eb3bf4](https://github.com/filebrowser/filebrowser/commit/5eb3bf40586c2ffc32f4834b5dd59f0eb719c1f7))
* Translate frontend/src/i18n/en.json in sk ([07dfdce](https://github.com/filebrowser/filebrowser/commit/07dfdce8e4c371f4ca7480f3cef0bd66ff5c9abb))
### Bug Fixes
* norsk loading ([619f683](https://github.com/filebrowser/filebrowser/commit/619f6837b0d1ec6c654d30f4ecedd6696874721f))
### Reverts
* Revert "chore(release): 2.42.0" ([d778c19](https://github.com/filebrowser/filebrowser/commit/d778c192ae02c5e73781f7632e3b7276c5811e17))
### Build
* bump go version to 1.23.11 ([c7a5c7e](https://github.com/filebrowser/filebrowser/commit/c7a5c7efee2b2bede89ec90bafd1af61c39519ff))
* bump to go 1.24 ([c1b0207](https://github.com/filebrowser/filebrowser/commit/c1b0207800b4bb52c8dd459c1d69ce0f785473b6))
## [2.41.0](https://github.com/filebrowser/filebrowser/compare/v2.40.2...v2.41.0) (2025-07-22)

View File

@@ -62,6 +62,7 @@ import FileList from "./FileList.vue";
import { files as api } from "@/api";
import buttons from "@/utils/buttons";
import * as upload from "@/utils/upload";
import { removePrefix } from "@/api/utils";
export default {
name: "copy",
@@ -76,7 +77,7 @@ export default {
computed: {
...mapState(useFileStore, ["req", "selected"]),
...mapState(useAuthStore, ["user"]),
...mapWritableState(useFileStore, ["reload"]),
...mapWritableState(useFileStore, ["reload", "preselect"]),
},
methods: {
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
@@ -100,6 +101,7 @@ export default {
.copy(items, overwrite, rename)
.then(() => {
buttons.success("copy");
this.preselect = removePrefix(items[0].to);
if (this.$route.path === this.dest) {
this.reload = true;

View File

@@ -48,16 +48,15 @@ export default {
"selectedCount",
"req",
"selected",
"currentPrompt",
]),
...mapWritableState(useFileStore, ["reload"]),
...mapState(useLayoutStore, ["currentPrompt"]),
...mapWritableState(useFileStore, ["reload", "preselect"]),
},
methods: {
...mapActions(useLayoutStore, ["closeHovers"]),
submit: async function () {
buttons.loading("delete");
window.sessionStorage.setItem("modified", "true");
try {
if (!this.isListing) {
await api.remove(this.$route.path);
@@ -81,6 +80,12 @@ export default {
await Promise.all(promises);
buttons.success("delete");
const nearbyItem =
this.req.items[Math.max(0, Math.min(this.selected) - 1)];
this.preselect = nearbyItem?.path;
this.reload = true;
} catch (e) {
buttons.done("delete");

View File

@@ -17,7 +17,7 @@
</button>
<button
id="focus-prompt"
@click="submit"
@click="currentPrompt.confirm"
class="button button--flat button--red"
:aria-label="$t('buttons.discardChanges')"
:title="$t('buttons.discardChanges')"
@@ -30,22 +30,16 @@
</template>
<script>
import { mapActions } from "pinia";
import url from "@/utils/url";
import { mapState, mapActions } from "pinia";
import { useLayoutStore } from "@/stores/layout";
import { useFileStore } from "@/stores/file";
export default {
name: "discardEditorChanges",
computed: {
...mapState(useLayoutStore, ["currentPrompt"]),
},
methods: {
...mapActions(useLayoutStore, ["closeHovers"]),
...mapActions(useFileStore, ["updateRequest"]),
submit: async function () {
this.updateRequest(null);
const uri = url.removeLastDir(this.$route.path) + "/";
this.$router.push({ path: uri });
},
},
};
</script>

View File

@@ -55,7 +55,7 @@
</template>
<script>
import { mapActions, mapState } from "pinia";
import { mapActions, mapState, mapWritableState } from "pinia";
import { useFileStore } from "@/stores/file";
import { useLayoutStore } from "@/stores/layout";
import { useAuthStore } from "@/stores/auth";
@@ -63,6 +63,7 @@ import FileList from "./FileList.vue";
import { files as api } from "@/api";
import buttons from "@/utils/buttons";
import * as upload from "@/utils/upload";
import { removePrefix } from "@/api/utils";
export default {
name: "move",
@@ -77,6 +78,7 @@ export default {
computed: {
...mapState(useFileStore, ["req", "selected"]),
...mapState(useAuthStore, ["user"]),
...mapWritableState(useFileStore, ["preselect"]),
excludedFolders() {
return this.selected
.filter((idx) => this.req.items[idx].isDir)
@@ -104,6 +106,7 @@ export default {
.move(items, overwrite, rename)
.then(() => {
buttons.success("move");
this.preselect = removePrefix(items[0].to);
this.$router.push({ path: this.dest });
})
.catch((e) => {

View File

@@ -46,6 +46,7 @@ import { useFileStore } from "@/stores/file";
import { useLayoutStore } from "@/stores/layout";
import url from "@/utils/url";
import { files as api } from "@/api";
import { removePrefix } from "@/api/utils";
export default {
name: "rename",
@@ -65,7 +66,7 @@ export default {
"selectedCount",
"isListing",
]),
...mapWritableState(useFileStore, ["reload"]),
...mapWritableState(useFileStore, ["reload", "preselect"]),
},
methods: {
...mapActions(useLayoutStore, ["closeHovers"]),
@@ -97,7 +98,6 @@ export default {
newLink =
url.removeLastDir(oldLink) + "/" + encodeURIComponent(this.name);
window.sessionStorage.setItem("modified", "true");
try {
await api.move([{ from: oldLink, to: newLink }]);
if (!this.isListing) {
@@ -105,6 +105,8 @@ export default {
return;
}
this.preselect = removePrefix(newLink);
this.reload = true;
} catch (e) {
this.$showError(e);

View File

@@ -30,6 +30,7 @@ export default {
ja: "日本語",
ko: "한국어",
"nl-be": "Dutch (Belgium)",
no: "Norsk",
pl: "Polski",
"pt-br": "Português",
pt: "Português (Brasil)",

View File

@@ -96,6 +96,9 @@ main {
height: 3em;
background: var(--background);
border-bottom: 1px solid var(--divider);
position: sticky;
z-index: 1000;
top: 4em;
}
.breadcrumbs span,

View File

@@ -329,6 +329,7 @@ main .spinner .bounce2 {
#editor-container {
display: flex;
flex-direction: column;
justify-content: center;
background-color: var(--background);
position: fixed;
padding-top: 4em;
@@ -351,6 +352,8 @@ main .spinner .bounce2 {
#editor-container .breadcrumbs {
height: 2.3em;
padding: 0 1em;
position: relative;
top: 0;
}
/*** RTL - flip and position arrow of path ***/

View File

@@ -27,6 +27,7 @@ import("dayjs/locale/vi");
import("dayjs/locale/zh-cn");
import("dayjs/locale/zh-tw");
import("dayjs/locale/cs");
import("dayjs/locale/nb");
// All i18n resources specified in the plugin `include` option can be loaded
// at once using the import syntax
@@ -101,7 +102,6 @@ export function detectLocale() {
case /^tr\b/.test(locale):
locale = "tr";
break;
// ua wasnt a valid locale for ukraine
case /^uk\b/.test(locale):
locale = "uk";
break;
@@ -115,6 +115,10 @@ export function detectLocale() {
case /^nl-be\b/.test(locale):
locale = "nl-be";
break;
case /^nb\b/.test(locale):
case /^no\b/.test(locale):
locale = "no";
break;
default:
locale = "en";
}

266
frontend/src/i18n/no.json Normal file
View File

@@ -0,0 +1,266 @@
{
"buttons": {
"cancel": "Avbryt",
"clear": "Fjern",
"close": "Lukk",
"continue": "Fortsett",
"copy": "Kopier",
"copyFile": "Fortsett",
"copyToClipboard": "Kopier til utklippstavlen",
"copyDownloadLinkToClipboard": "Kopier nedlastingslenken til utklippstavlen",
"create": "Opprett",
"delete": "Slett",
"download": "Nedlast",
"file": "Fil",
"folder": "Mappe",
"fullScreen": "Skru på fullskjerm",
"hideDotfiles": "Skjul punktfiler",
"info": "Info",
"more": "Meir",
"move": "Flytt",
"moveFile": "Flytt Fil",
"new": "Ny",
"next": "Neste",
"ok": "Ok",
"permalink": "Få permanent link",
"previous": "Tidligere",
"preview": "Forhåndsvisning",
"publish": "Publiser",
"rename": "Gi nytt navn",
"replace": "Bytt ut\n ",
"reportIssue": "Rapporter problem",
"save": "Lagre",
"schedule": "Planlegg ",
"search": "Søk",
"select": "Velg",
"selectMultiple": "Velg Fleire",
"share": "Del",
"shell": "Skru på shell",
"submit": "Send",
"switchView": "Skift visning",
"toggleSidebar": "Skru på sidebar",
"update": "Opptater",
"upload": "Last opp",
"openFile": "Open file",
"discardChanges": "Slett"
},
"download": {
"downloadFile": "Nedlast filen",
"downloadFolder": "Nedlast mappen",
"downloadSelected": "Nedlast merket"
},
"upload": {
"abortUpload": "Er du sikker på at du ønsker å avbryte?"
},
"errors": {
"forbidden": "Du har ikkje tilgang til denne filen.",
"internal": "Noko gikk virkelig galt.",
"notFound": "Denne lokasjonen kan ikkje bli nådd.",
"connection": "Denne serveren kan ikkje nås."
},
"files": {
"body": "Kropp",
"closePreview": "Lukk forhandsvisning",
"files": "Filer",
"folders": "Mappe",
"home": "Hjem",
"lastModified": "Sist endret",
"loading": "Laster....",
"lonely": "Det føltes ensomt her...",
"metadata": "Metadata",
"multipleSelectionEnabled": "Fleire seksjoner på",
"name": "Navn",
"size": "Størrelse",
"sortByLastModified": "Sorter etter sist endret",
"sortByName": "Sorter etter navn",
"sortBySize": "Sorter etter størrelse",
"noPreview": "Forhåndsvisning er ikkje tilgjengeleg for denne filen."
},
"help": {
"click": "velg fil eller katalog",
"ctrl": {
"click": "velg flere filer eller mapper",
"f": "opner søk",
"s": "lagr en fil eller last ned direktoratet der du er"
},
"del": "slett markert filer",
"doubleClick": "open en fil eller direktorat",
"esc": "visk av seleksjon og/eller lukk dette varselet",
"f1": "denne informasjonen",
"f2": "gi nytt navn til denne filen",
"help": "Hjelp"
},
"login": {
"createAnAccount": "Opprett ein konto",
"loginInstead": "Du har allerede ein konto",
"password": "Passord",
"passwordConfirm": "Passordbekreftelse",
"passwordsDontMatch": "Passordene samsvarer ikkje",
"signup": "Registrer deg",
"submit": "Logg inn",
"username": "Brukernavn",
"usernameTaken": "Brukernavn er allerede i bruk",
"wrongCredentials": "Feil legitimasjon"
},
"permanent": "Permanent",
"prompts": {
"copy": "Kopiere",
"copyMessage": "Velg hvor du vil kopiere filene dine:",
"currentlyNavigating": "Navigerer nå på:",
"deleteMessageMultiple": "Er du sikker på at du vil slette {count} fil(er)?",
"deleteMessageSingle": "Er du sikker på at du vil slette denne filen/mappen?",
"deleteMessageShare": "Er du sikker på at du vil slette denne delingen ({path})?",
"deleteUser": "Er du sikker at du vil slette denne brukeren?",
"deleteTitle": "Slett filer",
"displayName": "Vis Navn:",
"download": "Last ned filer",
"downloadMessage": "Velg kva format du ønsker å laste ned.",
"error": "Noko gikk galt.",
"fileInfo": "Fil informasjon",
"filesSelected": "{count} filer valgt.",
"lastModified": "Sist endret",
"move": "Flytt",
"moveMessage": "Velg nytt hjem for filen(e)/mappen(e)din:",
"newArchetype": "Opprett et nytt innlegg basert på en arketype. Filen din opprettes i innholdsmappen.",
"newDir": "Nytt Direktorat",
"newDirMessage": "Navn gi ditt nye direktorat",
"newFile": "Ny fil",
"newFileMessage": "Navn gi ditt nye fil",
"numberDirs": "Nummer av direktorat",
"numberFiles": "Nummer av filer",
"rename": "Gi nytt navn",
"renameMessage": "Sett inn nytt navn for",
"replace": "Bytt ut",
"replaceMessage": "En av filene du prøver å laste opp har et motstridende navn. Vil du hoppe over denne filen og fortsette opplastingen eller erstatte den eksisterende?\n",
"schedule": "Planlegg",
"scheduleMessage": "Velg en dato og et klokkeslett for å planlegge publiseringen av dette innlegget.",
"show": "Vis",
"size": "Størrelse",
"upload": "Last opp",
"uploadFiles": "Laster opp {filer} filer...",
"uploadMessage": "Velg et alternativ for opplasting.",
"optionalPassword": "Valgfritt passord",
"resolution": "Oppløysning",
"discardEditorChanges": "Er du sikker på at du vil forkaste endringene du har gjort?"
},
"search": {
"images": "Bilde",
"music": "Musikk",
"pdf": "PDF",
"pressToSearch": "Trykk enter for å søke...",
"search": "Søk...",
"typeToSearch": "Trykk for å søke...",
"types": "Typer",
"video": "Video"
},
"settings": {
"admin": "Admin",
"administrator": "Administrator",
"allowCommands": "Utfør kommandoer",
"allowEdit": "Rediger, gi nytt navn til og slett filer eller mapper",
"allowNew": "Opprett nye filer og direktorater",
"allowPublish": "Publiser nye innlegg og sider",
"allowSignup": "Tilat brukere å registrere seg",
"avoidChanges": "(la stå tomt for å unngå endringer)",
"branding": "Merkevarebygging",
"brandingDirectoryPath": "Bane for merkevarekatalog",
"brandingHelp": "Du kan tilpasse hvordan Filleser-instansen din ser ut og føles ved å endre navnet, erstatte logoen, legge til egendefinerte stiler og til og med deaktivere eksterne lenker til GitHub.\n\nFor mer informasjon om tilpasset merkevarebygging, se {0}.",
"changePassword": "Skift Passord",
"commandRunner": "Kommandoløper",
"commandRunnerHelp": "Her kan du angi kommandoer som skal utføres i de navngitte hendelsene. Du må skrive én per linje. Miljøvariablene {0} og {1} vil være tilgjengelige, siden de er {0} relative til {1}. For mer informasjon om denne funksjonen og de tilgjengelige miljøvariablene, vennligst les {2}.",
"commandsUpdated": "Komando opptatert!",
"createUserDir": "Opprett brukerens hjemmappe automatisk når du legger til en ny bruker",
"minimumPasswordLength": "Minimum passord lengde",
"tusUploads": "Klumpede opplastinger",
"tusUploadsHelp": "Filleseren støtter opplasting av delte filer, noe som gjør det mulig å lage effektive, pålitelige, gjenopptakbare og delte filer, selv på upålitelige nettverk.",
"tusUploadsChunkSize": "Angir maksimal størrelse på en forespørsel (direkte opplastinger vil bli brukt for mindre opplastinger). Du kan legge inn et heltall som angir bytestørrelsen, eller en streng som 10 MB, 1 GB osv.",
"tusUploadsRetryCount": "Antall nye forsøk som skal utføres hvis en del ikke lastes opp.",
"userHomeBasePath": "Basissti for brukerens hjemmekataloger",
"userScopeGenerationPlaceholder": "Omfanget vil bli generert automatisk",
"createUserHomeDirectory": "Opprett bruker hjemme direktorat",
"customStylesheet": "Egendefinert stilark",
"defaultUserDescription": "Dette er standardinnstillingene for nye brukere.",
"disableExternalLinks": "Deaktiver eksterne lenker (unntatt dokumentasjon)",
"disableUsedDiskPercentage": "Deaktiver grafen for prosentandelen brukt disk",
"documentation": "dokumentasjon",
"examples": "Eksempel",
"executeOnShell": "Kjør på skall",
"executeOnShellDescription": "Som standard kjører Filleseren kommandoene ved å kalle binærfilene direkte. Hvis du heller ønsker å kjøre dem på et skall (som Bash eller PowerShell), kan du definere det her med de nødvendige argumentene og flaggene. Hvis dette er angitt, vil kommandoen du kjører bli lagt til som et argument. Dette gjelder både brukerkommandoer og hendelseshooker.",
"globalRules": "Dette er et globalt sett med regler for tillatelse og forbud. De gjelder for alle brukere. Du kan definere spesifikke regler for hver brukers innstillinger for å overstyre disse.",
"globalSettings": "Globale Innstillinger",
"hideDotfiles": "Skjul punktfiler",
"insertPath": "Sett inn banen",
"insertRegex": "sett inn regex-uttrykk",
"instanceName": "Forekomstnavn",
"language": "Språk",
"lockPassword": "Hindre brukeren i å endre passordet",
"newPassword": "Sett ditt nye passord",
"newPasswordConfirm": "Bekreft ditt nye passord",
"newUser": "Ny bruker",
"password": "Passord",
"passwordUpdated": "Passord opptatert!",
"path": "Veg",
"perm": {
"create": "Opprett filer og direktorater",
"delete": "Slett filer og direktorater",
"download": "Nedlast",
"execute": "Utfør kommandoer",
"modify": "Endre filer",
"rename": "Gi nytt navn eller flytt filer og direktorater",
"share": "Del filer"
},
"permissions": "Tilaterser",
"permissionsHelp": "Du kan angi brukeren som administrator eller velge tillatelsene individuelt. Hvis du velger «Administrator», vil alle de andre alternativene bli automatisk avkrysset. Administrasjon av brukere er fortsatt et privilegium for en administrator.\n",
"profileSettings": "Profil Innstilinger",
"ruleExample1": "forhindrer tilgang til noen dotfiler (som .git, .gitignore) i alle mapper.\n",
"ruleExample2": "blokkerer tilgangen til filen med navnet Caddyfile på roten av omfanget.",
"rules": "Regler",
"rulesHelp": "Her kan du definere et sett med tillatelses- og forbudsregler for denne spesifikke brukeren. De blokkerte filene vil ikke vises i listene, og de vil ikke være tilgjengelige for brukeren. Vi støtter regex og stier i forhold til brukerens omfang.",
"scope": "Omfang",
"setDateFormat": "Sett eksakt dato format",
"settingsUpdated": "Innstilinger opptatert!",
"shareDuration": "Del tidsbruk",
"shareManagement": "Del Ledelse",
"shareDeleted": "Delte ting slettet!",
"singleClick": "Bruk enkeltklikk for å åpne filer og mapper",
"themes": {
"default": "Systemstandard",
"dark": "Mørk",
"light": "Lyst",
"title": "Tema"
},
"user": "Bruker",
"userCommands": "Kommando",
"userCommandsHelp": "En mellomromsseparert liste med tilgjengelige kommandoer for denne brukeren. Eksempel:\n",
"userCreated": "Bruker opprettet!",
"userDefaults": "Bruker systemstandard instillinger",
"userDeleted": "Bruker slettet!",
"userManagement": "Brukeradministrasjon",
"userUpdated": "Bruker opprettet!",
"username": "Brukernavn",
"users": "Bruker"
},
"sidebar": {
"help": "Hjelp",
"hugoNew": "Hugo Ny",
"login": "Logg inn",
"logout": "Logg Ut",
"myFiles": "Mine filer",
"newFile": "Ny fil",
"newFolder": "Ny mappe",
"preview": "Forhåndsvis",
"settings": "Innstillinger",
"signup": "Registrer deg",
"siteSettings": "Side innstillinger"
},
"success": {
"linkCopied": "Link koppiert!"
},
"time": {
"days": "Dager",
"hours": "Timer",
"minutes": "Minutt",
"seconds": "Sekunder",
"unit": "Time format"
}
}

View File

@@ -3,7 +3,7 @@
"cancel": "Zrušiť",
"clear": "Zrušiť výber",
"close": "Zavrieť",
"continue": "Continue",
"continue": "Pokračovať",
"copy": "Kopírovať",
"copyFile": "Kopírovať súbor",
"copyToClipboard": "Kopírovať do schránky",

View File

@@ -9,6 +9,7 @@ export const useFileStore = defineStore("file", {
selected: number[];
multiple: boolean;
isFiles: boolean;
preselect: string | null;
} => ({
req: null,
oldReq: null,
@@ -16,6 +17,7 @@ export const useFileStore = defineStore("file", {
selected: [],
multiple: false,
isFiles: false,
preselect: null,
}),
getters: {
selectedCount: (state) => state.selected.length,

View File

@@ -1,7 +1,7 @@
<template>
<div>
<header-bar
v-if="error || fileStore.req?.type === null"
v-if="error || fileStore.req?.type === undefined"
showMenu
showLogo
/>
@@ -9,7 +9,7 @@
<breadcrumbs base="/files" />
<errors v-if="error" :errorCode="error.status" />
<component v-else-if="currentView" :is="currentView"></component>
<div v-else-if="currentView !== null">
<div v-else>
<h2 class="message delayed">
<div class="spinner">
<div class="bounce1"></div>
@@ -102,15 +102,7 @@ onUnmounted(() => {
fetchDataController.abort();
});
watch(route, (to, from) => {
if (from.path.endsWith("/")) {
window.sessionStorage.setItem(
"listFrozen",
(!to.path.endsWith("/")).toString()
);
} else if (to.path.endsWith("/")) {
fileStore.updateRequest(null);
}
watch(route, () => {
fetchData();
});
watch(reload, (newValue) => {
@@ -122,6 +114,32 @@ watch(uploadError, (newValue) => {
// Define functions
const applyPreSelection = () => {
const preselect = fileStore.preselect;
fileStore.preselect = null;
if (!fileStore.req?.isDir || fileStore.oldReq === null) return;
let index = -1;
if (preselect) {
// Find item with the specified path
index = fileStore.req.items.findIndex((item) => item.path === preselect);
} else if (fileStore.oldReq.path.startsWith(fileStore.req.path)) {
// Get immediate child folder of the previous path
const name = fileStore.oldReq.path
.substring(fileStore.req.path.length)
.split("/")
.shift();
index = fileStore.req.items.findIndex(
(val) => val.path == fileStore.req!.path + name
);
}
if (index === -1) return;
fileStore.selected.push(index);
};
const fetchData = async () => {
// Reset view information.
fileStore.reload = false;
@@ -130,12 +148,7 @@ const fetchData = async () => {
layoutStore.closeHovers();
// Set loading to true and reset the error.
if (
window.sessionStorage.getItem("listFrozen") !== "true" &&
window.sessionStorage.getItem("modified") !== "true"
) {
layoutStore.loading = true;
}
layoutStore.loading = true;
error.value = null;
let url = route.path;
@@ -149,6 +162,9 @@ const fetchData = async () => {
fileStore.updateRequest(res);
document.title = `${res.name || t("sidebar.myFiles")} - ${t("files.files")} - ${name}`;
layoutStore.loading = false;
// Selects the post-reload target item or the previously visited child folder
applyPreSelection();
} catch (err) {
if (err instanceof StatusError && err.is_canceled) {
return;

View File

@@ -32,17 +32,25 @@
/>
</header-bar>
<Breadcrumbs base="/files" noLink />
<!-- preview container -->
<div
v-show="isPreview && isMarkdownFile"
id="preview-container"
class="md_preview"
v-html="previewContent"
></div>
<div class="loading delayed" v-if="layoutStore.loading">
<div class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
</div>
<template v-else>
<Breadcrumbs base="/files" noLink />
<form v-show="!isPreview || !isMarkdownFile" id="editor"></form>
<div
v-show="isPreview && isMarkdownFile"
id="preview-container"
class="md_preview"
v-html="previewContent"
></div>
<form v-show="!isPreview || !isMarkdownFile" id="editor"></form>
</template>
</div>
</template>
@@ -146,12 +154,19 @@ onBeforeUnmount(() => {
});
onBeforeRouteUpdate((to, from, next) => {
if (!editor.value?.session.getUndoManager().isClean()) {
layoutStore.showHover("discardEditorChanges");
next(false);
} else {
if (editor.value?.session.getUndoManager().isClean()) {
next();
return;
}
layoutStore.showHover({
prompt: "discardEditorChanges",
confirm: (event: Event) => {
event.preventDefault();
next();
},
});
});
const keyEvent = (event: KeyboardEvent) => {
@@ -216,13 +231,6 @@ const decreaseFontSize = () => {
};
const close = () => {
if (!editor.value?.session.getUndoManager().isClean()) {
layoutStore.showHover("discardEditorChanges");
return;
}
fileStore.updateRequest(null);
const uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri });
};

View File

@@ -303,6 +303,7 @@ import {
import { useRoute } from "vue-router";
import { useI18n } from "vue-i18n";
import { storeToRefs } from "pinia";
import { removePrefix } from "@/api/utils";
const showLimit = ref<number>(50);
const columnWidth = ref<number>(280);
@@ -420,25 +421,19 @@ const isMobile = computed(() => {
watch(req, () => {
// Reset the show value
if (
window.sessionStorage.getItem("listFrozen") !== "true" &&
window.sessionStorage.getItem("modified") !== "true"
) {
showLimit.value = 50;
showLimit.value = 50;
nextTick(() => {
// Ensures that the listing is displayed
// How much every listing item affects the window height
setItemWeight();
nextTick(() => {
// Ensures that the listing is displayed
// How much every listing item affects the window height
setItemWeight();
// Scroll to the item opened previously
if (!revealPreviousItem()) {
// Fill and fit the window with listing items
fillWindow(true);
});
}
if (req.value?.isDir) {
window.sessionStorage.setItem("listFrozen", "false");
window.sessionStorage.setItem("modified", "false");
}
}
});
});
onMounted(() => {
@@ -448,8 +443,11 @@ onMounted(() => {
// How much every listing item affects the window height
setItemWeight();
// Fill and fit the window with listing items
fillWindow(true);
// Scroll to the item opened previously
if (!revealPreviousItem()) {
// Fill and fit the window with listing items
fillWindow(true);
}
// Add the needed event listeners to the window and document.
window.addEventListener("keydown", keyEvent);
@@ -589,10 +587,13 @@ const paste = (event: Event) => {
return;
}
const preselect = removePrefix(route.path) + items[0].name;
let action = (overwrite: boolean, rename: boolean) => {
api
.copy(items, overwrite, rename)
.then(() => {
fileStore.preselect = preselect;
fileStore.reload = true;
})
.catch($showError);
@@ -604,6 +605,7 @@ const paste = (event: Event) => {
.move(items, overwrite, rename)
.then(() => {
clipboardStore.resetClipboard();
fileStore.preselect = preselect;
fileStore.reload = true;
})
.catch($showError);
@@ -731,6 +733,8 @@ const drop = async (event: DragEvent) => {
const conflict = upload.checkConflict(files, items);
const preselect = removePrefix(path) + (files[0].fullPath || files[0].name);
if (conflict) {
layoutStore.showHover({
prompt: "replace",
@@ -738,11 +742,13 @@ const drop = async (event: DragEvent) => {
event.preventDefault();
layoutStore.closeHovers();
upload.handleFiles(files, path, false);
fileStore.preselect = preselect;
},
confirm: (event: Event) => {
event.preventDefault();
layoutStore.closeHovers();
upload.handleFiles(files, path, true);
fileStore.preselect = preselect;
},
});
@@ -750,6 +756,7 @@ const drop = async (event: DragEvent) => {
}
upload.handleFiles(files, path);
fileStore.preselect = preselect;
};
const uploadInput = (event: Event) => {
@@ -953,4 +960,21 @@ const fillWindow = (fit = false) => {
// Set the number of displayed items
showLimit.value = showQuantity > totalItems ? totalItems : showQuantity;
};
const revealPreviousItem = () => {
if (!fileStore.req || !fileStore.oldReq) return;
const index = fileStore.selected[0];
if (index === undefined) return;
showLimit.value =
index + Math.ceil((window.innerHeight * 2) / itemWeight.value);
nextTick(() => {
const items = document.querySelectorAll("#listing .item");
items[index].scrollIntoView({ block: "center" });
});
return true;
};
</script>

View File

@@ -301,10 +301,8 @@ watch(route, () => {
// Specify hooks
onMounted(async () => {
window.addEventListener("keydown", key);
if (fileStore.oldReq) {
listing.value = fileStore.oldReq.items;
updatePreview();
}
listing.value = fileStore.oldReq?.items ?? null;
updatePreview();
});
onBeforeUnmount(() => window.removeEventListener("keydown", key));
@@ -317,11 +315,16 @@ const deleteFile = () => {
if (listing.value === null) {
return;
}
listing.value = listing.value.filter((item) => item.name !== name.value);
const index = listing.value.findIndex((item) => item.name == name.value);
listing.value.splice(index, 1);
if (hasNext.value) {
next();
} else if (!hasPrevious.value && !hasNext.value) {
const nearbyItem = listing.value[Math.max(0, index - 1)];
fileStore.preselect = nearbyItem?.path;
close();
} else {
prev();
@@ -427,8 +430,6 @@ const toggleNavigation = throttle(function () {
}, 500);
const close = () => {
fileStore.updateRequest(null);
const uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri });
};

View File

@@ -5,6 +5,7 @@
"exclude": [
"src/components/Shell.vue",
"src/components/prompts/Copy.vue",
"src/components/prompts/Move.vue",
"src/components/prompts/Delete.vue",
"src/components/prompts/FileList.vue",
"src/components/prompts/Rename.vue",

2
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/filebrowser/filebrowser/v2
go 1.23.0
go 1.24
require (
github.com/asdine/storm/v3 v3.2.1

View File

@@ -1,6 +1,6 @@
module github.com/filebrowser/filebrowser/v2/tools
go 1.23.0
go 1.24
require (
github.com/golangci/golangci-lint/v2 v2.1.6