diff --git a/app/renderer/css/main.css b/app/renderer/css/main.css index 0898191f5..02cd873cb 100644 --- a/app/renderer/css/main.css +++ b/app/renderer/css/main.css @@ -45,7 +45,7 @@ body { #view-controls-container { height: calc(100% - 208px); scrollbar-gutter: stable both-edges; - overflow-y: hidden; + overflow: hidden; } #view-controls-container::-webkit-scrollbar { @@ -419,6 +419,10 @@ webview.focus { left: -5px; } +.sortable-chosen .server-tooltip { + display: none; +} + #collapse-button { bottom: 30px; left: 20px; diff --git a/app/renderer/js/main.ts b/app/renderer/js/main.ts index 7b4abce90..2c32a1ae6 100644 --- a/app/renderer/js/main.ts +++ b/app/renderer/js/main.ts @@ -6,6 +6,7 @@ import url from "node:url"; import {Menu, app, dialog, session} from "@electron/remote"; import * as remote from "@electron/remote"; import * as Sentry from "@sentry/electron/renderer"; +import SortableJS from "sortablejs"; import type {Config} from "../../common/config-util.js"; import * as ConfigUtil from "../../common/config-util.js"; @@ -57,7 +58,7 @@ const dingSound = new Audio( export class ServerManagerView { $addServerButton: HTMLButtonElement; - $tabsContainer: Element; + $tabsContainer: HTMLElement; $reloadButton: HTMLButtonElement; $loadingIndicator: HTMLButtonElement; $settingsButton: HTMLButtonElement; @@ -81,6 +82,7 @@ export class ServerManagerView { tabIndex: number; presetOrgs: string[]; preferenceView?: PreferenceView; + sortableSidebar?: SortableJS; constructor() { this.$addServerButton = document.querySelector("#add-tab")!; this.$tabsContainer = document.querySelector("#tabs-container")!; @@ -235,6 +237,29 @@ export class ServerManagerView { initSidebar(): void { const showSidebar = ConfigUtil.getConfigItem("showSidebar", true); this.toggleSidebar(showSidebar); + this.sortableSidebar = new SortableJS(this.$tabsContainer, { + animation: 150, + onEnd: (event: SortableJS.SortableEvent) => { + // Update the domain order in the database + if ( + event.oldIndex !== null && + event.newIndex !== null && + event.oldIndex !== event.newIndex + ) { + DomainUtil.updateDomainOrder( + event.oldIndex ?? 0, + event.newIndex ?? 0, + ); + + // Update the current active tab index + this.activeTabIndex = event.newIndex ?? 0; + ConfigUtil.setConfigItem("lastActiveTab", event.newIndex ?? 0); + + // Reload the app to give the tabs their new indexes + ipcRenderer.send("reload-full-app"); + } + }, + }); } // Remove the stale UA string from the disk if the app is not freshly diff --git a/app/renderer/js/utils/domain-util.ts b/app/renderer/js/utils/domain-util.ts index 465bb3b55..73a394045 100644 --- a/app/renderer/js/utils/domain-util.ts +++ b/app/renderer/js/utils/domain-util.ts @@ -72,6 +72,26 @@ export function updateDomain(index: number, server: ServerConf): void { db.push(`/domains[${index}]`, server, true); } +export function updateDomainOrder(oldIndex: number, newIndex: number): void { + const domains = getDomains(); + + if ( + !( + oldIndex < 0 || + oldIndex >= domains.length || + newIndex < 0 || + newIndex >= domains.length + ) + ) { + const [movedDomain] = domains.splice(oldIndex, 1); + domains.splice(newIndex, 0, movedDomain); + + for (const [index, domain] of domains.entries()) { + updateDomain(index, domain); + } + } +} + export async function addDomain(server: { url: string; alias: string; diff --git a/package-lock.json b/package-lock.json index 80cd10cd0..14e219b9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@types/i18n": "^0.13.1", "@types/node": "~18.17.19", "@types/requestidlecallback": "^0.3.4", + "@types/sortablejs": "^1.15.8", "@types/yaireo__tagify": "^4.3.2", "@yaireo/tagify": "^4.5.0", "adm-zip": "^0.5.5", @@ -43,6 +44,7 @@ "prettier": "^3.0.3", "rimraf": "^5.0.0", "semver": "^7.3.5", + "sortablejs": "^1.15.2", "stylelint": "^16.1.0", "stylelint-config-standard": "^36.0.0", "tape": "^5.2.2", @@ -2207,6 +2209,11 @@ "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, + "node_modules/@types/sortablejs": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz", + "integrity": "sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==" + }, "node_modules/@types/verror": { "version": "1.10.9", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.9.tgz", @@ -9508,6 +9515,11 @@ "npm": ">= 3.0.0" } }, + "node_modules/sortablejs": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz", + "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index 6076f3a4c..37a18ab5d 100644 --- a/package.json +++ b/package.json @@ -155,6 +155,7 @@ "@types/i18n": "^0.13.1", "@types/node": "~18.17.19", "@types/requestidlecallback": "^0.3.4", + "@types/sortablejs": "^1.15.8", "@types/yaireo__tagify": "^4.3.2", "@yaireo/tagify": "^4.5.0", "adm-zip": "^0.5.5", @@ -176,6 +177,7 @@ "prettier": "^3.0.3", "rimraf": "^5.0.0", "semver": "^7.3.5", + "sortablejs": "^1.15.2", "stylelint": "^16.1.0", "stylelint-config-standard": "^36.0.0", "tape": "^5.2.2",