diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 441da0de..450e4e29 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -14,7 +14,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4.2.0 - name: Setup node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 20.12.2 cache: 'npm' diff --git a/.github/workflows/deploy-pull-request.yml b/.github/workflows/deploy-pull-request.yml index 9c0bea78..b330c3c1 100644 --- a/.github/workflows/deploy-pull-request.yml +++ b/.github/workflows/deploy-pull-request.yml @@ -15,7 +15,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download pr number - uses: dawidd6/action-download-artifact@07ab29fd4a977ae4d2b275087cf67563dfdf0295 + uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 with: workflow: ${{ github.event.workflow.id }} run_id: ${{ github.event.workflow_run.id }} @@ -24,7 +24,7 @@ jobs: id: pr run: echo "id=$(> $GITHUB_OUTPUT - name: Download artifact - uses: dawidd6/action-download-artifact@07ab29fd4a977ae4d2b275087cf67563dfdf0295 + uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 with: workflow: ${{ github.event.workflow.id }} run_id: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml index 4e88c78d..398785ab 100644 --- a/.github/workflows/docker-pr.yml +++ b/.github/workflows/docker-pr.yml @@ -13,7 +13,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4.2.0 - name: Build Docker image - uses: docker/build-push-action@v6.15.0 + uses: docker/build-push-action@v6.18.0 with: context: . push: false diff --git a/.github/workflows/netlify-dev.yml b/.github/workflows/netlify-dev.yml index 34308c21..66cd5ad5 100644 --- a/.github/workflows/netlify-dev.yml +++ b/.github/workflows/netlify-dev.yml @@ -13,7 +13,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4.2.0 - name: Setup node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 20.12.2 cache: 'npm' diff --git a/.github/workflows/prod-deploy.yml b/.github/workflows/prod-deploy.yml index 44205ff2..24edda96 100644 --- a/.github/workflows/prod-deploy.yml +++ b/.github/workflows/prod-deploy.yml @@ -12,7 +12,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4.2.0 - name: Setup node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: 20.12.2 cache: 'npm' @@ -52,7 +52,7 @@ jobs: gpg --export | xxd -p echo '${{ secrets.GNUPG_PASSPHRASE }}' | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --armor --detach-sign cinny-${{ steps.vars.outputs.tag }}.tar.gz - name: Upload tagged release - uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda + uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 with: files: | cinny-${{ steps.vars.outputs.tag }}.tar.gz @@ -72,25 +72,25 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.10.0 - name: Login to Docker Hub - uses: docker/login-action@v3.4.0 + uses: docker/login-action@v3.5.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to the Container registry - uses: docker/login-action@v3.4.0 + uses: docker/login-action@v3.5.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v5.7.0 + uses: docker/metadata-action@v5.8.0 with: images: | ${{ secrets.DOCKER_USERNAME }}/cinny ghcr.io/${{ github.repository }} - name: Build and push Docker image - uses: docker/build-push-action@v6.15.0 + uses: docker/build-push-action@v6.18.0 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/Dockerfile b/Dockerfile index abb65ee5..718fed72 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN npm run build ## App -FROM nginx:1.27.4-alpine +FROM nginx:1.29.0-alpine COPY --from=builder /src/dist /app COPY --from=builder /src/docker-nginx.conf /etc/nginx/conf.d/default.conf diff --git a/index.html b/index.html index 9196cf3d..2eefeee2 100644 --- a/index.html +++ b/index.html @@ -90,6 +90,7 @@ window.global ||= window;
+
diff --git a/package-lock.json b/package-lock.json index 306de799..4aacff72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cinny", - "version": "4.8.0", + "version": "4.10.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cinny", - "version": "4.8.0", + "version": "4.10.0", "license": "AGPL-3.0-only", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "1.1.6", @@ -16,7 +16,6 @@ "@tanstack/react-query": "5.24.1", "@tanstack/react-query-devtools": "5.24.1", "@tanstack/react-virtual": "3.2.0", - "@tippyjs/react": "4.2.6", "@vanilla-extract/css": "1.9.3", "@vanilla-extract/recipes": "0.3.0", "@vanilla-extract/vite-plugin": "3.7.1", @@ -32,10 +31,8 @@ "emojibase": "15.3.1", "emojibase-data": "15.3.2", "file-saver": "2.0.5", - "flux": "4.0.3", "focus-trap-react": "10.0.2", - "folds": "2.1.0", - "formik": "2.4.6", + "folds": "2.3.0", "html-dom-parser": "4.0.0", "html-react-parser": "4.2.0", "i18next": "23.12.2", @@ -50,17 +47,14 @@ "millify": "6.1.0", "pdfjs-dist": "4.2.67", "prismjs": "1.30.0", - "prop-types": "15.8.1", "react": "18.2.0", "react-aria": "3.29.1", - "react-autosize-textarea": "7.1.0", "react-blurhash": "0.2.0", "react-colorful": "5.6.1", "react-dom": "18.2.0", "react-error-boundary": "4.0.13", "react-google-recaptcha": "2.1.0", "react-i18next": "15.0.0", - "react-modal": "3.16.1", "react-range": "1.8.14", "react-router-dom": "6.20.0", "sanitize-html": "2.12.1", @@ -68,7 +62,6 @@ "slate-dom": "0.112.2", "slate-history": "0.110.3", "slate-react": "0.112.1", - "tippy.js": "6.3.7", "ua-parser-js": "1.0.35" }, "devDependencies": { @@ -97,9 +90,8 @@ "eslint-plugin-react": "7.31.11", "eslint-plugin-react-hooks": "4.6.0", "prettier": "2.8.1", - "sass": "1.56.2", "typescript": "4.9.4", - "vite": "5.4.15", + "vite": "5.4.19", "vite-plugin-pwa": "0.20.5", "vite-plugin-static-copy": "1.0.4", "vite-plugin-top-level-await": "1.4.4" @@ -2313,15 +2305,6 @@ "node": ">= 8" } }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/@react-aria/breadcrumbs": { "version": "3.5.20", "resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.20.tgz", @@ -4524,18 +4507,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@tippyjs/react": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", - "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", - "dependencies": { - "tippy.js": "^6.3.1" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -4601,15 +4572,6 @@ "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==", "dev": true }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", - "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, "node_modules/@types/is-hotkey": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.10.tgz", @@ -4643,12 +4605,14 @@ "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "dev": true }, "node_modules/@types/react": { "version": "18.2.39", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz", "integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==", + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4697,7 +4661,8 @@ "node_modules/@types/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==" + "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==", + "dev": true }, "node_modules/@types/semver": { "version": "7.5.8", @@ -5320,11 +5285,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -5346,11 +5306,6 @@ "node": ">= 4.0.0" } }, - "node_modules/autosize": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.4.tgz", - "integrity": "sha512-5yxLQ22O0fCRGoxGfeLSNt3J8LB1v+umtpMnPW6XjkTWXKoN0AmXAIhelJcDtFT/Y/wYWmfE+oqU10Q0b8FhaQ==" - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -5830,11 +5785,6 @@ "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", "license": "MIT" }, - "node_modules/computed-style": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/computed-style/-/computed-style-0.1.4.tgz", - "integrity": "sha512-WpAmaKbMNmS3OProfHIdJiNleNJdgUrJfbKArXua28QF7+0CoZjlLn0lp6vlc+dl5r2/X9GQiQRQQU4BzSa69w==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5896,14 +5846,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -7019,11 +6961,6 @@ "node": ">=0.8.x" } }, - "node_modules/exenv": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7095,33 +7032,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fbemitter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", - "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", - "dependencies": { - "fbjs": "^3.0.0" - } - }, - "node_modules/fbjs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", - "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", - "dependencies": { - "cross-fetch": "^3.1.5", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^1.0.35" - } - }, - "node_modules/fbjs-css-vars": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" - }, "node_modules/fdir": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", @@ -7230,18 +7140,6 @@ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true }, - "node_modules/flux": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", - "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", - "dependencies": { - "fbemitter": "^3.0.0", - "fbjs": "^3.0.1" - }, - "peerDependencies": { - "react": "^15.0.2 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/focus-trap": { "version": "7.6.4", "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz", @@ -7265,15 +7163,16 @@ } }, "node_modules/folds": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/folds/-/folds-2.1.0.tgz", - "integrity": "sha512-KwAG8bH3jsyZ9FKPMg+6ABV2YOcpp4nL0cCelsalnaPeRThkc5fgG1Xj5mhmdffYKjEXpEbERi5qmGbepgJryg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/folds/-/folds-2.3.0.tgz", + "integrity": "sha512-1KoM21jrg5daxvKrmSY0V04wa946KlNT0z6h017Rsnw2fdtNC6J0f34Ce5GF46Tzi00gZ/7SvCDXMzW/7e5s0w==", + "license": "Apache-2.0", "peerDependencies": { - "@vanilla-extract/css": "^1.9.2", - "@vanilla-extract/recipes": "^0.3.0", - "classnames": "^2.3.2", - "react": "^17.0.0", - "react-dom": "^17.0.0" + "@vanilla-extract/css": "1.9.2", + "@vanilla-extract/recipes": "0.3.0", + "classnames": "2.3.2", + "react": "17.0.0", + "react-dom": "17.0.0" } }, "node_modules/for-each": { @@ -7285,38 +7184,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/formik": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", - "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==", - "funding": [ - { - "type": "individual", - "url": "https://opencollective.com/formik" - } - ], - "dependencies": { - "@types/hoist-non-react-statics": "^3.3.1", - "deepmerge": "^2.1.1", - "hoist-non-react-statics": "^3.3.0", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "react-fast-compare": "^2.0.1", - "tiny-warning": "^1.0.2", - "tslib": "^2.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/formik/node_modules/deepmerge": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", - "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fs-extra": { "version": "11.3.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", @@ -7895,12 +7762,6 @@ "url": "https://opencollective.com/immer" } }, - "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -8635,17 +8496,6 @@ "node": ">=10" } }, - "node_modules/line-height": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/line-height/-/line-height-0.3.1.tgz", - "integrity": "sha512-YExecgqPwnp5gplD2+Y8e8A5+jKpr25+DzMbFdI1/1UAr0FJrTFv4VkHLf8/6B590i1wUPJWMKKldkd/bdQ//w==", - "dependencies": { - "computed-style": "~0.1.3" - }, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/linkify-react": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.1.3.tgz", @@ -8679,11 +8529,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -9540,14 +9385,6 @@ "node": ">=6" } }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dependencies": { - "asap": "~2.0.3" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -9671,20 +9508,6 @@ "react": ">=16.4.1" } }, - "node_modules/react-autosize-textarea": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/react-autosize-textarea/-/react-autosize-textarea-7.1.0.tgz", - "integrity": "sha512-BHpjCDkuOlllZn3nLazY2F8oYO1tS2jHnWhcjTWQdcKiiMU6gHLNt/fzmqMSyerR0eTdKtfSIqtSeTtghNwS+g==", - "dependencies": { - "autosize": "^4.0.2", - "line-height": "^0.3.1", - "prop-types": "^15.5.6" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.0 || ^16.0.0", - "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" - } - }, "node_modules/react-blurhash": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/react-blurhash/-/react-blurhash-0.2.0.tgz", @@ -9727,11 +9550,6 @@ "react": ">=16.13.1" } }, - "node_modules/react-fast-compare": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", - "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" - }, "node_modules/react-google-recaptcha": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.1.0.tgz", @@ -9770,29 +9588,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-modal": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", - "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", - "dependencies": { - "exenv": "^1.2.0", - "prop-types": "^15.7.2", - "react-lifecycles-compat": "^3.0.0", - "warning": "^4.0.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", - "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" - } - }, "node_modules/react-property": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz", @@ -10251,23 +10046,6 @@ "postcss": "^8.3.11" } }, - "node_modules/sass": { - "version": "1.56.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.2.tgz", - "integrity": "sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w==", - "dev": true, - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -10367,11 +10145,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10968,14 +10741,6 @@ "node": ">=12.0.0" } }, - "node_modules/tippy.js": { - "version": "6.3.7", - "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", - "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", - "dependencies": { - "@popperjs/core": "^2.9.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -11330,9 +11095,9 @@ } }, "node_modules/vite": { - "version": "5.4.15", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.15.tgz", - "integrity": "sha512-6ANcZRivqL/4WtwPGTKNaosuNJr5tWiftOC7liM7G9+rMb8+oeJeyzymDu4rTN93seySBmbjSfsS3Vzr19KNtA==", + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "license": "MIT", "dependencies": { "esbuild": "^0.21.3", @@ -11890,14 +11655,6 @@ "node": ">=0.10.0" } }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 8772462a..5a2356f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cinny", - "version": "4.8.0", + "version": "4.10.0", "description": "Yet another matrix client", "main": "index.js", "type": "module", @@ -27,7 +27,6 @@ "@tanstack/react-query": "5.24.1", "@tanstack/react-query-devtools": "5.24.1", "@tanstack/react-virtual": "3.2.0", - "@tippyjs/react": "4.2.6", "@vanilla-extract/css": "1.9.3", "@vanilla-extract/recipes": "0.3.0", "@vanilla-extract/vite-plugin": "3.7.1", @@ -43,10 +42,8 @@ "emojibase": "15.3.1", "emojibase-data": "15.3.2", "file-saver": "2.0.5", - "flux": "4.0.3", "focus-trap-react": "10.0.2", - "folds": "2.1.0", - "formik": "2.4.6", + "folds": "2.3.0", "html-dom-parser": "4.0.0", "html-react-parser": "4.2.0", "i18next": "23.12.2", @@ -61,17 +58,14 @@ "millify": "6.1.0", "pdfjs-dist": "4.2.67", "prismjs": "1.30.0", - "prop-types": "15.8.1", "react": "18.2.0", "react-aria": "3.29.1", - "react-autosize-textarea": "7.1.0", "react-blurhash": "0.2.0", "react-colorful": "5.6.1", "react-dom": "18.2.0", "react-error-boundary": "4.0.13", "react-google-recaptcha": "2.1.0", "react-i18next": "15.0.0", - "react-modal": "3.16.1", "react-range": "1.8.14", "react-router-dom": "6.20.0", "sanitize-html": "2.12.1", @@ -79,7 +73,6 @@ "slate-dom": "0.112.2", "slate-history": "0.110.3", "slate-react": "0.112.1", - "tippy.js": "6.3.7", "ua-parser-js": "1.0.35" }, "devDependencies": { @@ -108,9 +101,8 @@ "eslint-plugin-react": "7.31.11", "eslint-plugin-react-hooks": "4.6.0", "prettier": "2.8.1", - "sass": "1.56.2", "typescript": "4.9.4", - "vite": "5.4.15", + "vite": "5.4.19", "vite-plugin-pwa": "0.20.5", "vite-plugin-static-copy": "1.0.4", "vite-plugin-top-level-await": "1.4.4" diff --git a/public/res/ic/filled/category.svg b/public/res/ic/filled/category.svg deleted file mode 100644 index 87b2588d..00000000 --- a/public/res/ic/filled/category.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/public/res/ic/filled/pin.svg b/public/res/ic/filled/pin.svg deleted file mode 100644 index 6a701474..00000000 --- a/public/res/ic/filled/pin.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/filled/star.svg b/public/res/ic/filled/star.svg deleted file mode 100644 index 378c891e..00000000 --- a/public/res/ic/filled/star.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/public/res/ic/outlined/add-pin.svg b/public/res/ic/outlined/add-pin.svg deleted file mode 100644 index 9634bede..00000000 --- a/public/res/ic/outlined/add-pin.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/add-user.svg b/public/res/ic/outlined/add-user.svg deleted file mode 100644 index c3803d80..00000000 --- a/public/res/ic/outlined/add-user.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/ball.svg b/public/res/ic/outlined/ball.svg deleted file mode 100644 index d4b89ff5..00000000 --- a/public/res/ic/outlined/ball.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/bell-off.svg b/public/res/ic/outlined/bell-off.svg deleted file mode 100644 index 79ce8a33..00000000 --- a/public/res/ic/outlined/bell-off.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/bell-ping.svg b/public/res/ic/outlined/bell-ping.svg deleted file mode 100644 index 3431bea1..00000000 --- a/public/res/ic/outlined/bell-ping.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/bell-ring.svg b/public/res/ic/outlined/bell-ring.svg deleted file mode 100644 index 57fc2679..00000000 --- a/public/res/ic/outlined/bell-ring.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/bell.svg b/public/res/ic/outlined/bell.svg deleted file mode 100644 index 43d470b5..00000000 --- a/public/res/ic/outlined/bell.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/bin.svg b/public/res/ic/outlined/bin.svg deleted file mode 100644 index 984be625..00000000 --- a/public/res/ic/outlined/bin.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/public/res/ic/outlined/bulb.svg b/public/res/ic/outlined/bulb.svg deleted file mode 100644 index 00e80886..00000000 --- a/public/res/ic/outlined/bulb.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/category.svg b/public/res/ic/outlined/category.svg deleted file mode 100644 index c7c33b38..00000000 --- a/public/res/ic/outlined/category.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/check.svg b/public/res/ic/outlined/check.svg deleted file mode 100644 index 72a18327..00000000 --- a/public/res/ic/outlined/check.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/chevron-bottom.svg b/public/res/ic/outlined/chevron-bottom.svg deleted file mode 100644 index 5562b7aa..00000000 --- a/public/res/ic/outlined/chevron-bottom.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/public/res/ic/outlined/chevron-left.svg b/public/res/ic/outlined/chevron-left.svg deleted file mode 100644 index ba9e12cc..00000000 --- a/public/res/ic/outlined/chevron-left.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/public/res/ic/outlined/chevron-right.svg b/public/res/ic/outlined/chevron-right.svg deleted file mode 100644 index 7f6a806e..00000000 --- a/public/res/ic/outlined/chevron-right.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/public/res/ic/outlined/chevron-top.svg b/public/res/ic/outlined/chevron-top.svg deleted file mode 100644 index f5948fe9..00000000 --- a/public/res/ic/outlined/chevron-top.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/public/res/ic/outlined/circle-plus.svg b/public/res/ic/outlined/circle-plus.svg deleted file mode 100644 index 41690a08..00000000 --- a/public/res/ic/outlined/circle-plus.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/cmd.svg b/public/res/ic/outlined/cmd.svg deleted file mode 100644 index 75ae0d98..00000000 --- a/public/res/ic/outlined/cmd.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/coin.svg b/public/res/ic/outlined/coin.svg deleted file mode 100644 index 025424e8..00000000 --- a/public/res/ic/outlined/coin.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/cross.svg b/public/res/ic/outlined/cross.svg deleted file mode 100644 index 0acda884..00000000 --- a/public/res/ic/outlined/cross.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/cup.svg b/public/res/ic/outlined/cup.svg deleted file mode 100644 index 8921e2c9..00000000 --- a/public/res/ic/outlined/cup.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/dog.svg b/public/res/ic/outlined/dog.svg deleted file mode 100644 index 3b252956..00000000 --- a/public/res/ic/outlined/dog.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/download.svg b/public/res/ic/outlined/download.svg deleted file mode 100644 index 677014f3..00000000 --- a/public/res/ic/outlined/download.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/emoji-add.svg b/public/res/ic/outlined/emoji-add.svg deleted file mode 100644 index c4cacef2..00000000 --- a/public/res/ic/outlined/emoji-add.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/public/res/ic/outlined/emoji.svg b/public/res/ic/outlined/emoji.svg deleted file mode 100644 index 0daac879..00000000 --- a/public/res/ic/outlined/emoji.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/explore.svg b/public/res/ic/outlined/explore.svg deleted file mode 100644 index 7cc2a479..00000000 --- a/public/res/ic/outlined/explore.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/external.svg b/public/res/ic/outlined/external.svg deleted file mode 100644 index adade1bd..00000000 --- a/public/res/ic/outlined/external.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/res/ic/outlined/eye-blind.svg b/public/res/ic/outlined/eye-blind.svg deleted file mode 100644 index fbc8e2ae..00000000 --- a/public/res/ic/outlined/eye-blind.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/res/ic/outlined/eye.svg b/public/res/ic/outlined/eye.svg deleted file mode 100644 index 1ce868bf..00000000 --- a/public/res/ic/outlined/eye.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/res/ic/outlined/file.svg b/public/res/ic/outlined/file.svg deleted file mode 100644 index d6a2a27a..00000000 --- a/public/res/ic/outlined/file.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/flag.svg b/public/res/ic/outlined/flag.svg deleted file mode 100644 index 8fce98d6..00000000 --- a/public/res/ic/outlined/flag.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/hash-globe.svg b/public/res/ic/outlined/hash-globe.svg deleted file mode 100644 index ce3df083..00000000 --- a/public/res/ic/outlined/hash-globe.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/hash-lock.svg b/public/res/ic/outlined/hash-lock.svg deleted file mode 100644 index ae263ced..00000000 --- a/public/res/ic/outlined/hash-lock.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/hash-plus.svg b/public/res/ic/outlined/hash-plus.svg deleted file mode 100644 index 69737fd5..00000000 --- a/public/res/ic/outlined/hash-plus.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/hash-search.svg b/public/res/ic/outlined/hash-search.svg deleted file mode 100644 index f135e898..00000000 --- a/public/res/ic/outlined/hash-search.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/hash-shield.svg b/public/res/ic/outlined/hash-shield.svg deleted file mode 100644 index dfd344b1..00000000 --- a/public/res/ic/outlined/hash-shield.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/hash.svg b/public/res/ic/outlined/hash.svg deleted file mode 100644 index dcb8b964..00000000 --- a/public/res/ic/outlined/hash.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/heart.svg b/public/res/ic/outlined/heart.svg deleted file mode 100644 index c5b940b6..00000000 --- a/public/res/ic/outlined/heart.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/home.svg b/public/res/ic/outlined/home.svg deleted file mode 100644 index 3c7a02df..00000000 --- a/public/res/ic/outlined/home.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/horizontal-menu.svg b/public/res/ic/outlined/horizontal-menu.svg deleted file mode 100644 index a19b3c35..00000000 --- a/public/res/ic/outlined/horizontal-menu.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/inbox.svg b/public/res/ic/outlined/inbox.svg deleted file mode 100644 index 65435876..00000000 --- a/public/res/ic/outlined/inbox.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/info.svg b/public/res/ic/outlined/info.svg deleted file mode 100644 index 30a57887..00000000 --- a/public/res/ic/outlined/info.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/public/res/ic/outlined/invite-arrow.svg b/public/res/ic/outlined/invite-arrow.svg deleted file mode 100644 index 370bf8e8..00000000 --- a/public/res/ic/outlined/invite-arrow.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/invite-cancel-arrow.svg b/public/res/ic/outlined/invite-cancel-arrow.svg deleted file mode 100644 index 795a773a..00000000 --- a/public/res/ic/outlined/invite-cancel-arrow.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/invite.svg b/public/res/ic/outlined/invite.svg deleted file mode 100644 index 3896e15e..00000000 --- a/public/res/ic/outlined/invite.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/join-arrow.svg b/public/res/ic/outlined/join-arrow.svg deleted file mode 100644 index 90cfa651..00000000 --- a/public/res/ic/outlined/join-arrow.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/leave-arrow.svg b/public/res/ic/outlined/leave-arrow.svg deleted file mode 100644 index a51ac1d1..00000000 --- a/public/res/ic/outlined/leave-arrow.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/lock.svg b/public/res/ic/outlined/lock.svg deleted file mode 100644 index 77021f0f..00000000 --- a/public/res/ic/outlined/lock.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/markdown.svg b/public/res/ic/outlined/markdown.svg deleted file mode 100644 index 775afbfb..00000000 --- a/public/res/ic/outlined/markdown.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/message-unread.svg b/public/res/ic/outlined/message-unread.svg deleted file mode 100644 index fc5e9ff0..00000000 --- a/public/res/ic/outlined/message-unread.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - diff --git a/public/res/ic/outlined/message.svg b/public/res/ic/outlined/message.svg deleted file mode 100644 index d36e9a30..00000000 --- a/public/res/ic/outlined/message.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/pause.svg b/public/res/ic/outlined/pause.svg deleted file mode 100644 index c312613b..00000000 --- a/public/res/ic/outlined/pause.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - diff --git a/public/res/ic/outlined/peace.svg b/public/res/ic/outlined/peace.svg deleted file mode 100644 index 8a7c81a3..00000000 --- a/public/res/ic/outlined/peace.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/pencil.svg b/public/res/ic/outlined/pencil.svg deleted file mode 100644 index 1b8ac24a..00000000 --- a/public/res/ic/outlined/pencil.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/photo.svg b/public/res/ic/outlined/photo.svg deleted file mode 100644 index af01a330..00000000 --- a/public/res/ic/outlined/photo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/pin.svg b/public/res/ic/outlined/pin.svg deleted file mode 100644 index 211242cd..00000000 --- a/public/res/ic/outlined/pin.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/play.svg b/public/res/ic/outlined/play.svg deleted file mode 100644 index 87b3a8f6..00000000 --- a/public/res/ic/outlined/play.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/plus.svg b/public/res/ic/outlined/plus.svg deleted file mode 100644 index ce37594e..00000000 --- a/public/res/ic/outlined/plus.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/power.svg b/public/res/ic/outlined/power.svg deleted file mode 100644 index 8aeb6db8..00000000 --- a/public/res/ic/outlined/power.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/recent-clock.svg b/public/res/ic/outlined/recent-clock.svg deleted file mode 100644 index 30b10d59..00000000 --- a/public/res/ic/outlined/recent-clock.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - diff --git a/public/res/ic/outlined/reply-arrow.svg b/public/res/ic/outlined/reply-arrow.svg deleted file mode 100644 index 3cda01cd..00000000 --- a/public/res/ic/outlined/reply-arrow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/search.svg b/public/res/ic/outlined/search.svg deleted file mode 100644 index 75dd6320..00000000 --- a/public/res/ic/outlined/search.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/send.svg b/public/res/ic/outlined/send.svg deleted file mode 100644 index aa487132..00000000 --- a/public/res/ic/outlined/send.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/settings.svg b/public/res/ic/outlined/settings.svg deleted file mode 100644 index ee640b39..00000000 --- a/public/res/ic/outlined/settings.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - diff --git a/public/res/ic/outlined/shield-empty.svg b/public/res/ic/outlined/shield-empty.svg deleted file mode 100644 index 6bc9d304..00000000 --- a/public/res/ic/outlined/shield-empty.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/shield-user.svg b/public/res/ic/outlined/shield-user.svg deleted file mode 100644 index bd5f07c5..00000000 --- a/public/res/ic/outlined/shield-user.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/shield.svg b/public/res/ic/outlined/shield.svg deleted file mode 100644 index 9bb46fa1..00000000 --- a/public/res/ic/outlined/shield.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/space-globe.svg b/public/res/ic/outlined/space-globe.svg deleted file mode 100644 index 63d71f1d..00000000 --- a/public/res/ic/outlined/space-globe.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/space-lock.svg b/public/res/ic/outlined/space-lock.svg deleted file mode 100644 index b15705ca..00000000 --- a/public/res/ic/outlined/space-lock.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/space-plus.svg b/public/res/ic/outlined/space-plus.svg deleted file mode 100644 index 4d69a1ef..00000000 --- a/public/res/ic/outlined/space-plus.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/space.svg b/public/res/ic/outlined/space.svg deleted file mode 100644 index a4b54b3e..00000000 --- a/public/res/ic/outlined/space.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/star.svg b/public/res/ic/outlined/star.svg deleted file mode 100644 index 290f159a..00000000 --- a/public/res/ic/outlined/star.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/sticker.svg b/public/res/ic/outlined/sticker.svg deleted file mode 100644 index bc486e5e..00000000 --- a/public/res/ic/outlined/sticker.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/res/ic/outlined/sun.svg b/public/res/ic/outlined/sun.svg deleted file mode 100644 index d8ed06fd..00000000 --- a/public/res/ic/outlined/sun.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/res/ic/outlined/tick-mark.svg b/public/res/ic/outlined/tick-mark.svg deleted file mode 100644 index 8e76ed55..00000000 --- a/public/res/ic/outlined/tick-mark.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/user.svg b/public/res/ic/outlined/user.svg deleted file mode 100644 index 6756a1b2..00000000 --- a/public/res/ic/outlined/user.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/public/res/ic/outlined/vertical-menu.svg b/public/res/ic/outlined/vertical-menu.svg deleted file mode 100644 index ec5c544c..00000000 --- a/public/res/ic/outlined/vertical-menu.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/public/res/ic/outlined/vlc.svg b/public/res/ic/outlined/vlc.svg deleted file mode 100644 index 8a2b844f..00000000 --- a/public/res/ic/outlined/vlc.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/public/res/ic/outlined/volume-full.svg b/public/res/ic/outlined/volume-full.svg deleted file mode 100644 index 20419e72..00000000 --- a/public/res/ic/outlined/volume-full.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/public/res/ic/outlined/volume-mute.svg b/public/res/ic/outlined/volume-mute.svg deleted file mode 100644 index beb06771..00000000 --- a/public/res/ic/outlined/volume-mute.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/src/app/atoms/avatar/Avatar.jsx b/src/app/atoms/avatar/Avatar.jsx deleted file mode 100644 index 27bf7c90..00000000 --- a/src/app/atoms/avatar/Avatar.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Avatar.scss'; - -import Text from '../text/Text'; -import RawIcon from '../system-icons/RawIcon'; - -import ImageBrokenSVG from '../../../../public/res/svg/image-broken.svg'; -import { avatarInitials } from '../../../util/common'; - -const Avatar = React.forwardRef(({ text, bgColor, iconSrc, iconColor, imageSrc, size }, ref) => { - let textSize = 's1'; - if (size === 'large') textSize = 'h1'; - if (size === 'small') textSize = 'b1'; - if (size === 'extra-small') textSize = 'b3'; - - return ( -
- {imageSrc !== null ? ( - { - e.target.style.backgroundColor = 'transparent'; - }} - onError={(e) => { - e.target.src = ImageBrokenSVG; - }} - alt="" - /> - ) : ( - - {iconSrc !== null ? ( - - ) : ( - text !== null && ( - - {avatarInitials(text)} - - ) - )} - - )} -
- ); -}); - -Avatar.defaultProps = { - text: null, - bgColor: 'transparent', - iconSrc: null, - iconColor: null, - imageSrc: null, - size: 'normal', -}; - -Avatar.propTypes = { - text: PropTypes.string, - bgColor: PropTypes.string, - iconSrc: PropTypes.string, - iconColor: PropTypes.string, - imageSrc: PropTypes.string, - size: PropTypes.oneOf(['large', 'normal', 'small', 'extra-small']), -}; - -export default Avatar; diff --git a/src/app/atoms/avatar/Avatar.scss b/src/app/atoms/avatar/Avatar.scss deleted file mode 100644 index ea69c9e8..00000000 --- a/src/app/atoms/avatar/Avatar.scss +++ /dev/null @@ -1,56 +0,0 @@ -@use '../../partials/flex'; - -.avatar-container { - display: inline-flex; - width: 42px; - height: 42px; - border-radius: var(--bo-radius); - position: relative; - - &__large { - width: var(--av-large); - height: var(--av-large); - } - &__normal { - width: var(--av-normal); - height: var(--av-normal); - } - - &__small { - width: var(--av-small); - height: var(--av-small); - } - - &__extra-small { - width: var(--av-extra-small); - height: var(--av-extra-small); - } - - > img { - width: 100%; - height: 100%; - object-fit: cover; - border-radius: inherit; - background-color: var(--bg-surface-hover); - } - - .avatar__border { - @extend .cp-fx__row--c-c; - - position: absolute; - top: 0; - left: 0; - - width: 100%; - height: 100%; - border-radius: inherit; - - .text { - color: white; - } - &--active { - @extend .avatar__border; - box-shadow: var(--bs-surface-border); - } - } -} \ No newline at end of file diff --git a/src/app/atoms/avatar/render.js b/src/app/atoms/avatar/render.js deleted file mode 100644 index e8cf1a66..00000000 --- a/src/app/atoms/avatar/render.js +++ /dev/null @@ -1,57 +0,0 @@ -import { avatarInitials, cssVar } from '../../../util/common'; - -// renders the avatar and returns it as an URL -export default async function renderAvatar({ - text, bgColor, imageSrc, size, borderRadius, scale, -}) { - try { - const canvas = document.createElement('canvas'); - canvas.width = size * scale; - canvas.height = size * scale; - - const ctx = canvas.getContext('2d'); - - ctx.scale(scale, scale); - - // rounded corners - ctx.beginPath(); - ctx.moveTo(size, size); - ctx.arcTo(0, size, 0, 0, borderRadius); - ctx.arcTo(0, 0, size, 0, borderRadius); - ctx.arcTo(size, 0, size, size, borderRadius); - ctx.arcTo(size, size, 0, size, borderRadius); - - if (imageSrc) { - // clip corners of image - ctx.closePath(); - ctx.clip(); - - const img = new Image(); - img.crossOrigin = 'anonymous'; - const promise = new Promise((resolve, reject) => { - img.onerror = reject; - img.onload = resolve; - }); - img.src = imageSrc; - await promise; - - ctx.drawImage(img, 0, 0, size, size); - } else { - // colored background - ctx.fillStyle = cssVar(bgColor); - ctx.fill(); - - // centered letter - ctx.fillStyle = '#fff'; - ctx.font = `${cssVar('--fs-s1')} ${cssVar('--font-primary')}`; - ctx.textBaseline = 'middle'; - ctx.textAlign = 'center'; - ctx.fillText(avatarInitials(text), size / 2, size / 2); - } - - return canvas.toDataURL(); - } catch (e) { - console.error(e); - return imageSrc; - } -} diff --git a/src/app/atoms/badge/NotificationBadge.jsx b/src/app/atoms/badge/NotificationBadge.jsx deleted file mode 100644 index 12c1bd44..00000000 --- a/src/app/atoms/badge/NotificationBadge.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './NotificationBadge.scss'; - -import Text from '../text/Text'; - -function NotificationBadge({ alert, content }) { - const notificationClass = alert ? ' notification-badge--alert' : ''; - return ( -
- {content !== null && {content}} -
- ); -} - -NotificationBadge.defaultProps = { - alert: false, - content: null, -}; - -NotificationBadge.propTypes = { - alert: PropTypes.bool, - content: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - ]), -}; - -export default NotificationBadge; diff --git a/src/app/atoms/badge/NotificationBadge.scss b/src/app/atoms/badge/NotificationBadge.scss deleted file mode 100644 index f5cfa73f..00000000 --- a/src/app/atoms/badge/NotificationBadge.scss +++ /dev/null @@ -1,21 +0,0 @@ -.notification-badge { - min-width: 16px; - min-height: 8px; - padding: 0 var(--sp-ultra-tight); - background-color: var(--bg-badge); - border-radius: var(--bo-radius); - - .text { - color: var(--tc-badge); - text-align: center; - } - - &--alert { - background-color: var(--bg-positive); - } - - &:empty { - min-width: 8px; - margin: 0 var(--sp-ultra-tight); - } -} \ No newline at end of file diff --git a/src/app/atoms/button/Button.jsx b/src/app/atoms/button/Button.jsx deleted file mode 100644 index 1c1c950c..00000000 --- a/src/app/atoms/button/Button.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Button.scss'; - -import Text from '../text/Text'; -import RawIcon from '../system-icons/RawIcon'; -import { blurOnBubbling } from './script'; - -const Button = React.forwardRef(({ - id, className, variant, iconSrc, - type, onClick, children, disabled, -}, ref) => { - const iconClass = (iconSrc === null) ? '' : `btn-${variant}--icon`; - return ( - - ); -}); - -Button.defaultProps = { - id: '', - className: null, - variant: 'surface', - iconSrc: null, - type: 'button', - onClick: null, - disabled: false, -}; - -Button.propTypes = { - id: PropTypes.string, - className: PropTypes.string, - variant: PropTypes.oneOf(['surface', 'primary', 'positive', 'caution', 'danger']), - iconSrc: PropTypes.string, - type: PropTypes.oneOf(['button', 'submit', 'reset']), - onClick: PropTypes.func, - children: PropTypes.node.isRequired, - disabled: PropTypes.bool, -}; - -export default Button; diff --git a/src/app/atoms/button/Button.scss b/src/app/atoms/button/Button.scss deleted file mode 100644 index e1a01bb0..00000000 --- a/src/app/atoms/button/Button.scss +++ /dev/null @@ -1,81 +0,0 @@ -@use 'state'; -@use '../../partials/dir'; -@use '../../partials/text'; - -.btn-surface, -.btn-primary, -.btn-positive, -.btn-caution, -.btn-danger { - display: inline-flex; - align-items: center; - justify-content: center; - - min-width: 80px; - padding: var(--sp-extra-tight) var(--sp-normal); - background-color: transparent; - border: none; - border-radius: var(--bo-radius); - cursor: pointer; - @include state.disabled; - - & .text { - @extend .cp-txt__ellipsis; - } - - &--icon { - @include dir.side(padding, var(--sp-tight), var(--sp-loose)); - - } - .ic-raw { - @include dir.side(margin, 0, var(--sp-extra-tight)); - flex-shrink: 0; - } -} - -@mixin color($textColor, $iconColor) { - .text { - color: $textColor; - } - .ic-raw { - background-color: $iconColor; - } -} - - -.btn-surface { - box-shadow: var(--bs-surface-border); - @include color(var(--tc-surface-high), var(--ic-surface-normal)); - @include state.hover(var(--bg-surface-hover)); - @include state.focus(var(--bs-surface-outline)); - @include state.active(var(--bg-surface-active)); -} - -.btn-primary { - background-color: var(--bg-primary); - @include color(var(--tc-primary-high), var(--ic-primary-normal)); - @include state.hover(var(--bg-primary-hover)); - @include state.focus(var(--bs-primary-outline)); - @include state.active(var(--bg-primary-active)); -} -.btn-positive { - box-shadow: var(--bs-positive-border); - @include color(var(--tc-positive-high), var(--ic-positive-normal)); - @include state.hover(var(--bg-positive-hover)); - @include state.focus(var(--bs-positive-outline)); - @include state.active(var(--bg-positive-active)); -} -.btn-caution { - box-shadow: var(--bs-caution-border); - @include color(var(--tc-caution-high), var(--ic-caution-normal)); - @include state.hover(var(--bg-caution-hover)); - @include state.focus(var(--bs-caution-outline)); - @include state.active(var(--bg-caution-active)); -} -.btn-danger { - box-shadow: var(--bs-danger-border); - @include color(var(--tc-danger-high), var(--ic-danger-normal)); - @include state.hover(var(--bg-danger-hover)); - @include state.focus(var(--bs-danger-outline)); - @include state.active(var(--bg-danger-active)); -} \ No newline at end of file diff --git a/src/app/atoms/button/Checkbox.jsx b/src/app/atoms/button/Checkbox.jsx deleted file mode 100644 index 7fcea3b5..00000000 --- a/src/app/atoms/button/Checkbox.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Checkbox.scss'; - -function Checkbox({ - variant, isActive, onToggle, - disabled, tabIndex, -}) { - const className = `checkbox checkbox-${variant}${isActive ? ' checkbox--active' : ''}`; - if (onToggle === null) return ; - return ( - // eslint-disable-next-line jsx-a11y/control-has-associated-label - - ); - if (tooltip === null) return btn; - return ( - {tooltip}} - > - {btn} - - ); -}); - -IconButton.defaultProps = { - variant: 'surface', - size: 'normal', - type: 'button', - tooltip: null, - tooltipPlacement: 'top', - onClick: null, - tabIndex: 0, - disabled: false, - isImage: false, - className: '', -}; - -IconButton.propTypes = { - variant: PropTypes.oneOf(['surface', 'primary', 'positive', 'caution', 'danger']), - size: PropTypes.oneOf(['normal', 'small', 'extra-small']), - type: PropTypes.oneOf(['button', 'submit', 'reset']), - tooltip: PropTypes.string, - tooltipPlacement: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), - src: PropTypes.string.isRequired, - onClick: PropTypes.func, - tabIndex: PropTypes.number, - disabled: PropTypes.bool, - isImage: PropTypes.bool, - className: PropTypes.string, -}; - -export default IconButton; diff --git a/src/app/atoms/button/IconButton.scss b/src/app/atoms/button/IconButton.scss deleted file mode 100644 index aa6480c0..00000000 --- a/src/app/atoms/button/IconButton.scss +++ /dev/null @@ -1,56 +0,0 @@ -@use 'state'; - -.ic-btn { - padding: var(--sp-extra-tight); - border: none; - border-radius: var(--bo-radius); - background-color: transparent; - font-size: 0; - line-height: 0; - cursor: pointer; - @include state.disabled; -} - -@mixin color($color) { - .ic-raw { - background-color: $color; - } -} -@mixin focus($color) { - &:focus { - outline: none; - background-color: $color; - } -} - -.ic-btn-surface { - @include color(var(--ic-surface-normal)); - @include state.hover(var(--bg-surface-hover)); - @include focus(var(--bg-surface-hover)); - @include state.active(var(--bg-surface-active)); -} -.ic-btn-primary { - @include color(var(--ic-primary-normal)); - @include state.hover(var(--bg-primary-hover)); - @include focus(var(--bg-primary-hover)); - @include state.active(var(--bg-primary-active)); - background-color: var(--bg-primary); -} -.ic-btn-positive { - @include color(var(--ic-positive-normal)); - @include state.hover(var(--bg-positive-hover)); - @include focus(var(--bg-positive-hover)); - @include state.active(var(--bg-positive-active)); -} -.ic-btn-caution { - @include color(var(--ic-caution-normal)); - @include state.hover(var(--bg-caution-hover)); - @include focus(var(--bg-caution-hover)); - @include state.active(var(--bg-caution-active)); -} -.ic-btn-danger { - @include color(var(--ic-danger-normal)); - @include state.hover(var(--bg-danger-hover)); - @include focus(var(--bg-danger-hover)); - @include state.active(var(--bg-danger-active)); -} \ No newline at end of file diff --git a/src/app/atoms/button/RadioButton.jsx b/src/app/atoms/button/RadioButton.jsx deleted file mode 100644 index 35b68baf..00000000 --- a/src/app/atoms/button/RadioButton.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './RadioButton.scss'; - -function RadioButton({ isActive, onToggle, disabled }) { - if (onToggle === null) return ; - return ( - // eslint-disable-next-line jsx-a11y/control-has-associated-label - - ); -} - -Chip.propTypes = { - iconSrc: PropTypes.string, - iconColor: PropTypes.string, - text: PropTypes.string, - children: PropTypes.element, - onClick: PropTypes.func, -}; - -Chip.defaultProps = { - iconSrc: null, - iconColor: null, - text: null, - children: null, - onClick: null, -}; - -export default Chip; diff --git a/src/app/atoms/chip/Chip.scss b/src/app/atoms/chip/Chip.scss deleted file mode 100644 index 7396b0dc..00000000 --- a/src/app/atoms/chip/Chip.scss +++ /dev/null @@ -1,31 +0,0 @@ -@use '../../partials/dir'; - -.chip { - padding: var(--sp-ultra-tight) var(--sp-extra-tight); - - display: inline-flex; - flex-direction: row; - align-items: center; - - background: var(--bg-surface-low); - border-radius: var(--bo-radius); - box-shadow: var(--bs-surface-border); - cursor: pointer; - - @media (hover: hover) { - &:hover { - background-color: var(--bg-surface-hover); - } - } - - & > .text { - flex: 1; - color: var(--tc-surface-high); - } - - & > .ic-raw { - width: 16px; - height: 16px; - @include dir.side(margin, 0, var(--sp-ultra-tight)); - } -} \ No newline at end of file diff --git a/src/app/atoms/context-menu/ContextMenu.jsx b/src/app/atoms/context-menu/ContextMenu.jsx deleted file mode 100644 index 7d1acd44..00000000 --- a/src/app/atoms/context-menu/ContextMenu.jsx +++ /dev/null @@ -1,115 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import './ContextMenu.scss'; - -import Tippy from '@tippyjs/react'; -import 'tippy.js/animations/scale-extreme.css'; - -import Text from '../text/Text'; -import Button from '../button/Button'; -import ScrollView from '../scroll/ScrollView'; - -function ContextMenu({ - content, placement, maxWidth, render, afterToggle, -}) { - const [isVisible, setVisibility] = useState(false); - const showMenu = () => setVisibility(true); - const hideMenu = () => setVisibility(false); - - useEffect(() => { - if (afterToggle !== null) afterToggle(isVisible); - }, [isVisible]); - - return ( - {typeof content === 'function' ? content(hideMenu) : content}} - placement={placement} - interactive - arrow={false} - maxWidth={maxWidth} - duration={200} - > - {render(isVisible ? hideMenu : showMenu)} - - ); -} - -ContextMenu.defaultProps = { - maxWidth: 'unset', - placement: 'right', - afterToggle: null, -}; - -ContextMenu.propTypes = { - content: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.func, - ]).isRequired, - placement: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), - maxWidth: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - ]), - render: PropTypes.func.isRequired, - afterToggle: PropTypes.func, -}; - -function MenuHeader({ children }) { - return ( -
- { children } -
- ); -} - -MenuHeader.propTypes = { - children: PropTypes.node.isRequired, -}; - -function MenuItem({ - variant, iconSrc, type, - onClick, children, disabled, -}) { - return ( -
- -
- ); -} - -MenuItem.defaultProps = { - variant: 'surface', - iconSrc: null, - type: 'button', - disabled: false, - onClick: null, -}; - -MenuItem.propTypes = { - variant: PropTypes.oneOf(['surface', 'positive', 'caution', 'danger']), - iconSrc: PropTypes.string, - type: PropTypes.oneOf(['button', 'submit']), - onClick: PropTypes.func, - children: PropTypes.node.isRequired, - disabled: PropTypes.bool, -}; - -function MenuBorder() { - return
; -} - -export { - ContextMenu as default, MenuHeader, MenuItem, MenuBorder, -}; diff --git a/src/app/atoms/context-menu/ContextMenu.scss b/src/app/atoms/context-menu/ContextMenu.scss deleted file mode 100644 index 2df9f0a4..00000000 --- a/src/app/atoms/context-menu/ContextMenu.scss +++ /dev/null @@ -1,81 +0,0 @@ -@use '../../partials/flex'; -@use '../../partials/text'; -@use '../../partials/dir'; - -.context-menu { - background-color: var(--bg-surface); - box-shadow: var(--bs-popup); - border-radius: var(--bo-radius); - overflow: hidden; - - &:focus { - outline: none; - } - & .tippy-content > div > .scrollbar { - max-height: 90vh; - } -} - -.context-menu__click-wrapper { - display: inline-flex; - - &:focus { - outline: none; - } -} - -.context-menu__header { - height: 34px; - padding: 0 var(--sp-normal); - margin-bottom: var(--sp-ultra-tight); - display: flex; - align-items: center; - border-bottom: 1px solid var(--bg-surface-border); - - .text { - @extend .cp-txt__ellipsis; - color: var(--tc-surface-low); - } - - &:not(:first-child) { - margin-top: var(--sp-extra-tight); - border-top: 1px solid var(--bg-surface-border); - } -} - -.context-menu__item { - display: flex; - button[class^="btn"] { - @extend .cp-fx__item-one; - justify-content: flex-start; - border-radius: 0; - box-shadow: none; - white-space: nowrap; - padding: var(--sp-extra-tight) var(--sp-normal); - - & > .ic-raw { - @include dir.side(margin, 0, var(--sp-tight)); - } - - // if item doesn't have icon - .text:first-child { - @include dir.side( - margin, - calc(var(--ic-small) + var(--sp-tight)), - 0 - ); - } - } - .btn-surface:focus { - background-color: var(--bg-surface-hover); - } - .btn-positive:focus { - background-color: var(--bg-positive-hover); - } - .btn-caution:focus { - background-color: var(--bg-caution-hover); - } - .btn-danger:focus { - background-color: var(--bg-danger-hover); - } -} \ No newline at end of file diff --git a/src/app/atoms/context-menu/ReusableContextMenu.jsx b/src/app/atoms/context-menu/ReusableContextMenu.jsx deleted file mode 100644 index 59bdb142..00000000 --- a/src/app/atoms/context-menu/ReusableContextMenu.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; - -import cons from '../../../client/state/cons'; -import navigation from '../../../client/state/navigation'; - -import ContextMenu from './ContextMenu'; - -let key = null; -function ReusableContextMenu() { - const [data, setData] = useState(null); - const openerRef = useRef(null); - - const closeMenu = () => { - key = null; - if (data) openerRef.current.click(); - }; - - useEffect(() => { - if (data) { - const { cords } = data; - openerRef.current.style.transform = `translate(${cords.x}px, ${cords.y}px)`; - openerRef.current.style.width = `${cords.width}px`; - openerRef.current.style.height = `${cords.height}px`; - openerRef.current.click(); - } - const handleContextMenuOpen = (placement, cords, render, afterClose) => { - if (key) { - closeMenu(); - return; - } - setData({ - placement, cords, render, afterClose, - }); - }; - navigation.on(cons.events.navigation.REUSABLE_CONTEXT_MENU_OPENED, handleContextMenuOpen); - return () => { - navigation.removeListener( - cons.events.navigation.REUSABLE_CONTEXT_MENU_OPENED, - handleContextMenuOpen, - ); - }; - }, [data]); - - const handleAfterToggle = (isVisible) => { - if (isVisible) { - key = Math.random(); - return; - } - data?.afterClose?.(); - if (setData) setData(null); - - if (key === null) return; - const copyKey = key; - setTimeout(() => { - if (key === copyKey) key = null; - }, 200); - }; - - return ( - ( - - )} - /> - ); -} - -export default ReusableContextMenu; diff --git a/src/app/atoms/divider/Divider.jsx b/src/app/atoms/divider/Divider.jsx deleted file mode 100644 index 76721241..00000000 --- a/src/app/atoms/divider/Divider.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Divider.scss'; - -import Text from '../text/Text'; - -function Divider({ text, variant, align }) { - const dividerClass = ` divider--${variant} divider--${align}`; - return ( -
- {text !== null && {text}} -
- ); -} - -Divider.defaultProps = { - text: null, - variant: 'surface', - align: 'center', -}; - -Divider.propTypes = { - text: PropTypes.string, - variant: PropTypes.oneOf(['surface', 'primary', 'positive', 'caution', 'danger']), - align: PropTypes.oneOf(['left', 'center', 'right']), -}; - -export default Divider; diff --git a/src/app/atoms/divider/Divider.scss b/src/app/atoms/divider/Divider.scss deleted file mode 100644 index 0f013ff0..00000000 --- a/src/app/atoms/divider/Divider.scss +++ /dev/null @@ -1,68 +0,0 @@ -.divider-line { - content: ''; - display: inline-block; - flex: 1; - border-bottom: 1px solid var(--local-divider-color); - opacity: var(--local-divider-opacity); -} - -.divider { - display: flex; - align-items: center; - - &--center::before, - &--right::before { - @extend .divider-line; - } - &--center::after, - &--left::after { - @extend .divider-line; - } - - &__text { - padding: 2px var(--sp-extra-tight); - border-radius: calc(var(--bo-radius) / 2); - } -} - -.divider--surface { - --local-divider-color: var(--bg-divider); - --local-divider-opacity: 1; - - .divider__text { - color: var(--tc-surface-low); - border: 1px solid var(--bg-divider); - } -} -.divider--primary { - --local-divider-color: var(--bg-primary); - --local-divider-opacity: .8; - .divider__text { - color: var(--tc-primary-high); - background-color: var(--bg-primary); - } -} -.divider--positive { - --local-divider-color: var(--bg-positive); - --local-divider-opacity: .8; - .divider__text { - color: var(--bg-surface); - background-color: var(--bg-positive); - } -} -.divider--danger { - --local-divider-color: var(--bg-danger); - --local-divider-opacity: .8; - .divider__text { - color: var(--bg-surface); - background-color: var(--bg-danger); - } -} -.divider--caution { - --local-divider-color: var(--bg-caution); - --local-divider-opacity: .8; - .divider__text { - color: var(--bg-surface); - background-color: var(--bg-caution); - } -} diff --git a/src/app/atoms/header/Header.jsx b/src/app/atoms/header/Header.jsx deleted file mode 100644 index 3c81e423..00000000 --- a/src/app/atoms/header/Header.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Header.scss'; - -function Header({ children }) { - return ( -
- {children} -
- ); -} - -Header.propTypes = { - children: PropTypes.node.isRequired, -}; - -function TitleWrapper({ children }) { - return ( -
- {children} -
- ); -} - -TitleWrapper.propTypes = { - children: PropTypes.node.isRequired, -}; - -export { Header as default, TitleWrapper }; diff --git a/src/app/atoms/header/Header.scss b/src/app/atoms/header/Header.scss deleted file mode 100644 index 9e45f824..00000000 --- a/src/app/atoms/header/Header.scss +++ /dev/null @@ -1,43 +0,0 @@ -@use '../../partials/text'; -@use '../../partials/dir'; - -.header { - @include dir.side(padding, var(--sp-normal), var(--sp-extra-tight)); - width: 100%; - height: var(--header-height); - border-bottom: 1px solid var(--bg-surface-border); - display: flex; - align-items: center; - - &__title-wrapper { - flex: 1; - min-width: 0; - display: flex; - align-items: center; - margin: 0 var(--sp-tight); - - &:first-child { - @include dir.side(margin, 0, var(--sp-tight)); - } - - & > .text:first-child { - @extend .cp-txt__ellipsis; - min-width: 0; - } - & > .text-b3{ - flex: 1; - min-width: 0; - - margin-top: var(--sp-ultra-tight); - @include dir.side(margin, var(--sp-tight), 0); - @include dir.side(padding, var(--sp-tight), 0); - @include dir.side(border, 1px solid var(--bg-surface-border), none); - - max-height: calc(2 * var(--lh-b3)); - overflow: hidden; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - display: -webkit-box; - } - } -} \ No newline at end of file diff --git a/src/app/atoms/input/Input.jsx b/src/app/atoms/input/Input.jsx deleted file mode 100644 index 96c94967..00000000 --- a/src/app/atoms/input/Input.jsx +++ /dev/null @@ -1,97 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Input.scss'; - -import TextareaAutosize from 'react-autosize-textarea'; - -function Input({ - id, label, name, value, placeholder, - required, type, onChange, forwardRef, - resizable, minHeight, onResize, state, - onKeyDown, disabled, autoFocus, -}) { - return ( -
- { label !== '' && } - { resizable - ? ( - - ) : ( - - )} -
- ); -} - -Input.defaultProps = { - id: null, - name: '', - label: '', - value: '', - placeholder: '', - type: 'text', - required: false, - onChange: null, - forwardRef: null, - resizable: false, - minHeight: 46, - onResize: null, - state: 'normal', - onKeyDown: null, - disabled: false, - autoFocus: false, -}; - -Input.propTypes = { - id: PropTypes.string, - name: PropTypes.string, - label: PropTypes.string, - value: PropTypes.string, - placeholder: PropTypes.string, - required: PropTypes.bool, - type: PropTypes.string, - onChange: PropTypes.func, - forwardRef: PropTypes.shape({}), - resizable: PropTypes.bool, - minHeight: PropTypes.number, - onResize: PropTypes.func, - state: PropTypes.oneOf(['normal', 'success', 'error']), - onKeyDown: PropTypes.func, - disabled: PropTypes.bool, - autoFocus: PropTypes.bool, -}; - -export default Input; diff --git a/src/app/atoms/input/Input.scss b/src/app/atoms/input/Input.scss deleted file mode 100644 index 40fe43ec..00000000 --- a/src/app/atoms/input/Input.scss +++ /dev/null @@ -1,52 +0,0 @@ -@use '../../atoms/scroll/scrollbar'; - -.input { - display: block; - width: 100%; - min-width: 0px; - margin: 0; - padding: var(--sp-tight) var(--sp-normal); - background-color: var(--bg-surface-low); - color: var(--tc-surface-normal); - box-shadow: none; - border-radius: var(--bo-radius); - border: 1px solid var(--bg-surface-border); - font-size: var(--fs-b2); - letter-spacing: var(--ls-b2); - line-height: var(--lh-b2); - - :disabled { - opacity: 0.4; - cursor: no-drop; - } - - &__label { - display: inline-block; - margin-bottom: var(--sp-ultra-tight); - color: var(--tc-surface-low); - } - - &--resizable { - resize: vertical !important; - overflow-y: auto !important; - @include scrollbar.scroll; - @include scrollbar.scroll__v; - @include scrollbar.scroll--auto-hide; - } - &--success { - border: 1px solid var(--bg-positive); - box-shadow: none !important; - } - &--error { - border: 1px solid var(--bg-danger); - box-shadow: none !important; - } - - &:focus { - outline: none; - box-shadow: var(--bs-primary-border); - } - &::placeholder { - color: var(--tc-surface-low) - } -} \ No newline at end of file diff --git a/src/app/atoms/modal/RawModal.jsx b/src/app/atoms/modal/RawModal.jsx deleted file mode 100644 index 450be0e0..00000000 --- a/src/app/atoms/modal/RawModal.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; -import './RawModal.scss'; - -import Modal from 'react-modal'; - -import navigation from '../../../client/state/navigation'; - -Modal.setAppElement('#root'); - -function RawModal({ - className, overlayClassName, - isOpen, size, onAfterOpen, onAfterClose, - onRequestClose, closeFromOutside, children, -}) { - let modalClass = (className !== null) ? `${className} ` : ''; - switch (size) { - case 'large': - modalClass += 'raw-modal__large '; - break; - case 'medium': - modalClass += 'raw-modal__medium '; - break; - case 'small': - default: - modalClass += 'raw-modal__small '; - } - - useEffect(() => { - navigation.setIsRawModalVisible(isOpen); - }, [isOpen]); - - const modalOverlayClass = (overlayClassName !== null) ? `${overlayClassName} ` : ''; - return ( - - {children} - - ); -} - -RawModal.defaultProps = { - className: null, - overlayClassName: null, - size: 'small', - onAfterOpen: null, - onAfterClose: null, - onRequestClose: null, - closeFromOutside: true, -}; - -RawModal.propTypes = { - className: PropTypes.string, - overlayClassName: PropTypes.string, - isOpen: PropTypes.bool.isRequired, - size: PropTypes.oneOf(['large', 'medium', 'small']), - onAfterOpen: PropTypes.func, - onAfterClose: PropTypes.func, - onRequestClose: PropTypes.func, - closeFromOutside: PropTypes.bool, - children: PropTypes.node.isRequired, -}; - -export default RawModal; diff --git a/src/app/atoms/modal/RawModal.scss b/src/app/atoms/modal/RawModal.scss deleted file mode 100644 index d612a4bc..00000000 --- a/src/app/atoms/modal/RawModal.scss +++ /dev/null @@ -1,66 +0,0 @@ -.raw-modal { - --small-modal-width: 525px; - --medium-modal-width: 712px; - --large-modal-width: 1024px; - - position: relative; - width: 100%; - max-height: 100%; - border-radius: var(--bo-radius); - box-shadow: var(--bs-popup); - outline: none; - overflow: hidden; - - &__small { - max-width: var(--small-modal-width); - } - &__medium { - max-width: var(--medium-modal-width); - } - &__large { - max-width: var(--large-modal-width); - } - - &__overlay { - position: fixed; - top: 0; - left: 0; - z-index: 999; - - display: flex; - justify-content: center; - align-items: center; - - padding: var(--sp-normal); - width: 100%; - height: 100%; - background-color: var(--bg-overlay); - } -} - -.ReactModal__Overlay { - animation: raw-modal--overlay 150ms; -} - -.ReactModal__Content { - animation: raw-modal--content 150ms; -} - -@keyframes raw-modal--content { - 0% { - transform: translateY(100px); - opacity: .5; - } - 100% { - transform: translateY(0); - opacity: 1; - } -} -@keyframes raw-modal--overlay { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} diff --git a/src/app/atoms/scroll/ScrollView.jsx b/src/app/atoms/scroll/ScrollView.jsx deleted file mode 100644 index 26c0c83a..00000000 --- a/src/app/atoms/scroll/ScrollView.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './ScrollView.scss'; - -const ScrollView = React.forwardRef(({ - horizontal, vertical, autoHide, invisible, onScroll, children, -}, ref) => { - let scrollbarClasses = ''; - if (horizontal) scrollbarClasses += ' scrollbar__h'; - if (vertical) scrollbarClasses += ' scrollbar__v'; - if (autoHide) scrollbarClasses += ' scrollbar--auto-hide'; - if (invisible) scrollbarClasses += ' scrollbar--invisible'; - return ( -
- {children} -
- ); -}); - -ScrollView.defaultProps = { - horizontal: false, - vertical: true, - autoHide: false, - invisible: false, - onScroll: null, -}; - -ScrollView.propTypes = { - horizontal: PropTypes.bool, - vertical: PropTypes.bool, - autoHide: PropTypes.bool, - invisible: PropTypes.bool, - onScroll: PropTypes.func, - children: PropTypes.node.isRequired, -}; - -export default ScrollView; diff --git a/src/app/atoms/scroll/ScrollView.scss b/src/app/atoms/scroll/ScrollView.scss deleted file mode 100644 index 251037e1..00000000 --- a/src/app/atoms/scroll/ScrollView.scss +++ /dev/null @@ -1,31 +0,0 @@ -@use '../../partials/dir'; -@use '_scrollbar'; - -@mixin paddingForSafari($padding) { - @media not all and (min-resolution:.001dpcm) { - @include dir.side(padding, 0, $padding); - } -} - -.scrollbar { - width: 100%; - height: 100%; - @include scrollbar.scroll; - @include paddingForSafari(var(--sp-extra-tight)); - - &__h { - @include scrollbar.scroll__h; - } - - &__v { - @include scrollbar.scroll__v; - } - - &--auto-hide { - @include scrollbar.scroll--auto-hide; - } - &--invisible { - @include scrollbar.scroll--invisible; - @include paddingForSafari(0); - } -} \ No newline at end of file diff --git a/src/app/atoms/scroll/_scrollbar.scss b/src/app/atoms/scroll/_scrollbar.scss deleted file mode 100644 index 78ec75ad..00000000 --- a/src/app/atoms/scroll/_scrollbar.scss +++ /dev/null @@ -1,65 +0,0 @@ -.firefox-scrollbar { - scrollbar-width: thin; - scrollbar-color: var(--bg-surface-hover) transparent; - &--transparent { - scrollbar-color: transparent transparent; - } -} -.webkit-scrollbar { - &::-webkit-scrollbar { - width: 8px; - height: 8px; - } -} -.webkit-scrollbar-track { - &::-webkit-scrollbar-track { - background-color: transparent; - } -} -.webkit-scrollbar-thumb { - &::-webkit-scrollbar-thumb { - background-color: var(--bg-surface-hover); - } - &::-webkit-scrollbar-thumb:hover { - background-color: var(--bg-surface-active); - } - &--transparent { - &::-webkit-scrollbar-thumb { - background-color: transparent; - } - } -} - -@mixin scroll { - overflow: hidden; - // Below code stop scroll when x-scrollable content come in timeline - // overscroll-behavior: none; - @extend .firefox-scrollbar; - @extend .webkit-scrollbar; - @extend .webkit-scrollbar-track; - @extend .webkit-scrollbar-thumb; -} - -@mixin scroll__h { - overflow-x: scroll; -} -@mixin scroll__v { - overflow-y: scroll; -} -@mixin scroll--auto-hide { - @extend .firefox-scrollbar--transparent; - @extend .webkit-scrollbar-thumb--transparent; - - &:hover { - @extend .firefox-scrollbar; - @extend .webkit-scrollbar-thumb; - } -} -@mixin scroll--invisible { - -ms-overflow-style: none; - scrollbar-width: none; - - &::-webkit-scrollbar { - display: none; - } -} \ No newline at end of file diff --git a/src/app/atoms/segmented-controls/SegmentedControls.jsx b/src/app/atoms/segmented-controls/SegmentedControls.jsx deleted file mode 100644 index 1d54dd06..00000000 --- a/src/app/atoms/segmented-controls/SegmentedControls.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import './SegmentedControls.scss'; - -import { blurOnBubbling } from '../button/script'; - -import Text from '../text/Text'; -import RawIcon from '../system-icons/RawIcon'; - -function SegmentedControls({ - selected, segments, onSelect, -}) { - const [select, setSelect] = useState(selected); - - function selectSegment(segmentIndex) { - setSelect(segmentIndex); - onSelect(segmentIndex); - } - - useEffect(() => { - setSelect(selected); - }, [selected]); - - return ( -
- { - segments.map((segment, index) => ( - - )) - } -
- ); -} - -SegmentedControls.propTypes = { - selected: PropTypes.number.isRequired, - segments: PropTypes.arrayOf(PropTypes.shape({ - iconSrc: PropTypes.string, - text: PropTypes.string, - })).isRequired, - onSelect: PropTypes.func.isRequired, -}; - -export default SegmentedControls; diff --git a/src/app/atoms/segmented-controls/SegmentedControls.scss b/src/app/atoms/segmented-controls/SegmentedControls.scss deleted file mode 100644 index fb1fd987..00000000 --- a/src/app/atoms/segmented-controls/SegmentedControls.scss +++ /dev/null @@ -1,57 +0,0 @@ -@use '../button/state'; -@use '../../partials/dir'; - -.segmented-controls { - background-color: var(--bg-surface-low); - border-radius: var(--bo-radius); - border: 1px solid var(--bg-surface-border); - - display: inline-flex; - overflow: hidden; -} - -.segment-btn { - padding: var(--sp-extra-tight) 0; - cursor: pointer; - @include state.hover(var(--bg-surface-hover)); - @include state.active(var(--bg-surface-active)); - - &__base { - padding: 0 var(--sp-normal); - display: flex; - align-items: center; - justify-content: center; - @include dir.side(border, 1px solid var(--bg-surface-border), none); - - & .text:nth-child(2) { - margin: 0 var(--sp-extra-tight); - } - } - &:first-child &__base { - border: none; - } - - &--active { - background-color: var(--bg-surface); - border: 1px solid var(--bg-surface-border); - border-width: 0 1px 0 1px; - - & .segment-btn__base, - & + .segment-btn .segment-btn__base { - border: none; - } - &:first-child{ - border-left: none; - } - &:last-child { - border-right: none; - } - [dir=rtl] & { - border-left: 1px solid var(--bg-surface-border); - border-right: 1px solid var(--bg-surface-border); - - &:first-child { border-right: none;} - &:last-child { border-left: none;} - } - } -} \ No newline at end of file diff --git a/src/app/atoms/spinner/Spinner.jsx b/src/app/atoms/spinner/Spinner.jsx deleted file mode 100644 index 61c9747e..00000000 --- a/src/app/atoms/spinner/Spinner.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Spinner.scss'; - -function Spinner({ size }) { - return ( -
- ); -} - -Spinner.defaultProps = { - size: 'normal', -}; - -Spinner.propTypes = { - size: PropTypes.oneOf(['normal', 'small']), -}; - -export default Spinner; diff --git a/src/app/atoms/spinner/Spinner.scss b/src/app/atoms/spinner/Spinner.scss deleted file mode 100644 index 73fbf676..00000000 --- a/src/app/atoms/spinner/Spinner.scss +++ /dev/null @@ -1,22 +0,0 @@ -.donut-spinner { - display: inline-block; - border: 4px solid var(--bg-surface-border); - border-left-color: var(--tc-surface-normal); - border-radius: 50%; - animation: donut-spin 1.2s cubic-bezier(0.73, 0.32, 0.67, 0.86) infinite; - - &--normal { - width: 40px; - height: 40px; - } - &--small { - width: 28px; - height: 28px; - } -} - -@keyframes donut-spin { - to { - transform: rotate(1turn); - } -} \ No newline at end of file diff --git a/src/app/atoms/system-icons/RawIcon.jsx b/src/app/atoms/system-icons/RawIcon.jsx deleted file mode 100644 index a6697f4f..00000000 --- a/src/app/atoms/system-icons/RawIcon.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './RawIcon.scss'; - -function RawIcon({ color, size, src, isImage }) { - const style = {}; - if (color !== null) style.backgroundColor = color; - if (isImage) { - style.backgroundColor = 'transparent'; - style.backgroundImage = `url("${src}")`; - } else { - style.WebkitMaskImage = `url("${src}")`; - style.maskImage = `url("${src}")`; - } - - return ; -} - -RawIcon.defaultProps = { - color: null, - size: 'normal', - isImage: false, -}; - -RawIcon.propTypes = { - color: PropTypes.string, - size: PropTypes.oneOf(['large', 'normal', 'small', 'extra-small']), - src: PropTypes.string.isRequired, - isImage: PropTypes.bool, -}; - -export default RawIcon; diff --git a/src/app/atoms/system-icons/RawIcon.scss b/src/app/atoms/system-icons/RawIcon.scss deleted file mode 100644 index 56fc9b3c..00000000 --- a/src/app/atoms/system-icons/RawIcon.scss +++ /dev/null @@ -1,28 +0,0 @@ -@mixin icSize($size) { - width: $size; - height: $size; -} - -.ic-raw { - display: inline-block; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-size: cover; - mask-size: cover; - background-color: var(--ic-surface-normal); - - background-size: cover; - background-repeat: no-repeat; -} -.ic-raw-large { - @include icSize(var(--ic-large)); -} -.ic-raw-normal { - @include icSize(var(--ic-normal)); -} -.ic-raw-small { - @include icSize(var(--ic-small)); -} -.ic-raw-extra-small { - @include icSize(var(--ic-extra-small)); -} \ No newline at end of file diff --git a/src/app/atoms/tabs/Tabs.jsx b/src/app/atoms/tabs/Tabs.jsx deleted file mode 100644 index bcdc8ef7..00000000 --- a/src/app/atoms/tabs/Tabs.jsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import './Tabs.scss'; - -import Button from '../button/Button'; -import ScrollView from '../scroll/ScrollView'; - -function TabItem({ - selected, iconSrc, - onClick, children, disabled, -}) { - const isSelected = selected ? 'tab-item--selected' : ''; - - return ( - - ); -} - -TabItem.defaultProps = { - selected: false, - iconSrc: null, - onClick: null, - disabled: false, -}; - -TabItem.propTypes = { - selected: PropTypes.bool, - iconSrc: PropTypes.string, - onClick: PropTypes.func, - children: PropTypes.node.isRequired, - disabled: PropTypes.bool, -}; - -function Tabs({ items, defaultSelected, onSelect }) { - const [selectedItem, setSelectedItem] = useState(items[defaultSelected]); - - const handleTabSelection = (item, index, target) => { - if (selectedItem === item) return; - target.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); - setSelectedItem(item); - onSelect(item, index); - }; - - return ( -
- -
- {items.map((item, index) => ( - handleTabSelection(item, index, e.currentTarget)} - > - {item.text} - - ))} -
-
-
- ); -} - -Tabs.defaultProps = { - defaultSelected: 0, -}; - -Tabs.propTypes = { - items: PropTypes.arrayOf( - PropTypes.shape({ - iconSrc: PropTypes.string, - text: PropTypes.string, - disabled: PropTypes.bool, - }), - ).isRequired, - defaultSelected: PropTypes.number, - onSelect: PropTypes.func.isRequired, -}; - -export default Tabs; diff --git a/src/app/atoms/tabs/Tabs.scss b/src/app/atoms/tabs/Tabs.scss deleted file mode 100644 index 39ddddec..00000000 --- a/src/app/atoms/tabs/Tabs.scss +++ /dev/null @@ -1,45 +0,0 @@ -@use '../../partials/dir'; - -.tabs { - height: var(--header-height); - box-shadow: inset 0 -1px 0 var(--bg-surface-border); - - &__content { - min-width: 100%; - height: 100%; - display: flex; - } -} - -.tab-item { - flex-shrink: 0; - - @include dir.side(padding, var(--sp-normal), 24px); - border-radius: 0; - height: 100%; - box-shadow: none; - border-radius: var(--bo-radius) var(--bo-radius) 0 0; - - &:focus, - &:active { - background-color: var(--bg-surface-active); - box-shadow: none; - } - - &--selected { - --bs-tab-selected: inset 0 -2px 0 var(--tc-surface-high); - box-shadow: var(--bs-tab-selected); - - & .ic-raw { - background-color: var(--ic-surface-high); - } - & .text { - font-weight: var(--fw-medium); - } - &:focus, - &:active { - background-color: var(--bg-surface-active); - box-shadow: var(--bs-tab-selected); - } - } -} \ No newline at end of file diff --git a/src/app/atoms/text/Text.jsx b/src/app/atoms/text/Text.jsx deleted file mode 100644 index 3f507ee3..00000000 --- a/src/app/atoms/text/Text.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Text.scss'; - -function Text({ - className, style, variant, weight, - primary, span, children, -}) { - const classes = []; - if (className) classes.push(className); - - classes.push(`text text-${variant} text-${weight}`); - if (primary) classes.push('font-primary'); - - const textClass = classes.join(' '); - if (span) return { children }; - if (variant === 'h1') return

{ children }

; - if (variant === 'h2') return

{ children }

; - if (variant === 's1') return

{ children }

; - return

{ children }

; -} - -Text.defaultProps = { - className: null, - style: null, - variant: 'b1', - weight: 'normal', - primary: false, - span: false, -}; - -Text.propTypes = { - className: PropTypes.string, - style: PropTypes.shape({}), - variant: PropTypes.oneOf(['h1', 'h2', 's1', 'b1', 'b2', 'b3']), - weight: PropTypes.oneOf(['light', 'normal', 'medium', 'bold']), - primary: PropTypes.bool, - span: PropTypes.bool, - children: PropTypes.node.isRequired, -}; - -export default Text; diff --git a/src/app/atoms/text/Text.scss b/src/app/atoms/text/Text.scss deleted file mode 100644 index 256bf6ea..00000000 --- a/src/app/atoms/text/Text.scss +++ /dev/null @@ -1,61 +0,0 @@ -@mixin font($type) { - font-size: var(--fs-#{$type}); - letter-spacing: var(--ls-#{$type}); - line-height: var(--lh-#{$type}); - - & img.emoji, - & img[data-mx-emoticon] { - height: calc(var(--lh-#{$type}) - .25rem); - } -} - -.text { - margin: 0; - padding: 0; - color: var(--tc-surface-high); - - & img.emoji, - & img[data-mx-emoticon] { - margin: 0 !important; - margin-right: 2px !important; - padding: 0 !important; - position: relative; - top: -.1rem; - vertical-align: middle; - } -} - -.text-light { - font-weight: var(--fw-light); -} -.text-normal { - font-weight: var(--fw-normal); -} -.text-medium { - font-weight: var(--fw-medium); -} -.text-bold { - font-weight: var(--fw-bold); -} - -.text-h1 { - @include font(h1); -} -.text-h2 { - @include font(h2); -} -.text-s1 { - @include font(s1); -} -.text-b1 { - @include font(b1); - color: var(--tc-surface-normal); -} -.text-b2 { - @include font(b2); - color: var(--tc-surface-normal); -} -.text-b3 { - @include font(b3); - color: var(--tc-surface-low); -} \ No newline at end of file diff --git a/src/app/atoms/time/Time.jsx b/src/app/atoms/time/Time.jsx deleted file mode 100644 index 750b958f..00000000 --- a/src/app/atoms/time/Time.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import dateFormat from 'dateformat'; -import { isInSameDay } from '../../../util/common'; - -function Time({ timestamp, fullTime }) { - const date = new Date(timestamp); - - const formattedFullTime = dateFormat(date, 'dd mmmm yyyy, hh:MM TT'); - let formattedDate = formattedFullTime; - - if (!fullTime) { - const compareDate = new Date(); - const isToday = isInSameDay(date, compareDate); - compareDate.setDate(compareDate.getDate() - 1); - const isYesterday = isInSameDay(date, compareDate); - - formattedDate = dateFormat(date, isToday || isYesterday ? 'hh:MM TT' : 'dd/mm/yyyy'); - if (isYesterday) { - formattedDate = `Yesterday, ${formattedDate}`; - } - } - - return ( - - ); -} - -Time.defaultProps = { - fullTime: false, -}; - -Time.propTypes = { - timestamp: PropTypes.number.isRequired, - fullTime: PropTypes.bool, -}; - -export default Time; diff --git a/src/app/atoms/tooltip/Tooltip.jsx b/src/app/atoms/tooltip/Tooltip.jsx deleted file mode 100644 index 0f9067f5..00000000 --- a/src/app/atoms/tooltip/Tooltip.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './Tooltip.scss'; -import Tippy from '@tippyjs/react'; - -function Tooltip({ - className, placement, content, delay, children, -}) { - return ( - - {children} - - ); -} - -Tooltip.defaultProps = { - placement: 'top', - className: '', - delay: [200, 0], -}; - -Tooltip.propTypes = { - className: PropTypes.string, - placement: PropTypes.string, - content: PropTypes.node.isRequired, - delay: PropTypes.arrayOf(PropTypes.number), - children: PropTypes.node.isRequired, -}; - -export default Tooltip; diff --git a/src/app/atoms/tooltip/Tooltip.scss b/src/app/atoms/tooltip/Tooltip.scss deleted file mode 100644 index f609aa58..00000000 --- a/src/app/atoms/tooltip/Tooltip.scss +++ /dev/null @@ -1,10 +0,0 @@ -.tooltip { - padding: var(--sp-extra-tight) var(--sp-normal); - background-color: var(--bg-tooltip); - border-radius: var(--bo-radius); - box-shadow: var(--bs-popup); - - .text { - color: var(--tc-tooltip); - } -} \ No newline at end of file diff --git a/src/app/components/CapabilitiesAndMediaConfigLoader.tsx b/src/app/components/CapabilitiesAndMediaConfigLoader.tsx deleted file mode 100644 index 574d0ca7..00000000 --- a/src/app/components/CapabilitiesAndMediaConfigLoader.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ReactNode, useCallback, useEffect } from 'react'; -import { Capabilities } from 'matrix-js-sdk'; -import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback'; -import { useMatrixClient } from '../hooks/useMatrixClient'; -import { MediaConfig } from '../hooks/useMediaConfig'; -import { promiseFulfilledResult } from '../utils/common'; - -type CapabilitiesAndMediaConfigLoaderProps = { - children: (capabilities?: Capabilities, mediaConfig?: MediaConfig) => ReactNode; -}; -export function CapabilitiesAndMediaConfigLoader({ - children, -}: CapabilitiesAndMediaConfigLoaderProps) { - const mx = useMatrixClient(); - - const [state, load] = useAsyncCallback< - [Capabilities | undefined, MediaConfig | undefined], - unknown, - [] - >( - useCallback(async () => { - const result = await Promise.allSettled([mx.getCapabilities(), mx.getMediaConfig()]); - const capabilities = promiseFulfilledResult(result[0]); - const mediaConfig = promiseFulfilledResult(result[1]); - return [capabilities, mediaConfig]; - }, [mx]) - ); - - useEffect(() => { - load(); - }, [load]); - - const [capabilities, mediaConfig] = - state.status === AsyncStatus.Success ? state.data : [undefined, undefined]; - return children(capabilities, mediaConfig); -} diff --git a/src/app/components/DeviceVerificationSetup.tsx b/src/app/components/DeviceVerificationSetup.tsx index fb09c150..433fa6a1 100644 --- a/src/app/components/DeviceVerificationSetup.tsx +++ b/src/app/components/DeviceVerificationSetup.tsx @@ -20,7 +20,7 @@ import { PasswordInput } from './password-input'; import { ContainerColor } from '../styles/ContainerColor.css'; import { copyToClipboard } from '../utils/dom'; import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback'; -import { clearSecretStorageKeys } from '../../client/state/secretStorageKeys'; +import { clearSecretStorageKeys } from '../../client/secretStorageKeys'; import { ActionUIA, ActionUIAFlowsLoader } from './ActionUIA'; import { useMatrixClient } from '../hooks/useMatrixClient'; import { useAlive } from '../hooks/useAlive'; diff --git a/src/app/components/ManualVerification.tsx b/src/app/components/ManualVerification.tsx index 03f3e710..f7cde92b 100644 --- a/src/app/components/ManualVerification.tsx +++ b/src/app/components/ManualVerification.tsx @@ -19,7 +19,7 @@ import { SecretStorageKeyContent } from '../../types/matrix/accountData'; import { SecretStorageRecoveryKey, SecretStorageRecoveryPassphrase } from './SecretStorage'; import { useMatrixClient } from '../hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '../hooks/useAsyncCallback'; -import { storePrivateKey } from '../../client/state/secretStorageKeys'; +import { storePrivateKey } from '../../client/secretStorageKeys'; export enum ManualVerificationMethod { RecoveryPassphrase = 'passphrase', diff --git a/src/app/components/ServerConfigsLoader.tsx b/src/app/components/ServerConfigsLoader.tsx new file mode 100644 index 00000000..3c8ce8eb --- /dev/null +++ b/src/app/components/ServerConfigsLoader.tsx @@ -0,0 +1,52 @@ +import { ReactNode, useCallback, useMemo } from 'react'; +import { Capabilities, validateAuthMetadata, ValidatedAuthMetadata } from 'matrix-js-sdk'; +import { AsyncStatus, useAsyncCallbackValue } from '../hooks/useAsyncCallback'; +import { useMatrixClient } from '../hooks/useMatrixClient'; +import { MediaConfig } from '../hooks/useMediaConfig'; +import { promiseFulfilledResult } from '../utils/common'; + +export type ServerConfigs = { + capabilities?: Capabilities; + mediaConfig?: MediaConfig; + authMetadata?: ValidatedAuthMetadata; +}; + +type ServerConfigsLoaderProps = { + children: (configs: ServerConfigs) => ReactNode; +}; +export function ServerConfigsLoader({ children }: ServerConfigsLoaderProps) { + const mx = useMatrixClient(); + const fallbackConfigs = useMemo(() => ({}), []); + + const [configsState] = useAsyncCallbackValue( + useCallback(async () => { + const result = await Promise.allSettled([ + mx.getCapabilities(), + mx.getMediaConfig(), + mx.getAuthMetadata(), + ]); + + const capabilities = promiseFulfilledResult(result[0]); + const mediaConfig = promiseFulfilledResult(result[1]); + const authMetadata = promiseFulfilledResult(result[2]); + let validatedAuthMetadata: ValidatedAuthMetadata | undefined; + + try { + validatedAuthMetadata = validateAuthMetadata(authMetadata); + } catch (e) { + console.error(e); + } + + return { + capabilities, + mediaConfig, + authMetadata: validatedAuthMetadata, + }; + }, [mx]) + ); + + const configs: ServerConfigs = + configsState.status === AsyncStatus.Success ? configsState.data : fallbackConfigs; + + return children(configs); +} diff --git a/src/app/components/UserRoomProfileRenderer.tsx b/src/app/components/UserRoomProfileRenderer.tsx new file mode 100644 index 00000000..ca7aa837 --- /dev/null +++ b/src/app/components/UserRoomProfileRenderer.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Menu, PopOut, toRem } from 'folds'; +import FocusTrap from 'focus-trap-react'; +import { useCloseUserRoomProfile, useUserRoomProfileState } from '../state/hooks/userRoomProfile'; +import { UserRoomProfile } from './user-profile'; +import { UserRoomProfileState } from '../state/userRoomProfile'; +import { useAllJoinedRoomsSet, useGetRoom } from '../hooks/useGetRoom'; +import { stopPropagation } from '../utils/keyboard'; +import { SpaceProvider } from '../hooks/useSpace'; +import { RoomProvider } from '../hooks/useRoom'; + +function UserRoomProfileContextMenu({ state }: { state: UserRoomProfileState }) { + const { roomId, spaceId, userId, cords, position } = state; + const allJoinedRooms = useAllJoinedRoomsSet(); + const getRoom = useGetRoom(allJoinedRooms); + const room = getRoom(roomId); + const space = spaceId ? getRoom(spaceId) : undefined; + + const close = useCloseUserRoomProfile(); + + if (!room) return null; + + return ( + + + + + + + + + + } + /> + ); +} + +export function UserRoomProfileRenderer() { + const state = useUserRoomProfileState(); + + if (!state) return null; + return ; +} diff --git a/src/app/components/create-room/AdditionalCreatorInput.tsx b/src/app/components/create-room/AdditionalCreatorInput.tsx new file mode 100644 index 00000000..84b92231 --- /dev/null +++ b/src/app/components/create-room/AdditionalCreatorInput.tsx @@ -0,0 +1,294 @@ +import { + Box, + Button, + Chip, + config, + Icon, + Icons, + Input, + Line, + Menu, + MenuItem, + PopOut, + RectCords, + Scroll, + Text, + toRem, +} from 'folds'; +import { isKeyHotkey } from 'is-hotkey'; +import FocusTrap from 'focus-trap-react'; +import React, { + ChangeEventHandler, + KeyboardEventHandler, + MouseEventHandler, + useMemo, + useState, +} from 'react'; +import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../utils/matrix'; +import { useDirectUsers } from '../../hooks/useDirectUsers'; +import { SettingTile } from '../setting-tile'; +import { useMatrixClient } from '../../hooks/useMatrixClient'; +import { stopPropagation } from '../../utils/keyboard'; +import { useAsyncSearch, UseAsyncSearchOptions } from '../../hooks/useAsyncSearch'; +import { highlightText, makeHighlightRegex } from '../../plugins/react-custom-html-parser'; + +export const useAdditionalCreators = (defaultCreators?: string[]) => { + const mx = useMatrixClient(); + const [additionalCreators, setAdditionalCreators] = useState( + () => defaultCreators?.filter((id) => id !== mx.getSafeUserId()) ?? [] + ); + + const addAdditionalCreator = (userId: string) => { + if (userId === mx.getSafeUserId()) return; + + setAdditionalCreators((creators) => { + const creatorsSet = new Set(creators); + creatorsSet.add(userId); + return Array.from(creatorsSet); + }); + }; + + const removeAdditionalCreator = (userId: string) => { + setAdditionalCreators((creators) => { + const creatorsSet = new Set(creators); + creatorsSet.delete(userId); + return Array.from(creatorsSet); + }); + }; + + return { + additionalCreators, + addAdditionalCreator, + removeAdditionalCreator, + }; +}; + +const SEARCH_OPTIONS: UseAsyncSearchOptions = { + limit: 1000, + matchOptions: { + contain: true, + }, +}; +const getUserIdString = (userId: string) => getMxIdLocalPart(userId) ?? userId; + +type AdditionalCreatorInputProps = { + additionalCreators: string[]; + onSelect: (userId: string) => void; + onRemove: (userId: string) => void; + disabled?: boolean; +}; +export function AdditionalCreatorInput({ + additionalCreators, + onSelect, + onRemove, + disabled, +}: AdditionalCreatorInputProps) { + const mx = useMatrixClient(); + const [menuCords, setMenuCords] = useState(); + const directUsers = useDirectUsers(); + + const [validUserId, setValidUserId] = useState(); + const filteredUsers = useMemo( + () => directUsers.filter((userId) => !additionalCreators.includes(userId)), + [directUsers, additionalCreators] + ); + const [result, search, resetSearch] = useAsyncSearch( + filteredUsers, + getUserIdString, + SEARCH_OPTIONS + ); + const queryHighlighRegex = result?.query ? makeHighlightRegex([result.query]) : undefined; + + const suggestionUsers = result + ? result.items + : filteredUsers.sort((a, b) => (a.toLocaleLowerCase() >= b.toLocaleLowerCase() ? 1 : -1)); + + const handleOpenMenu: MouseEventHandler = (evt) => { + setMenuCords(evt.currentTarget.getBoundingClientRect()); + }; + const handleCloseMenu = () => { + setMenuCords(undefined); + setValidUserId(undefined); + resetSearch(); + }; + + const handleCreatorChange: ChangeEventHandler = (evt) => { + const creatorInput = evt.currentTarget; + const creator = creatorInput.value.trim(); + if (isUserId(creator)) { + setValidUserId(creator); + } else { + setValidUserId(undefined); + const term = + getMxIdLocalPart(creator) ?? (creator.startsWith('@') ? creator.slice(1) : creator); + if (term) { + search(term); + } else { + resetSearch(); + } + } + }; + + const handleSelectUserId = (userId?: string) => { + if (userId && isUserId(userId)) { + onSelect(userId); + handleCloseMenu(); + } + }; + + const handleCreatorKeyDown: KeyboardEventHandler = (evt) => { + if (isKeyHotkey('enter', evt)) { + evt.preventDefault(); + const creator = evt.currentTarget.value.trim(); + handleSelectUserId(isUserId(creator) ? creator : suggestionUsers[0]); + } + }; + + const handleEnterClick = () => { + handleSelectUserId(validUserId); + }; + + return ( + + + + + {mx.getSafeUserId()} + + {additionalCreators.map((creator) => ( + } + onClick={() => onRemove(creator)} + disabled={disabled} + > + {creator} + + ))} + evt.key === 'ArrowDown', + isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', + escapeDeactivates: stopPropagation, + }} + > + + + + + + + + + + + {!validUserId && suggestionUsers.length > 0 ? ( + + + {suggestionUsers.map((userId) => ( + handleSelectUserId(userId)} + after={ + + {getMxIdServer(userId)} + + } + > + + + + {queryHighlighRegex + ? highlightText(queryHighlighRegex, [ + getMxIdLocalPart(userId) ?? userId, + ]) + : getMxIdLocalPart(userId)} + + + + + ))} + + + ) : ( + + + No Suggestions + + + Please provide the user ID and hit Enter. + + + )} + + + + + } + > + + + + + + + + ); +} diff --git a/src/app/components/create-room/CreateRoomAliasInput.tsx b/src/app/components/create-room/CreateRoomAliasInput.tsx new file mode 100644 index 00000000..e84658c0 --- /dev/null +++ b/src/app/components/create-room/CreateRoomAliasInput.tsx @@ -0,0 +1,118 @@ +import React, { + FormEventHandler, + KeyboardEventHandler, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import { MatrixError } from 'matrix-js-sdk'; +import { Box, color, Icon, Icons, Input, Spinner, Text, toRem } from 'folds'; +import { isKeyHotkey } from 'is-hotkey'; +import { getMxIdServer } from '../../utils/matrix'; +import { useMatrixClient } from '../../hooks/useMatrixClient'; +import { replaceSpaceWithDash } from '../../utils/common'; +import { AsyncState, AsyncStatus, useAsync } from '../../hooks/useAsyncCallback'; +import { useDebounce } from '../../hooks/useDebounce'; + +export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) { + const mx = useMatrixClient(); + const aliasInputRef = useRef(null); + const [aliasAvail, setAliasAvail] = useState>({ + status: AsyncStatus.Idle, + }); + + useEffect(() => { + if (aliasAvail.status === AsyncStatus.Success && aliasInputRef.current?.value === '') { + setAliasAvail({ status: AsyncStatus.Idle }); + } + }, [aliasAvail]); + + const checkAliasAvail = useAsync( + useCallback( + async (aliasLocalPart: string) => { + const roomAlias = `#${aliasLocalPart}:${getMxIdServer(mx.getSafeUserId())}`; + try { + const result = await mx.getRoomIdForAlias(roomAlias); + return typeof result.room_id !== 'string'; + } catch (e) { + if (e instanceof MatrixError && e.httpStatus === 404) { + return true; + } + throw e; + } + }, + [mx] + ), + setAliasAvail + ); + const aliasAvailable: boolean | undefined = + aliasAvail.status === AsyncStatus.Success ? aliasAvail.data : undefined; + + const debounceCheckAliasAvail = useDebounce(checkAliasAvail, { wait: 500 }); + + const handleAliasChange: FormEventHandler = (evt) => { + const aliasInput = evt.currentTarget; + const aliasLocalPart = replaceSpaceWithDash(aliasInput.value); + if (aliasLocalPart) { + aliasInput.value = aliasLocalPart; + debounceCheckAliasAvail(aliasLocalPart); + } else { + setAliasAvail({ status: AsyncStatus.Idle }); + } + }; + + const handleAliasKeyDown: KeyboardEventHandler = (evt) => { + if (isKeyHotkey('enter', evt)) { + evt.preventDefault(); + + const aliasInput = evt.currentTarget; + const aliasLocalPart = replaceSpaceWithDash(aliasInput.value); + if (aliasLocalPart) { + checkAliasAvail(aliasLocalPart); + } else { + setAliasAvail({ status: AsyncStatus.Idle }); + } + } + }; + + return ( + + Address (Optional) + + Pick an unique address to make it discoverable. + + + ) : ( + + ) + } + after={ + + :{getMxIdServer(mx.getSafeUserId())} + + } + onKeyDown={handleAliasKeyDown} + name="aliasInput" + size="500" + variant={aliasAvailable === true ? 'Success' : 'SurfaceVariant'} + radii="400" + autoComplete="off" + disabled={disabled} + /> + {aliasAvailable === false && ( + + + + This address is already taken. Please select a different one. + + + )} + + ); +} diff --git a/src/app/components/create-room/CreateRoomKindSelector.tsx b/src/app/components/create-room/CreateRoomKindSelector.tsx new file mode 100644 index 00000000..096954fb --- /dev/null +++ b/src/app/components/create-room/CreateRoomKindSelector.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { Box, Text, Icon, Icons, config, IconSrc } from 'folds'; +import { SequenceCard } from '../sequence-card'; +import { SettingTile } from '../setting-tile'; + +export enum CreateRoomKind { + Private = 'private', + Restricted = 'restricted', + Public = 'public', +} +type CreateRoomKindSelectorProps = { + value?: CreateRoomKind; + onSelect: (value: CreateRoomKind) => void; + canRestrict?: boolean; + disabled?: boolean; + getIcon: (kind: CreateRoomKind) => IconSrc; +}; +export function CreateRoomKindSelector({ + value, + onSelect, + canRestrict, + disabled, + getIcon, +}: CreateRoomKindSelectorProps) { + return ( + + {canRestrict && ( + onSelect(CreateRoomKind.Restricted)} + disabled={disabled} + > + } + after={value === CreateRoomKind.Restricted && } + > + Restricted + + Only member of parent space can join. + + + + )} + onSelect(CreateRoomKind.Private)} + disabled={disabled} + > + } + after={value === CreateRoomKind.Private && } + > + Private + + Only people with invite can join. + + + + onSelect(CreateRoomKind.Public)} + disabled={disabled} + > + } + after={value === CreateRoomKind.Public && } + > + Public + + Anyone with the address can join. + + + + + ); +} diff --git a/src/app/components/create-room/RoomVersionSelector.tsx b/src/app/components/create-room/RoomVersionSelector.tsx new file mode 100644 index 00000000..219ded0c --- /dev/null +++ b/src/app/components/create-room/RoomVersionSelector.tsx @@ -0,0 +1,117 @@ +import React, { MouseEventHandler, useState } from 'react'; +import { + Box, + Button, + Chip, + config, + Icon, + Icons, + Menu, + PopOut, + RectCords, + Text, + toRem, +} from 'folds'; +import FocusTrap from 'focus-trap-react'; +import { SettingTile } from '../setting-tile'; +import { SequenceCard } from '../sequence-card'; +import { stopPropagation } from '../../utils/keyboard'; + +export function RoomVersionSelector({ + versions, + value, + onChange, + disabled, +}: { + versions: string[]; + value: string; + onChange: (value: string) => void; + disabled?: boolean; +}) { + const [menuCords, setMenuCords] = useState(); + + const handleMenu: MouseEventHandler = (evt) => { + setMenuCords(evt.currentTarget.getBoundingClientRect()); + }; + + const handleSelect = (version: string) => { + setMenuCords(undefined); + onChange(version); + }; + + return ( + + setMenuCords(undefined), + clickOutsideDeactivates: true, + isKeyForward: (evt: KeyboardEvent) => + evt.key === 'ArrowDown' || evt.key === 'ArrowRight', + isKeyBackward: (evt: KeyboardEvent) => + evt.key === 'ArrowUp' || evt.key === 'ArrowLeft', + escapeDeactivates: stopPropagation, + }} + > + + + Versions + + {versions.map((version) => ( + handleSelect(version)} + type="button" + > + + {version} + + + ))} + + + + + } + > + + + } + /> + + ); +} diff --git a/src/app/components/create-room/index.ts b/src/app/components/create-room/index.ts new file mode 100644 index 00000000..ffd9cb3d --- /dev/null +++ b/src/app/components/create-room/index.ts @@ -0,0 +1,5 @@ +export * from './CreateRoomKindSelector'; +export * from './CreateRoomAliasInput'; +export * from './RoomVersionSelector'; +export * from './utils'; +export * from './AdditionalCreatorInput'; diff --git a/src/app/components/create-room/utils.ts b/src/app/components/create-room/utils.ts new file mode 100644 index 00000000..a0ca7488 --- /dev/null +++ b/src/app/components/create-room/utils.ts @@ -0,0 +1,140 @@ +import { + ICreateRoomOpts, + ICreateRoomStateEvent, + JoinRule, + MatrixClient, + RestrictedAllowType, + Room, +} from 'matrix-js-sdk'; +import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types'; +import { CreateRoomKind } from './CreateRoomKindSelector'; +import { RoomType, StateEvent } from '../../../types/matrix/room'; +import { getViaServers } from '../../plugins/via-servers'; +import { getMxIdServer } from '../../utils/matrix'; + +export const createRoomCreationContent = ( + type: RoomType | undefined, + allowFederation: boolean, + additionalCreators: string[] | undefined +): object => { + const content: Record = {}; + if (typeof type === 'string') { + content.type = type; + } + if (allowFederation === false) { + content['m.federate'] = false; + } + if (Array.isArray(additionalCreators)) { + content.additional_creators = additionalCreators; + } + + return content; +}; + +export const createRoomJoinRulesState = ( + kind: CreateRoomKind, + parent: Room | undefined, + knock: boolean +) => { + let content: RoomJoinRulesEventContent = { + join_rule: knock ? JoinRule.Knock : JoinRule.Invite, + }; + + if (kind === CreateRoomKind.Public) { + content = { + join_rule: JoinRule.Public, + }; + } + + if (kind === CreateRoomKind.Restricted && parent) { + content = { + join_rule: knock ? ('knock_restricted' as JoinRule) : JoinRule.Restricted, + allow: [ + { + type: RestrictedAllowType.RoomMembership, + room_id: parent.roomId, + }, + ], + }; + } + + return { + type: StateEvent.RoomJoinRules, + state_key: '', + content, + }; +}; + +export const createRoomParentState = (parent: Room) => ({ + type: StateEvent.SpaceParent, + state_key: parent.roomId, + content: { + canonical: true, + via: getViaServers(parent), + }, +}); + +export const createRoomEncryptionState = () => ({ + type: 'm.room.encryption', + state_key: '', + content: { + algorithm: 'm.megolm.v1.aes-sha2', + }, +}); + +export type CreateRoomData = { + version: string; + type?: RoomType; + parent?: Room; + kind: CreateRoomKind; + name: string; + topic?: string; + aliasLocalPart?: string; + encryption?: boolean; + knock: boolean; + allowFederation: boolean; + additionalCreators?: string[]; +}; +export const createRoom = async (mx: MatrixClient, data: CreateRoomData): Promise => { + const initialState: ICreateRoomStateEvent[] = []; + + if (data.encryption) { + initialState.push(createRoomEncryptionState()); + } + + if (data.parent) { + initialState.push(createRoomParentState(data.parent)); + } + + initialState.push(createRoomJoinRulesState(data.kind, data.parent, data.knock)); + + const options: ICreateRoomOpts = { + room_version: data.version, + name: data.name, + topic: data.topic, + room_alias_name: data.aliasLocalPart, + creation_content: createRoomCreationContent( + data.type, + data.allowFederation, + data.additionalCreators + ), + initial_state: initialState, + }; + + const result = await mx.createRoom(options); + + if (data.parent) { + await mx.sendStateEvent( + data.parent.roomId, + StateEvent.SpaceChild as any, + { + auto_join: false, + suggested: false, + via: [getMxIdServer(mx.getUserId() ?? '') ?? ''], + }, + result.room_id + ); + } + + return result.room_id; +}; diff --git a/src/app/components/editor/Editor.css.ts b/src/app/components/editor/Editor.css.ts index 09a444ec..d128ed07 100644 --- a/src/app/components/editor/Editor.css.ts +++ b/src/app/components/editor/Editor.css.ts @@ -41,21 +41,21 @@ export const EditorTextarea = style([ }, ]); -export const EditorPlaceholder = style([ +export const EditorPlaceholderContainer = style([ DefaultReset, { - position: 'absolute', - zIndex: 1, - width: '100%', opacity: config.opacity.Placeholder, pointerEvents: 'none', userSelect: 'none', + }, +]); - selectors: { - '&:not(:first-child)': { - display: 'none', - }, - }, +export const EditorPlaceholderTextVisual = style([ + DefaultReset, + { + display: 'block', + paddingTop: toRem(13), + paddingLeft: toRem(1), }, ]); diff --git a/src/app/components/editor/Editor.tsx b/src/app/components/editor/Editor.tsx index 044d0837..bd848dd5 100644 --- a/src/app/components/editor/Editor.tsx +++ b/src/app/components/editor/Editor.tsx @@ -106,22 +106,17 @@ export const CustomEditor = forwardRef( [editor, onKeyDown] ); - const renderPlaceholder = useCallback(({ attributes, children }: RenderPlaceholderProps) => { - // drop style attribute as we use our custom placeholder css. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { style, ...props } = attributes; - return ( - - {children} - - ); - }, []); + const renderPlaceholder = useCallback( + ({ attributes, children }: RenderPlaceholderProps) => ( + + {/* Inner component to style the actual text position and appearance */} + + {children} + + + ), + [] + ); return (
diff --git a/src/app/components/editor/Elements.tsx b/src/app/components/editor/Elements.tsx index a7438ecd..675c4542 100644 --- a/src/app/components/editor/Elements.tsx +++ b/src/app/components/editor/Elements.tsx @@ -157,7 +157,7 @@ export function RenderElement({ attributes, element, children }: RenderElementPr } + tooltip={} delay={500} > {(triggerRef) => ( diff --git a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx index cc431f58..b0c64f60 100644 --- a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx @@ -9,7 +9,7 @@ import { getDirectRoomAvatarUrl } from '../../../utils/room'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { AutocompleteQuery } from './autocompleteQuery'; import { AutocompleteMenu } from './AutocompleteMenu'; -import { getMxIdServer, validMxId } from '../../../utils/matrix'; +import { getMxIdServer, isRoomAlias } from '../../../utils/matrix'; import { UseAsyncSearchOptions, useAsyncSearch } from '../../../hooks/useAsyncSearch'; import { onTabPress } from '../../../utils/keyboard'; import { useKeyDown } from '../../../hooks/useKeyDown'; @@ -22,7 +22,7 @@ import { getViaServers } from '../../../plugins/via-servers'; type MentionAutoCompleteHandler = (roomAliasOrId: string, name: string) => void; const roomAliasFromQueryText = (mx: MatrixClient, text: string) => - validMxId(`#${text}`) + isRoomAlias(`#${text}`) ? `#${text}` : `#${text}${text.endsWith(':') ? '' : ':'}${getMxIdServer(mx.getUserId() ?? '')}`; diff --git a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx index d6c0f302..7a8012eb 100644 --- a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx @@ -15,7 +15,7 @@ import { import { onTabPress } from '../../../utils/keyboard'; import { createMentionElement, moveCursor, replaceWithElement } from '../utils'; import { useKeyDown } from '../../../hooks/useKeyDown'; -import { getMxIdLocalPart, getMxIdServer, validMxId } from '../../../utils/matrix'; +import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../../utils/matrix'; import { getMemberDisplayName, getMemberSearchStr } from '../../../utils/room'; import { UserAvatar } from '../../user-avatar'; import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication'; @@ -24,7 +24,7 @@ import { Membership } from '../../../../types/matrix/room'; type MentionAutoCompleteHandler = (userId: string, name: string) => void; const userIdFromQueryText = (mx: MatrixClient, text: string) => - validMxId(`@${text}`) + isUserId(`@${text}`) ? `@${text}` : `@${text}${text.endsWith(':') ? '' : ':'}${getMxIdServer(mx.getUserId() ?? '')}`; diff --git a/src/app/components/event-readers/EventReaders.tsx b/src/app/components/event-readers/EventReaders.tsx index de1416b6..c7900237 100644 --- a/src/app/components/event-readers/EventReaders.tsx +++ b/src/app/components/event-readers/EventReaders.tsx @@ -19,9 +19,11 @@ import { getMemberDisplayName } from '../../utils/room'; import { getMxIdLocalPart } from '../../utils/matrix'; import * as css from './EventReaders.css'; import { useMatrixClient } from '../../hooks/useMatrixClient'; -import { openProfileViewer } from '../../../client/action/navigation'; import { UserAvatar } from '../user-avatar'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; +import { useOpenUserRoomProfile } from '../../state/hooks/userRoomProfile'; +import { useSpaceOptionally } from '../../hooks/useSpace'; +import { getMouseEventCords } from '../../utils/dom'; export type EventReadersProps = { room: Room; @@ -33,6 +35,8 @@ export const EventReaders = as<'div', EventReadersProps>( const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); const latestEventReaders = useRoomEventReaders(room, eventId); + const openProfile = useOpenUserRoomProfile(); + const space = useSpaceOptionally(); const getName = (userId: string) => getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId) ?? userId; @@ -57,19 +61,32 @@ export const EventReaders = as<'div', EventReadersProps>( {latestEventReaders.map((readerId) => { const name = getName(readerId); - const avatarMxcUrl = room - .getMember(readerId) - ?.getMxcAvatarUrl(); - const avatarUrl = avatarMxcUrl ? mx.mxcUrlToHttp(avatarMxcUrl, 100, 100, 'crop', undefined, false, useAuthentication) : undefined; + const avatarMxcUrl = room.getMember(readerId)?.getMxcAvatarUrl(); + const avatarUrl = avatarMxcUrl + ? mx.mxcUrlToHttp( + avatarMxcUrl, + 100, + 100, + 'crop', + undefined, + false, + useAuthentication + ) + : undefined; return ( { - requestClose(); - openProfileViewer(readerId, room.roomId); + onClick={(event) => { + openProfile( + room.roomId, + space?.roomId, + readerId, + getMouseEventCords(event.nativeEvent), + 'Bottom' + ); }} before={ diff --git a/src/app/components/image-pack-view/RoomImagePack.tsx b/src/app/components/image-pack-view/RoomImagePack.tsx index 9dd45c1f..92b4ff21 100644 --- a/src/app/components/image-pack-view/RoomImagePack.tsx +++ b/src/app/components/image-pack-view/RoomImagePack.tsx @@ -1,12 +1,14 @@ import React, { useCallback, useMemo } from 'react'; import { Room } from 'matrix-js-sdk'; -import { usePowerLevels, usePowerLevelsAPI } from '../../hooks/usePowerLevels'; +import { usePowerLevels } from '../../hooks/usePowerLevels'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { ImagePackContent } from './ImagePackContent'; import { ImagePack, PackContent } from '../../plugins/custom-emoji'; import { StateEvent } from '../../../types/matrix/room'; import { useRoomImagePack } from '../../hooks/useImagePacks'; import { randomStr } from '../../utils/common'; +import { useRoomPermissions } from '../../hooks/useRoomPermissions'; +import { useRoomCreators } from '../../hooks/useRoomCreators'; type RoomImagePackProps = { room: Room; @@ -17,9 +19,10 @@ export function RoomImagePack({ room, stateKey }: RoomImagePackProps) { const mx = useMatrixClient(); const userId = mx.getUserId()!; const powerLevels = usePowerLevels(room); + const creators = useRoomCreators(room); - const { getPowerLevel, canSendStateEvent } = usePowerLevelsAPI(powerLevels); - const canEditImagePack = canSendStateEvent(StateEvent.PoniesRoomEmotes, getPowerLevel(userId)); + const permissions = useRoomPermissions(creators, powerLevels); + const canEditImagePack = permissions.stateEvent(StateEvent.PoniesRoomEmotes, userId); const fallbackPack = useMemo(() => { const fakePackId = randomStr(4); diff --git a/src/app/components/invite-user-prompt/InviteUserPrompt.tsx b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx new file mode 100644 index 00000000..ddac0576 --- /dev/null +++ b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx @@ -0,0 +1,291 @@ +import React, { + ChangeEventHandler, + FormEventHandler, + KeyboardEventHandler, + useCallback, + useMemo, + useRef, + useState, +} from 'react'; +import { + Overlay, + OverlayBackdrop, + OverlayCenter, + Box, + Header, + config, + Text, + IconButton, + Icon, + Icons, + Input, + Button, + Spinner, + color, + TextArea, + Dialog, + Menu, + toRem, + Scroll, + MenuItem, +} from 'folds'; +import { Room } from 'matrix-js-sdk'; +import { isKeyHotkey } from 'is-hotkey'; +import FocusTrap from 'focus-trap-react'; +import { stopPropagation } from '../../utils/keyboard'; +import { useDirectUsers } from '../../hooks/useDirectUsers'; +import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../utils/matrix'; +import { Membership } from '../../../types/matrix/room'; +import { useAsyncSearch, UseAsyncSearchOptions } from '../../hooks/useAsyncSearch'; +import { highlightText, makeHighlightRegex } from '../../plugins/react-custom-html-parser'; +import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; +import { useMatrixClient } from '../../hooks/useMatrixClient'; +import { BreakWord } from '../../styles/Text.css'; +import { useAlive } from '../../hooks/useAlive'; + +const SEARCH_OPTIONS: UseAsyncSearchOptions = { + limit: 1000, + matchOptions: { + contain: true, + }, +}; +const getUserIdString = (userId: string) => getMxIdLocalPart(userId) ?? userId; + +type InviteUserProps = { + room: Room; + requestClose: () => void; +}; +export function InviteUserPrompt({ room, requestClose }: InviteUserProps) { + const mx = useMatrixClient(); + const alive = useAlive(); + + const inputRef = useRef(null); + const directUsers = useDirectUsers(); + const [validUserId, setValidUserId] = useState(); + + const filteredUsers = useMemo( + () => + directUsers.filter((userId) => { + const membership = room.getMember(userId)?.membership; + return membership !== Membership.Join; + }), + [directUsers, room] + ); + const [result, search, resetSearch] = useAsyncSearch( + filteredUsers, + getUserIdString, + SEARCH_OPTIONS + ); + const queryHighlighRegex = result?.query + ? makeHighlightRegex(result.query.split(' ')) + : undefined; + + const [inviteState, invite] = useAsyncCallback( + useCallback( + async (userId, reason) => { + await mx.invite(room.roomId, userId, reason); + }, + [mx, room] + ) + ); + + const inviting = inviteState.status === AsyncStatus.Loading; + + const handleReset = () => { + if (inputRef.current) inputRef.current.value = ''; + setValidUserId(undefined); + resetSearch(); + }; + + const handleSubmit: FormEventHandler = (evt) => { + evt.preventDefault(); + const target = evt.target as HTMLFormElement | undefined; + + if (inviting || !validUserId) return; + + const reasonInput = target?.reasonInput as HTMLTextAreaElement | undefined; + const reason = reasonInput?.value.trim(); + + invite(validUserId, reason || undefined).then(() => { + if (alive()) { + handleReset(); + if (reasonInput) reasonInput.value = ''; + } + }); + }; + + const handleSearchChange: ChangeEventHandler = (evt) => { + const value = evt.currentTarget.value.trim(); + if (isUserId(value)) { + setValidUserId(value); + } else { + setValidUserId(undefined); + const term = getMxIdLocalPart(value) ?? (value.startsWith('@') ? value.slice(1) : value); + if (term) { + search(term); + } else { + resetSearch(); + } + } + }; + + const handleUserId = (userId: string) => { + if (inputRef.current) { + inputRef.current.value = userId; + setValidUserId(userId); + resetSearch(); + inputRef.current.focus(); + } + }; + + const handleKeyDown: KeyboardEventHandler = (evt) => { + if (isKeyHotkey('escape', evt)) { + resetSearch(); + return; + } + if (isKeyHotkey('tab', evt) && result && result.items.length > 0) { + evt.preventDefault(); + const userId = result.items[0]; + handleUserId(userId); + } + }; + + return ( + }> + + inputRef.current, + clickOutsideDeactivates: true, + onDeactivate: requestClose, + escapeDeactivates: stopPropagation, + }} + > + + +
+ + + Invite + + + + + + + +
+ + + User ID +
+ + {result && result.items.length > 0 && ( + isKeyHotkey('arrowdown', evt), + isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt), + escapeDeactivates: stopPropagation, + }} + > + + + +
+ {result.items.map((userId) => { + const username = `${getMxIdLocalPart(userId)}`; + const userServer = getMxIdServer(userId); + + return ( + handleUserId(userId)} + after={ + + {userServer} + + } + disabled={inviting} + > + + + + {queryHighlighRegex + ? highlightText(queryHighlighRegex, [ + username ?? userId, + ]) + : username} + + + + + ); + })} +
+
+
+
+
+ )} +
+
+ + Reason (Optional) +