Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d59a4de48 | ||
|
|
73c74d0477 | ||
|
|
70ffd7ded8 | ||
|
|
92a3a8d6fa | ||
|
|
6e9cd02b2b | ||
|
|
d0f90af251 | ||
|
|
c53eb27c6f | ||
|
|
38773e89ff | ||
|
|
19cb30d360 | ||
|
|
50a734b977 | ||
|
|
b988758ac2 | ||
|
|
2567328e8b | ||
|
|
a7568fcbbf | ||
|
|
27a06ae90c | ||
|
|
211fd19031 | ||
|
|
fe18611b4b | ||
|
|
5e9b45ad5f | ||
|
|
af833daee4 | ||
|
|
1ad5317d6e | ||
|
|
7cf5df80ce | ||
|
|
d6b880d110 | ||
|
|
cf58a4376e | ||
|
|
a76dcb289a | ||
|
|
48ec6224e7 | ||
|
|
127dd8baf4 | ||
|
|
d25c3ff4fc | ||
|
|
f0e9de4cf9 | ||
|
|
92607a788e | ||
|
|
82948c1f55 |
26
.github/dependabot.yml
vendored
26
.github/dependabot.yml
vendored
@@ -1,22 +1,28 @@
|
|||||||
# Docs: <https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/customizing-dependency-updates>
|
# Docs: <https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/customizing-dependency-updates>
|
||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: github-actions
|
- package-ecosystem: github-actions
|
||||||
directory: /
|
directory: /
|
||||||
schedule: {interval: weekly}
|
schedule:
|
||||||
reviewers: [ajbura]
|
interval: weekly
|
||||||
assignees: [ajbura]
|
day: "tuesday"
|
||||||
|
time: "01:00"
|
||||||
|
timezone: "Asia/Kolkata"
|
||||||
|
|
||||||
- package-ecosystem: docker
|
- package-ecosystem: docker
|
||||||
directory: /
|
directory: /
|
||||||
schedule: {interval: weekly}
|
schedule:
|
||||||
reviewers: [ajbura]
|
interval: weekly
|
||||||
assignees: [ajbura]
|
day: "tuesday"
|
||||||
|
time: "01:00"
|
||||||
|
timezone: "Asia/Kolkata"
|
||||||
|
|
||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: /
|
directory: /
|
||||||
schedule: {interval: weekly}
|
schedule:
|
||||||
reviewers: [ajbura]
|
interval: weekly
|
||||||
assignees: [ajbura]
|
day: "tuesday"
|
||||||
|
time: "01:00"
|
||||||
|
timezone: "Asia/Kolkata"
|
||||||
|
|||||||
59
.github/workflows/build-pull-request.yml
vendored
59
.github/workflows/build-pull-request.yml
vendored
@@ -1,32 +1,39 @@
|
|||||||
name: 'Build PR'
|
name: 'Build pull request'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: ['opened', 'synchronize']
|
types: ['opened', 'synchronize']
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build-pull-request:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
PR_NUMBER: ${{github.event.number}}
|
PR_NUMBER: ${{github.event.number}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.0.0
|
- name: Check out the repo
|
||||||
- name: Build
|
uses: actions/checkout@v3.0.0
|
||||||
run: npm ci && npm run build
|
- name: Build app
|
||||||
- name: Upload Artifact
|
run: npm ci && npm run build
|
||||||
uses: actions/upload-artifact@v3.0.0
|
- name: Upload artifact
|
||||||
with:
|
uses: actions/upload-artifact@v3.0.0
|
||||||
name: previewbuild
|
with:
|
||||||
path: dist
|
name: previewbuild
|
||||||
retention-days: 1
|
path: dist
|
||||||
- uses: actions/github-script@v6.0.0
|
retention-days: 1
|
||||||
with:
|
- name: Get PR info
|
||||||
script: |
|
uses: actions/github-script@v6.0.0
|
||||||
var fs = require('fs');
|
with:
|
||||||
fs.writeFileSync('${{github.workspace}}/pr.json', JSON.stringify(context.payload.pull_request));
|
script: |
|
||||||
- name: Upload PR Info
|
var fs = require('fs');
|
||||||
uses: actions/upload-artifact@v3.0.0
|
fs.writeFileSync('${{github.workspace}}/pr.json', JSON.stringify(context.payload.pull_request));
|
||||||
with:
|
- name: Upload PR Info
|
||||||
name: pr.json
|
uses: actions/upload-artifact@v3.0.0
|
||||||
path: pr.json
|
with:
|
||||||
retention-days: 1
|
name: pr.json
|
||||||
|
path: pr.json
|
||||||
|
retention-days: 1
|
||||||
|
- name: Build Docker image
|
||||||
|
uses: docker/build-push-action@v2.10.0
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: false
|
||||||
|
|||||||
150
.github/workflows/deploy-pull-request.yml
vendored
150
.github/workflows/deploy-pull-request.yml
vendored
@@ -1,78 +1,78 @@
|
|||||||
name: Upload Preview Build to Netlify
|
name: Upload Preview Build to Netlify
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: ["Build PR"]
|
workflows: ["Build pull request"]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
get-build-and-deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >
|
if: >
|
||||||
${{ github.event.workflow_run.conclusion == 'success' }}
|
${{ github.event.workflow_run.conclusion == 'success' }}
|
||||||
steps:
|
steps:
|
||||||
# There's a 'download artifact' action but it hasn't been updated for the
|
# There's a 'download artifact' action but it hasn't been updated for the
|
||||||
# workflow_run action (https://github.com/actions/download-artifact/issues/60)
|
# workflow_run action (https://github.com/actions/download-artifact/issues/60)
|
||||||
# so instead we get this mess:
|
# so instead we get this mess:
|
||||||
- name: 'Download artifact'
|
- name: 'Download artifact'
|
||||||
uses: actions/github-script@v6.0.0
|
uses: actions/github-script@v6.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
run_id: ${{github.event.workflow_run.id }},
|
run_id: ${{github.event.workflow_run.id }},
|
||||||
});
|
});
|
||||||
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||||
return artifact.name == "previewbuild"
|
return artifact.name == "previewbuild"
|
||||||
})[0];
|
})[0];
|
||||||
var download = await github.rest.actions.downloadArtifact({
|
var download = await github.rest.actions.downloadArtifact({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
artifact_id: matchArtifact.id,
|
artifact_id: matchArtifact.id,
|
||||||
archive_format: 'zip',
|
archive_format: 'zip',
|
||||||
});
|
});
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
fs.writeFileSync('${{github.workspace}}/previewbuild.zip', Buffer.from(download.data));
|
fs.writeFileSync('${{github.workspace}}/previewbuild.zip', Buffer.from(download.data));
|
||||||
var prInfoArtifact = artifacts.data.artifacts.filter((artifact) => {
|
var prInfoArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||||
return artifact.name == "pr.json"
|
return artifact.name == "pr.json"
|
||||||
})[0];
|
})[0];
|
||||||
var download = await github.rest.actions.downloadArtifact({
|
var download = await github.rest.actions.downloadArtifact({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
artifact_id: prInfoArtifact.id,
|
artifact_id: prInfoArtifact.id,
|
||||||
archive_format: 'zip',
|
archive_format: 'zip',
|
||||||
});
|
});
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
fs.writeFileSync('${{github.workspace}}/pr.json.zip', Buffer.from(download.data));
|
fs.writeFileSync('${{github.workspace}}/pr.json.zip', Buffer.from(download.data));
|
||||||
- name: Extract Artifacts
|
- name: Extract Artifacts
|
||||||
run: unzip -d dist previewbuild.zip && rm previewbuild.zip && unzip pr.json.zip && rm pr.json.zip
|
run: unzip -d dist previewbuild.zip && rm previewbuild.zip && unzip pr.json.zip && rm pr.json.zip
|
||||||
- name: 'Read PR Info'
|
- name: 'Read PR Info'
|
||||||
id: readctx
|
id: readctx
|
||||||
uses: actions/github-script@v6.0.0
|
uses: actions/github-script@v6.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var pr = JSON.parse(fs.readFileSync('${{github.workspace}}/pr.json'));
|
var pr = JSON.parse(fs.readFileSync('${{github.workspace}}/pr.json'));
|
||||||
console.log(`::set-output name=prnumber::${pr.number}`);
|
console.log(`::set-output name=prnumber::${pr.number}`);
|
||||||
- name: Deploy to Netlify
|
- name: Deploy to Netlify
|
||||||
id: netlify
|
id: netlify
|
||||||
uses: nwtgck/actions-netlify@v1.2.3
|
uses: nwtgck/actions-netlify@v1.2.3
|
||||||
with:
|
with:
|
||||||
publish-dir: dist
|
publish-dir: dist
|
||||||
deploy-message: "Deploy from GitHub Actions"
|
deploy-message: "Deploy from GitHub Actions"
|
||||||
# These don't work because we're in workflow_run
|
# These don't work because we're in workflow_run
|
||||||
enable-pull-request-comment: false
|
enable-pull-request-comment: false
|
||||||
enable-commit-comment: false
|
enable-commit-comment: false
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }}
|
||||||
timeout-minutes: 1
|
timeout-minutes: 1
|
||||||
- name: Edit PR Description
|
- name: Edit PR Description
|
||||||
uses: velas/pr-description@v1.0.1
|
uses: velas/pr-description@v1.0.1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
pull-request-number: ${{ steps.readctx.outputs.prnumber }}
|
pull-request-number: ${{ steps.readctx.outputs.prnumber }}
|
||||||
description-message: |
|
description-message: |
|
||||||
Preview: ${{ steps.netlify.outputs.deploy-url }}
|
Preview: ${{ steps.netlify.outputs.deploy-url }}
|
||||||
⚠️ Exercise caution. Use test accounts. ⚠️
|
⚠️ Exercise caution. Use test accounts. ⚠️
|
||||||
|
|||||||
34
.github/workflows/docker.yaml
vendored
34
.github/workflows/docker.yaml
vendored
@@ -1,34 +0,0 @@
|
|||||||
name: Publish Docker image
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
push_to_registry:
|
|
||||||
name: Push Docker image to Docker Hub
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out the repo
|
|
||||||
uses: actions/checkout@v3.0.0
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v1.14.1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3.6.2
|
|
||||||
with:
|
|
||||||
images: ajbura/cinny
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
uses: docker/build-push-action@v2.9.0
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
21
.github/workflows/netlify-prod.yaml
vendored
21
.github/workflows/netlify-prod.yaml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: 'Deploy to Netlify (prod)'
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
name: 'Deploy'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3.0.0
|
|
||||||
- uses: jsmrcaga/action-netlify-deploy@v1.7.2
|
|
||||||
with:
|
|
||||||
install_command: "npm ci"
|
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
||||||
BUILD_DIRECTORY: "dist"
|
|
||||||
NETLIFY_DEPLOY_MESSAGE: "Prod deploy v${{ github.ref }}"
|
|
||||||
NETLIFY_DEPLOY_TO_PROD: true
|
|
||||||
56
.github/workflows/prod-deploy.yaml
vendored
Normal file
56
.github/workflows/prod-deploy.yaml
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
name: 'Production deploy'
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-to-netlify:
|
||||||
|
name: 'Deploy to Netlify'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v3.0.0
|
||||||
|
- name: Build and deploy to Netlify
|
||||||
|
uses: jsmrcaga/action-netlify-deploy@v1.7.2
|
||||||
|
with:
|
||||||
|
install_command: "npm ci"
|
||||||
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||||
|
BUILD_DIRECTORY: "dist"
|
||||||
|
NETLIFY_DEPLOY_MESSAGE: "Prod deploy v${{ github.ref }}"
|
||||||
|
NETLIFY_DEPLOY_TO_PROD: true
|
||||||
|
- name: Get version from tag
|
||||||
|
id: vars
|
||||||
|
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
|
||||||
|
- name: Create tar.gz
|
||||||
|
run: tar -czvf cinny-${{ steps.vars.outputs.tag }}.tar.gz dist
|
||||||
|
- name: Upload tagged release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
cinny-${{ steps.vars.outputs.tag }}.tar.gz
|
||||||
|
|
||||||
|
push_to_dockerhub:
|
||||||
|
name: Push Docker image to Docker Hub
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v3.0.0
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1.14.1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3.6.2
|
||||||
|
with:
|
||||||
|
images: ajbura/cinny
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v2.10.0
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
## Builder
|
## Builder
|
||||||
FROM node:17.6.0-alpine3.15 as builder
|
FROM node:17.7.1-alpine3.15 as builder
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY package.json package-lock.json /src
|
COPY package.json package-lock.json /src/
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
COPY . /src
|
COPY . /src/
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
267
package-lock.json
generated
267
package-lock.json
generated
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/inter": "^4.5.4",
|
"@fontsource/inter": "^4.5.5",
|
||||||
"@fontsource/roboto": "^4.5.3",
|
"@fontsource/roboto": "^4.5.3",
|
||||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
"react-modal": "^3.14.4",
|
"react-modal": "^3.14.4",
|
||||||
"sanitize-html": "^2.7.0",
|
"sanitize-html": "^2.7.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"twemoji": "^13.1.0"
|
"twemoji": "^14.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.5",
|
"@babel/core": "^7.17.7",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"@babel/preset-react": "^7.16.7",
|
"@babel/preset-react": "^7.16.7",
|
||||||
"assert": "^2.0.0",
|
"assert": "^2.0.0",
|
||||||
@@ -51,13 +51,13 @@
|
|||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"copy-webpack-plugin": "^10.2.4",
|
"copy-webpack-plugin": "^10.2.4",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
"css-loader": "^6.7.0",
|
"css-loader": "^6.7.1",
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||||
"eslint": "^8.10.0",
|
"eslint": "^8.11.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"eslint-plugin-react": "^7.29.3",
|
"eslint-plugin-react": "^7.29.4",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"favicons": "^6.2.2",
|
"favicons": "^6.2.2",
|
||||||
"favicons-webpack-plugin": "^5.0.2",
|
"favicons-webpack-plugin": "^5.0.2",
|
||||||
@@ -106,27 +106,27 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/compat-data": {
|
"node_modules/@babel/compat-data": {
|
||||||
"version": "7.16.8",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz",
|
||||||
"integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==",
|
"integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/core": {
|
"node_modules/@babel/core": {
|
||||||
"version": "7.17.5",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz",
|
||||||
"integrity": "sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==",
|
"integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.1.0",
|
"@ampproject/remapping": "^2.1.0",
|
||||||
"@babel/code-frame": "^7.16.7",
|
"@babel/code-frame": "^7.16.7",
|
||||||
"@babel/generator": "^7.17.3",
|
"@babel/generator": "^7.17.7",
|
||||||
"@babel/helper-compilation-targets": "^7.16.7",
|
"@babel/helper-compilation-targets": "^7.17.7",
|
||||||
"@babel/helper-module-transforms": "^7.16.7",
|
"@babel/helper-module-transforms": "^7.17.7",
|
||||||
"@babel/helpers": "^7.17.2",
|
"@babel/helpers": "^7.17.7",
|
||||||
"@babel/parser": "^7.17.3",
|
"@babel/parser": "^7.17.7",
|
||||||
"@babel/template": "^7.16.7",
|
"@babel/template": "^7.16.7",
|
||||||
"@babel/traverse": "^7.17.3",
|
"@babel/traverse": "^7.17.3",
|
||||||
"@babel/types": "^7.17.0",
|
"@babel/types": "^7.17.0",
|
||||||
@@ -145,9 +145,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.17.3",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
|
||||||
"integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==",
|
"integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.17.0",
|
"@babel/types": "^7.17.0",
|
||||||
@@ -184,12 +184,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-compilation-targets": {
|
"node_modules/@babel/helper-compilation-targets": {
|
||||||
"version": "7.16.7",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz",
|
||||||
"integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==",
|
"integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/compat-data": "^7.16.4",
|
"@babel/compat-data": "^7.17.7",
|
||||||
"@babel/helper-validator-option": "^7.16.7",
|
"@babel/helper-validator-option": "^7.16.7",
|
||||||
"browserslist": "^4.17.5",
|
"browserslist": "^4.17.5",
|
||||||
"semver": "^6.3.0"
|
"semver": "^6.3.0"
|
||||||
@@ -344,19 +344,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-module-transforms": {
|
"node_modules/@babel/helper-module-transforms": {
|
||||||
"version": "7.16.7",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz",
|
||||||
"integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==",
|
"integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-environment-visitor": "^7.16.7",
|
"@babel/helper-environment-visitor": "^7.16.7",
|
||||||
"@babel/helper-module-imports": "^7.16.7",
|
"@babel/helper-module-imports": "^7.16.7",
|
||||||
"@babel/helper-simple-access": "^7.16.7",
|
"@babel/helper-simple-access": "^7.17.7",
|
||||||
"@babel/helper-split-export-declaration": "^7.16.7",
|
"@babel/helper-split-export-declaration": "^7.16.7",
|
||||||
"@babel/helper-validator-identifier": "^7.16.7",
|
"@babel/helper-validator-identifier": "^7.16.7",
|
||||||
"@babel/template": "^7.16.7",
|
"@babel/template": "^7.16.7",
|
||||||
"@babel/traverse": "^7.16.7",
|
"@babel/traverse": "^7.17.3",
|
||||||
"@babel/types": "^7.16.7"
|
"@babel/types": "^7.17.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -414,12 +414,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-simple-access": {
|
"node_modules/@babel/helper-simple-access": {
|
||||||
"version": "7.16.7",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz",
|
||||||
"integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==",
|
"integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.16.7"
|
"@babel/types": "^7.17.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -483,13 +483,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helpers": {
|
"node_modules/@babel/helpers": {
|
||||||
"version": "7.17.2",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz",
|
||||||
"integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==",
|
"integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.16.7",
|
"@babel/template": "^7.16.7",
|
||||||
"@babel/traverse": "^7.17.0",
|
"@babel/traverse": "^7.17.3",
|
||||||
"@babel/types": "^7.17.0"
|
"@babel/types": "^7.17.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -511,9 +511,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.17.3",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz",
|
||||||
"integrity": "sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==",
|
"integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@@ -1782,16 +1782,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz",
|
||||||
"integrity": "sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==",
|
"integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
"espree": "^9.3.1",
|
"espree": "^9.3.1",
|
||||||
"globals": "^13.9.0",
|
"globals": "^13.9.0",
|
||||||
"ignore": "^4.0.6",
|
"ignore": "^5.2.0",
|
||||||
"import-fresh": "^3.2.1",
|
"import-fresh": "^3.2.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
@@ -1816,19 +1816,10 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc/node_modules/ignore": {
|
|
||||||
"version": "4.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
|
||||||
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@fontsource/inter": {
|
"node_modules/@fontsource/inter": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-4.5.5.tgz",
|
||||||
"integrity": "sha512-D0icTFpt9bWvB/OEXMztYf0bhUQZoDIYpsco5C7GVfxgKDRl8Jdn3N2aHHQqwjgRUUvRuyMv8HrRM8Hrt4U52w=="
|
"integrity": "sha512-mWnePEroLfaJQWmynipzOVcH6JwT8Jta3+yLsC5Pm/snHBXnOiAOnjBqYjKnvXwJ4eUPt2AaAhyrtwCgWQRGOg=="
|
||||||
},
|
},
|
||||||
"node_modules/@fontsource/roboto": {
|
"node_modules/@fontsource/roboto": {
|
||||||
"version": "4.5.3",
|
"version": "4.5.3",
|
||||||
@@ -4669,9 +4660,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/css-loader": {
|
"node_modules/css-loader": {
|
||||||
"version": "6.7.0",
|
"version": "6.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz",
|
||||||
"integrity": "sha512-S7HCfCiDHLA+VXKqdZwyRZgoO0R9BnKDnVIoHMq5grl3N86zAu7MB+FBWHr5xOJC8SmvpTLha/2NpfFkFEN/ig==",
|
"integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"icss-utils": "^5.1.0",
|
"icss-utils": "^5.1.0",
|
||||||
@@ -5643,12 +5634,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.10.0",
|
"version": "8.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz",
|
||||||
"integrity": "sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==",
|
"integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/eslintrc": "^1.2.0",
|
"@eslint/eslintrc": "^1.2.1",
|
||||||
"@humanwhocodes/config-array": "^0.9.2",
|
"@humanwhocodes/config-array": "^0.9.2",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
@@ -5856,9 +5847,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-react": {
|
"node_modules/eslint-plugin-react": {
|
||||||
"version": "7.29.3",
|
"version": "7.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz",
|
||||||
"integrity": "sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg==",
|
"integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array-includes": "^3.1.4",
|
"array-includes": "^3.1.4",
|
||||||
@@ -13146,20 +13137,20 @@
|
|||||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||||
},
|
},
|
||||||
"node_modules/twemoji": {
|
"node_modules/twemoji": {
|
||||||
"version": "13.1.0",
|
"version": "14.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-13.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-14.0.1.tgz",
|
||||||
"integrity": "sha512-e3fZRl2S9UQQdBFLYXtTBT6o4vidJMnpWUAhJA+yLGR+kaUTZAt3PixC0cGvvxWSuq2MSz/o0rJraOXrWw/4Ew==",
|
"integrity": "sha512-eoqhea0sUhmC10iTacksyp1v9O4BP1jKmVqtK+Nztw40/dzawSHkXL3/xCpyh+mukmEvJ0Gw9VLvwZfQ9HKXDw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs-extra": "^8.0.1",
|
"fs-extra": "^8.0.1",
|
||||||
"jsonfile": "^5.0.0",
|
"jsonfile": "^5.0.0",
|
||||||
"twemoji-parser": "13.1.0",
|
"twemoji-parser": "14.0.0",
|
||||||
"universalify": "^0.1.2"
|
"universalify": "^0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/twemoji-parser": {
|
"node_modules/twemoji-parser": {
|
||||||
"version": "13.1.0",
|
"version": "14.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-13.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-14.0.0.tgz",
|
||||||
"integrity": "sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg=="
|
"integrity": "sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA=="
|
||||||
},
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
@@ -14123,24 +14114,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/compat-data": {
|
"@babel/compat-data": {
|
||||||
"version": "7.16.8",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz",
|
||||||
"integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==",
|
"integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@babel/core": {
|
"@babel/core": {
|
||||||
"version": "7.17.5",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz",
|
||||||
"integrity": "sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==",
|
"integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ampproject/remapping": "^2.1.0",
|
"@ampproject/remapping": "^2.1.0",
|
||||||
"@babel/code-frame": "^7.16.7",
|
"@babel/code-frame": "^7.16.7",
|
||||||
"@babel/generator": "^7.17.3",
|
"@babel/generator": "^7.17.7",
|
||||||
"@babel/helper-compilation-targets": "^7.16.7",
|
"@babel/helper-compilation-targets": "^7.17.7",
|
||||||
"@babel/helper-module-transforms": "^7.16.7",
|
"@babel/helper-module-transforms": "^7.17.7",
|
||||||
"@babel/helpers": "^7.17.2",
|
"@babel/helpers": "^7.17.7",
|
||||||
"@babel/parser": "^7.17.3",
|
"@babel/parser": "^7.17.7",
|
||||||
"@babel/template": "^7.16.7",
|
"@babel/template": "^7.16.7",
|
||||||
"@babel/traverse": "^7.17.3",
|
"@babel/traverse": "^7.17.3",
|
||||||
"@babel/types": "^7.17.0",
|
"@babel/types": "^7.17.0",
|
||||||
@@ -14152,9 +14143,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/generator": {
|
"@babel/generator": {
|
||||||
"version": "7.17.3",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
|
||||||
"integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==",
|
"integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/types": "^7.17.0",
|
"@babel/types": "^7.17.0",
|
||||||
@@ -14182,12 +14173,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/helper-compilation-targets": {
|
"@babel/helper-compilation-targets": {
|
||||||
"version": "7.16.7",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz",
|
||||||
"integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==",
|
"integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/compat-data": "^7.16.4",
|
"@babel/compat-data": "^7.17.7",
|
||||||
"@babel/helper-validator-option": "^7.16.7",
|
"@babel/helper-validator-option": "^7.16.7",
|
||||||
"browserslist": "^4.17.5",
|
"browserslist": "^4.17.5",
|
||||||
"semver": "^6.3.0"
|
"semver": "^6.3.0"
|
||||||
@@ -14300,19 +14291,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/helper-module-transforms": {
|
"@babel/helper-module-transforms": {
|
||||||
"version": "7.16.7",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz",
|
||||||
"integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==",
|
"integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/helper-environment-visitor": "^7.16.7",
|
"@babel/helper-environment-visitor": "^7.16.7",
|
||||||
"@babel/helper-module-imports": "^7.16.7",
|
"@babel/helper-module-imports": "^7.16.7",
|
||||||
"@babel/helper-simple-access": "^7.16.7",
|
"@babel/helper-simple-access": "^7.17.7",
|
||||||
"@babel/helper-split-export-declaration": "^7.16.7",
|
"@babel/helper-split-export-declaration": "^7.16.7",
|
||||||
"@babel/helper-validator-identifier": "^7.16.7",
|
"@babel/helper-validator-identifier": "^7.16.7",
|
||||||
"@babel/template": "^7.16.7",
|
"@babel/template": "^7.16.7",
|
||||||
"@babel/traverse": "^7.16.7",
|
"@babel/traverse": "^7.17.3",
|
||||||
"@babel/types": "^7.16.7"
|
"@babel/types": "^7.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/helper-optimise-call-expression": {
|
"@babel/helper-optimise-call-expression": {
|
||||||
@@ -14355,12 +14346,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/helper-simple-access": {
|
"@babel/helper-simple-access": {
|
||||||
"version": "7.16.7",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz",
|
||||||
"integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==",
|
"integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/types": "^7.16.7"
|
"@babel/types": "^7.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/helper-skip-transparent-expression-wrappers": {
|
"@babel/helper-skip-transparent-expression-wrappers": {
|
||||||
@@ -14406,13 +14397,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/helpers": {
|
"@babel/helpers": {
|
||||||
"version": "7.17.2",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz",
|
||||||
"integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==",
|
"integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/template": "^7.16.7",
|
"@babel/template": "^7.16.7",
|
||||||
"@babel/traverse": "^7.17.0",
|
"@babel/traverse": "^7.17.3",
|
||||||
"@babel/types": "^7.17.0"
|
"@babel/types": "^7.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -14428,9 +14419,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/parser": {
|
"@babel/parser": {
|
||||||
"version": "7.17.3",
|
"version": "7.17.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz",
|
||||||
"integrity": "sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==",
|
"integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
|
||||||
@@ -15289,16 +15280,16 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz",
|
||||||
"integrity": "sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==",
|
"integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
"espree": "^9.3.1",
|
"espree": "^9.3.1",
|
||||||
"globals": "^13.9.0",
|
"globals": "^13.9.0",
|
||||||
"ignore": "^4.0.6",
|
"ignore": "^5.2.0",
|
||||||
"import-fresh": "^3.2.1",
|
"import-fresh": "^3.2.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
@@ -15313,19 +15304,13 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"type-fest": "^0.20.2"
|
"type-fest": "^0.20.2"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ignore": {
|
|
||||||
"version": "4.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
|
||||||
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
|
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@fontsource/inter": {
|
"@fontsource/inter": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-4.5.5.tgz",
|
||||||
"integrity": "sha512-D0icTFpt9bWvB/OEXMztYf0bhUQZoDIYpsco5C7GVfxgKDRl8Jdn3N2aHHQqwjgRUUvRuyMv8HrRM8Hrt4U52w=="
|
"integrity": "sha512-mWnePEroLfaJQWmynipzOVcH6JwT8Jta3+yLsC5Pm/snHBXnOiAOnjBqYjKnvXwJ4eUPt2AaAhyrtwCgWQRGOg=="
|
||||||
},
|
},
|
||||||
"@fontsource/roboto": {
|
"@fontsource/roboto": {
|
||||||
"version": "4.5.3",
|
"version": "4.5.3",
|
||||||
@@ -17681,9 +17666,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"css-loader": {
|
"css-loader": {
|
||||||
"version": "6.7.0",
|
"version": "6.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz",
|
||||||
"integrity": "sha512-S7HCfCiDHLA+VXKqdZwyRZgoO0R9BnKDnVIoHMq5grl3N86zAu7MB+FBWHr5xOJC8SmvpTLha/2NpfFkFEN/ig==",
|
"integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"icss-utils": "^5.1.0",
|
"icss-utils": "^5.1.0",
|
||||||
@@ -18419,12 +18404,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eslint": {
|
"eslint": {
|
||||||
"version": "8.10.0",
|
"version": "8.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz",
|
||||||
"integrity": "sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==",
|
"integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@eslint/eslintrc": "^1.2.0",
|
"@eslint/eslintrc": "^1.2.1",
|
||||||
"@humanwhocodes/config-array": "^0.9.2",
|
"@humanwhocodes/config-array": "^0.9.2",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
@@ -18672,9 +18657,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-react": {
|
"eslint-plugin-react": {
|
||||||
"version": "7.29.3",
|
"version": "7.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz",
|
||||||
"integrity": "sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg==",
|
"integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"array-includes": "^3.1.4",
|
"array-includes": "^3.1.4",
|
||||||
@@ -24068,20 +24053,20 @@
|
|||||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||||
},
|
},
|
||||||
"twemoji": {
|
"twemoji": {
|
||||||
"version": "13.1.0",
|
"version": "14.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-13.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-14.0.1.tgz",
|
||||||
"integrity": "sha512-e3fZRl2S9UQQdBFLYXtTBT6o4vidJMnpWUAhJA+yLGR+kaUTZAt3PixC0cGvvxWSuq2MSz/o0rJraOXrWw/4Ew==",
|
"integrity": "sha512-eoqhea0sUhmC10iTacksyp1v9O4BP1jKmVqtK+Nztw40/dzawSHkXL3/xCpyh+mukmEvJ0Gw9VLvwZfQ9HKXDw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs-extra": "^8.0.1",
|
"fs-extra": "^8.0.1",
|
||||||
"jsonfile": "^5.0.0",
|
"jsonfile": "^5.0.0",
|
||||||
"twemoji-parser": "13.1.0",
|
"twemoji-parser": "14.0.0",
|
||||||
"universalify": "^0.1.2"
|
"universalify": "^0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"twemoji-parser": {
|
"twemoji-parser": {
|
||||||
"version": "13.1.0",
|
"version": "14.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-13.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-14.0.0.tgz",
|
||||||
"integrity": "sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg=="
|
"integrity": "sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA=="
|
||||||
},
|
},
|
||||||
"type-check": {
|
"type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"description": "Yet another matrix client",
|
"description": "Yet another matrix client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"author": "Ajay Bura",
|
"author": "Ajay Bura",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/inter": "^4.5.4",
|
"@fontsource/inter": "^4.5.5",
|
||||||
"@fontsource/roboto": "^4.5.3",
|
"@fontsource/roboto": "^4.5.3",
|
||||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
@@ -44,10 +44,10 @@
|
|||||||
"react-modal": "^3.14.4",
|
"react-modal": "^3.14.4",
|
||||||
"sanitize-html": "^2.7.0",
|
"sanitize-html": "^2.7.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"twemoji": "^13.1.0"
|
"twemoji": "^14.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.5",
|
"@babel/core": "^7.17.7",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"@babel/preset-react": "^7.16.7",
|
"@babel/preset-react": "^7.16.7",
|
||||||
"assert": "^2.0.0",
|
"assert": "^2.0.0",
|
||||||
@@ -57,13 +57,13 @@
|
|||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"copy-webpack-plugin": "^10.2.4",
|
"copy-webpack-plugin": "^10.2.4",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
"css-loader": "^6.7.0",
|
"css-loader": "^6.7.1",
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||||
"eslint": "^8.10.0",
|
"eslint": "^8.11.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"eslint-plugin-react": "^7.29.3",
|
"eslint-plugin-react": "^7.29.4",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"favicons": "^6.2.2",
|
"favicons": "^6.2.2",
|
||||||
"favicons-webpack-plugin": "^5.0.2",
|
"favicons-webpack-plugin": "^5.0.2",
|
||||||
|
|||||||
16
public/res/ic/outlined/recent-clock.svg
Normal file
16
public/res/ic/outlined/recent-clock.svg
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<polygon fill="#010101" points="11,6 11,12 15.2,16.2 16.7,14.8 13,11.2 13,6 "/>
|
||||||
|
<path fill="#010101" d="M12,2C6.5,2,2,6.5,2,12H0.2L3,14.8L5.8,12H4c0-4.4,3.6-8,8-8s8,3.6,8,8s-3.6,8-8,8c-1.9,0-3.7-0.7-5-1.8
|
||||||
|
l-1.2,1.6C7.4,21.2,9.6,22,12,22c5.5,0,10-4.5,10-10S17.5,2,12,2z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon fill="#010101" points="49,44 49,50 53.2,54.2 54.7,52.8 51,49.2 51,44 "/>
|
||||||
|
<path fill="#010101" d="M50,40c-5.5,0-10,4.5-10,10h-1.8l2.8,2.8l2.8-2.8H42c0-4.4,3.6-8,8-8s8,3.6,8,8s-3.6,8-8,8
|
||||||
|
c-1.9,0-3.7-0.7-5-1.8l-1.2,1.6c1.7,1.4,3.9,2.2,6.3,2.2c5.5,0,10-4.5,10-10S55.5,40,50,40z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -150,10 +150,13 @@ const MessageReplyWrapper = React.memo(({ roomTimeline, eventId }) => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const focusReply = () => {
|
const focusReply = (ev) => {
|
||||||
if (reply?.event === null) return;
|
if (!ev.keyCode || ev.keyCode === 32 || ev.keyCode === 13) {
|
||||||
if (reply?.event.isRedacted()) return;
|
if (ev.keyCode) ev.preventDefault();
|
||||||
roomTimeline.loadEventTimeline(eventId);
|
if (reply?.event === null) return;
|
||||||
|
if (reply?.event.isRedacted()) return;
|
||||||
|
roomTimeline.loadEventTimeline(eventId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -201,10 +204,10 @@ const MessageBody = React.memo(({
|
|||||||
// Count the number of emojis
|
// Count the number of emojis
|
||||||
const nEmojis = content.filter((e) => e.type === 'img').length;
|
const nEmojis = content.filter((e) => e.type === 'img').length;
|
||||||
|
|
||||||
// Make sure there's no text besides whitespace
|
// Make sure there's no text besides whitespace and variation selector U+FE0F
|
||||||
if (nEmojis <= 10 && content.every((element) => (
|
if (nEmojis <= 10 && content.every((element) => (
|
||||||
(typeof element === 'object' && element.type === 'img')
|
(typeof element === 'object' && element.type === 'img')
|
||||||
|| (typeof element === 'string' && /^\s*$/g.test(element))
|
|| (typeof element === 'string' && /^[\s\ufe0f]*$/g.test(element))
|
||||||
))) {
|
))) {
|
||||||
emojiOnly = true;
|
emojiOnly = true;
|
||||||
}
|
}
|
||||||
@@ -466,6 +469,18 @@ function isMedia(mE) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if editedTimeline has mEventId then pass editedMEvent else pass mEvent to openViewSource
|
||||||
|
function handleOpenViewSource(mEvent, roomTimeline) {
|
||||||
|
const eventId = mEvent.getId();
|
||||||
|
const { editedTimeline } = roomTimeline ?? {};
|
||||||
|
let editedMEvent;
|
||||||
|
if (editedTimeline?.has(eventId)) {
|
||||||
|
const editedList = editedTimeline.get(eventId);
|
||||||
|
editedMEvent = editedList[editedList.length - 1];
|
||||||
|
}
|
||||||
|
openViewSource(editedMEvent !== undefined ? editedMEvent : mEvent);
|
||||||
|
}
|
||||||
|
|
||||||
const MessageOptions = React.memo(({
|
const MessageOptions = React.memo(({
|
||||||
roomTimeline, mEvent, edit, reply,
|
roomTimeline, mEvent, edit, reply,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -513,7 +528,7 @@ const MessageOptions = React.memo(({
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
iconSrc={CmdIC}
|
iconSrc={CmdIC}
|
||||||
onClick={() => openViewSource(mEvent)}
|
onClick={() => handleOpenViewSource(mEvent, roomTimeline)}
|
||||||
>
|
>
|
||||||
View source
|
View source
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -32,28 +32,9 @@ const items = [{
|
|||||||
type: cons.notifs.MUTE,
|
type: cons.notifs.MUTE,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
function getNotifType(roomId) {
|
|
||||||
const mx = initMatrix.matrixClient;
|
|
||||||
const pushRule = mx.getRoomPushRule('global', roomId);
|
|
||||||
|
|
||||||
if (typeof pushRule === 'undefined') {
|
|
||||||
const overridePushRules = mx.getAccountData('m.push_rules')?.getContent()?.global?.override;
|
|
||||||
if (typeof overridePushRules === 'undefined') return 0;
|
|
||||||
|
|
||||||
const isMuteOverride = overridePushRules.find((rule) => (
|
|
||||||
rule.rule_id === roomId
|
|
||||||
&& rule.actions[0] === 'dont_notify'
|
|
||||||
&& rule.conditions[0].kind === 'event_match'
|
|
||||||
));
|
|
||||||
|
|
||||||
return isMuteOverride ? cons.notifs.MUTE : cons.notifs.DEFAULT;
|
|
||||||
}
|
|
||||||
if (pushRule.actions[0] === 'notify') return cons.notifs.ALL_MESSAGES;
|
|
||||||
return cons.notifs.MENTIONS_AND_KEYWORDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRoomNotifType(roomId, newType) {
|
function setRoomNotifType(roomId, newType) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
|
const { notifications } = initMatrix;
|
||||||
const roomPushRule = mx.getRoomPushRule('global', roomId);
|
const roomPushRule = mx.getRoomPushRule('global', roomId);
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
@@ -76,7 +57,7 @@ function setRoomNotifType(roomId, newType) {
|
|||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldState = getNotifType(roomId);
|
const oldState = notifications.getNotiType(roomId);
|
||||||
if (oldState === cons.notifs.MUTE) {
|
if (oldState === cons.notifs.MUTE) {
|
||||||
promises.push(mx.deletePushRule('global', 'override', roomId));
|
promises.push(mx.deletePushRule('global', 'override', roomId));
|
||||||
}
|
}
|
||||||
@@ -115,8 +96,9 @@ function setRoomNotifType(roomId, newType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function useNotifications(roomId) {
|
function useNotifications(roomId) {
|
||||||
const [activeType, setActiveType] = useState(getNotifType(roomId));
|
const { notifications } = initMatrix;
|
||||||
useEffect(() => setActiveType(getNotifType(roomId)), [roomId]);
|
const [activeType, setActiveType] = useState(notifications.getNotiType(roomId));
|
||||||
|
useEffect(() => setActiveType(notifications.getNotiType(roomId)), [roomId]);
|
||||||
|
|
||||||
const setNotification = useCallback((item) => {
|
const setNotification = useCallback((item) => {
|
||||||
if (item.type === activeType.type) return;
|
if (item.type === activeType.type) return;
|
||||||
|
|||||||
@@ -11,13 +11,16 @@ import NotificationBadge from '../../atoms/badge/NotificationBadge';
|
|||||||
import { blurOnBubbling } from '../../atoms/button/script';
|
import { blurOnBubbling } from '../../atoms/button/script';
|
||||||
|
|
||||||
function RoomSelectorWrapper({
|
function RoomSelectorWrapper({
|
||||||
isSelected, isUnread, onClick,
|
isSelected, isMuted, isUnread, onClick,
|
||||||
content, options, onContextMenu,
|
content, options, onContextMenu,
|
||||||
}) {
|
}) {
|
||||||
let myClass = isUnread ? ' room-selector--unread' : '';
|
const classes = ['room-selector'];
|
||||||
myClass += isSelected ? ' room-selector--selected' : '';
|
if (isMuted) classes.push('room-selector--muted');
|
||||||
|
if (isUnread) classes.push('room-selector--unread');
|
||||||
|
if (isSelected) classes.push('room-selector--selected');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`room-selector${myClass}`}>
|
<div className={classes.join(' ')}>
|
||||||
<button
|
<button
|
||||||
className="room-selector__content"
|
className="room-selector__content"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -32,11 +35,13 @@ function RoomSelectorWrapper({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
RoomSelectorWrapper.defaultProps = {
|
RoomSelectorWrapper.defaultProps = {
|
||||||
|
isMuted: false,
|
||||||
options: null,
|
options: null,
|
||||||
onContextMenu: null,
|
onContextMenu: null,
|
||||||
};
|
};
|
||||||
RoomSelectorWrapper.propTypes = {
|
RoomSelectorWrapper.propTypes = {
|
||||||
isSelected: PropTypes.bool.isRequired,
|
isSelected: PropTypes.bool.isRequired,
|
||||||
|
isMuted: PropTypes.bool,
|
||||||
isUnread: PropTypes.bool.isRequired,
|
isUnread: PropTypes.bool.isRequired,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
content: PropTypes.node.isRequired,
|
content: PropTypes.node.isRequired,
|
||||||
@@ -46,12 +51,13 @@ RoomSelectorWrapper.propTypes = {
|
|||||||
|
|
||||||
function RoomSelector({
|
function RoomSelector({
|
||||||
name, parentName, roomId, imageSrc, iconSrc,
|
name, parentName, roomId, imageSrc, iconSrc,
|
||||||
isSelected, isUnread, notificationCount, isAlert,
|
isSelected, isMuted, isUnread, notificationCount, isAlert,
|
||||||
options, onClick, onContextMenu,
|
options, onClick, onContextMenu,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<RoomSelectorWrapper
|
<RoomSelectorWrapper
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
|
isMuted={isMuted}
|
||||||
isUnread={isUnread}
|
isUnread={isUnread}
|
||||||
content={(
|
content={(
|
||||||
<>
|
<>
|
||||||
@@ -91,6 +97,7 @@ RoomSelector.defaultProps = {
|
|||||||
isSelected: false,
|
isSelected: false,
|
||||||
imageSrc: null,
|
imageSrc: null,
|
||||||
iconSrc: null,
|
iconSrc: null,
|
||||||
|
isMuted: false,
|
||||||
options: null,
|
options: null,
|
||||||
onContextMenu: null,
|
onContextMenu: null,
|
||||||
};
|
};
|
||||||
@@ -101,6 +108,7 @@ RoomSelector.propTypes = {
|
|||||||
imageSrc: PropTypes.string,
|
imageSrc: PropTypes.string,
|
||||||
iconSrc: PropTypes.string,
|
iconSrc: PropTypes.string,
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
|
isMuted: PropTypes.bool,
|
||||||
isUnread: PropTypes.bool.isRequired,
|
isUnread: PropTypes.bool.isRequired,
|
||||||
notificationCount: PropTypes.oneOfType([
|
notificationCount: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
border-radius: var(--bo-radius);
|
border-radius: var(--bo-radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&--muted {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
&--unread {
|
&--unread {
|
||||||
.room-selector__content > .text {
|
.room-selector__content > .text {
|
||||||
color: var(--tc-surface-high);
|
color: var(--tc-surface-high);
|
||||||
|
|||||||
@@ -33,9 +33,11 @@ function Directs() {
|
|||||||
|
|
||||||
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||||
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||||
|
notifications.on(cons.events.notifications.MUTE_TOGGLED, notiChanged);
|
||||||
return () => {
|
return () => {
|
||||||
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||||
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||||
|
notifications.removeListener(cons.events.notifications.MUTE_TOGGLED, notiChanged);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,22 @@ function DrawerBreadcrumb({ spaceId }) {
|
|||||||
const noti = notifications.getNoti(roomId);
|
const noti = notifications.getNoti(roomId);
|
||||||
if (!notifications.hasNoti(childId)) return noti;
|
if (!notifications.hasNoti(childId)) return noti;
|
||||||
if (noti.from === null) return noti;
|
if (noti.from === null) return noti;
|
||||||
if (noti.from.has(childId) && noti.from.size === 1) return null;
|
|
||||||
|
|
||||||
const childNoti = notifications.getNoti(childId);
|
const childNoti = notifications.getNoti(childId);
|
||||||
|
|
||||||
return {
|
let noOther = true;
|
||||||
total: noti.total - childNoti.total,
|
let total = 0;
|
||||||
highlight: noti.highlight - childNoti.highlight,
|
let highlight = 0;
|
||||||
};
|
noti.from.forEach((fromId) => {
|
||||||
|
if (childNoti.from.has(fromId)) return;
|
||||||
|
noOther = false;
|
||||||
|
const fromNoti = notifications.getNoti(fromId);
|
||||||
|
total += fromNoti.total;
|
||||||
|
highlight += fromNoti.highlight;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (noOther) return null;
|
||||||
|
return { total, highlight };
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -15,10 +15,8 @@ const drawerPostie = new Postie();
|
|||||||
function Home({ spaceId }) {
|
function Home({ spaceId }) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const { roomList, notifications, accountData } = initMatrix;
|
const { roomList, notifications, accountData } = initMatrix;
|
||||||
const {
|
const { spaces, rooms, directs } = roomList;
|
||||||
spaces, rooms, directs, roomIdToParents,
|
useCategorizedSpaces();
|
||||||
} = roomList;
|
|
||||||
const categorizedSpaces = useCategorizedSpaces();
|
|
||||||
const isCategorized = accountData.categorizedSpaces.has(spaceId);
|
const isCategorized = accountData.categorizedSpaces.has(spaceId);
|
||||||
|
|
||||||
let categories = null;
|
let categories = null;
|
||||||
@@ -26,14 +24,14 @@ function Home({ spaceId }) {
|
|||||||
let roomIds = [];
|
let roomIds = [];
|
||||||
let directIds = [];
|
let directIds = [];
|
||||||
|
|
||||||
const spaceChildIds = roomList.getSpaceChildren(spaceId);
|
if (spaceId) {
|
||||||
if (spaceChildIds) {
|
const spaceChildIds = roomList.getSpaceChildren(spaceId);
|
||||||
spaceIds = spaceChildIds.filter((roomId) => spaces.has(roomId));
|
spaceIds = spaceChildIds.filter((roomId) => spaces.has(roomId));
|
||||||
roomIds = spaceChildIds.filter((roomId) => rooms.has(roomId));
|
roomIds = spaceChildIds.filter((roomId) => rooms.has(roomId));
|
||||||
directIds = spaceChildIds.filter((roomId) => directs.has(roomId));
|
directIds = spaceChildIds.filter((roomId) => directs.has(roomId));
|
||||||
} else {
|
} else {
|
||||||
spaceIds = [...spaces].filter((roomId) => !roomIdToParents.has(roomId));
|
spaceIds = roomList.getOrphanSpaces();
|
||||||
roomIds = [...rooms].filter((roomId) => !roomIdToParents.has(roomId));
|
roomIds = roomList.getOrphanRooms();
|
||||||
}
|
}
|
||||||
|
|
||||||
spaceIds.sort(AtoZ);
|
spaceIds.sort(AtoZ);
|
||||||
@@ -42,6 +40,7 @@ function Home({ spaceId }) {
|
|||||||
|
|
||||||
if (isCategorized) {
|
if (isCategorized) {
|
||||||
categories = roomList.getCategorizedSpaces(spaceIds);
|
categories = roomList.getCategorizedSpaces(spaceIds);
|
||||||
|
categories.delete(spaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -63,9 +62,11 @@ function Home({ spaceId }) {
|
|||||||
|
|
||||||
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||||
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||||
|
notifications.on(cons.events.notifications.MUTE_TOGGLED, notiChanged);
|
||||||
return () => {
|
return () => {
|
||||||
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||||
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||||
|
notifications.removeListener(cons.events.notifications.MUTE_TOGGLED, notiChanged);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import './RoomsCategory.scss';
|
import './RoomsCategory.scss';
|
||||||
|
|
||||||
import initMatrix from '../../../client/initMatrix';
|
import initMatrix from '../../../client/initMatrix';
|
||||||
import { selectSpace, selectRoom,openReusableContextMenu } from '../../../client/action/navigation';
|
import { selectSpace, selectRoom, openReusableContextMenu } from '../../../client/action/navigation';
|
||||||
import { getEventCords } from '../../../util/common';
|
import { getEventCords } from '../../../util/common';
|
||||||
|
|
||||||
import Text from '../../atoms/text/Text';
|
import Text from '../../atoms/text/Text';
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import React, { useEffect } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import initMatrix from '../../../client/initMatrix';
|
import initMatrix from '../../../client/initMatrix';
|
||||||
|
import cons from '../../../client/state/cons';
|
||||||
import navigation from '../../../client/state/navigation';
|
import navigation from '../../../client/state/navigation';
|
||||||
import { openReusableContextMenu } from '../../../client/action/navigation';
|
import { openReusableContextMenu } from '../../../client/action/navigation';
|
||||||
import { getEventCords, abbreviateNumber } from '../../../util/common';
|
import { getEventCords, abbreviateNumber } from '../../../util/common';
|
||||||
@@ -23,9 +24,12 @@ function Selector({
|
|||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const noti = initMatrix.notifications;
|
const noti = initMatrix.notifications;
|
||||||
const room = mx.getRoom(roomId);
|
const room = mx.getRoom(roomId);
|
||||||
|
|
||||||
let imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
let imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
||||||
if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
||||||
|
|
||||||
|
const isMuted = noti.getNotiType(roomId) === cons.notifs.MUTE;
|
||||||
|
|
||||||
const [, forceUpdate] = useForceUpdate();
|
const [, forceUpdate] = useForceUpdate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -56,7 +60,8 @@ function Selector({
|
|||||||
imageSrc={isDM ? imageSrc : null}
|
imageSrc={isDM ? imageSrc : null}
|
||||||
iconSrc={isDM ? null : joinRuleToIconSrc(room.getJoinRule(), room.isSpaceRoom())}
|
iconSrc={isDM ? null : joinRuleToIconSrc(room.getJoinRule(), room.isSpaceRoom())}
|
||||||
isSelected={navigation.selectedRoomId === roomId}
|
isSelected={navigation.selectedRoomId === roomId}
|
||||||
isUnread={noti.hasNoti(roomId)}
|
isMuted={isMuted}
|
||||||
|
isUnread={!isMuted && noti.hasNoti(roomId)}
|
||||||
notificationCount={abbreviateNumber(noti.getTotalNoti(roomId))}
|
notificationCount={abbreviateNumber(noti.getTotalNoti(roomId))}
|
||||||
isAlert={noti.getHighlightNoti(roomId) !== 0}
|
isAlert={noti.getHighlightNoti(roomId) !== 0}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|||||||
35
src/app/organisms/room/EventLimit.js
Normal file
35
src/app/organisms/room/EventLimit.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
class EventLimit {
|
||||||
|
constructor() {
|
||||||
|
this._from = 0;
|
||||||
|
|
||||||
|
this.SMALLEST_EVT_HEIGHT = 32;
|
||||||
|
this.PAGES_COUNT = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
get maxEvents() {
|
||||||
|
return Math.round(document.body.clientHeight / this.SMALLEST_EVT_HEIGHT) * this.PAGES_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
get from() {
|
||||||
|
return this._from;
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return this._from + this.maxEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrom(from) {
|
||||||
|
this._from = from < 0 ? 0 : from;
|
||||||
|
}
|
||||||
|
|
||||||
|
paginate(backwards, limit, timelineLength) {
|
||||||
|
this._from = backwards ? this._from - limit : this._from + limit;
|
||||||
|
|
||||||
|
if (!backwards && this.length > timelineLength) {
|
||||||
|
this._from = timelineLength - this.maxEvents;
|
||||||
|
}
|
||||||
|
if (this._from < 0) this._from = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventLimit;
|
||||||
@@ -7,16 +7,13 @@ import React, {
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import './RoomViewContent.scss';
|
import './RoomViewContent.scss';
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
|
||||||
import dateFormat from 'dateformat';
|
import dateFormat from 'dateformat';
|
||||||
|
|
||||||
import initMatrix from '../../../client/initMatrix';
|
import initMatrix from '../../../client/initMatrix';
|
||||||
import cons from '../../../client/state/cons';
|
import cons from '../../../client/state/cons';
|
||||||
import navigation from '../../../client/state/navigation';
|
import navigation from '../../../client/state/navigation';
|
||||||
import { openProfileViewer } from '../../../client/action/navigation';
|
import { openProfileViewer } from '../../../client/action/navigation';
|
||||||
import {
|
import { diffMinutes, isInSameDay, Throttle } from '../../../util/common';
|
||||||
diffMinutes, isInSameDay, Throttle, getScrollInfo,
|
|
||||||
} from '../../../util/common';
|
|
||||||
|
|
||||||
import Divider from '../../atoms/divider/Divider';
|
import Divider from '../../atoms/divider/Divider';
|
||||||
import ScrollView from '../../atoms/scroll/ScrollView';
|
import ScrollView from '../../atoms/scroll/ScrollView';
|
||||||
@@ -27,17 +24,15 @@ import TimelineChange from '../../molecules/message/TimelineChange';
|
|||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import { useForceUpdate } from '../../hooks/useForceUpdate';
|
import { useForceUpdate } from '../../hooks/useForceUpdate';
|
||||||
import { parseTimelineChange } from './common';
|
import { parseTimelineChange } from './common';
|
||||||
|
import TimelineScroll from './TimelineScroll';
|
||||||
|
import EventLimit from './EventLimit';
|
||||||
|
|
||||||
const DEFAULT_MAX_EVENTS = 50;
|
|
||||||
const PAG_LIMIT = 30;
|
const PAG_LIMIT = 30;
|
||||||
const MAX_MSG_DIFF_MINUTES = 5;
|
const MAX_MSG_DIFF_MINUTES = 5;
|
||||||
const PLACEHOLDER_COUNT = 2;
|
const PLACEHOLDER_COUNT = 2;
|
||||||
const PLACEHOLDERS_HEIGHT = 96 * PLACEHOLDER_COUNT;
|
const PLACEHOLDERS_HEIGHT = 96 * PLACEHOLDER_COUNT;
|
||||||
const SCROLL_TRIGGER_POS = PLACEHOLDERS_HEIGHT * 4;
|
const SCROLL_TRIGGER_POS = PLACEHOLDERS_HEIGHT * 4;
|
||||||
|
|
||||||
const SMALLEST_MSG_HEIGHT = 32;
|
|
||||||
const PAGES_COUNT = 4;
|
|
||||||
|
|
||||||
function loadingMsgPlaceholders(key, count = 2) {
|
function loadingMsgPlaceholders(key, count = 2) {
|
||||||
const pl = [];
|
const pl = [];
|
||||||
const genPlaceholders = () => {
|
const genPlaceholders = () => {
|
||||||
@@ -124,178 +119,7 @@ function renderEvent(roomTimeline, mEvent, prevMEvent, isFocus = false) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineScroll extends EventEmitter {
|
function useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef) {
|
||||||
constructor(target) {
|
|
||||||
super();
|
|
||||||
if (target === null) {
|
|
||||||
throw new Error('Can not initialize TimelineScroll, target HTMLElement in null');
|
|
||||||
}
|
|
||||||
this.scroll = target;
|
|
||||||
|
|
||||||
this.backwards = false;
|
|
||||||
this.inTopHalf = false;
|
|
||||||
this.maxEvents = DEFAULT_MAX_EVENTS;
|
|
||||||
|
|
||||||
this.isScrollable = false;
|
|
||||||
this.top = 0;
|
|
||||||
this.bottom = 0;
|
|
||||||
this.height = 0;
|
|
||||||
this.viewHeight = 0;
|
|
||||||
|
|
||||||
this.topMsg = null;
|
|
||||||
this.bottomMsg = null;
|
|
||||||
this.diff = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToBottom() {
|
|
||||||
const scrollInfo = getScrollInfo(this.scroll);
|
|
||||||
const maxScrollTop = scrollInfo.height - scrollInfo.viewHeight;
|
|
||||||
|
|
||||||
this._scrollTo(scrollInfo, maxScrollTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore scroll using previous calc by this._updateTopBottomMsg() and this._calcDiff.
|
|
||||||
tryRestoringScroll() {
|
|
||||||
const scrollInfo = getScrollInfo(this.scroll);
|
|
||||||
|
|
||||||
let scrollTop = 0;
|
|
||||||
const ot = this.inTopHalf ? this.topMsg?.offsetTop : this.bottomMsg?.offsetTop;
|
|
||||||
if (!ot) scrollTop = Math.round(this.height - this.viewHeight);
|
|
||||||
else scrollTop = ot - this.diff;
|
|
||||||
|
|
||||||
this._scrollTo(scrollInfo, scrollTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToIndex(index, offset = 0) {
|
|
||||||
const scrollInfo = getScrollInfo(this.scroll);
|
|
||||||
const msgs = this.scroll.lastElementChild.lastElementChild.children;
|
|
||||||
const offsetTop = msgs[index]?.offsetTop;
|
|
||||||
|
|
||||||
if (offsetTop === undefined) return;
|
|
||||||
// if msg is already in visible are we don't need to scroll to that
|
|
||||||
if (offsetTop > scrollInfo.top && offsetTop < (scrollInfo.top + scrollInfo.viewHeight)) return;
|
|
||||||
const to = offsetTop - offset;
|
|
||||||
|
|
||||||
this._scrollTo(scrollInfo, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
_scrollTo(scrollInfo, scrollTop) {
|
|
||||||
this.scroll.scrollTop = scrollTop;
|
|
||||||
|
|
||||||
// browser emit 'onscroll' event only if the 'element.scrollTop' value changes.
|
|
||||||
// so here we flag that the upcoming 'onscroll' event is
|
|
||||||
// emitted as side effect of assigning 'this.scroll.scrollTop' above
|
|
||||||
// only if it's changes.
|
|
||||||
// by doing so we prevent this._updateCalc() from calc again.
|
|
||||||
if (scrollTop !== this.top) {
|
|
||||||
this.scrolledByCode = true;
|
|
||||||
}
|
|
||||||
const sInfo = { ...scrollInfo };
|
|
||||||
|
|
||||||
const maxScrollTop = scrollInfo.height - scrollInfo.viewHeight;
|
|
||||||
|
|
||||||
sInfo.top = (scrollTop > maxScrollTop) ? maxScrollTop : scrollTop;
|
|
||||||
this._updateCalc(sInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we maintain reference of top and bottom messages
|
|
||||||
// to restore the scroll position when
|
|
||||||
// messages gets removed from either end and added to other.
|
|
||||||
_updateTopBottomMsg() {
|
|
||||||
const msgs = this.scroll.lastElementChild.lastElementChild.children;
|
|
||||||
const lMsgIndex = msgs.length - 1;
|
|
||||||
|
|
||||||
this.topMsg = msgs[0]?.className === 'ph-msg'
|
|
||||||
? msgs[PLACEHOLDER_COUNT]
|
|
||||||
: msgs[0];
|
|
||||||
this.bottomMsg = msgs[lMsgIndex]?.className === 'ph-msg'
|
|
||||||
? msgs[lMsgIndex - PLACEHOLDER_COUNT]
|
|
||||||
: msgs[lMsgIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
// we calculate the difference between first/last message and current scrollTop.
|
|
||||||
// if we are going above we calc diff between first and scrollTop
|
|
||||||
// else otherwise.
|
|
||||||
// NOTE: This will help to restore the scroll when msgs get's removed
|
|
||||||
// from one end and added to other end
|
|
||||||
_calcDiff(scrollInfo) {
|
|
||||||
if (!this.topMsg || !this.bottomMsg) return 0;
|
|
||||||
if (this.inTopHalf) {
|
|
||||||
return this.topMsg.offsetTop - scrollInfo.top;
|
|
||||||
}
|
|
||||||
return this.bottomMsg.offsetTop - scrollInfo.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
|
||||||
_calcMaxEvents(scrollInfo) {
|
|
||||||
return Math.round(scrollInfo.viewHeight / SMALLEST_MSG_HEIGHT) * PAGES_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateCalc(scrollInfo) {
|
|
||||||
const halfViewHeight = Math.round(scrollInfo.viewHeight / 2);
|
|
||||||
const scrollMiddle = scrollInfo.top + halfViewHeight;
|
|
||||||
const lastMiddle = this.top + halfViewHeight;
|
|
||||||
|
|
||||||
this.backwards = scrollMiddle < lastMiddle;
|
|
||||||
this.inTopHalf = scrollMiddle < scrollInfo.height / 2;
|
|
||||||
|
|
||||||
this.isScrollable = scrollInfo.isScrollable;
|
|
||||||
this.top = scrollInfo.top;
|
|
||||||
this.bottom = scrollInfo.height - (scrollInfo.top + scrollInfo.viewHeight);
|
|
||||||
this.height = scrollInfo.height;
|
|
||||||
|
|
||||||
// only calculate maxEvents if viewHeight change
|
|
||||||
if (this.viewHeight !== scrollInfo.viewHeight) {
|
|
||||||
this.maxEvents = this._calcMaxEvents(scrollInfo);
|
|
||||||
this.viewHeight = scrollInfo.viewHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateTopBottomMsg();
|
|
||||||
this.diff = this._calcDiff(scrollInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
calcScroll() {
|
|
||||||
if (this.scrolledByCode) {
|
|
||||||
this.scrolledByCode = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollInfo = getScrollInfo(this.scroll);
|
|
||||||
this._updateCalc(scrollInfo);
|
|
||||||
|
|
||||||
this.emit('scroll', this.backwards);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let timelineScroll = null;
|
|
||||||
let jumpToItemIndex = -1;
|
|
||||||
const throttle = new Throttle();
|
|
||||||
const limit = {
|
|
||||||
from: 0,
|
|
||||||
getMaxEvents() {
|
|
||||||
return timelineScroll?.maxEvents ?? DEFAULT_MAX_EVENTS;
|
|
||||||
},
|
|
||||||
getEndIndex() {
|
|
||||||
return this.from + this.getMaxEvents();
|
|
||||||
},
|
|
||||||
calcNextFrom(backwards, tLength) {
|
|
||||||
let newFrom = backwards ? this.from - PAG_LIMIT : this.from + PAG_LIMIT;
|
|
||||||
if (!backwards && newFrom + this.getMaxEvents() > tLength) {
|
|
||||||
newFrom = tLength - this.getMaxEvents();
|
|
||||||
}
|
|
||||||
if (newFrom < 0) newFrom = 0;
|
|
||||||
return newFrom;
|
|
||||||
},
|
|
||||||
setFrom(from) {
|
|
||||||
if (from < 0) {
|
|
||||||
this.from = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.from = from;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function useTimeline(roomTimeline, eventId, readEventStore) {
|
|
||||||
const [timelineInfo, setTimelineInfo] = useState(null);
|
const [timelineInfo, setTimelineInfo] = useState(null);
|
||||||
|
|
||||||
const setEventTimeline = async (eId) => {
|
const setEventTimeline = async (eId) => {
|
||||||
@@ -309,6 +133,7 @@ function useTimeline(roomTimeline, eventId, readEventStore) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const limit = eventLimitRef.current;
|
||||||
const initTimeline = (eId) => {
|
const initTimeline = (eId) => {
|
||||||
// NOTICE: eId can be id of readUpto, reply or specific event.
|
// NOTICE: eId can be id of readUpto, reply or specific event.
|
||||||
// readUpTo: when user click jump to unread message button.
|
// readUpTo: when user click jump to unread message button.
|
||||||
@@ -320,20 +145,20 @@ function useTimeline(roomTimeline, eventId, readEventStore) {
|
|||||||
|
|
||||||
if (isSpecificEvent) {
|
if (isSpecificEvent) {
|
||||||
focusEventIndex = roomTimeline.getEventIndex(eId);
|
focusEventIndex = roomTimeline.getEventIndex(eId);
|
||||||
} else if (!readEventStore.getItem()) {
|
} else if (!readUptoEvtStore.getItem()) {
|
||||||
// either opening live timeline or jump to unread.
|
// either opening live timeline or jump to unread.
|
||||||
focusEventIndex = roomTimeline.getUnreadEventIndex(readUpToId);
|
focusEventIndex = roomTimeline.getUnreadEventIndex(readUpToId);
|
||||||
if (roomTimeline.hasEventInTimeline(readUpToId)) {
|
if (roomTimeline.hasEventInTimeline(readUpToId)) {
|
||||||
readEventStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
|
readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
focusEventIndex = roomTimeline.getUnreadEventIndex(readEventStore.getItem().getId());
|
focusEventIndex = roomTimeline.getUnreadEventIndex(readUptoEvtStore.getItem().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (focusEventIndex > -1) {
|
if (focusEventIndex > -1) {
|
||||||
limit.setFrom(focusEventIndex - Math.round(limit.getMaxEvents() / 2));
|
limit.setFrom(focusEventIndex - Math.round(limit.maxEvents / 2));
|
||||||
} else {
|
} else {
|
||||||
limit.setFrom(roomTimeline.timeline.length - limit.getMaxEvents());
|
limit.setFrom(roomTimeline.timeline.length - limit.maxEvents);
|
||||||
}
|
}
|
||||||
setTimelineInfo({ focusEventId: isSpecificEvent ? eId : null });
|
setTimelineInfo({ focusEventId: isSpecificEvent ? eId : null });
|
||||||
};
|
};
|
||||||
@@ -350,36 +175,45 @@ function useTimeline(roomTimeline, eventId, readEventStore) {
|
|||||||
return timelineInfo;
|
return timelineInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
function usePaginate(roomTimeline, readEventStore, forceUpdateLimit) {
|
function usePaginate(
|
||||||
|
roomTimeline,
|
||||||
|
readUptoEvtStore,
|
||||||
|
forceUpdateLimit,
|
||||||
|
timelineScrollRef,
|
||||||
|
eventLimitRef,
|
||||||
|
) {
|
||||||
const [info, setInfo] = useState(null);
|
const [info, setInfo] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleOnPagination = (backwards, loaded) => {
|
const handlePaginatedFromServer = (backwards, loaded) => {
|
||||||
|
const limit = eventLimitRef.current;
|
||||||
if (loaded === 0) return;
|
if (loaded === 0) return;
|
||||||
if (!readEventStore.getItem()) {
|
if (!readUptoEvtStore.getItem()) {
|
||||||
const readUpToId = roomTimeline.getReadUpToEventId();
|
const readUpToId = roomTimeline.getReadUpToEventId();
|
||||||
readEventStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
|
readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
|
||||||
}
|
}
|
||||||
limit.setFrom(limit.calcNextFrom(backwards, roomTimeline.timeline.length));
|
limit.paginate(backwards, PAG_LIMIT, roomTimeline.timeline.length);
|
||||||
setTimeout(() => setInfo({
|
setTimeout(() => setInfo({
|
||||||
backwards,
|
backwards,
|
||||||
loaded,
|
loaded,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
roomTimeline.on(cons.events.roomTimeline.PAGINATED, handleOnPagination);
|
roomTimeline.on(cons.events.roomTimeline.PAGINATED, handlePaginatedFromServer);
|
||||||
return () => {
|
return () => {
|
||||||
roomTimeline.on(cons.events.roomTimeline.PAGINATED, handleOnPagination);
|
roomTimeline.on(cons.events.roomTimeline.PAGINATED, handlePaginatedFromServer);
|
||||||
};
|
};
|
||||||
}, [roomTimeline]);
|
}, [roomTimeline]);
|
||||||
|
|
||||||
const autoPaginate = useCallback(async () => {
|
const autoPaginate = useCallback(async () => {
|
||||||
|
const timelineScroll = timelineScrollRef.current;
|
||||||
|
const limit = eventLimitRef.current;
|
||||||
if (roomTimeline.isOngoingPagination) return;
|
if (roomTimeline.isOngoingPagination) return;
|
||||||
const tLength = roomTimeline.timeline.length;
|
const tLength = roomTimeline.timeline.length;
|
||||||
|
|
||||||
if (timelineScroll.bottom < SCROLL_TRIGGER_POS) {
|
if (timelineScroll.bottom < SCROLL_TRIGGER_POS) {
|
||||||
if (limit.getEndIndex() < tLength) {
|
if (limit.length < tLength) {
|
||||||
// paginate from memory
|
// paginate from memory
|
||||||
limit.setFrom(limit.calcNextFrom(false, tLength));
|
limit.paginate(false, PAG_LIMIT, tLength);
|
||||||
forceUpdateLimit();
|
forceUpdateLimit();
|
||||||
} else if (roomTimeline.canPaginateForward()) {
|
} else if (roomTimeline.canPaginateForward()) {
|
||||||
// paginate from server.
|
// paginate from server.
|
||||||
@@ -390,7 +224,7 @@ function usePaginate(roomTimeline, readEventStore, forceUpdateLimit) {
|
|||||||
if (timelineScroll.top < SCROLL_TRIGGER_POS) {
|
if (timelineScroll.top < SCROLL_TRIGGER_POS) {
|
||||||
if (limit.from > 0) {
|
if (limit.from > 0) {
|
||||||
// paginate from memory
|
// paginate from memory
|
||||||
limit.setFrom(limit.calcNextFrom(true, tLength));
|
limit.paginate(true, PAG_LIMIT, tLength);
|
||||||
forceUpdateLimit();
|
forceUpdateLimit();
|
||||||
} else if (roomTimeline.canPaginateBackward()) {
|
} else if (roomTimeline.canPaginateBackward()) {
|
||||||
// paginate from server.
|
// paginate from server.
|
||||||
@@ -402,16 +236,25 @@ function usePaginate(roomTimeline, readEventStore, forceUpdateLimit) {
|
|||||||
return [info, autoPaginate];
|
return [info, autoPaginate];
|
||||||
}
|
}
|
||||||
|
|
||||||
function useHandleScroll(roomTimeline, autoPaginate, readEventStore, forceUpdateLimit) {
|
function useHandleScroll(
|
||||||
|
roomTimeline,
|
||||||
|
autoPaginate,
|
||||||
|
readUptoEvtStore,
|
||||||
|
forceUpdateLimit,
|
||||||
|
timelineScrollRef,
|
||||||
|
eventLimitRef,
|
||||||
|
) {
|
||||||
const handleScroll = useCallback(() => {
|
const handleScroll = useCallback(() => {
|
||||||
|
const timelineScroll = timelineScrollRef.current;
|
||||||
|
const limit = eventLimitRef.current;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
// emit event to toggle scrollToBottom button visibility
|
// emit event to toggle scrollToBottom button visibility
|
||||||
const isAtBottom = (
|
const isAtBottom = (
|
||||||
timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()
|
timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()
|
||||||
&& limit.getEndIndex() >= roomTimeline.timeline.length
|
&& limit.length >= roomTimeline.timeline.length
|
||||||
);
|
);
|
||||||
roomTimeline.emit(cons.events.roomTimeline.AT_BOTTOM, isAtBottom);
|
roomTimeline.emit(cons.events.roomTimeline.AT_BOTTOM, isAtBottom);
|
||||||
if (isAtBottom && readEventStore.getItem()) {
|
if (isAtBottom && readUptoEvtStore.getItem()) {
|
||||||
requestAnimationFrame(() => roomTimeline.markAllAsRead());
|
requestAnimationFrame(() => roomTimeline.markAllAsRead());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -419,11 +262,13 @@ function useHandleScroll(roomTimeline, autoPaginate, readEventStore, forceUpdate
|
|||||||
}, [roomTimeline]);
|
}, [roomTimeline]);
|
||||||
|
|
||||||
const handleScrollToLive = useCallback(() => {
|
const handleScrollToLive = useCallback(() => {
|
||||||
if (readEventStore.getItem()) {
|
const timelineScroll = timelineScrollRef.current;
|
||||||
|
const limit = eventLimitRef.current;
|
||||||
|
if (readUptoEvtStore.getItem()) {
|
||||||
requestAnimationFrame(() => roomTimeline.markAllAsRead());
|
requestAnimationFrame(() => roomTimeline.markAllAsRead());
|
||||||
}
|
}
|
||||||
if (roomTimeline.isServingLiveTimeline()) {
|
if (roomTimeline.isServingLiveTimeline()) {
|
||||||
limit.setFrom(roomTimeline.timeline.length - limit.getMaxEvents());
|
limit.setFrom(roomTimeline.timeline.length - limit.maxEvents);
|
||||||
timelineScroll.scrollToBottom();
|
timelineScroll.scrollToBottom();
|
||||||
forceUpdateLimit();
|
forceUpdateLimit();
|
||||||
return;
|
return;
|
||||||
@@ -434,29 +279,32 @@ function useHandleScroll(roomTimeline, autoPaginate, readEventStore, forceUpdate
|
|||||||
return [handleScroll, handleScrollToLive];
|
return [handleScroll, handleScrollToLive];
|
||||||
}
|
}
|
||||||
|
|
||||||
function useEventArrive(roomTimeline, readEventStore) {
|
function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef) {
|
||||||
const myUserId = initMatrix.matrixClient.getUserId();
|
const myUserId = initMatrix.matrixClient.getUserId();
|
||||||
const [newEvent, setEvent] = useState(null);
|
const [newEvent, setEvent] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const timelineScroll = timelineScrollRef.current;
|
||||||
|
const limit = eventLimitRef.current;
|
||||||
const sendReadReceipt = (event) => {
|
const sendReadReceipt = (event) => {
|
||||||
if (event.isSending()) return;
|
if (event.isSending()) return;
|
||||||
if (myUserId === event.getSender()) {
|
if (myUserId === event.getSender()) {
|
||||||
roomTimeline.markAllAsRead();
|
roomTimeline.markAllAsRead();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const readUpToEvent = readEventStore.getItem();
|
const readUpToEvent = readUptoEvtStore.getItem();
|
||||||
const readUpToId = roomTimeline.getReadUpToEventId();
|
const readUpToId = roomTimeline.getReadUpToEventId();
|
||||||
|
const isUnread = readUpToEvent?.getId() === readUpToId;
|
||||||
|
|
||||||
// if user doesn't have focus on app don't mark messages as read.
|
// if user doesn't have focus on app don't mark messages as read.
|
||||||
if (document.visibilityState === 'hidden' || timelineScroll.bottom >= 16) {
|
if (document.visibilityState === 'hidden' || timelineScroll.bottom >= 16) {
|
||||||
if (readUpToEvent === readUpToId) return;
|
if (isUnread) return;
|
||||||
readEventStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
|
readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// user has not mark room as read
|
// user has not mark room as read
|
||||||
const isUnreadMsg = readUpToEvent?.getId() === readUpToId;
|
if (!isUnread) {
|
||||||
if (!isUnreadMsg) {
|
|
||||||
roomTimeline.markAllAsRead();
|
roomTimeline.markAllAsRead();
|
||||||
}
|
}
|
||||||
const { timeline } = roomTimeline;
|
const { timeline } = roomTimeline;
|
||||||
@@ -470,11 +318,11 @@ function useEventArrive(roomTimeline, readEventStore) {
|
|||||||
const tLength = roomTimeline.timeline.length;
|
const tLength = roomTimeline.timeline.length;
|
||||||
const isUserViewingLive = (
|
const isUserViewingLive = (
|
||||||
roomTimeline.isServingLiveTimeline()
|
roomTimeline.isServingLiveTimeline()
|
||||||
&& limit.getEndIndex() >= tLength - 1
|
&& limit.length >= tLength - 1
|
||||||
&& timelineScroll.bottom < SCROLL_TRIGGER_POS
|
&& timelineScroll.bottom < SCROLL_TRIGGER_POS
|
||||||
);
|
);
|
||||||
if (isUserViewingLive) {
|
if (isUserViewingLive) {
|
||||||
limit.setFrom(tLength - limit.getMaxEvents());
|
limit.setFrom(tLength - limit.maxEvents);
|
||||||
sendReadReceipt(event);
|
sendReadReceipt(event);
|
||||||
setEvent(event);
|
setEvent(event);
|
||||||
return;
|
return;
|
||||||
@@ -486,7 +334,7 @@ function useEventArrive(roomTimeline, readEventStore) {
|
|||||||
}
|
}
|
||||||
const isUserDitchedLive = (
|
const isUserDitchedLive = (
|
||||||
roomTimeline.isServingLiveTimeline()
|
roomTimeline.isServingLiveTimeline()
|
||||||
&& limit.getEndIndex() >= tLength - 1
|
&& limit.length >= tLength - 1
|
||||||
);
|
);
|
||||||
if (isUserDitchedLive) {
|
if (isUserDitchedLive) {
|
||||||
// This stateUpdate will help to put the
|
// This stateUpdate will help to put the
|
||||||
@@ -506,6 +354,7 @@ function useEventArrive(roomTimeline, readEventStore) {
|
|||||||
}, [roomTimeline]);
|
}, [roomTimeline]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const timelineScroll = timelineScrollRef.current;
|
||||||
if (!roomTimeline.initialized) return;
|
if (!roomTimeline.initialized) return;
|
||||||
if (timelineScroll.bottom < 16
|
if (timelineScroll.bottom < 16
|
||||||
&& !roomTimeline.canPaginateForward()
|
&& !roomTimeline.canPaginateForward()
|
||||||
@@ -517,27 +366,49 @@ function useEventArrive(roomTimeline, readEventStore) {
|
|||||||
}, [newEvent, roomTimeline]);
|
}, [newEvent, roomTimeline]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let jumpToItemIndex = -1;
|
||||||
|
|
||||||
function RoomViewContent({ eventId, roomTimeline }) {
|
function RoomViewContent({ eventId, roomTimeline }) {
|
||||||
|
const [throttle] = useState(new Throttle());
|
||||||
|
|
||||||
const timelineSVRef = useRef(null);
|
const timelineSVRef = useRef(null);
|
||||||
const readEventStore = useStore(roomTimeline);
|
const timelineScrollRef = useRef(null);
|
||||||
const timelineInfo = useTimeline(roomTimeline, eventId, readEventStore);
|
const eventLimitRef = useRef(null);
|
||||||
|
|
||||||
|
const readUptoEvtStore = useStore(roomTimeline);
|
||||||
const [onLimitUpdate, forceUpdateLimit] = useForceUpdate();
|
const [onLimitUpdate, forceUpdateLimit] = useForceUpdate();
|
||||||
const [paginateInfo, autoPaginate] = usePaginate(roomTimeline, readEventStore, forceUpdateLimit);
|
|
||||||
const [handleScroll, handleScrollToLive] = useHandleScroll(
|
const timelineInfo = useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef);
|
||||||
roomTimeline, autoPaginate, readEventStore, forceUpdateLimit,
|
const [paginateInfo, autoPaginate] = usePaginate(
|
||||||
|
roomTimeline,
|
||||||
|
readUptoEvtStore,
|
||||||
|
forceUpdateLimit,
|
||||||
|
timelineScrollRef,
|
||||||
|
eventLimitRef,
|
||||||
);
|
);
|
||||||
useEventArrive(roomTimeline, readEventStore);
|
const [handleScroll, handleScrollToLive] = useHandleScroll(
|
||||||
|
roomTimeline,
|
||||||
|
autoPaginate,
|
||||||
|
readUptoEvtStore,
|
||||||
|
forceUpdateLimit,
|
||||||
|
timelineScrollRef,
|
||||||
|
eventLimitRef,
|
||||||
|
);
|
||||||
|
useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef);
|
||||||
|
|
||||||
const { timeline } = roomTimeline;
|
const { timeline } = roomTimeline;
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!roomTimeline.initialized) {
|
if (!roomTimeline.initialized) {
|
||||||
timelineScroll = new TimelineScroll(timelineSVRef.current);
|
timelineScrollRef.current = new TimelineScroll(timelineSVRef.current);
|
||||||
|
eventLimitRef.current = new EventLimit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// when active timeline changes
|
// when active timeline changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!roomTimeline.initialized) return undefined;
|
if (!roomTimeline.initialized) return undefined;
|
||||||
|
const timelineScroll = timelineScrollRef.current;
|
||||||
|
|
||||||
if (timeline.length > 0) {
|
if (timeline.length > 0) {
|
||||||
if (jumpToItemIndex === -1) {
|
if (jumpToItemIndex === -1) {
|
||||||
@@ -547,7 +418,7 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
|||||||
}
|
}
|
||||||
if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()) {
|
if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()) {
|
||||||
const readUpToId = roomTimeline.getReadUpToEventId();
|
const readUpToId = roomTimeline.getReadUpToEventId();
|
||||||
if (readEventStore.getItem()?.getId() === readUpToId || readUpToId === null) {
|
if (readUptoEvtStore.getItem()?.getId() === readUpToId || readUpToId === null) {
|
||||||
requestAnimationFrame(() => roomTimeline.markAllAsRead());
|
requestAnimationFrame(() => roomTimeline.markAllAsRead());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -555,11 +426,9 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
|||||||
}
|
}
|
||||||
autoPaginate();
|
autoPaginate();
|
||||||
|
|
||||||
timelineScroll.on('scroll', handleScroll);
|
|
||||||
roomTimeline.on(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
|
roomTimeline.on(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
|
||||||
return () => {
|
return () => {
|
||||||
if (timelineSVRef.current === null) return;
|
if (timelineSVRef.current === null) return;
|
||||||
timelineScroll.removeListener('scroll', handleScroll);
|
|
||||||
roomTimeline.removeListener(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
|
roomTimeline.removeListener(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
|
||||||
};
|
};
|
||||||
}, [timelineInfo]);
|
}, [timelineInfo]);
|
||||||
@@ -567,6 +436,7 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
|||||||
// when paginating from server
|
// when paginating from server
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!roomTimeline.initialized) return;
|
if (!roomTimeline.initialized) return;
|
||||||
|
const timelineScroll = timelineScrollRef.current;
|
||||||
timelineScroll.tryRestoringScroll();
|
timelineScroll.tryRestoringScroll();
|
||||||
autoPaginate();
|
autoPaginate();
|
||||||
}, [paginateInfo]);
|
}, [paginateInfo]);
|
||||||
@@ -574,28 +444,35 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
|||||||
// when paginating locally
|
// when paginating locally
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!roomTimeline.initialized) return;
|
if (!roomTimeline.initialized) return;
|
||||||
|
const timelineScroll = timelineScrollRef.current;
|
||||||
timelineScroll.tryRestoringScroll();
|
timelineScroll.tryRestoringScroll();
|
||||||
}, [onLimitUpdate]);
|
}, [onLimitUpdate]);
|
||||||
|
|
||||||
const handleTimelineScroll = (event) => {
|
const handleTimelineScroll = (event) => {
|
||||||
const { target } = event;
|
const timelineScroll = timelineScrollRef.current;
|
||||||
if (!target) return;
|
if (!event.target) return;
|
||||||
throttle._(() => timelineScroll?.calcScroll(), 400)(target);
|
|
||||||
|
throttle._(() => {
|
||||||
|
const backwards = timelineScroll?.calcScroll();
|
||||||
|
if (typeof backwards !== 'boolean') return;
|
||||||
|
handleScroll(backwards);
|
||||||
|
}, 200)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderTimeline = () => {
|
const renderTimeline = () => {
|
||||||
const tl = [];
|
const tl = [];
|
||||||
|
const limit = eventLimitRef.current;
|
||||||
|
|
||||||
let itemCountIndex = 0;
|
let itemCountIndex = 0;
|
||||||
jumpToItemIndex = -1;
|
jumpToItemIndex = -1;
|
||||||
const readEvent = readEventStore.getItem();
|
const readUptoEvent = readUptoEvtStore.getItem();
|
||||||
let unreadDivider = false;
|
let unreadDivider = false;
|
||||||
|
|
||||||
if (roomTimeline.canPaginateBackward() || limit.from > 0) {
|
if (roomTimeline.canPaginateBackward() || limit.from > 0) {
|
||||||
tl.push(loadingMsgPlaceholders(1, PLACEHOLDER_COUNT));
|
tl.push(loadingMsgPlaceholders(1, PLACEHOLDER_COUNT));
|
||||||
itemCountIndex += PLACEHOLDER_COUNT;
|
itemCountIndex += PLACEHOLDER_COUNT;
|
||||||
}
|
}
|
||||||
for (let i = limit.from; i < limit.getEndIndex(); i += 1) {
|
for (let i = limit.from; i < limit.length; i += 1) {
|
||||||
if (i >= timeline.length) break;
|
if (i >= timeline.length) break;
|
||||||
const mEvent = timeline[i];
|
const mEvent = timeline[i];
|
||||||
const prevMEvent = timeline[i - 1] ?? null;
|
const prevMEvent = timeline[i - 1] ?? null;
|
||||||
@@ -614,9 +491,9 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
|||||||
|
|
||||||
let isNewEvent = false;
|
let isNewEvent = false;
|
||||||
if (!unreadDivider) {
|
if (!unreadDivider) {
|
||||||
unreadDivider = (readEvent
|
unreadDivider = (readUptoEvent
|
||||||
&& prevMEvent?.getTs() <= readEvent.getTs()
|
&& prevMEvent?.getTs() <= readUptoEvent.getTs()
|
||||||
&& readEvent.getTs() < mEvent.getTs());
|
&& readUptoEvent.getTs() < mEvent.getTs());
|
||||||
if (unreadDivider) {
|
if (unreadDivider) {
|
||||||
isNewEvent = true;
|
isNewEvent = true;
|
||||||
tl.push(<Divider key={`new-${mEvent.getId()}`} variant="positive" text="New messages" />);
|
tl.push(<Divider key={`new-${mEvent.getId()}`} variant="positive" text="New messages" />);
|
||||||
@@ -637,7 +514,7 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
|||||||
tl.push(renderEvent(roomTimeline, mEvent, isNewEvent ? null : prevMEvent, isFocus));
|
tl.push(renderEvent(roomTimeline, mEvent, isNewEvent ? null : prevMEvent, isFocus));
|
||||||
itemCountIndex += 1;
|
itemCountIndex += 1;
|
||||||
}
|
}
|
||||||
if (roomTimeline.canPaginateForward() || limit.getEndIndex() < timeline.length) {
|
if (roomTimeline.canPaginateForward() || limit.length < timeline.length) {
|
||||||
tl.push(loadingMsgPlaceholders(2, PLACEHOLDER_COUNT));
|
tl.push(loadingMsgPlaceholders(2, PLACEHOLDER_COUNT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
136
src/app/organisms/room/TimelineScroll.js
Normal file
136
src/app/organisms/room/TimelineScroll.js
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { getScrollInfo } from '../../../util/common';
|
||||||
|
|
||||||
|
class TimelineScroll {
|
||||||
|
constructor(target) {
|
||||||
|
if (target === null) {
|
||||||
|
throw new Error('Can not initialize TimelineScroll, target HTMLElement in null');
|
||||||
|
}
|
||||||
|
this.scroll = target;
|
||||||
|
|
||||||
|
this.backwards = false;
|
||||||
|
this.inTopHalf = false;
|
||||||
|
|
||||||
|
this.isScrollable = false;
|
||||||
|
this.top = 0;
|
||||||
|
this.bottom = 0;
|
||||||
|
this.height = 0;
|
||||||
|
this.viewHeight = 0;
|
||||||
|
|
||||||
|
this.topMsg = null;
|
||||||
|
this.bottomMsg = null;
|
||||||
|
this.diff = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToBottom() {
|
||||||
|
const scrollInfo = getScrollInfo(this.scroll);
|
||||||
|
const maxScrollTop = scrollInfo.height - scrollInfo.viewHeight;
|
||||||
|
|
||||||
|
this._scrollTo(scrollInfo, maxScrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use previous calc by this._updateTopBottomMsg() & this._calcDiff.
|
||||||
|
tryRestoringScroll() {
|
||||||
|
const scrollInfo = getScrollInfo(this.scroll);
|
||||||
|
|
||||||
|
let scrollTop = 0;
|
||||||
|
const ot = this.inTopHalf ? this.topMsg?.offsetTop : this.bottomMsg?.offsetTop;
|
||||||
|
if (!ot) scrollTop = Math.round(this.height - this.viewHeight);
|
||||||
|
else scrollTop = ot - this.diff;
|
||||||
|
|
||||||
|
this._scrollTo(scrollInfo, scrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToIndex(index, offset = 0) {
|
||||||
|
const scrollInfo = getScrollInfo(this.scroll);
|
||||||
|
const msgs = this.scroll.lastElementChild.lastElementChild.children;
|
||||||
|
const offsetTop = msgs[index]?.offsetTop;
|
||||||
|
|
||||||
|
if (offsetTop === undefined) return;
|
||||||
|
// if msg is already in visible are we don't need to scroll to that
|
||||||
|
if (offsetTop > scrollInfo.top && offsetTop < (scrollInfo.top + scrollInfo.viewHeight)) return;
|
||||||
|
const to = offsetTop - offset;
|
||||||
|
|
||||||
|
this._scrollTo(scrollInfo, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
_scrollTo(scrollInfo, scrollTop) {
|
||||||
|
this.scroll.scrollTop = scrollTop;
|
||||||
|
|
||||||
|
// browser emit 'onscroll' event only if the 'element.scrollTop' value changes.
|
||||||
|
// so here we flag that the upcoming 'onscroll' event is
|
||||||
|
// emitted as side effect of assigning 'this.scroll.scrollTop' above
|
||||||
|
// only if it's changes.
|
||||||
|
// by doing so we prevent this._updateCalc() from calc again.
|
||||||
|
if (scrollTop !== this.top) {
|
||||||
|
this.scrolledByCode = true;
|
||||||
|
}
|
||||||
|
const sInfo = { ...scrollInfo };
|
||||||
|
|
||||||
|
const maxScrollTop = scrollInfo.height - scrollInfo.viewHeight;
|
||||||
|
|
||||||
|
sInfo.top = (scrollTop > maxScrollTop) ? maxScrollTop : scrollTop;
|
||||||
|
this._updateCalc(sInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we maintain reference of top and bottom messages
|
||||||
|
// to restore the scroll position when
|
||||||
|
// messages gets removed from either end and added to other.
|
||||||
|
_updateTopBottomMsg() {
|
||||||
|
const msgs = this.scroll.lastElementChild.lastElementChild.children;
|
||||||
|
const lMsgIndex = msgs.length - 1;
|
||||||
|
|
||||||
|
// TODO: classname 'ph-msg' prevent this class from being used
|
||||||
|
const PLACEHOLDER_COUNT = 2;
|
||||||
|
this.topMsg = msgs[0]?.className === 'ph-msg'
|
||||||
|
? msgs[PLACEHOLDER_COUNT]
|
||||||
|
: msgs[0];
|
||||||
|
this.bottomMsg = msgs[lMsgIndex]?.className === 'ph-msg'
|
||||||
|
? msgs[lMsgIndex - PLACEHOLDER_COUNT]
|
||||||
|
: msgs[lMsgIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// we calculate the difference between first/last message and current scrollTop.
|
||||||
|
// if we are going above we calc diff between first and scrollTop
|
||||||
|
// else otherwise.
|
||||||
|
// NOTE: This will help to restore the scroll when msgs get's removed
|
||||||
|
// from one end and added to other end
|
||||||
|
_calcDiff(scrollInfo) {
|
||||||
|
if (!this.topMsg || !this.bottomMsg) return 0;
|
||||||
|
if (this.inTopHalf) {
|
||||||
|
return this.topMsg.offsetTop - scrollInfo.top;
|
||||||
|
}
|
||||||
|
return this.bottomMsg.offsetTop - scrollInfo.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateCalc(scrollInfo) {
|
||||||
|
const halfViewHeight = Math.round(scrollInfo.viewHeight / 2);
|
||||||
|
const scrollMiddle = scrollInfo.top + halfViewHeight;
|
||||||
|
const lastMiddle = this.top + halfViewHeight;
|
||||||
|
|
||||||
|
this.backwards = scrollMiddle < lastMiddle;
|
||||||
|
this.inTopHalf = scrollMiddle < scrollInfo.height / 2;
|
||||||
|
|
||||||
|
this.isScrollable = scrollInfo.isScrollable;
|
||||||
|
this.top = scrollInfo.top;
|
||||||
|
this.bottom = scrollInfo.height - (scrollInfo.top + scrollInfo.viewHeight);
|
||||||
|
this.height = scrollInfo.height;
|
||||||
|
this.viewHeight = scrollInfo.viewHeight;
|
||||||
|
|
||||||
|
this._updateTopBottomMsg();
|
||||||
|
this.diff = this._calcDiff(scrollInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
calcScroll() {
|
||||||
|
if (this.scrolledByCode) {
|
||||||
|
this.scrolledByCode = false;
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollInfo = getScrollInfo(this.scroll);
|
||||||
|
this._updateCalc(scrollInfo);
|
||||||
|
|
||||||
|
return this.backwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimelineScroll;
|
||||||
@@ -2,8 +2,8 @@ import { openSearch, toggleRoomSettings } from '../action/navigation';
|
|||||||
import navigation from '../state/navigation';
|
import navigation from '../state/navigation';
|
||||||
|
|
||||||
function listenKeyboard(event) {
|
function listenKeyboard(event) {
|
||||||
// Ctrl +
|
// Ctrl/Cmd +
|
||||||
if (event.ctrlKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
// k - for search Modal
|
// k - for search Modal
|
||||||
if (event.keyCode === 75) {
|
if (event.keyCode === 75) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -17,6 +17,17 @@ function isNotifEvent(mEvent) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMutedRule(rule) {
|
||||||
|
return rule.actions[0] === 'dont_notify' && rule.conditions[0].kind === 'event_match';
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMutedRule(overrideRules, roomId) {
|
||||||
|
return overrideRules.find((rule) => (
|
||||||
|
rule.rule_id === roomId
|
||||||
|
&& isMutedRule(rule)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
class Notifications extends EventEmitter {
|
class Notifications extends EventEmitter {
|
||||||
constructor(roomList) {
|
constructor(roomList) {
|
||||||
super();
|
super();
|
||||||
@@ -39,11 +50,12 @@ class Notifications extends EventEmitter {
|
|||||||
_initNoti() {
|
_initNoti() {
|
||||||
const addNoti = (roomId) => {
|
const addNoti = (roomId) => {
|
||||||
const room = this.matrixClient.getRoom(roomId);
|
const room = this.matrixClient.getRoom(roomId);
|
||||||
|
if (this.getNotiType(room.roomId) === cons.notifs.MUTE) return;
|
||||||
if (this.doesRoomHaveUnread(room) === false) return;
|
if (this.doesRoomHaveUnread(room) === false) return;
|
||||||
|
|
||||||
const total = room.getUnreadNotificationCount('total');
|
const total = room.getUnreadNotificationCount('total');
|
||||||
const highlight = room.getUnreadNotificationCount('highlight');
|
const highlight = room.getUnreadNotificationCount('highlight');
|
||||||
const noti = this.getNoti(room.roomId);
|
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||||
this._setNoti(room.roomId, total - noti.total, highlight - noti.highlight);
|
|
||||||
};
|
};
|
||||||
[...this.roomList.rooms].forEach(addNoti);
|
[...this.roomList.rooms].forEach(addNoti);
|
||||||
[...this.roomList.directs].forEach(addNoti);
|
[...this.roomList.directs].forEach(addNoti);
|
||||||
@@ -66,6 +78,22 @@ class Notifications extends EventEmitter {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNotiType(roomId) {
|
||||||
|
const mx = this.matrixClient;
|
||||||
|
const pushRule = mx.getRoomPushRule('global', roomId);
|
||||||
|
|
||||||
|
if (pushRule === undefined) {
|
||||||
|
const overrideRules = mx.getAccountData('m.push_rules')?.getContent()?.global?.override;
|
||||||
|
if (overrideRules === undefined) return cons.notifs.DEFAULT;
|
||||||
|
|
||||||
|
const isMuted = findMutedRule(overrideRules, roomId);
|
||||||
|
|
||||||
|
return isMuted ? cons.notifs.MUTE : cons.notifs.DEFAULT;
|
||||||
|
}
|
||||||
|
if (pushRule.actions[0] === 'notify') return cons.notifs.ALL_MESSAGES;
|
||||||
|
return cons.notifs.MENTIONS_AND_KEYWORDS;
|
||||||
|
}
|
||||||
|
|
||||||
getNoti(roomId) {
|
getNoti(roomId) {
|
||||||
return this.roomIdToNoti.get(roomId) || { total: 0, highlight: 0, from: null };
|
return this.roomIdToNoti.get(roomId) || { total: 0, highlight: 0, from: null };
|
||||||
}
|
}
|
||||||
@@ -96,74 +124,64 @@ class Notifications extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getAllParentIds(roomId) {
|
_setNoti(roomId, total, highlight) {
|
||||||
let allParentIds = this.roomList.roomIdToParents.get(roomId);
|
const addNoti = (id, t, h, fromId) => {
|
||||||
if (allParentIds === undefined) return new Set();
|
const prevTotal = this.roomIdToNoti.get(id)?.total ?? null;
|
||||||
const parentIds = [...allParentIds];
|
const noti = this.getNoti(id);
|
||||||
|
|
||||||
parentIds.forEach((pId) => {
|
noti.total += t;
|
||||||
allParentIds = new Set(
|
noti.highlight += h;
|
||||||
[...allParentIds, ...this._getAllParentIds(pId)],
|
|
||||||
);
|
if (fromId) {
|
||||||
|
if (noti.from === null) noti.from = new Set();
|
||||||
|
noti.from.add(fromId);
|
||||||
|
}
|
||||||
|
this.roomIdToNoti.set(id, noti);
|
||||||
|
this.emit(cons.events.notifications.NOTI_CHANGED, id, noti.total, prevTotal);
|
||||||
|
};
|
||||||
|
|
||||||
|
const noti = this.getNoti(roomId);
|
||||||
|
const addT = total - noti.total;
|
||||||
|
const addH = highlight - noti.highlight;
|
||||||
|
if (addT < 0 || addH < 0) return;
|
||||||
|
|
||||||
|
addNoti(roomId, addT, addH);
|
||||||
|
const allParentSpaces = this.roomList.getAllParentSpaces(roomId);
|
||||||
|
allParentSpaces.forEach((spaceId) => {
|
||||||
|
addNoti(spaceId, addT, addH, roomId);
|
||||||
});
|
});
|
||||||
|
|
||||||
return allParentIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setNoti(roomId, total, highlight, childId) {
|
_deleteNoti(roomId, total, highlight) {
|
||||||
const prevTotal = this.roomIdToNoti.get(roomId)?.total ?? null;
|
const removeNoti = (id, t, h, fromId) => {
|
||||||
const noti = this.getNoti(roomId);
|
if (this.roomIdToNoti.has(id) === false) return;
|
||||||
|
|
||||||
if (!childId || this._remainingParentIds?.has(roomId)) {
|
const noti = this.getNoti(id);
|
||||||
noti.total += total;
|
const prevTotal = noti.total;
|
||||||
noti.highlight += highlight;
|
noti.total -= t;
|
||||||
}
|
noti.highlight -= h;
|
||||||
if (childId) {
|
if (noti.total < 0) {
|
||||||
if (noti.from === null) noti.from = new Set();
|
noti.total = 0;
|
||||||
noti.from.add(childId);
|
noti.highlight = 0;
|
||||||
}
|
}
|
||||||
|
if (fromId && noti.from !== null) {
|
||||||
|
if (!this.hasNoti(fromId)) noti.from.delete(fromId);
|
||||||
|
}
|
||||||
|
if (noti.from === null || noti.from.size === 0) {
|
||||||
|
this.roomIdToNoti.delete(id);
|
||||||
|
this.emit(cons.events.notifications.FULL_READ, id);
|
||||||
|
this.emit(cons.events.notifications.NOTI_CHANGED, id, null, prevTotal);
|
||||||
|
} else {
|
||||||
|
this.roomIdToNoti.set(id, noti);
|
||||||
|
this.emit(cons.events.notifications.NOTI_CHANGED, id, noti.total, prevTotal);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.roomIdToNoti.set(roomId, noti);
|
removeNoti(roomId, total, highlight);
|
||||||
this.emit(cons.events.notifications.NOTI_CHANGED, roomId, noti.total, prevTotal);
|
const allParentSpaces = this.roomList.getAllParentSpaces(roomId);
|
||||||
|
allParentSpaces.forEach((spaceId) => {
|
||||||
if (!childId) this._remainingParentIds = this._getAllParentIds(roomId);
|
removeNoti(spaceId, total, highlight, roomId);
|
||||||
else this._remainingParentIds.delete(roomId);
|
});
|
||||||
|
|
||||||
const parentIds = this.roomList.roomIdToParents.get(roomId);
|
|
||||||
if (typeof parentIds === 'undefined') {
|
|
||||||
if (!childId) this._remainingParentIds = undefined;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[...parentIds].forEach((parentId) => this._setNoti(parentId, total, highlight, roomId));
|
|
||||||
if (!childId) this._remainingParentIds = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
_deleteNoti(roomId, total, highlight, childId) {
|
|
||||||
if (this.roomIdToNoti.has(roomId) === false) return;
|
|
||||||
|
|
||||||
const noti = this.getNoti(roomId);
|
|
||||||
const prevTotal = noti.total;
|
|
||||||
noti.total -= total;
|
|
||||||
noti.highlight -= highlight;
|
|
||||||
if (noti.total < 0) {
|
|
||||||
noti.total = 0;
|
|
||||||
noti.highlight = 0;
|
|
||||||
}
|
|
||||||
if (childId && noti.from !== null) {
|
|
||||||
if (!this.hasNoti(childId)) noti.from.delete(childId);
|
|
||||||
}
|
|
||||||
if (noti.from === null || noti.from.size === 0) {
|
|
||||||
this.roomIdToNoti.delete(roomId);
|
|
||||||
this.emit(cons.events.notifications.FULL_READ, roomId);
|
|
||||||
this.emit(cons.events.notifications.NOTI_CHANGED, roomId, null, prevTotal);
|
|
||||||
} else {
|
|
||||||
this.roomIdToNoti.set(roomId, noti);
|
|
||||||
this.emit(cons.events.notifications.NOTI_CHANGED, roomId, noti.total, prevTotal);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentIds = this.roomList.roomIdToParents.get(roomId);
|
|
||||||
if (typeof parentIds === 'undefined') return;
|
|
||||||
[...parentIds].forEach((parentId) => this._deleteNoti(parentId, total, highlight, roomId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _displayPopupNoti(mEvent, room) {
|
async _displayPopupNoti(mEvent, room) {
|
||||||
@@ -204,7 +222,9 @@ class Notifications extends EventEmitter {
|
|||||||
|
|
||||||
_listenEvents() {
|
_listenEvents() {
|
||||||
this.matrixClient.on('Room.timeline', (mEvent, room) => {
|
this.matrixClient.on('Room.timeline', (mEvent, room) => {
|
||||||
|
if (room.isSpaceRoom()) return;
|
||||||
if (!isNotifEvent(mEvent)) return;
|
if (!isNotifEvent(mEvent)) return;
|
||||||
|
|
||||||
const liveEvents = room.getLiveTimeline().getEvents();
|
const liveEvents = room.getLiveTimeline().getEvents();
|
||||||
|
|
||||||
const lastTimelineEvent = liveEvents[liveEvents.length - 1];
|
const lastTimelineEvent = liveEvents[liveEvents.length - 1];
|
||||||
@@ -214,16 +234,58 @@ class Notifications extends EventEmitter {
|
|||||||
const total = room.getUnreadNotificationCount('total');
|
const total = room.getUnreadNotificationCount('total');
|
||||||
const highlight = room.getUnreadNotificationCount('highlight');
|
const highlight = room.getUnreadNotificationCount('highlight');
|
||||||
|
|
||||||
const noti = this.getNoti(room.roomId);
|
if (this.getNotiType(room.roomId) === cons.notifs.MUTE) {
|
||||||
this._setNoti(room.roomId, total - noti.total, highlight - noti.highlight);
|
this.deleteNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||||
|
|
||||||
if (this.matrixClient.getSyncState() === 'SYNCING') {
|
if (this.matrixClient.getSyncState() === 'SYNCING') {
|
||||||
this._displayPopupNoti(mEvent, room);
|
this._displayPopupNoti(mEvent, room);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.matrixClient.on('accountData', (mEvent, oldMEvent) => {
|
||||||
|
if (mEvent.getType() === 'm.push_rules') {
|
||||||
|
const override = mEvent?.getContent()?.global?.override;
|
||||||
|
const oldOverride = oldMEvent?.getContent()?.global?.override;
|
||||||
|
if (!override || !oldOverride) return;
|
||||||
|
|
||||||
|
const isMuteToggled = (rule, otherOverride) => {
|
||||||
|
const roomId = rule.rule_id;
|
||||||
|
const room = this.matrixClient.getRoom(roomId);
|
||||||
|
if (room === null) return false;
|
||||||
|
if (room.isSpaceRoom()) return false;
|
||||||
|
|
||||||
|
const isMuted = isMutedRule(rule);
|
||||||
|
if (!isMuted) return false;
|
||||||
|
const isOtherMuted = findMutedRule(otherOverride, roomId);
|
||||||
|
if (isOtherMuted) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutedRules = override.filter((rule) => isMuteToggled(rule, oldOverride));
|
||||||
|
const unMutedRules = oldOverride.filter((rule) => isMuteToggled(rule, override));
|
||||||
|
|
||||||
|
mutedRules.forEach((rule) => {
|
||||||
|
this.emit(cons.events.notifications.MUTE_TOGGLED, rule.rule_id, true);
|
||||||
|
this.deleteNoti(rule.rule_id);
|
||||||
|
});
|
||||||
|
unMutedRules.forEach((rule) => {
|
||||||
|
this.emit(cons.events.notifications.MUTE_TOGGLED, rule.rule_id, false);
|
||||||
|
const room = this.matrixClient.getRoom(rule.rule_id);
|
||||||
|
if (!this.doesRoomHaveUnread(room)) return;
|
||||||
|
const total = room.getUnreadNotificationCount('total');
|
||||||
|
const highlight = room.getUnreadNotificationCount('highlight');
|
||||||
|
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.matrixClient.on('Room.receipt', (mEvent, room) => {
|
this.matrixClient.on('Room.receipt', (mEvent, room) => {
|
||||||
if (mEvent.getType() === 'm.receipt') {
|
if (mEvent.getType() === 'm.receipt') {
|
||||||
|
if (room.isSpaceRoom()) return;
|
||||||
const content = mEvent.getContent();
|
const content = mEvent.getContent();
|
||||||
const readedEventId = Object.keys(content)[0];
|
const readedEventId = Object.keys(content)[0];
|
||||||
const readerUserId = Object.keys(content[readedEventId]['m.read'])[0];
|
const readerUserId = Object.keys(content[readedEventId]['m.read'])[0];
|
||||||
|
|||||||
@@ -36,6 +36,14 @@ class RoomList extends EventEmitter {
|
|||||||
return !this.roomIdToParents.has(roomId);
|
return !this.roomIdToParents.has(roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOrphanSpaces() {
|
||||||
|
return [...this.spaces].filter((roomId) => !this.roomIdToParents.has(roomId));
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrphanRooms() {
|
||||||
|
return [...this.rooms].filter((roomId) => !this.roomIdToParents.has(roomId));
|
||||||
|
}
|
||||||
|
|
||||||
getOrphans() {
|
getOrphans() {
|
||||||
const rooms = [...this.spaces].concat([...this.rooms]);
|
const rooms = [...this.spaces].concat([...this.rooms]);
|
||||||
return rooms.filter((roomId) => !this.roomIdToParents.has(roomId));
|
return rooms.filter((roomId) => !this.roomIdToParents.has(roomId));
|
||||||
@@ -43,13 +51,15 @@ class RoomList extends EventEmitter {
|
|||||||
|
|
||||||
getSpaceChildren(roomId) {
|
getSpaceChildren(roomId) {
|
||||||
const space = this.matrixClient.getRoom(roomId);
|
const space = this.matrixClient.getRoom(roomId);
|
||||||
|
if (space === null) return null;
|
||||||
const mSpaceChild = space?.currentState.getStateEvents('m.space.child');
|
const mSpaceChild = space?.currentState.getStateEvents('m.space.child');
|
||||||
const children = mSpaceChild?.map((mEvent) => {
|
|
||||||
|
const children = [];
|
||||||
|
mSpaceChild.forEach((mEvent) => {
|
||||||
const childId = mEvent.event.state_key;
|
const childId = mEvent.event.state_key;
|
||||||
if (isMEventSpaceChild(mEvent)) return childId;
|
if (isMEventSpaceChild(mEvent)) children.push(childId);
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
return children?.filter((childId) => childId !== null);
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCategorizedSpaces(spaceIds) {
|
getCategorizedSpaces(spaceIds) {
|
||||||
@@ -69,7 +79,7 @@ class RoomList extends EventEmitter {
|
|||||||
else mappedChild.add(childId);
|
else mappedChild.add(childId);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
spaceIds.map(categorizeSpace);
|
spaceIds.forEach(categorizeSpace);
|
||||||
|
|
||||||
return categorized;
|
return categorized;
|
||||||
}
|
}
|
||||||
@@ -89,32 +99,40 @@ class RoomList extends EventEmitter {
|
|||||||
if (parents.size === 0) this.roomIdToParents.delete(roomId);
|
if (parents.size === 0) this.roomIdToParents.delete(roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentSpaces(roomId) {
|
getAllParentSpaces(roomId) {
|
||||||
let parentIds = this.roomIdToParents.get(roomId);
|
const allParents = new Set();
|
||||||
if (parentIds) {
|
|
||||||
[...parentIds].forEach((parentId) => {
|
const addAllParentIds = (rId) => {
|
||||||
parentIds = new Set([...parentIds, ...this.getParentSpaces(parentId)]);
|
if (allParents.has(rId)) return;
|
||||||
});
|
allParents.add(rId);
|
||||||
}
|
|
||||||
return parentIds || new Set();
|
const parents = this.roomIdToParents.get(rId);
|
||||||
|
if (parents === undefined) return;
|
||||||
|
|
||||||
|
parents.forEach((id) => addAllParentIds(id));
|
||||||
|
};
|
||||||
|
addAllParentIds(roomId);
|
||||||
|
allParents.delete(roomId);
|
||||||
|
return allParents;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToSpaces(roomId) {
|
addToSpaces(roomId) {
|
||||||
this.spaces.add(roomId);
|
this.spaces.add(roomId);
|
||||||
const allParentSpaces = this.getParentSpaces(roomId);
|
|
||||||
|
|
||||||
|
const allParentSpaces = this.getAllParentSpaces(roomId);
|
||||||
const spaceChildren = this.getSpaceChildren(roomId);
|
const spaceChildren = this.getSpaceChildren(roomId);
|
||||||
spaceChildren?.forEach((childRoomId) => {
|
spaceChildren?.forEach((childId) => {
|
||||||
if (allParentSpaces.has(childRoomId)) return;
|
if (allParentSpaces.has(childId)) return;
|
||||||
this.addToRoomIdToParents(childRoomId, roomId);
|
this.addToRoomIdToParents(childId, roomId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteFromSpaces(roomId) {
|
deleteFromSpaces(roomId) {
|
||||||
this.spaces.delete(roomId);
|
this.spaces.delete(roomId);
|
||||||
|
|
||||||
const spaceChildren = this.getSpaceChildren(roomId);
|
const spaceChildren = this.getSpaceChildren(roomId);
|
||||||
spaceChildren?.forEach((childRoomId) => {
|
spaceChildren?.forEach((childId) => {
|
||||||
this.removeFromRoomIdToParents(childRoomId, roomId);
|
this.removeFromRoomIdToParents(childId, roomId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,12 +272,17 @@ class RoomList extends EventEmitter {
|
|||||||
|
|
||||||
this.matrixClient.on('RoomState.events', (mEvent, state) => {
|
this.matrixClient.on('RoomState.events', (mEvent, state) => {
|
||||||
if (mEvent.getType() === 'm.space.child') {
|
if (mEvent.getType() === 'm.space.child') {
|
||||||
const { event } = mEvent;
|
const roomId = mEvent.event.room_id;
|
||||||
|
const childId = mEvent.event.state_key;
|
||||||
if (isMEventSpaceChild(mEvent)) {
|
if (isMEventSpaceChild(mEvent)) {
|
||||||
const allParentSpaces = this.getParentSpaces(event.room_id);
|
const allParentSpaces = this.getAllParentSpaces(roomId);
|
||||||
if (allParentSpaces.has(event.state_key)) return;
|
// only add if it doesn't make a cycle
|
||||||
this.addToRoomIdToParents(event.state_key, event.room_id);
|
if (!allParentSpaces.has(childId)) {
|
||||||
} else this.removeFromRoomIdToParents(event.state_key, event.room_id);
|
this.addToRoomIdToParents(childId, roomId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.removeFromRoomIdToParents(childId, roomId);
|
||||||
|
}
|
||||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,19 +154,19 @@ class RoomTimeline extends EventEmitter {
|
|||||||
this._populateAllLinkedEvents(this.activeTimeline);
|
this._populateAllLinkedEvents(this.activeTimeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _reset(eventId) {
|
async _reset() {
|
||||||
if (this.isEncrypted()) await this.decryptAllEventsOfTimeline(this.activeTimeline);
|
if (this.isEncrypted()) await this.decryptAllEventsOfTimeline(this.activeTimeline);
|
||||||
this._populateTimelines();
|
this._populateTimelines();
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
this._listenEvents();
|
this._listenEvents();
|
||||||
}
|
}
|
||||||
this.emit(cons.events.roomTimeline.READY, eventId ?? null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadLiveTimeline() {
|
async loadLiveTimeline() {
|
||||||
this.activeTimeline = this.liveTimeline;
|
this.activeTimeline = this.liveTimeline;
|
||||||
await this._reset();
|
await this._reset();
|
||||||
|
this.emit(cons.events.roomTimeline.READY, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +176,8 @@ class RoomTimeline extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
const eventTimeline = await this.matrixClient.getEventTimeline(timelineSet, eventId);
|
const eventTimeline = await this.matrixClient.getEventTimeline(timelineSet, eventId);
|
||||||
this.activeTimeline = eventTimeline;
|
this.activeTimeline = eventTimeline;
|
||||||
await this._reset(eventId);
|
await this._reset();
|
||||||
|
this.emit(cons.events.roomTimeline.READY, eventId);
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const cons = {
|
const cons = {
|
||||||
version: '1.8.0',
|
version: '1.8.1',
|
||||||
secretKey: {
|
secretKey: {
|
||||||
ACCESS_TOKEN: 'cinny_access_token',
|
ACCESS_TOKEN: 'cinny_access_token',
|
||||||
DEVICE_ID: 'cinny_device_id',
|
DEVICE_ID: 'cinny_device_id',
|
||||||
@@ -107,6 +107,7 @@ const cons = {
|
|||||||
notifications: {
|
notifications: {
|
||||||
NOTI_CHANGED: 'NOTI_CHANGED',
|
NOTI_CHANGED: 'NOTI_CHANGED',
|
||||||
FULL_READ: 'FULL_READ',
|
FULL_READ: 'FULL_READ',
|
||||||
|
MUTE_TOGGLED: 'MUTE_TOGGLED',
|
||||||
},
|
},
|
||||||
roomTimeline: {
|
roomTimeline: {
|
||||||
READY: 'READY',
|
READY: 'READY',
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ class Settings extends EventEmitter {
|
|||||||
if (typeof this.hideNickAvatarEvents === 'boolean') return this.hideNickAvatarEvents;
|
if (typeof this.hideNickAvatarEvents === 'boolean') return this.hideNickAvatarEvents;
|
||||||
|
|
||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
if (settings === null) return false;
|
if (settings === null) return true;
|
||||||
if (typeof settings.hideNickAvatarEvents === 'undefined') return false;
|
if (typeof settings.hideNickAvatarEvents === 'undefined') return true;
|
||||||
return settings.hideNickAvatarEvents;
|
return settings.hideNickAvatarEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user