From 8179ed0f3e479ec0a221c848dab49393ce90aabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C5=ABdolfs=20O=C5=A1i=C5=86=C5=A1?= Date: Tue, 3 Sep 2024 15:41:36 +0200 Subject: [PATCH] Add our own router --- package-lock.json | 468 +++++++++++++-------------- package.json | 2 +- public/index.css | 7 - src/App.svelte | 46 ++- src/components/Header.svelte | 23 +- src/components/Icon.svelte | 3 + src/components/Link.svelte | 39 +++ src/lib/mutexExecutor.ts | 113 +++++++ src/lib/router.ts | 150 +++++++++ src/lib/router/definitions.ts | 38 +++ src/views/AuthenticationError.svelte | 36 +++ src/views/DesignSystem.svelte | 36 +-- src/views/Home.svelte | 8 + src/views/Startup.svelte | 47 --- 14 files changed, 684 insertions(+), 332 deletions(-) create mode 100644 src/components/Link.svelte create mode 100644 src/lib/mutexExecutor.ts create mode 100644 src/lib/router.ts create mode 100644 src/lib/router/definitions.ts create mode 100644 src/views/AuthenticationError.svelte create mode 100644 src/views/Home.svelte delete mode 100644 src/views/Startup.svelte diff --git a/package-lock.json b/package-lock.json index de7074a..620421d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@sveltejs/vite-plugin-svelte": "^3.1.2", "@tauri-apps/cli": "^2.0.0-rc.1", "@tsconfig/svelte": "^5.0.4", + "baconjs": "^3.0.19", "eslint": "^9.9.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.43.0", @@ -26,7 +27,6 @@ "svelte": "^4.2.19", "svelte-check": "^4.0.0", "svelte-eslint-parser": "^0.41.0", - "svelte-routing": "^2.13.0", "tslib": "^2.7.0", "typescript": "^5.2.2", "typescript-eslint": "^8.4.0", @@ -432,6 +432,18 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", @@ -478,35 +490,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", - "dev": true, - "dependencies": { - "acorn": "^8.12.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@eslint/js": { "version": "9.9.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz", @@ -882,23 +865,18 @@ } }, "node_modules/@tauri-apps/api": { - "version": "2.0.0-beta.15", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.0-beta.15.tgz", - "integrity": "sha512-H9w6iISmR+NvH4XuyCZB4zDN10tf9RFt6i/9JHEjaRhAowdAaJ+oiXq/3kedizNClHMtbTQ5j0oqDVPkZDAI8g==", - "engines": { - "node": ">= 18.18", - "npm": ">= 6.6.0", - "yarn": ">= 1.19.1" - }, + "version": "2.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.0-rc.4.tgz", + "integrity": "sha512-UNiIhhKG08j4ooss2oEEVexffmWkgkYlC2M3GcX3VPtNsqFgVNL8Mcw/4Y7rO9M9S+ffAMnLOF5ypzyuyb8tyg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/tauri" } }, "node_modules/@tauri-apps/cli": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.0.0-rc.1.tgz", - "integrity": "sha512-9AzVrUMdb6EZ/Lwtdqt03XqqG6d/3gTJPOw2E9zmCHprJWEwqEp4JIVHYYfrqkkZyKclD3m5ggXwfYwclcYLdw==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.0.0-rc.10.tgz", + "integrity": "sha512-mnoMyeD65DoVWzrLiLRW8Ns5Aktn9Ua7eKTOUEPq+r+1sQtWKxfnYTBEbEWnivduLhJCEDqGP5tyJaPcVXcEzA==", "dev": true, "bin": { "tauri": "tauri.js" @@ -911,22 +889,22 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.0.0-rc.1", - "@tauri-apps/cli-darwin-x64": "2.0.0-rc.1", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.0.0-rc.1", - "@tauri-apps/cli-linux-arm64-gnu": "2.0.0-rc.1", - "@tauri-apps/cli-linux-arm64-musl": "2.0.0-rc.1", - "@tauri-apps/cli-linux-x64-gnu": "2.0.0-rc.1", - "@tauri-apps/cli-linux-x64-musl": "2.0.0-rc.1", - "@tauri-apps/cli-win32-arm64-msvc": "2.0.0-rc.1", - "@tauri-apps/cli-win32-ia32-msvc": "2.0.0-rc.1", - "@tauri-apps/cli-win32-x64-msvc": "2.0.0-rc.1" + "@tauri-apps/cli-darwin-arm64": "2.0.0-rc.10", + "@tauri-apps/cli-darwin-x64": "2.0.0-rc.10", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.0.0-rc.10", + "@tauri-apps/cli-linux-arm64-gnu": "2.0.0-rc.10", + "@tauri-apps/cli-linux-arm64-musl": "2.0.0-rc.10", + "@tauri-apps/cli-linux-x64-gnu": "2.0.0-rc.10", + "@tauri-apps/cli-linux-x64-musl": "2.0.0-rc.10", + "@tauri-apps/cli-win32-arm64-msvc": "2.0.0-rc.10", + "@tauri-apps/cli-win32-ia32-msvc": "2.0.0-rc.10", + "@tauri-apps/cli-win32-x64-msvc": "2.0.0-rc.10" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-rc.1.tgz", - "integrity": "sha512-dJxyAi4P9fOkklBZXuwUVnHgdM/20fgM4zYdfejQfju5+q9GUqnMbrrIUqlJbQGf8EnrIdSWnieO8wU8GOwT0g==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-rc.10.tgz", + "integrity": "sha512-oAuG3n/dIqK5ZedknF1QOgVDlpEepAaaIFHpUi+eIdG1MFp82jgyHqplveVZ95F16j7RhjIMaEhiTF6cGR/baA==", "cpu": [ "arm64" ], @@ -940,9 +918,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-rc.1.tgz", - "integrity": "sha512-dwrqLzNIFk8a1Vf2YI8axHm7uvLfo4M4TSWCw2ZkgeSGWWK6Y6CYVZbBEjOEGIOf+GFAa9rVOSZRuMwpiufNng==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-rc.10.tgz", + "integrity": "sha512-n4ul0XUBqrA7KbNY6Vo52EBNqTXogYuV2qi5RWR0bIJF/A/vYjZ3LcC1TXXo/X57sDN55LWORrBe4c4Ds8MZrA==", "cpu": [ "x64" ], @@ -956,9 +934,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-rc.1.tgz", - "integrity": "sha512-jvMF4UNc3Jr/xHnw+4NNsWfk8WrcFrQVImAtKlCev9QepqfBmDh+FgXTvfysoBCSxEBS626TvYms3OhI0LOO4A==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-rc.10.tgz", + "integrity": "sha512-RByz0zRbngps5QMQVsbgCD03TiCMxwAhaZhNtojXQ2AiJFkv1Mu68W/prbpWucw6Ep1nM3/yTIm0aL6ozdh/gw==", "cpu": [ "arm" ], @@ -972,9 +950,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-rc.1.tgz", - "integrity": "sha512-TGmadGW8BjTq864AYrv/u+vTnAodnOuzv1VCUOV23O8st35GZG6V47jwNsSjQjhrcO1XzmJiRAtrcVKuTZ/xUA==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-rc.10.tgz", + "integrity": "sha512-ZqpbDIMp5b0jz1ddutJH6S5geLaBEmsMG6eZix+MgcZZRyEfahTMGCq3xkvv+tnrNNq7drvwBISCVSSS0zu3wQ==", "cpu": [ "arm64" ], @@ -988,9 +966,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-rc.1.tgz", - "integrity": "sha512-+SJsRTW0PvvD7awEn+tIPJ3s12s6RpKXdOib2mztoKocasrGRfz+EFZuXc42Iwk3xROsrQkiw2UAmcNLkW5NwA==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-rc.10.tgz", + "integrity": "sha512-EVh1xPqs5bi0aBYbv6Iy1ooFClyK6/wIsNw9DyxWwhPz9I7UNpDAgHm6lOhkMH26Cp/eQPiEA8OdfOLTfCY81A==", "cpu": [ "arm64" ], @@ -1004,9 +982,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-rc.1.tgz", - "integrity": "sha512-vjPrj2btS97IOp6cU42IrkI49SQZDSg8TPqwOwFqyQeAotCT1i0F38pLZWe1gLyPUowO8XdaaBdwYg6IRDKcZg==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-rc.10.tgz", + "integrity": "sha512-ZLcXJbRRMfgSkZdxBegP/4PlXkoVR1zpx2pE7mKkRgyvwJCx+A2f0+IZM+VVu/WRECxAdzVCbgxztTAOoLkdrg==", "cpu": [ "x64" ], @@ -1020,9 +998,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-rc.1.tgz", - "integrity": "sha512-MrE68/u6rMrkM1uM/DR1MNnMXiYebhSPGqqxshJ12SmFdk3yQ/Z73Wzvk8xv78eOExh4lTtEXI22YwaBCf9AEg==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-rc.10.tgz", + "integrity": "sha512-IgzRemlQT+SHfb2x8kq32xKGnR3r7S69Ogv5pBKIDX1/G2qQofM6wfy0OHnAyS4Bj0y2lZPjiYQBwmLIkK/BNw==", "cpu": [ "x64" ], @@ -1036,9 +1014,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.0.0-rc.1.tgz", - "integrity": "sha512-SLulbiUjg8BGf/zX+U1PGjB+JpsN2nLRGuW07BYwSDW3s3mp2aagLuOwaTaOPBrDzfIMRYq8KT54A4jfRjEZlg==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.0.0-rc.10.tgz", + "integrity": "sha512-RmSh5omDiCEZgw1fOhdEFi6MzAQ1rQBmvTM13K2p8XUxxaYb/MHYYZbNEMqxqWvsg4fidZ8hNSqRkB7YCCWWgg==", "cpu": [ "arm64" ], @@ -1052,9 +1030,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-rc.1.tgz", - "integrity": "sha512-rz85riTjcWdZlgTku6HcBx625Otdc0/NwDjRXgdXakL1ybw7E+G5YlLZNcQX25u17RKUAWX/2/VZ1pSz945Ovw==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-rc.10.tgz", + "integrity": "sha512-6zxZ1KnKqflC5YpJmXZyNNVaRXMdOiRijimua8zLzfoAo+adb6gd8V4o03rZF3BPHtmd35rPkZHlgMlg/th2Bw==", "cpu": [ "ia32" ], @@ -1068,9 +1046,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-rc.1.tgz", - "integrity": "sha512-aRO70dDbn4w3CbALMG+b7g460xmbSTuUiGmRh0r/MNVeoZk/YbqluBUyhXdWGxJb8OVubw/4RlczKYcPmJceMw==", + "version": "2.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-rc.10.tgz", + "integrity": "sha512-D7L9QnxUJcSykQ9S8AQ0CEdxaw3IMoyAwv2LR7x+w/j7Jg3UsEgnsX5ePkShBiqSmu/UXfSuQeGvAoA8kSLiUw==", "cpu": [ "x64" ], @@ -1084,28 +1062,19 @@ } }, "node_modules/@tauri-apps/plugin-shell": { - "version": "2.0.0-beta.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.0-beta.8.tgz", - "integrity": "sha512-rFXI6MvsCdSGbuKbDu/NaOePREb9YTVTdeugHdvvljnKWW3dvmThBb2h/8Hxj+Z7Cd8MUoRxPeJWUZbPbJ2Imw==", + "version": "2.0.0-rc.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.0-rc.1.tgz", + "integrity": "sha512-JtNROc0rqEwN/g93ig5pK4cl1vUo2yn+osCpY9de64cy/d9hRzof7AuYOgvt/Xcd5VPQmlgo2AGvUh5sQRSR1A==", "dependencies": { - "@tauri-apps/api": "2.0.0-beta.15" + "@tauri-apps/api": "^2.0.0-rc.4" } }, "node_modules/@tauri-apps/plugin-window-state": { - "version": "2.0.0-rc.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-window-state/-/plugin-window-state-2.0.0-rc.0.tgz", - "integrity": "sha512-lR8reD+D1yIHT+53v56WltLS0+Y2zIkKqTuwrvz1yNbY5Hk4Z6foFV2Byo4kJAAvi5vbeGtvxYAjSiczZK5euw==", + "version": "2.0.0-rc.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-window-state/-/plugin-window-state-2.0.0-rc.1.tgz", + "integrity": "sha512-fQG6G6G+b3mx2QE6dAFxl3iyKvz35DpGggIczKn+qRc4Mdjsb9y42iJMUpMpZAC2q9j8h3LfknguCacifVP5lA==", "dependencies": { - "@tauri-apps/api": "^2.0.0-rc.0" - } - }, - "node_modules/@tauri-apps/plugin-window-state/node_modules/@tauri-apps/api": { - "version": "2.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.0-rc.2.tgz", - "integrity": "sha512-qIJJ1gKkzpPmEmIGTpja6XhuCD8A+vEh/wKaO7TzbxKiws5w3E+Kg4sBtwrH85hjMyqT0SgAUVaUk9XS7FBg3g==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/tauri" + "@tauri-apps/api": "^2.0.0-rc.4" } }, "node_modules/@tsconfig/svelte": { @@ -1120,17 +1089,6 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/@types/node": { - "version": "22.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", - "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "undici-types": "~6.19.2" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz", @@ -1337,6 +1295,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -1411,6 +1381,18 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1435,6 +1417,12 @@ "node": ">= 0.4" } }, + "node_modules/baconjs": { + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/baconjs/-/baconjs-3.0.19.tgz", + "integrity": "sha512-/h7R6hTql8yk1FxYk/bTALea7fGcSJrUoLHFhX1WEkfI4C2mbR4sPbaNd0EhUIDJi3QwTBWEFHh7xEAaz3A3/A==", + "dev": true + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1524,6 +1512,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/code-red": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", @@ -1601,9 +1601,9 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -1812,34 +1812,6 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", @@ -1855,7 +1827,7 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { + "node_modules/eslint-visitor-keys": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", @@ -1867,7 +1839,7 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/espree": { + "node_modules/espree": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", @@ -1884,35 +1856,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -1986,6 +1929,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2007,6 +1962,20 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz", + "integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -2081,15 +2050,15 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/globals": { @@ -2120,9 +2089,9 @@ } }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -2323,12 +2292,12 @@ "dev": true }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/mdn-data": { @@ -2359,6 +2328,18 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2508,18 +2489,20 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "optional": true, + "peer": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -2625,9 +2608,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", - "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -2712,6 +2695,18 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2927,34 +2922,6 @@ "typescript": ">=5.0.0" } }, - "node_modules/svelte-check/node_modules/fdir": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.3.0.tgz", - "integrity": "sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==", - "dev": true, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/svelte-check/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/svelte-eslint-parser": { "version": "0.41.0", "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.0.tgz", @@ -2982,6 +2949,51 @@ } } }, + "node_modules/svelte-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/svelte-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/svelte-hmr": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", @@ -2994,12 +3006,6 @@ "svelte": "^3.19.0 || ^4.0.0" } }, - "node_modules/svelte-routing": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-2.13.0.tgz", - "integrity": "sha512-/NTxqTwLc7Dq306hARJrH2HLXOBtKd7hu8nxgoFDlK0AC4SOKnzisiX/9m8Uksei1QAWtlAEdF91YphNM8iDMg==", - "dev": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3084,14 +3090,6 @@ } } }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 2268593..a784bd0 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@tauri-apps/cli": "^2.0.0-rc.1", "@tsconfig/svelte": "^5.0.4", "eslint": "^9.9.1", + "baconjs": "^3.0.19", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.43.0", "prettier": "^3.3.3", @@ -34,7 +35,6 @@ "svelte": "^4.2.19", "svelte-check": "^4.0.0", "svelte-eslint-parser": "^0.41.0", - "svelte-routing": "^2.13.0", "tslib": "^2.7.0", "typescript": "^5.2.2", "typescript-eslint": "^8.4.0", diff --git a/public/index.css b/public/index.css index 412211b..72f71d3 100644 --- a/public/index.css +++ b/public/index.css @@ -15,10 +15,3 @@ html { height: 100%; width: 100%; } - -code { - font-family: var(--font-family-monospace); - font-size: var(--font-size-small); - background-color: var(--color-fill-ghost); - padding: 0.125rem 0.25rem; -} diff --git a/src/App.svelte b/src/App.svelte index 96051b0..493a4d6 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,16 +1,46 @@ - - - - - +{#if $activeRouteStore.resource === "booting"} + +{:else if $activeRouteStore.resource === "home"} + +{:else if $activeRouteStore.resource === "authenticationError"} + +{:else if $activeRouteStore.resource === "designSystem"} + +{:else} + {unreachable($activeRouteStore)} +{/if} diff --git a/src/components/Header.svelte b/src/components/Header.svelte index dc515ef..2a7ffc5 100644 --- a/src/components/Header.svelte +++ b/src/components/Header.svelte @@ -5,6 +5,8 @@ import Icon from "./Icon.svelte"; import Popover from "./Popover.svelte"; import ThemeSwitch from "./ThemeSwitch.svelte"; + + export let currentPage: string;
-
- - + - Repositories + {currentPage} diff --git a/src/components/Icon.svelte b/src/components/Icon.svelte index 2c8a716..b30200b 100644 --- a/src/components/Icon.svelte +++ b/src/components/Icon.svelte @@ -26,6 +26,9 @@ svg { display: flex; flex-shrink: 0; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; } diff --git a/src/components/Link.svelte b/src/components/Link.svelte new file mode 100644 index 0000000..a751fd0 --- /dev/null +++ b/src/components/Link.svelte @@ -0,0 +1,39 @@ + + + + + + + diff --git a/src/lib/mutexExecutor.ts b/src/lib/mutexExecutor.ts new file mode 100644 index 0000000..b9f545b --- /dev/null +++ b/src/lib/mutexExecutor.ts @@ -0,0 +1,113 @@ +// Copyright ยฉ 2021 The Radicle Upstream Contributors +// +// This file is part of radicle-upstream, distributed under the GPLv3 +// with Radicle Linking Exception. For full terms see the included +// LICENSE file. + +//@ts-expect-error the typescript bindings are out of date. +import * as Bacon from "baconjs"; + +// A task executor that runs only one task concurrently. If a new task +// is run, any previously running task is aborted and the promise +// returned from `run()` will return undefined. +// +// import * as mutexExecutor from "ui/src/mutexExecutor" +// const executor = mutexExecutor.create() +// const first = await executor.run(async () => { +// await sleep(1000) +// return "first" +// }) +// const second = await executor.run(async () => "second") +// +// In the example above the promise `first` will resolve to `undefined` +// while the promise `second` will resolve to "second". +// +// If the first tasks throws after the second task has run the +// behavior is the same. +// +// const first = await executor.run(async () => { +// await sleep(1000) +// throw new Error() +// }) +// +// The task call back receives an AbortSignal as a parameter. The abort +// event is emitted when another task is run. +export function create(): MutexExecutor { + return new MutexExecutor(); +} + +// A worker that asynchronously process one item at a time and provides +// the result as an event stream. +// +// import * as mutexExecutor from "ui/src/mutexExecutor" +// const worker = mutexExecutor.createWorker(async (value) => { +// await sleep(1000) +// return value +// }) +// +// const firstPromise = worker.output.firstToPromise() +// worker.push("first) +// assert.equal(await firstPromise, "first") +// +// When an item is submitted to the worker while the previous items is +// still being processed, the result of the first item will not be +// emitted to `worker.output`. Instead, only the last item will be +// emitted. +export function createWorker( + fn: (x: In, abortSignal: AbortSignal) => Promise, +): MutexWorker { + return new MutexWorker(fn); +} + +class MutexExecutor { + private runningTaskId = 0; + private abortController: AbortController | null = null; + + public async run( + f: (abortSignal: AbortSignal) => Promise, + ): Promise { + this.runningTaskId += 1; + const taskId = this.runningTaskId; + + if (this.abortController) { + this.abortController.abort(); + } + this.abortController = new AbortController(); + return f(this.abortController.signal).then( + data => { + if (this.runningTaskId === taskId) { + return data; + } else { + return undefined; + } + }, + err => { + if (this.runningTaskId === taskId) { + throw err; + } else { + return undefined; + } + }, + ); + } +} + +class MutexWorker { + private outputBus = new Bacon.Bus(); + private executor = new MutexExecutor(); + + public output: Bacon.EventStream; + + public constructor( + private fn: (x: In, abortSignal: AbortSignal) => Promise, + ) { + this.output = this.outputBus.toEventStream(); + } + + public async submit(x: In): Promise { + const output = await this.executor.run(abort => this.fn(x, abort)); + if (output !== undefined) { + this.outputBus.push(output); + } + } +} diff --git a/src/lib/router.ts b/src/lib/router.ts new file mode 100644 index 0000000..f3d4720 --- /dev/null +++ b/src/lib/router.ts @@ -0,0 +1,150 @@ +import type { LoadedRoute, Route } from "@app/lib/router/definitions"; + +import { get, writable } from "svelte/store"; + +import * as mutexExecutor from "@app/lib/mutexExecutor"; +import * as utils from "@app/lib/utils"; +import { loadRoute } from "@app/lib/router/definitions"; + +export { type Route }; + +const InitialStore = { resource: "booting" as const }; + +export const isLoading = writable(true); +export const activeRouteStore = writable(InitialStore); +export const activeUnloadedRouteStore = writable(InitialStore); + +let currentUrl: URL | undefined; + +export async function loadFromLocation(): Promise { + await navigateToUrl("replace", new URL(window.location.href)); +} + +async function navigateToUrl( + action: "push" | "replace", + url: URL, +): Promise { + const { pathname, hash } = url; + + if (url.origin !== window.origin) { + throw new Error("Cannot navigate to other origin"); + } + + if ( + currentUrl && + currentUrl.pathname === pathname && + currentUrl.search === url.search + ) { + return; + } + + const relativeUrl = pathname + url.search + (hash || ""); + url = new URL(relativeUrl, window.origin); + const route = urlToRoute(url); + + if (route) { + await navigate(action, route); + } else { + await navigate(action, { + // On 404 we redirect to the Home page, shouldn't happen. + resource: "home", + }); + } +} + +window.addEventListener("popstate", () => loadFromLocation()); + +const loadExecutor = mutexExecutor.create(); + +async function navigate( + action: "push" | "replace", + newRoute: Route, +): Promise { + isLoading.set(true); + const path = routeToPath(newRoute); + + if (action === "push") { + window.history.pushState(newRoute, "", path); + } else if (action === "replace") { + window.history.replaceState(newRoute, ""); + } + currentUrl = new URL(window.location.href); + const currentLoadedRoute = get(activeRouteStore); + + const loadedRoute = await loadExecutor.run(async () => { + return loadRoute(newRoute, currentLoadedRoute); + }); + + // Only let the last request through. + if (loadedRoute === undefined) { + return; + } + + setTitle(loadedRoute); + activeRouteStore.set(loadedRoute); + activeUnloadedRouteStore.set(newRoute); + isLoading.set(false); +} + +function setTitle(loadedRoute: LoadedRoute) { + const title: string[] = []; + + if (loadedRoute.resource === "booting") { + title.push("Radicle"); + } else if (loadedRoute.resource === "home") { + title.push("Home"); + title.push("Radicle"); + } else if (loadedRoute.resource === "authenticationError") { + title.push("Authentication Error"); + title.push("Radicle"); + } else if (loadedRoute.resource === "designSystem") { + title.push("Design System"); + title.push("Radicle"); + } else { + utils.unreachable(loadedRoute); + } + + document.title = title.join(" ยท "); +} + +export async function push(newRoute: Route): Promise { + await navigate("push", newRoute); +} + +export async function replace(newRoute: Route): Promise { + await navigate("replace", newRoute); +} + +function urlToRoute(url: URL): Route | null { + const segments = url.pathname.substring(1).split("/"); + + const resource = segments.shift(); + switch (resource) { + case "": { + return { resource: "home" }; + } + case "authenticationError": { + return { resource: "authenticationError", params: { error: "" } }; + } + case "designSystem": { + return { resource: "designSystem" }; + } + default: { + return null; + } + } +} + +export function routeToPath(route: Route): string { + if (route.resource === "home") { + return "/"; + } else if (route.resource === "authenticationError") { + return "/authenticationError"; + } else if (route.resource === "designSystem") { + return "/designSystem"; + } else if (route.resource === "booting") { + return ""; + } else { + return utils.unreachable(route); + } +} diff --git a/src/lib/router/definitions.ts b/src/lib/router/definitions.ts new file mode 100644 index 0000000..16722c5 --- /dev/null +++ b/src/lib/router/definitions.ts @@ -0,0 +1,38 @@ +interface BootingRoute { + resource: "booting"; +} + +interface AuthenticationErrorRoute { + resource: "authenticationError"; + params: { + error: string; + hint?: string; + }; +} + +interface HomeRoute { + resource: "home"; +} + +interface DesignSystemRoute { + resource: "designSystem"; +} + +export type Route = + | BootingRoute + | DesignSystemRoute + | HomeRoute + | AuthenticationErrorRoute; + +export type LoadedRoute = + | BootingRoute + | DesignSystemRoute + | HomeRoute + | AuthenticationErrorRoute; + +export async function loadRoute( + route: Route, + _previousLoaded: LoadedRoute, +): Promise { + return route; +} diff --git a/src/views/AuthenticationError.svelte b/src/views/AuthenticationError.svelte new file mode 100644 index 0000000..365aef0 --- /dev/null +++ b/src/views/AuthenticationError.svelte @@ -0,0 +1,36 @@ + + + + +
+ +
+ {error} +
+ {#if hint} +
{@html hint}
+ {/if} +
diff --git a/src/views/DesignSystem.svelte b/src/views/DesignSystem.svelte index 925388f..1c222e9 100644 --- a/src/views/DesignSystem.svelte +++ b/src/views/DesignSystem.svelte @@ -1,42 +1,18 @@ -
-
- - { - theme = "dark"; - }}> - - Dark - - - { - theme = "light"; - }}> - - Light - - -
+
+
+ +๐Ÿ‘‰ Home diff --git a/src/views/Home.svelte b/src/views/Home.svelte new file mode 100644 index 0000000..6815ad2 --- /dev/null +++ b/src/views/Home.svelte @@ -0,0 +1,8 @@ + + +
+ +๐Ÿ‘‰ Design System diff --git a/src/views/Startup.svelte b/src/views/Startup.svelte deleted file mode 100644 index 9b2dfee..0000000 --- a/src/views/Startup.svelte +++ /dev/null @@ -1,47 +0,0 @@ - - - - -{#if error} -
- -
- {error.err} -
- {#if error.hint} -
{@html error.hint}
- {/if} -
-{:else if !loading} - -{/if}