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/package-lock.json b/package-lock.json
index f85dd74d..70826ae7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "cinny",
- "version": "4.6.0",
+ "version": "4.9.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cinny",
- "version": "4.6.0",
+ "version": "4.9.1",
"license": "AGPL-3.0-only",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
@@ -21,6 +21,7 @@
"@vanilla-extract/recipes": "0.3.0",
"@vanilla-extract/vite-plugin": "3.7.1",
"await-to-js": "3.0.0",
+ "badwords-list": "2.0.1-4",
"blurhash": "2.0.4",
"browser-encrypt-attachment": "0.3.0",
"chroma-js": "3.1.2",
@@ -33,7 +34,7 @@
"file-saver": "2.0.5",
"flux": "4.0.3",
"focus-trap-react": "10.0.2",
- "folds": "2.1.0",
+ "folds": "2.2.0",
"formik": "2.4.6",
"html-dom-parser": "4.0.0",
"html-react-parser": "4.2.0",
@@ -45,7 +46,7 @@
"jotai": "2.6.0",
"linkify-react": "4.1.3",
"linkifyjs": "4.1.3",
- "matrix-js-sdk": "35.0.0",
+ "matrix-js-sdk": "37.5.0",
"millify": "6.1.0",
"pdfjs-dist": "4.2.67",
"prismjs": "1.30.0",
@@ -98,7 +99,7 @@
"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"
@@ -2263,17 +2264,19 @@
}
},
"node_modules/@matrix-org/matrix-sdk-crypto-wasm": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-11.1.0.tgz",
- "integrity": "sha512-JPuO9RCVDklDjbFzMvZfQb7PuiFkLY72bniRSu81lRzkkrcbZtmKqBFMm9H4f2FSz+tHVkDnmsvn12I4sdJJ5A==",
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-14.1.0.tgz",
+ "integrity": "sha512-vcSxHJIr6lP0Fgo8jl0sTHg+OZxZn+skGjiyB62erfgw/R2QqJl0ZVSY8SRcbk9LtHo/ZGld1tnaOyjL2e3cLQ==",
+ "license": "Apache-2.0",
"engines": {
- "node": ">= 10"
+ "node": ">= 18"
}
},
"node_modules/@matrix-org/olm": {
"version": "3.2.15",
"resolved": "https://registry.npmjs.org/@matrix-org/olm/-/olm-3.2.15.tgz",
- "integrity": "sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q=="
+ "integrity": "sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==",
+ "license": "Apache-2.0"
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
@@ -4589,7 +4592,8 @@
"node_modules/@types/events": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
- "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g=="
+ "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
+ "license": "MIT"
},
"node_modules/@types/file-saver": {
"version": "2.0.5",
@@ -4678,7 +4682,8 @@
"node_modules/@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
- "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "license": "MIT"
},
"node_modules/@types/sanitize-html": {
"version": "2.9.0",
@@ -5088,7 +5093,8 @@
"node_modules/another-json": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/another-json/-/another-json-0.2.0.tgz",
- "integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg=="
+ "integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==",
+ "license": "Apache-2.0"
},
"node_modules/ansi-regex": {
"version": "5.0.1",
@@ -5431,6 +5437,12 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
+ "node_modules/badwords-list": {
+ "version": "2.0.1-4",
+ "resolved": "https://registry.npmjs.org/badwords-list/-/badwords-list-2.0.1-4.tgz",
+ "integrity": "sha512-FxfZUp7B9yCnesNtFQS9v6PvZdxTYa14Q60JR6vhjdQdWI4naTjJIyx22JzoER8ooeT8SAAKoHLjKfCV7XgYUQ==",
+ "license": "MIT"
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -5438,9 +5450,10 @@
"devOptional": true
},
"node_modules/base-x": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz",
- "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ=="
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz",
+ "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==",
+ "license": "MIT"
},
"node_modules/base64-js": {
"version": "1.5.1",
@@ -5546,6 +5559,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz",
"integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==",
+ "license": "MIT",
"dependencies": {
"base-x": "^5.0.0"
}
@@ -5848,6 +5862,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6"
}
@@ -6999,6 +7014,7 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
"engines": {
"node": ">=0.8.x"
}
@@ -7249,15 +7265,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.2.0",
+ "resolved": "https://registry.npmjs.org/folds/-/folds-2.2.0.tgz",
+ "integrity": "sha512-uOfck5eWEIK11rhOAEdSoPIvMXwv+D1Go03pxSAKezWVb+uRoBdmE6LEqiLOF+ac4DGmZRMPvpdDsXCg7EVNIg==",
+ "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": {
@@ -8557,6 +8574,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "license": "MIT",
"engines": {
"node": ">=18"
}
@@ -8689,6 +8707,7 @@
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.6.0"
},
@@ -8764,21 +8783,23 @@
"node_modules/matrix-events-sdk": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz",
- "integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
+ "integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==",
+ "license": "Apache-2.0"
},
"node_modules/matrix-js-sdk": {
- "version": "35.0.0",
- "resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-35.0.0.tgz",
- "integrity": "sha512-X8hIsd/8x1SC9vRr8DiNKQxmdrfRujtvEWPz8mY4FxVDJG8HEGDHvqUmaSy2jrtnOUn4oHzGQVLFO3DnhsSf8w==",
+ "version": "37.5.0",
+ "resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-37.5.0.tgz",
+ "integrity": "sha512-5tyuAi5hnKud1UkVq8Z2/3c22hWGELBZzErJPZkE6Hju2uGUfGtrIx6uj6puv0ZjvsUU3X6Qgm8vdReKO1PGig==",
+ "license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.12.5",
- "@matrix-org/matrix-sdk-crypto-wasm": "^11.0.0",
+ "@matrix-org/matrix-sdk-crypto-wasm": "^14.0.1",
"@matrix-org/olm": "3.2.15",
"another-json": "^0.2.0",
"bs58": "^6.0.0",
"content-type": "^1.0.4",
"jwt-decode": "^4.0.0",
- "loglevel": "^1.7.1",
+ "loglevel": "^1.9.2",
"matrix-events-sdk": "0.0.1",
"matrix-widget-api": "^1.10.0",
"oidc-client-ts": "^3.0.1",
@@ -8792,21 +8813,23 @@
}
},
"node_modules/matrix-js-sdk/node_modules/uuid": {
- "version": "11.0.5",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz",
- "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
+ "license": "MIT",
"bin": {
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/matrix-widget-api": {
- "version": "1.12.0",
- "resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.12.0.tgz",
- "integrity": "sha512-6JRd9fJGGvuBRhcTg9wX+Skn/Q1wox3jdp5yYQKJ6pPw4urW9bkTR90APBKVDB1vorJKT44jml+lCzkDMRBjww==",
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.13.1.tgz",
+ "integrity": "sha512-mkOHUVzaN018TCbObfGOSaMW2GoUxOfcxNNlTVx5/HeMk3OSQPQM0C9oEME5Liiv/dBUoSrEB64V8wF7e/gb1w==",
+ "license": "Apache-2.0",
"dependencies": {
"@types/events": "^3.0.0",
"events": "^3.2.0"
@@ -9198,9 +9221,10 @@
}
},
"node_modules/oidc-client-ts": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.1.0.tgz",
- "integrity": "sha512-IDopEXjiwjkmJLYZo6BTlvwOtnlSniWZkKZoXforC/oLZHC9wkIxd25Kwtmo5yKFMMVcsp3JY6bhcNJqdYk8+g==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.2.1.tgz",
+ "integrity": "sha512-hS5AJ5s/x4bXhHvNJT1v+GGvzHUwdRWqNQQbSrp10L1IRmzfRGKQ3VWN3dstJb+oF3WtAyKezwD2+dTEIyBiAA==",
+ "license": "Apache-2.0",
"dependencies": {
"jwt-decode": "^4.0.0"
},
@@ -9288,6 +9312,7 @@
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
"integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "license": "MIT",
"dependencies": {
"@types/retry": "0.12.0",
"retry": "^0.13.1"
@@ -10051,6 +10076,7 @@
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "license": "MIT",
"engines": {
"node": ">= 4"
}
@@ -10264,6 +10290,7 @@
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.15.0.tgz",
"integrity": "sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==",
+ "license": "MIT",
"bin": {
"sdp-verify": "checker.js"
}
@@ -11172,7 +11199,8 @@
"node_modules/unhomoglyph": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz",
- "integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg=="
+ "integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==",
+ "license": "MIT"
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.1",
@@ -11303,9 +11331,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",
diff --git a/package.json b/package.json
index b489dc2c..f1816cdd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cinny",
- "version": "4.6.0",
+ "version": "4.9.1",
"description": "Yet another matrix client",
"main": "index.js",
"type": "module",
@@ -32,6 +32,7 @@
"@vanilla-extract/recipes": "0.3.0",
"@vanilla-extract/vite-plugin": "3.7.1",
"await-to-js": "3.0.0",
+ "badwords-list": "2.0.1-4",
"blurhash": "2.0.4",
"browser-encrypt-attachment": "0.3.0",
"chroma-js": "3.1.2",
@@ -44,7 +45,7 @@
"file-saver": "2.0.5",
"flux": "4.0.3",
"focus-trap-react": "10.0.2",
- "folds": "2.1.0",
+ "folds": "2.2.0",
"formik": "2.4.6",
"html-dom-parser": "4.0.0",
"html-react-parser": "4.2.0",
@@ -56,7 +57,7 @@
"jotai": "2.6.0",
"linkify-react": "4.1.3",
"linkifyjs": "4.1.3",
- "matrix-js-sdk": "35.0.0",
+ "matrix-js-sdk": "37.5.0",
"millify": "6.1.0",
"pdfjs-dist": "4.2.67",
"prismjs": "1.30.0",
@@ -109,7 +110,7 @@
"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/src/app/atoms/time/Time.jsx b/src/app/atoms/time/Time.jsx
index 750b958f..d7bbe439 100644
--- a/src/app/atoms/time/Time.jsx
+++ b/src/app/atoms/time/Time.jsx
@@ -4,10 +4,25 @@ import PropTypes from 'prop-types';
import dateFormat from 'dateformat';
import { isInSameDay } from '../../../util/common';
-function Time({ timestamp, fullTime }) {
+/**
+ * Renders a formatted timestamp.
+ *
+ * Displays the time in hour:minute format if the message is from today or yesterday, unless `fullTime` is true.
+ * For older messages, it shows the date and time.
+ *
+ * @param {number} timestamp - The timestamp to display.
+ * @param {boolean} [fullTime=false] - If true, always show the full date and time.
+ * @param {boolean} hour24Clock - Whether to use 24-hour time format.
+ * @param {string} dateFormatString - Format string for the date part.
+ * @returns {JSX.Element} A
} />
+ } />
join} />
} />
} />
} />
+ } />
- v4.6.0
+ v4.9.1
Twitter
diff --git a/src/app/pages/auth/SSOLogin.tsx b/src/app/pages/auth/SSOLogin.tsx
index d0cdaeb6..3ff1a229 100644
--- a/src/app/pages/auth/SSOLogin.tsx
+++ b/src/app/pages/auth/SSOLogin.tsx
@@ -1,19 +1,21 @@
import { Avatar, AvatarImage, Box, Button, Text } from 'folds';
-import { IIdentityProvider, createClient } from 'matrix-js-sdk';
+import { IIdentityProvider, SSOAction, createClient } from 'matrix-js-sdk';
import React, { useMemo } from 'react';
import { useAutoDiscoveryInfo } from '../../hooks/useAutoDiscoveryInfo';
type SSOLoginProps = {
providers?: IIdentityProvider[];
redirectUrl: string;
+ action?: SSOAction;
saveScreenSpace?: boolean;
};
-export function SSOLogin({ providers, redirectUrl, saveScreenSpace }: SSOLoginProps) {
+export function SSOLogin({ providers, redirectUrl, action, saveScreenSpace }: SSOLoginProps) {
const discovery = useAutoDiscoveryInfo();
const baseUrl = discovery['m.homeserver'].base_url;
const mx = useMemo(() => createClient({ baseUrl }), [baseUrl]);
- const getSSOIdUrl = (ssoId?: string): string => mx.getSsoLoginUrl(redirectUrl, 'sso', ssoId);
+ const getSSOIdUrl = (ssoId?: string): string =>
+ mx.getSsoLoginUrl(redirectUrl, 'sso', ssoId, action);
const withoutIcon = providers
? providers.find(
diff --git a/src/app/pages/auth/login/Login.tsx b/src/app/pages/auth/login/Login.tsx
index 6b9f1223..2f04a733 100644
--- a/src/app/pages/auth/login/Login.tsx
+++ b/src/app/pages/auth/login/Login.tsx
@@ -1,6 +1,7 @@
import React, { useMemo } from 'react';
import { Box, Text, color } from 'folds';
import { Link, useSearchParams } from 'react-router-dom';
+import { SSOAction } from 'matrix-js-sdk';
import { useAuthFlows } from '../../../hooks/useAuthFlows';
import { useAuthServer } from '../../../hooks/useAuthServer';
import { useParsedLoginFlows } from '../../../hooks/useParsedLoginFlows';
@@ -76,6 +77,7 @@ export function Login() {
diff --git a/src/app/pages/auth/login/loginUtil.ts b/src/app/pages/auth/login/loginUtil.ts
index 1e2248d9..7e1c7153 100644
--- a/src/app/pages/auth/login/loginUtil.ts
+++ b/src/app/pages/auth/login/loginUtil.ts
@@ -73,7 +73,7 @@ export const login = async (
}
const mx = createClient({ baseUrl: url });
- const [err, res] = await to(mx.login(data.type, data));
+ const [err, res] = await to(mx.loginRequest(data));
if (err) {
if (err.httpStatus === 400) {
diff --git a/src/app/pages/auth/register/Register.tsx b/src/app/pages/auth/register/Register.tsx
index d2986d70..7176489b 100644
--- a/src/app/pages/auth/register/Register.tsx
+++ b/src/app/pages/auth/register/Register.tsx
@@ -1,6 +1,7 @@
import React, { useMemo } from 'react';
import { Box, Text, color } from 'folds';
import { Link, useSearchParams } from 'react-router-dom';
+import { SSOAction } from 'matrix-js-sdk';
import { useAuthServer } from '../../../hooks/useAuthServer';
import { RegisterFlowStatus, useAuthFlows } from '../../../hooks/useAuthFlows';
import { useParsedLoginFlows } from '../../../hooks/useParsedLoginFlows';
@@ -83,6 +84,7 @@ export function Register() {
diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx
index 846d8ff3..c48dbf53 100644
--- a/src/app/pages/client/ClientRoot.tsx
+++ b/src/app/pages/client/ClientRoot.tsx
@@ -25,7 +25,7 @@ import {
} from '../../../client/initMatrix';
import { getSecret } from '../../../client/state/auth';
import { SplashScreen } from '../../components/splash-screen';
-import { CapabilitiesAndMediaConfigLoader } from '../../components/CapabilitiesAndMediaConfigLoader';
+import { ServerConfigsLoader } from '../../components/ServerConfigsLoader';
import { CapabilitiesProvider } from '../../hooks/useCapabilities';
import { MediaConfigProvider } from '../../hooks/useMediaConfig';
import { MatrixClientProvider } from '../../hooks/useMatrixClient';
@@ -37,6 +37,7 @@ import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { useSyncState } from '../../hooks/useSyncState';
import { stopPropagation } from '../../utils/keyboard';
import { SyncStatus } from './SyncStatus';
+import { AuthMetadataProvider } from '../../hooks/useAuthMetadata';
function ClientRootLoading() {
return (
@@ -207,18 +208,20 @@ export function ClientRoot({ children }: ClientRootProps) {
) : (
-
- {(capabilities, mediaConfig) => (
-
-
- {children}
-
-
-
+
+ {(serverConfigs) => (
+
+
+
+ {children}
+
+
+
+
)}
-
+
)}
diff --git a/src/app/pages/client/SidebarNav.tsx b/src/app/pages/client/SidebarNav.tsx
index 27759a2d..6139f1fe 100644
--- a/src/app/pages/client/SidebarNav.tsx
+++ b/src/app/pages/client/SidebarNav.tsx
@@ -19,7 +19,8 @@ import {
SettingsTab,
UnverifiedTab,
} from './sidebar';
-import { openCreateRoom, openSearch } from '../../../client/action/navigation';
+import { openSearch } from '../../../client/action/navigation';
+import { CreateTab } from './sidebar/CreateTab';
export function SidebarNav() {
const scrollRef = useRef(null);
@@ -37,20 +38,7 @@ export function SidebarNav() {
-
-
- {(triggerRef) => (
- openCreateRoom(true)}
- >
-
-
- )}
-
-
+
}
diff --git a/src/app/pages/client/WelcomePage.tsx b/src/app/pages/client/WelcomePage.tsx
index d2133adc..55456855 100644
--- a/src/app/pages/client/WelcomePage.tsx
+++ b/src/app/pages/client/WelcomePage.tsx
@@ -24,7 +24,7 @@ export function WelcomePage() {
target="_blank"
rel="noreferrer noopener"
>
- v4.6.0
+ v4.9.1
}
diff --git a/src/app/pages/client/create/Create.tsx b/src/app/pages/client/create/Create.tsx
new file mode 100644
index 00000000..288169b6
--- /dev/null
+++ b/src/app/pages/client/create/Create.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { Box, Icon, Icons, Scroll } from 'folds';
+import {
+ Page,
+ PageContent,
+ PageContentCenter,
+ PageHero,
+ PageHeroSection,
+} from '../../../components/page';
+import { CreateSpaceForm } from '../../../features/create-space';
+import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
+
+export function Create() {
+ const { navigateSpace } = useRoomNavigate();
+
+ return (
+
+
+
+
+
+
+
+ }
+ title="Create Space"
+ subTitle="Build a space for your community."
+ />
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/pages/client/create/index.ts b/src/app/pages/client/create/index.ts
new file mode 100644
index 00000000..48cba6e7
--- /dev/null
+++ b/src/app/pages/client/create/index.ts
@@ -0,0 +1 @@
+export * from './Create';
diff --git a/src/app/pages/client/explore/Explore.tsx b/src/app/pages/client/explore/Explore.tsx
index 420e1a16..dae83166 100644
--- a/src/app/pages/client/explore/Explore.tsx
+++ b/src/app/pages/client/explore/Explore.tsx
@@ -209,7 +209,7 @@ export function Explore() {
@@ -243,11 +243,7 @@ export function Explore() {
-
+
diff --git a/src/app/pages/client/explore/Server.tsx b/src/app/pages/client/explore/Server.tsx
index 1f493df1..48f267cc 100644
--- a/src/app/pages/client/explore/Server.tsx
+++ b/src/app/pages/client/explore/Server.tsx
@@ -507,7 +507,7 @@ export function PublicRooms() {
)}
- {screenSize !== ScreenSize.Mobile && }
+ {screenSize !== ScreenSize.Mobile && }
{server}
diff --git a/src/app/pages/client/home/CreateRoom.tsx b/src/app/pages/client/home/CreateRoom.tsx
new file mode 100644
index 00000000..20c01bae
--- /dev/null
+++ b/src/app/pages/client/home/CreateRoom.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import { Box, Icon, Icons, Scroll, IconButton } from 'folds';
+import {
+ Page,
+ PageContent,
+ PageContentCenter,
+ PageHeader,
+ PageHero,
+ PageHeroSection,
+} from '../../../components/page';
+import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
+import { BackRouteHandler } from '../../../components/BackRouteHandler';
+import { CreateRoomForm } from '../../../features/create-room';
+import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
+
+export function HomeCreateRoom() {
+ const screenSize = useScreenSizeContext();
+
+ const { navigateRoom } = useRoomNavigate();
+
+ return (
+
+ {screenSize === ScreenSize.Mobile && (
+
+
+
+ {(onBack) => (
+
+
+
+ )}
+
+
+
+ )}
+
+
+
+
+
+
+ }
+ title="Create Room"
+ subTitle="Build a Room for Real-Time Conversations"
+ />
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/pages/client/home/Home.tsx b/src/app/pages/client/home/Home.tsx
index af4164fd..2597bb73 100644
--- a/src/app/pages/client/home/Home.tsx
+++ b/src/app/pages/client/home/Home.tsx
@@ -29,10 +29,20 @@ import {
NavItemContent,
NavLink,
} from '../../../components/nav';
-import { getExplorePath, getHomeRoomPath, getHomeSearchPath } from '../../pathUtils';
+import {
+ encodeSearchParamValueArray,
+ getExplorePath,
+ getHomeCreatePath,
+ getHomeRoomPath,
+ getHomeSearchPath,
+ withSearchParam,
+} from '../../pathUtils';
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
-import { useHomeSearchSelected } from '../../../hooks/router/useHomeSelected';
+import {
+ useHomeCreateSelected,
+ useHomeSearchSelected,
+} from '../../../hooks/router/useHomeSelected';
import { useHomeRooms } from './useHomeRooms';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { VirtualTile } from '../../../components/virtualizer';
@@ -41,7 +51,6 @@ import { makeNavCategoryId } from '../../../state/closedNavCategories';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
-import { openCreateRoom, openJoinAlias } from '../../../../client/action/navigation';
import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page';
import { useRoomsUnread } from '../../../state/hooks/unread';
import { markAsRead } from '../../../../client/action/notifications';
@@ -53,6 +62,9 @@ import {
getRoomNotificationMode,
useRoomsNotificationPreferencesContext,
} from '../../../hooks/useRoomsNotificationPreferences';
+import { UseStateProvider } from '../../../components/UseStateProvider';
+import { JoinAddressPrompt } from '../../../components/join-address-prompt';
+import { _RoomSearchParams } from '../../paths';
type HomeMenuProps = {
requestClose: () => void;
@@ -69,11 +81,6 @@ const HomeMenu = forwardRef(({ requestClose }, re
requestClose();
};
- const handleJoinAddress = () => {
- openJoinAlias();
- requestClose();
- };
-
return (
- }
- >
-
- Join with Address
-
-
);
@@ -174,7 +171,7 @@ function HomeEmpty() {
}
options={
<>
-
+ {reportRoomSupported && reportAllStatus.status !== AsyncStatus.Success && (
+ }
+ disabled={loading}
+ >
+
+ Report All
+
+
+ )}
+ {unignoredUsers.length > 0 && (
+ }
+ >
+
+ Block All
+
+
+ )}
+
+
+
+
+
+ }
+ onClick={() => setShowInvites(!showInvites)}
+ >
+ {showInvites ? 'Hide All' : 'View All'}
+
+
+
+
+ {showInvites &&
+ invites.map((invite) => (
+
+ ))}
+
+ ) : (
+
+
+ }
+ title="No Spam Invites"
+ subTitle="Invites detected as spam appear here."
+ />
+
+
+ )}
+
+ );
+}
+
export function Invites() {
const mx = useMatrixClient();
- const userId = mx.getUserId()!;
- const mDirects = useAtomValue(mDirectAtom);
- const directInvites = useDirectInvites(mx, allInvitesAtom, mDirects);
- const spaceInvites = useSpaceInvites(mx, allInvitesAtom);
- const roomInvites = useRoomInvites(mx, allInvitesAtom, mDirects);
+ const useAuthentication = useMediaAuthentication();
+ const { navigateRoom, navigateSpace } = useRoomNavigate();
+ const allRooms = useAtomValue(allRoomsAtom);
+ const allInviteIds = useAtomValue(allInvitesAtom);
+
+ const [filter, setFilter] = useState(InviteFilter.Known);
+
+ const invitesData = allInviteIds
+ .map((inviteId) => mx.getRoom(inviteId))
+ .filter((inviteRoom) => !!inviteRoom)
+ .map((inviteRoom) => makeInviteData(mx, inviteRoom, useAuthentication));
+
+ const [knownInvites, unknownInvites, spamInvites] = useMemo(() => {
+ const known: InviteData[] = [];
+ const unknown: InviteData[] = [];
+ const spam: InviteData[] = [];
+ invitesData.forEach((invite) => {
+ if (hasBadWords(invite) || bannedInRooms(mx, allRooms, invite.senderId)) {
+ spam.push(invite);
+ return;
+ }
+
+ if (getCommonRooms(mx, allRooms, invite.senderId).length === 0) {
+ unknown.push(invite);
+ return;
+ }
+
+ known.push(invite);
+ });
+
+ return [known, unknown, spam];
+ }, [mx, allRooms, invitesData]);
+
const containerRef = useRef(null);
const [compact, setCompact] = useState(document.body.clientWidth <= COMPACT_CARD_WIDTH);
useElementSizeObserver(
@@ -212,21 +718,15 @@ export function Invites() {
);
const screenSize = useScreenSizeContext();
- const { navigateRoom, navigateSpace } = useRoomNavigate();
+ const [hour24Clock] = useSetting(settingsAtom, 'hour24Clock');
+ const [dateFormatString] = useSetting(settingsAtom, 'dateFormatString');
- const renderInvite = (roomId: string, direct: boolean, handleNavigate: (rId: string) => void) => {
- const room = mx.getRoom(roomId);
- if (!room) return null;
- return (
-
- );
+ const handleNavigate = (roomId: string, space: boolean) => {
+ if (space) {
+ navigateSpace(roomId);
+ return;
+ }
+ navigateRoom(roomId);
};
return (
@@ -247,7 +747,7 @@ export function Invites() {
{screenSize !== ScreenSize.Mobile && }
- Invitations
+ Invites
@@ -258,47 +758,46 @@ export function Invites() {
- {directInvites.length > 0 && (
-
- Direct Messages
-
- {directInvites.map((roomId) => renderInvite(roomId, true, navigateRoom))}
-
-
+
+
+ Filter
+
+
+ {filter === InviteFilter.Known && (
+
)}
- {spaceInvites.length > 0 && (
-
- Spaces
-
- {spaceInvites.map((roomId) => renderInvite(roomId, false, navigateSpace))}
-
-
+
+ {filter === InviteFilter.Unknown && (
+
)}
- {roomInvites.length > 0 && (
-
- Rooms
-
- {roomInvites.map((roomId) => renderInvite(roomId, false, navigateRoom))}
-
-
+
+ {filter === InviteFilter.Spam && (
+
)}
- {directInvites.length === 0 &&
- spaceInvites.length === 0 &&
- roomInvites.length === 0 && (
-
-
- No Pending Invitations
-
- You don't have any new pending invitations to display yet.
-
-
-
- )}
diff --git a/src/app/pages/client/inbox/Notifications.tsx b/src/app/pages/client/inbox/Notifications.tsx
index 80ce25a9..afdfec6d 100644
--- a/src/app/pages/client/inbox/Notifications.tsx
+++ b/src/app/pages/client/inbox/Notifications.tsx
@@ -84,16 +84,19 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
import { BackRouteHandler } from '../../../components/BackRouteHandler';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
import { allRoomsAtom } from '../../../state/room-list/roomList';
-import { usePowerLevels, usePowerLevelsAPI } from '../../../hooks/usePowerLevels';
-import {
- getTagIconSrc,
- useAccessibleTagColors,
- usePowerLevelTags,
-} from '../../../hooks/usePowerLevelTags';
+import { usePowerLevels } from '../../../hooks/usePowerLevels';
+import { usePowerLevelTags } from '../../../hooks/usePowerLevelTags';
import { useTheme } from '../../../hooks/useTheme';
import { PowerIcon } from '../../../components/power';
import colorMXID from '../../../../util/colorMXID';
import { mDirectAtom } from '../../../state/mDirectList';
+import {
+ getPowerTagIconSrc,
+ useAccessiblePowerTagColors,
+ useGetMemberPowerTag,
+} from '../../../hooks/useMemberPowerTag';
+import { useRoomCreatorsTag } from '../../../hooks/useRoomCreatorsTag';
+import { useRoomCreators } from '../../../hooks/useRoomCreators';
type RoomNotificationsGroup = {
roomId: string;
@@ -205,6 +208,8 @@ type RoomNotificationsGroupProps = {
hideActivity: boolean;
onOpen: (roomId: string, eventId: string) => void;
legacyUsernameColor?: boolean;
+ hour24Clock: boolean;
+ dateFormatString: string;
};
function RoomNotificationsGroupComp({
room,
@@ -214,16 +219,22 @@ function RoomNotificationsGroupComp({
hideActivity,
onOpen,
legacyUsernameColor,
+ hour24Clock,
+ dateFormatString,
}: RoomNotificationsGroupProps) {
const mx = useMatrixClient();
const useAuthentication = useMediaAuthentication();
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
const powerLevels = usePowerLevels(room);
- const { getPowerLevel } = usePowerLevelsAPI(powerLevels);
- const [powerLevelTags, getPowerLevelTag] = usePowerLevelTags(room, powerLevels);
+ const creators = useRoomCreators(room);
+
+ const creatorsTag = useRoomCreatorsTag();
+ const powerLevelTags = usePowerLevelTags(room, powerLevels);
+ const getMemberPowerTag = useGetMemberPowerTag(room, creators, powerLevels);
+
const theme = useTheme();
- const accessibleTagColors = useAccessibleTagColors(theme.kind, powerLevelTags);
+ const accessibleTagColors = useAccessiblePowerTagColors(theme.kind, creatorsTag, powerLevelTags);
const mentionClickHandler = useMentionClickHandler(room.roomId);
const spoilerClickHandler = useSpoilerClickHandler();
@@ -443,13 +454,12 @@ function RoomNotificationsGroupComp({
const threadRootId =
relation?.rel_type === RelationType.Thread ? relation.event_id : undefined;
- const senderPowerLevel = getPowerLevel(event.sender);
- const powerLevelTag = getPowerLevelTag(senderPowerLevel);
- const tagColor = powerLevelTag?.color
- ? accessibleTagColors?.get(powerLevelTag.color)
+ const memberPowerTag = getMemberPowerTag(event.sender);
+ const tagColor = memberPowerTag?.color
+ ? accessibleTagColors?.get(memberPowerTag.color)
: undefined;
- const tagIconSrc = powerLevelTag?.icon
- ? getTagIconSrc(mx, useAuthentication, powerLevelTag.icon)
+ const tagIconSrc = memberPowerTag?.icon
+ ? getPowerTagIconSrc(mx, useAuthentication, memberPowerTag.icon)
: undefined;
const usernameColor = legacyUsernameColor ? colorMXID(event.sender) : tagColor;
@@ -496,7 +506,11 @@ function RoomNotificationsGroupComp({
{tagIconSrc && }
-
+
@@ -549,6 +562,8 @@ export function Notifications() {
const [mediaAutoLoad] = useSetting(settingsAtom, 'mediaAutoLoad');
const [urlPreview] = useSetting(settingsAtom, 'urlPreview');
const [legacyUsernameColor] = useSetting(settingsAtom, 'legacyUsernameColor');
+ const [hour24Clock] = useSetting(settingsAtom, 'hour24Clock');
+ const [dateFormatString] = useSetting(settingsAtom, 'dateFormatString');
const screenSize = useScreenSizeContext();
const mDirects = useAtomValue(mDirectAtom);
@@ -713,6 +728,8 @@ export function Notifications() {
legacyUsernameColor={
legacyUsernameColor || mDirects.has(groupRoom.roomId)
}
+ hour24Clock={hour24Clock}
+ dateFormatString={dateFormatString}
/>
);
diff --git a/src/app/pages/client/sidebar/CreateTab.tsx b/src/app/pages/client/sidebar/CreateTab.tsx
new file mode 100644
index 00000000..e6575cb4
--- /dev/null
+++ b/src/app/pages/client/sidebar/CreateTab.tsx
@@ -0,0 +1,134 @@
+import React, { MouseEventHandler, useState } from 'react';
+import { Box, config, Icon, Icons, Menu, PopOut, RectCords, Text } from 'folds';
+import FocusTrap from 'focus-trap-react';
+import { useNavigate } from 'react-router-dom';
+import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar';
+import { stopPropagation } from '../../../utils/keyboard';
+import { SequenceCard } from '../../../components/sequence-card';
+import { SettingTile } from '../../../components/setting-tile';
+import { ContainerColor } from '../../../styles/ContainerColor.css';
+import {
+ encodeSearchParamValueArray,
+ getCreatePath,
+ getSpacePath,
+ withSearchParam,
+} from '../../pathUtils';
+import { useCreateSelected } from '../../../hooks/router/useCreateSelected';
+import { JoinAddressPrompt } from '../../../components/join-address-prompt';
+import { _RoomSearchParams } from '../../paths';
+
+export function CreateTab() {
+ const createSelected = useCreateSelected();
+
+ const navigate = useNavigate();
+ const [menuCords, setMenuCords] = useState();
+ const [joinAddress, setJoinAddress] = useState(false);
+
+ const handleMenu: MouseEventHandler = (evt) => {
+ setMenuCords(menuCords ? undefined : evt.currentTarget.getBoundingClientRect());
+ };
+
+ const handleCreateSpace = () => {
+ navigate(getCreatePath());
+ setMenuCords(undefined);
+ };
+
+ const handleJoinWithAddress = () => {
+ setJoinAddress(true);
+ setMenuCords(undefined);
+ };
+
+ return (
+
+
+ {(triggerRef) => (
+ setMenuCords(undefined),
+ clickOutsideDeactivates: true,
+ isKeyForward: (evt: KeyboardEvent) =>
+ evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
+ isKeyBackward: (evt: KeyboardEvent) =>
+ evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
+ escapeDeactivates: stopPropagation,
+ }}
+ >
+
+
+ }
+ >
+
+
+
+ {joinAddress && (
+ setJoinAddress(false)}
+ onOpen={(roomIdOrAlias, viaServers) => {
+ setJoinAddress(false);
+ const path = getSpacePath(roomIdOrAlias);
+ navigate(
+ viaServers
+ ? withSearchParam<_RoomSearchParams>(path, {
+ viaServers: encodeSearchParamValueArray(viaServers),
+ })
+ : path
+ );
+ }}
+ />
+ )}
+
+ )}
+
+
+ );
+}
diff --git a/src/app/pages/client/sidebar/SettingsTab.tsx b/src/app/pages/client/sidebar/SettingsTab.tsx
index 83cd118c..bb212184 100644
--- a/src/app/pages/client/sidebar/SettingsTab.tsx
+++ b/src/app/pages/client/sidebar/SettingsTab.tsx
@@ -28,7 +28,7 @@ export function SettingsTab() {
return (
-
+
{(triggerRef) => (
(
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
const roomToParents = useAtomValue(roomToParentsAtom);
const powerLevels = usePowerLevels(room);
- const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
- const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
+ const creators = useRoomCreators(room);
+
+ const permissions = useRoomPermissions(creators, powerLevels);
+ const canInvite = permissions.action('invite', mx.getSafeUserId());
const openSpaceSettings = useOpenSpaceSettings();
const allChild = useSpaceChildren(
@@ -744,13 +748,14 @@ export function SpaceTabs({ scrollRef }: SpaceTabsProps) {
const targetSpaceId = target.getAttribute('data-id');
if (!targetSpaceId) return;
+ const spacePath = getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId));
if (screenSize === ScreenSize.Mobile) {
- navigate(getSpacePath(getCanonicalAliasOrRoomId(mx, targetSpaceId)));
+ navigate(spacePath);
return;
}
const activePath = navToActivePath.get(targetSpaceId);
- if (activePath) {
+ if (activePath && activePath.pathname.startsWith(spacePath)) {
navigate(joinPathComponent(activePath));
return;
}
diff --git a/src/app/pages/client/space/RoomProvider.tsx b/src/app/pages/client/space/RoomProvider.tsx
index a9632137..0fd52ab6 100644
--- a/src/app/pages/client/space/RoomProvider.tsx
+++ b/src/app/pages/client/space/RoomProvider.tsx
@@ -1,21 +1,24 @@
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
-import { useAtomValue } from 'jotai';
+import { useAtom, useAtomValue } from 'jotai';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { JoinBeforeNavigate } from '../../../features/join-before-navigate';
import { useSpace } from '../../../hooks/useSpace';
-import { getAllParents } from '../../../utils/room';
+import { getAllParents, getSpaceChildren } from '../../../utils/room';
import { roomToParentsAtom } from '../../../state/room/roomToParents';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { useSearchParamsViaServers } from '../../../hooks/router/useSearchParamsViaServers';
import { mDirectAtom } from '../../../state/mDirectList';
+import { settingsAtom } from '../../../state/settings';
+import { useSetting } from '../../../state/hooks/settings';
export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
const mx = useMatrixClient();
const space = useSpace();
- const roomToParents = useAtomValue(roomToParentsAtom);
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
+ const [roomToParents, setRoomToParents] = useAtom(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
const allRooms = useAtomValue(allRoomsAtom);
@@ -24,12 +27,36 @@ export function SpaceRouteRoomProvider({ children }: { children: ReactNode }) {
const roomId = useSelectedRoom();
const room = mx.getRoom(roomId);
- if (
- !room ||
- room.isSpaceRoom() ||
- !allRooms.includes(room.roomId) ||
- !getAllParents(roomToParents, room.roomId).has(space.roomId)
- ) {
+ if (!room || !allRooms.includes(room.roomId)) {
+ // room is not joined
+ return (
+
+ );
+ }
+
+ if (developerTools && room.isSpaceRoom() && room.roomId === space.roomId) {
+ // allow to view space timeline
+ return (
+
+ {children}
+
+ );
+ }
+
+ if (!getAllParents(roomToParents, room.roomId).has(space.roomId)) {
+ if (getSpaceChildren(space).includes(room.roomId)) {
+ // fill missing roomToParent mapping
+ setRoomToParents({
+ type: 'PUT',
+ parent: space.roomId,
+ children: [room.roomId],
+ });
+ }
+
return (
(({ room, requestClose }, ref) => {
const mx = useMatrixClient();
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
+ const [developerTools] = useSetting(settingsAtom, 'developerTools');
const roomToParents = useAtomValue(roomToParentsAtom);
const powerLevels = usePowerLevels(room);
- const { getPowerLevel, canDoAction } = usePowerLevelsAPI(powerLevels);
- const canInvite = canDoAction('invite', getPowerLevel(mx.getUserId() ?? ''));
+ const creators = useRoomCreators(room);
+
+ const permissions = useRoomPermissions(creators, powerLevels);
+ const canInvite = permissions.action('invite', mx.getSafeUserId());
const openSpaceSettings = useOpenSpaceSettings();
+ const { navigateRoom } = useRoomNavigate();
const allChild = useSpaceChildren(
allRoomsAtom,
@@ -118,6 +131,11 @@ const SpaceMenu = forwardRef(({ room, requestClo
requestClose();
};
+ const handleOpenTimeline = () => {
+ navigateRoom(room.roomId);
+ requestClose();
+ };
+
return (