Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a41440857d | ||
|
|
3b7f71b573 | ||
|
|
0e79db7a3d | ||
|
|
78951a3bc4 | ||
|
|
44318d1ecd | ||
|
|
a6fb44e1ab | ||
|
|
374d2d1962 | ||
|
|
3ad143228d | ||
|
|
4ea14c853e | ||
|
|
38bbc1c6f5 | ||
|
|
9a34daa2bc | ||
|
|
e5e3f5f0a3 | ||
|
|
899a89cb3b | ||
|
|
63cb564818 | ||
|
|
e93511fde7 | ||
|
|
eeac5c6c45 | ||
|
|
f7b48333df | ||
|
|
cb88a6fd9c | ||
|
|
7b95546b80 | ||
|
|
5f9ea6c06a | ||
|
|
d54d1d964d | ||
|
|
868f271588 | ||
|
|
7606c9d325 | ||
|
|
3c66a6a1c1 | ||
|
|
bf900e7468 | ||
|
|
52cbcb9128 | ||
|
|
97e7ba25a6 | ||
|
|
bc9ff23372 | ||
|
|
c176ed8861 | ||
|
|
63c0653006 | ||
|
|
fbf899465d | ||
|
|
603e478794 | ||
|
|
347d23641b | ||
|
|
c4e973e83b | ||
|
|
d22abe300b | ||
|
|
2bab3119b0 | ||
|
|
ea3d7b8892 | ||
|
|
7eb2f78174 | ||
|
|
7db04440ff | ||
|
|
062beb718d | ||
|
|
b8f3cf1b47 | ||
|
|
c280c1339b | ||
|
|
543acc9fff | ||
|
|
55d609c82b | ||
|
|
f144c1cdbd | ||
|
|
1531535151 | ||
|
|
4fe4c8bb03 | ||
|
|
1302682900 | ||
|
|
62e0821be2 | ||
|
|
856e616247 | ||
|
|
71fdba3c4a | ||
|
|
7803bd5c06 | ||
|
|
8dc9e01439 | ||
|
|
1d6ce13a6c | ||
|
|
6a907b1542 | ||
|
|
bef759e358 | ||
|
|
63023c5266 | ||
|
|
b6f2f9998e | ||
|
|
6d9115f593 | ||
|
|
02f7d7c74d | ||
|
|
eeb4ec0e73 | ||
|
|
9efa05d4de | ||
|
|
72a8483a23 | ||
|
|
d4da49512c | ||
|
|
6437970f7f | ||
|
|
bf79618221 | ||
|
|
dbeac1f8eb | ||
|
|
1625b8a665 | ||
|
|
5ab202e5c9 | ||
|
|
2f4fbc9d49 | ||
|
|
613ffefeb3 | ||
|
|
89e93be091 | ||
|
|
9b42077f7a | ||
|
|
81b88477bd | ||
|
|
52376e10a5 | ||
|
|
b8b3f9e7ec | ||
|
|
194eb30042 | ||
|
|
b52aa9ac76 | ||
|
|
c825c41aed | ||
|
|
6d8f704542 | ||
|
|
10a399f7e6 | ||
|
|
d59a118e83 | ||
|
|
07570f9e8a | ||
|
|
90972e938e | ||
|
|
4297eb34c9 | ||
|
|
507f9c3c22 | ||
|
|
33551089ad | ||
|
|
c8301c6ad6 | ||
|
|
c3396780d7 | ||
|
|
8ea2b0b63b | ||
|
|
e170d02bff | ||
|
|
1f872a9b0c | ||
|
|
bcdc1d8915 | ||
|
|
f577435e98 | ||
|
|
bf94550a84 | ||
|
|
a900b04254 | ||
|
|
3fe5a9d222 | ||
|
|
a07a7676b7 | ||
|
|
593cc03962 | ||
|
|
820f255929 | ||
|
|
7db4146c8d | ||
|
|
91a6916f4c | ||
|
|
78374d0301 | ||
|
|
9185ee0cf9 | ||
|
|
e2de51832c | ||
|
|
759f16d5b9 | ||
|
|
d0ddedc2b2 | ||
|
|
4291005161 | ||
|
|
ee144ccb2b | ||
|
|
7fea21f221 | ||
|
|
9a4b40f242 | ||
|
|
c2d353b973 | ||
|
|
8b6cecdbf8 | ||
|
|
83d8f2821a | ||
|
|
0225204b4d | ||
|
|
478957803d | ||
|
|
0181cb26d2 | ||
|
|
2ddb3595c7 | ||
|
|
76e49d1bd0 | ||
|
|
91f1ee748e | ||
|
|
9bddf64510 | ||
|
|
2200ae143e | ||
|
|
80d9a2ca7d | ||
|
|
cbd1bf35c6 | ||
|
|
c1be57b205 | ||
|
|
4848bef0dd | ||
|
|
a8f374dd43 | ||
|
|
d43e41e1ba | ||
|
|
437ac4c59b | ||
|
|
de218409ab | ||
|
|
060ed859d4 | ||
|
|
0783c43169 | ||
|
|
b5a317e021 | ||
|
|
15c1f6dadf | ||
|
|
efda9991f2 | ||
|
|
9fb651a04e | ||
|
|
55c652a02a | ||
|
|
2be706fb3f | ||
|
|
3e2cc8bfae | ||
|
|
a6c339e13a | ||
|
|
b0174f3acc | ||
|
|
a343d9999e | ||
|
|
0a2cca6e40 | ||
|
|
bca00f46a9 | ||
|
|
5848c02d50 | ||
|
|
29ddcfa1f9 | ||
|
|
831bb83f4e | ||
|
|
b8a8babc88 | ||
|
|
4a35aa7272 | ||
|
|
2ded7d9b1f | ||
|
|
19f674cf5f | ||
|
|
790bf5eac3 | ||
|
|
e1adc6a3bc | ||
|
|
bcedab5113 | ||
|
|
adca914d67 | ||
|
|
8583ab19f0 | ||
|
|
1aa16a43ee | ||
|
|
e6661d3b0d | ||
|
|
678e0dc6ac | ||
|
|
bdc10fb729 | ||
|
|
584fa87bbd | ||
|
|
ac155bbf4c | ||
|
|
0f6f65045d | ||
|
|
843b9ddffd | ||
|
|
e283d4cd21 | ||
|
|
4fe02d27c8 | ||
|
|
5a269c68ae | ||
|
|
570a1c85af | ||
|
|
f7ff1ef2bd | ||
|
|
04530a801c | ||
|
|
756cd0921b | ||
|
|
292eee33b9 | ||
|
|
583c834ba8 | ||
|
|
33949dbdb1 | ||
|
|
fd79ea4b9b | ||
|
|
b3bff6b43f | ||
|
|
80aa55b706 | ||
|
|
76c16ce294 | ||
|
|
715e6648ae | ||
|
|
984fad811c | ||
|
|
eafd2c8a13 | ||
|
|
82fb243ffe | ||
|
|
6cbf1b2eac | ||
|
|
043e411a98 | ||
|
|
f93b666bbf | ||
|
|
7e28aa1474 | ||
|
|
1deef51df0 | ||
|
|
6f7934badc | ||
|
|
2f2680be3c | ||
|
|
4cd8f4a94c | ||
|
|
a417980a81 | ||
|
|
af69955801 | ||
|
|
214d49f1d9 | ||
|
|
c4e36a1f97 | ||
|
|
5de6a1bea6 | ||
|
|
74a20a0e14 | ||
|
|
820d08017a | ||
|
|
258afec391 | ||
|
|
0cf5aac591 | ||
|
|
59fd34a4b4 | ||
|
|
1da3d252e8 | ||
|
|
3c1cc59d59 | ||
|
|
1692098d5d | ||
|
|
fbab53af22 | ||
|
|
ce1e263d57 | ||
|
|
9f99320fda | ||
|
|
20e1df43d0 | ||
|
|
728c5434bb | ||
|
|
542ac4f4e1 | ||
|
|
d23fc228d7 | ||
|
|
4ff3e47d54 | ||
|
|
96b22eb557 | ||
|
|
9187107751 | ||
|
|
c6812b5b11 | ||
|
|
adb584623e | ||
|
|
120e8de9d1 | ||
|
|
21726b63f8 | ||
|
|
04f910ee03 | ||
|
|
edace32213 | ||
|
|
5e527e434a | ||
|
|
1d90f7588b | ||
|
|
f8b8a35152 | ||
|
|
fa4c95a9b6 | ||
|
|
a478fc4805 | ||
|
|
febb28e9c4 | ||
|
|
c78a39af50 | ||
|
|
21e6049c16 | ||
|
|
6e418337cc | ||
|
|
48f34053ab | ||
|
|
abfe263750 | ||
|
|
9ba003b16d | ||
|
|
48793f3a95 | ||
|
|
d8cf98fd64 | ||
|
|
78e12d5bee | ||
|
|
bdb8bdf76c | ||
|
|
88b79eb3a5 | ||
|
|
b6428197ac | ||
|
|
a46138c8b9 | ||
|
|
1211ca277b | ||
|
|
e6f395c643 | ||
|
|
1979646b4b | ||
|
|
5c0eb20cb4 | ||
|
|
009966a5c7 | ||
|
|
4427b3b291 | ||
|
|
3dda4d6540 | ||
|
|
c9df0be874 | ||
|
|
ca2627d3cf | ||
|
|
47e6527b0e | ||
|
|
7decbb6eef | ||
|
|
68da1d0551 | ||
|
|
a6f21b6606 | ||
|
|
06a4e0c93b | ||
|
|
0ca1df24ed | ||
|
|
1d12a906d4 | ||
|
|
7bd7518963 | ||
|
|
a9c5765be5 | ||
|
|
2292f63fb6 | ||
|
|
db92b9f5ff | ||
|
|
f538639882 | ||
|
|
56bc8c2890 | ||
|
|
1cba4d3fa7 | ||
|
|
4c7820ceac | ||
|
|
118dcd8fa0 | ||
|
|
a142ade923 | ||
|
|
57ab10a87c | ||
|
|
8c1c3cd634 | ||
|
|
2d3634d6bf | ||
|
|
217f29f068 | ||
|
|
58c3eee153 | ||
|
|
d9e1fb620b | ||
|
|
a3f5b92484 | ||
|
|
8b96e0ab98 | ||
|
|
eef2d451b7 | ||
|
|
371e66a6df | ||
|
|
0d12144744 | ||
|
|
ba39724813 | ||
|
|
af6e6bfc67 | ||
|
|
315b5a1048 | ||
|
|
c410d4e9f5 | ||
|
|
299d976622 | ||
|
|
e8587f99c9 | ||
|
|
63ab96b71b | ||
|
|
e998438135 | ||
|
|
5fd7d64d21 | ||
|
|
f05037c7d6 | ||
|
|
d0fd654bf7 | ||
|
|
7165bd91cd | ||
|
|
d3431a5d53 | ||
|
|
fa6c865000 | ||
|
|
fd680a93e0 | ||
|
|
38b604ad41 | ||
|
|
2ca67bb61a | ||
|
|
95b814b751 | ||
|
|
9963f3f988 | ||
|
|
fde7d4a25a | ||
|
|
895b2c4f19 | ||
|
|
427ea9baab | ||
|
|
df718e4498 | ||
|
|
00956f5bba | ||
|
|
e48d216d79 | ||
|
|
489f178c7c | ||
|
|
3bd4eda789 | ||
|
|
fc6c7b8dc6 | ||
|
|
deef1f2c8a | ||
|
|
38bd38a487 | ||
|
|
40de64078a | ||
|
|
780bd5e65a | ||
|
|
2cd74b4ea9 | ||
|
|
0cd3df391e | ||
|
|
854d2b4c27 | ||
|
|
7227fc7c43 | ||
|
|
73dcb44121 | ||
|
|
54fd394ef5 | ||
|
|
fda71166df | ||
|
|
69b6055353 | ||
|
|
1bdd0449e0 | ||
|
|
a6fdf9010b | ||
|
|
941dae0625 | ||
|
|
4a715bfd17 | ||
|
|
0b70c7e490 | ||
|
|
0539836714 | ||
|
|
c08b0e654b | ||
|
|
b3cb48319a | ||
|
|
44553cc375 | ||
|
|
fbe287a702 | ||
|
|
5863dcdf67 | ||
|
|
f77bee25ef | ||
|
|
c11328a064 | ||
|
|
d04de2fba0 | ||
|
|
d2b435618c | ||
|
|
7525bb78e5 | ||
|
|
2075a572fe | ||
|
|
73723ba6ba | ||
|
|
0791820a6c | ||
|
|
931f352873 | ||
|
|
7c7d2e0fa4 | ||
|
|
3372fb6f74 | ||
|
|
bc856269ff | ||
|
|
06bae231ef | ||
|
|
65a0edc3a6 | ||
|
|
b7c322d473 | ||
|
|
0776a04362 | ||
|
|
e51fc5a585 | ||
|
|
3afc068a02 | ||
|
|
5cdad44abf | ||
|
|
43762df998 | ||
|
|
95228c6dd9 | ||
|
|
205fcf8487 | ||
|
|
336e8921ee | ||
|
|
ef149b9fcf | ||
|
|
766b4c13c3 | ||
|
|
f5605258e3 | ||
|
|
2ba4d2f2b7 | ||
|
|
2e050c066e | ||
|
|
3f83514427 | ||
|
|
8c227843c9 | ||
|
|
ba084c0a10 | ||
|
|
3fdd42706d | ||
|
|
b49b51a671 | ||
|
|
e5bb386dd2 | ||
|
|
2867bb3bc3 | ||
|
|
416fd02069 | ||
|
|
581963cfb4 | ||
|
|
3b14543e66 | ||
|
|
5dd2194eba | ||
|
|
5b7880f817 | ||
|
|
bafe1c5781 | ||
|
|
d760be58c3 | ||
|
|
3da9b70632 | ||
|
|
b7c5902f67 | ||
|
|
9a22b25564 | ||
|
|
44c3dec9dc | ||
|
|
87f3afd8fd | ||
|
|
53f1129242 | ||
|
|
74216f75e2 | ||
|
|
ed8eca0c1d | ||
|
|
dc8e6e53c7 | ||
|
|
989ab5a432 | ||
|
|
ec26c03d58 | ||
|
|
3b1b3387e7 | ||
|
|
62c03d1334 | ||
|
|
51e12184d7 | ||
|
|
8c01eb9c00 | ||
|
|
bf264d5add | ||
|
|
8b3bd38bad | ||
|
|
ba0de8800a | ||
|
|
cd5ae4cb7f | ||
|
|
6575542281 | ||
|
|
bf2559da80 | ||
|
|
fedc207de2 | ||
|
|
0d58478a73 | ||
|
|
abf24d1942 | ||
|
|
afe3f2f3f3 | ||
|
|
1d1cb567da | ||
|
|
699bbee544 | ||
|
|
9c54915e73 | ||
|
|
ed4390b99d | ||
|
|
039d9bae68 | ||
|
|
93ab48ac9a | ||
|
|
c95e312acb | ||
|
|
9279bc7060 | ||
|
|
fe61576dcd | ||
|
|
3f6e3074f2 | ||
|
|
53a8e2aa57 | ||
|
|
44ab6f181c | ||
|
|
3a3a830706 | ||
|
|
1a6e3e73c5 | ||
|
|
7d508e5a7d | ||
|
|
370c224d3a | ||
|
|
7bce501069 | ||
|
|
9d15445eba | ||
|
|
16ee13f1f7 | ||
|
|
8d25eb0acd | ||
|
|
9cb13a91cd | ||
|
|
30f8930773 | ||
|
|
05eaa8d3e0 | ||
|
|
a32ffdf6d4 | ||
|
|
0f97de1b09 | ||
|
|
e8d6ccec9a | ||
|
|
005434f79b | ||
|
|
fe997d8b01 | ||
|
|
7291932a0b | ||
|
|
49ade03a9a | ||
|
|
dd6dbd25da | ||
|
|
50bf90fada | ||
|
|
abb81b6390 | ||
|
|
36da3d3cba | ||
|
|
ae71a99aa4 | ||
|
|
a7034d6351 | ||
|
|
6010b4c252 | ||
|
|
8330f4fba9 | ||
|
|
dc6e153b92 | ||
|
|
a2655ee6a5 | ||
|
|
13248962af | ||
|
|
b698982186 | ||
|
|
5a299b21c5 | ||
|
|
bb90f11ec8 | ||
|
|
12369ba2ec |
No files matched your search
@@ -1,5 +1,2 @@
|
||||
webpack.common.js
|
||||
webpack.dev.js
|
||||
webpack.prod.js
|
||||
experiment
|
||||
node_modules
|
||||
@@ -4,25 +4,56 @@ module.exports = {
|
||||
es2021: true,
|
||||
},
|
||||
extends: [
|
||||
'plugin:react/recommended',
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
'airbnb',
|
||||
'prettier',
|
||||
],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 12,
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: [
|
||||
'react',
|
||||
'@typescript-eslint'
|
||||
],
|
||||
rules: {
|
||||
'linebreak-style': 0,
|
||||
'no-underscore-dangle': 0,
|
||||
|
||||
"import/prefer-default-export": "off",
|
||||
"import/extensions": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{
|
||||
devDependencies: true,
|
||||
},
|
||||
],
|
||||
|
||||
'react/no-unstable-nested-components': [
|
||||
'error',
|
||||
{ allowAsProps: true },
|
||||
],
|
||||
"react/jsx-filename-extension": [
|
||||
"error",
|
||||
{
|
||||
extensions: [".tsx", ".jsx"],
|
||||
},
|
||||
],
|
||||
|
||||
"react/require-default-props": "off",
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "error",
|
||||
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
},
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
github: ajbura
|
||||
liberapay: ajbura
|
||||
open_collective: cinny
|
||||
liberapay: ajbura
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Describe the bug
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
#### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
#### Expected behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
#### Screenshots
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
#### Desktop (please complete the following information):
|
||||
- OS: [e.g. Windows, MacOS]
|
||||
- Browser: [e.g. chrome, firefox]
|
||||
- Version: [e.g. 3.22]
|
||||
- Matrix homeserver: [e.g. matrix.org]
|
||||
|
||||
#### Additional context
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,57 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Report a bug
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## First of all
|
||||
1. Please search for [existing issues](https://github.com/ajbura/cinny/issues?q=is%3Aissue) about this problem first.
|
||||
2. Make sure Cinny is up to date.
|
||||
3. Make sure it's an issue with Cinny and not something else you are using.
|
||||
4. Remember to be friendly.
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear description of what the bug is. Include screenshots if applicable.
|
||||
placeholder: Bug description
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. Go to ...
|
||||
2. Click on ...
|
||||
3. See error
|
||||
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A clear description of what you expected to happen.
|
||||
|
||||
- type: textarea
|
||||
id: info
|
||||
attributes:
|
||||
label: Platform and versions
|
||||
description: "Provide OS, browser and Cinny version with your Homeserver."
|
||||
placeholder: |
|
||||
1. OS: [e.g. Windows 10, MacOS]
|
||||
2. Browser: [e.g. chrome 99.5, firefox 97.2]
|
||||
3. Cinny version: [e.g. 1.8.1 (app.cinny.in)]
|
||||
4. Matrix homeserver: [e.g. matrix.org]
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here.
|
||||
@@ -0,0 +1,4 @@
|
||||
contact_links:
|
||||
- name: 💬 Matrix Chat
|
||||
url: https://matrix.to/#/#cinny:matrix.org
|
||||
about: Ask questions and talk to other Cinny users and the maintainers
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Is your feature request related to a problem? Please describe.
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
#### Describe the solution you'd like
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
#### Describe alternatives you've considered
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
#### Additional context
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,33 @@
|
||||
name: 💡 Feature Request
|
||||
description: Suggest an idea
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Describe the problem
|
||||
description: A clear description of the problem this feature would solve
|
||||
placeholder: "I'm always frustrated when..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: "Describe the solution you'd like"
|
||||
description: A clear description of what change you would like
|
||||
placeholder: "I would like to..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives considered
|
||||
description: "Any alternative solutions you've considered"
|
||||
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here.
|
||||
@@ -1,15 +1,13 @@
|
||||
<!-- Please read https://github.com/ajbura/cinny/CONTRIBUTING.md before submitting your pull request -->
|
||||
<!-- Please read https://github.com/ajbura/cinny/blob/dev/CONTRIBUTING.md before submitting your pull request -->
|
||||
|
||||
### Description
|
||||
<!-- Please include a summary of the change. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
|
||||
|
||||
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
|
||||
|
||||
Fixes # (issue)
|
||||
Fixes #
|
||||
|
||||
#### Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
# Docs: <https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/customizing-dependency-updates>
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "tuesday"
|
||||
time: "01:00"
|
||||
timezone: "Asia/Kolkata"
|
||||
open-pull-requests-limit: 15
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "tuesday"
|
||||
time: "01:00"
|
||||
timezone: "Asia/Kolkata"
|
||||
open-pull-requests-limit: 5
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "tuesday"
|
||||
time: "01:00"
|
||||
timezone: "Asia/Kolkata"
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "tuesday"
|
||||
time: "01:00"
|
||||
timezone: "Asia/Kolkata"
|
||||
open-pull-requests-limit: 5
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base",
|
||||
":dependencyDashboardApproval"
|
||||
],
|
||||
"labels": [ "Dependencies" ],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": [ "lockFileMaintenance" ]
|
||||
}
|
||||
],
|
||||
"lockFileMaintenance": { "enabled": true },
|
||||
"dependencyDashboard": true
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: 'Build pull request'
|
||||
name: Build pull request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -6,34 +6,33 @@ on:
|
||||
|
||||
jobs:
|
||||
build-pull-request:
|
||||
name: Build pull request
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PR_NUMBER: ${{github.event.number}}
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3.0.0
|
||||
- name: Build app
|
||||
run: npm ci && npm run build
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3.0.0
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.2.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
name: previewbuild
|
||||
node-version: 18.12.1
|
||||
cache: "npm"
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build app
|
||||
run: npm run build
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: preview
|
||||
path: dist
|
||||
retention-days: 1
|
||||
- name: Get PR info
|
||||
uses: actions/github-script@v6.0.0
|
||||
- name: Save pr number
|
||||
run: echo ${PR_NUMBER} > ./pr.txt
|
||||
- name: Upload pr number
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
script: |
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/pr.json', JSON.stringify(context.payload.pull_request));
|
||||
- name: Upload PR Info
|
||||
uses: actions/upload-artifact@v3.0.0
|
||||
with:
|
||||
name: pr.json
|
||||
path: pr.json
|
||||
name: pr
|
||||
path: ./pr.txt
|
||||
retention-days: 1
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v2.10.0
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
@@ -1,78 +1,56 @@
|
||||
name: Upload Preview Build to Netlify
|
||||
name: Deploy PR to Netlify
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build pull request"]
|
||||
types:
|
||||
- completed
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
get-build-and-deploy:
|
||||
deploy-pull-request:
|
||||
name: Deploy pull request
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
# 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)
|
||||
# so instead we get this mess:
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v6.0.0
|
||||
- name: Download pr number
|
||||
uses: dawidd6/action-download-artifact@e6e25ac3a2b93187502a8be1ef9e9603afc34925
|
||||
with:
|
||||
script: |
|
||||
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{github.event.workflow_run.id }},
|
||||
});
|
||||
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "previewbuild"
|
||||
})[0];
|
||||
var download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/previewbuild.zip', Buffer.from(download.data));
|
||||
var prInfoArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "pr.json"
|
||||
})[0];
|
||||
var download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: prInfoArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/pr.json.zip', Buffer.from(download.data));
|
||||
- name: Extract Artifacts
|
||||
run: unzip -d dist previewbuild.zip && rm previewbuild.zip && unzip pr.json.zip && rm pr.json.zip
|
||||
- name: 'Read PR Info'
|
||||
id: readctx
|
||||
uses: actions/github-script@v6.0.0
|
||||
workflow: ${{ github.event.workflow.id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
name: pr
|
||||
- name: Output pr number
|
||||
id: pr
|
||||
run: echo "id=$(<pr.txt)" >> $GITHUB_OUTPUT
|
||||
- name: Download artifact
|
||||
uses: dawidd6/action-download-artifact@e6e25ac3a2b93187502a8be1ef9e9603afc34925
|
||||
with:
|
||||
script: |
|
||||
var fs = require('fs');
|
||||
var pr = JSON.parse(fs.readFileSync('${{github.workspace}}/pr.json'));
|
||||
console.log(`::set-output name=prnumber::${pr.number}`);
|
||||
workflow: ${{ github.event.workflow.id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
name: preview
|
||||
path: dist
|
||||
- name: Deploy to Netlify
|
||||
id: netlify
|
||||
uses: nwtgck/actions-netlify@v1.2.3
|
||||
uses: nwtgck/actions-netlify@5da65c9f74c7961c5501a3ba329b8d0912f39c03
|
||||
with:
|
||||
publish-dir: dist
|
||||
deploy-message: "Deploy from GitHub Actions"
|
||||
deploy-message: "Deploy PR ${{ steps.pr.outputs.id }}"
|
||||
alias: ${{ steps.pr.outputs.id }}
|
||||
# These don't work because we're in workflow_run
|
||||
enable-pull-request-comment: false
|
||||
enable-commit-comment: false
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_PR_CINNY }}
|
||||
timeout-minutes: 1
|
||||
- name: Edit PR Description
|
||||
uses: velas/pr-description@v1.0.1
|
||||
- name: Comment preview on PR
|
||||
uses: thollander/actions-comment-pull-request@c22fb302208b7b170d252a61a505d2ea27245eff
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
pull-request-number: ${{ steps.readctx.outputs.prnumber }}
|
||||
description-message: |
|
||||
pr_number: ${{ steps.pr.outputs.id }}
|
||||
comment_tag: ${{ steps.pr.outputs.id }}
|
||||
message: |
|
||||
Preview: ${{ steps.netlify.outputs.deploy-url }}
|
||||
⚠️ Exercise caution. Use test accounts. ⚠️
|
||||
⚠️ Exercise caution. Use test accounts. ⚠️
|
||||
@@ -0,0 +1,19 @@
|
||||
name: 'Docker check'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'Dockerfile'
|
||||
- '.github/workflows/docker-pr.yml'
|
||||
|
||||
jobs:
|
||||
docker-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.2.0
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
@@ -0,0 +1,26 @@
|
||||
name: NPM Lockfile Changes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'package-lock.json'
|
||||
|
||||
jobs:
|
||||
lockfile_changes:
|
||||
runs-on: ubuntu-latest
|
||||
# Permission overwrite is required for Dependabot PRs, see "Common issues" below.
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.2.0
|
||||
- name: NPM Lockfile Changes
|
||||
uses: codepunkt/npm-lockfile-changes@b40543471c36394409466fdb277a73a0856d7891
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Optional inputs, can be deleted safely if you are happy with default values.
|
||||
collapsibleThreshold: 25
|
||||
failOnDowngrade: false
|
||||
path: package-lock.json
|
||||
updateComment: true
|
||||
@@ -1,22 +0,0 @@
|
||||
name: 'Deploy to Netlify (dev)'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
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_SITE2_ID }}
|
||||
BUILD_DIRECTORY: "dist"
|
||||
NETLIFY_DEPLOY_MESSAGE: "Dev deploy v${{ github.ref }}"
|
||||
NETLIFY_DEPLOY_TO_PROD: true
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Deploy to Netlify (dev)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
deploy-to-netlify:
|
||||
name: Deploy to Netlify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.2.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: 18.12.1
|
||||
cache: "npm"
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build app
|
||||
run: npm run build
|
||||
- name: Deploy to Netlify
|
||||
uses: nwtgck/actions-netlify@5da65c9f74c7961c5501a3ba329b8d0912f39c03
|
||||
with:
|
||||
publish-dir: dist
|
||||
deploy-message: "Dev deploy ${{ github.sha }}"
|
||||
enable-commit-comment: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
production-deploy: true
|
||||
github-deployment-environment: nightly
|
||||
github-deployment-description: 'Nightly deployment on each commit to dev branch'
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_DEV }}
|
||||
timeout-minutes: 1
|
||||
@@ -1,56 +0,0 @@
|
||||
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 }}
|
||||
@@ -0,0 +1,97 @@
|
||||
name: Production deploy
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
deploy-and-tarball:
|
||||
name: Netlify deploy and tarball
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.2.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: 18.12.1
|
||||
cache: "npm"
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build app
|
||||
run: npm run build
|
||||
- name: Deploy to Netlify
|
||||
uses: nwtgck/actions-netlify@5da65c9f74c7961c5501a3ba329b8d0912f39c03
|
||||
with:
|
||||
publish-dir: dist
|
||||
deploy-message: "Prod deploy ${{ github.ref_name }}"
|
||||
enable-commit-comment: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
production-deploy: true
|
||||
github-deployment-environment: stable
|
||||
github-deployment-description: 'Stable deployment on each release'
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_APP }}
|
||||
timeout-minutes: 1
|
||||
- name: Get version from tag
|
||||
id: vars
|
||||
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
|
||||
- name: Create tar.gz
|
||||
run: tar -czvf cinny-${{ steps.vars.outputs.tag }}.tar.gz dist
|
||||
- name: Sign tar.gz
|
||||
run: |
|
||||
echo '${{ secrets.GNUPG_KEY }}' | gpg --batch --import
|
||||
# Sadly a few lines in the private key match a few lines in the public key,
|
||||
# As a result just --export --armor gives us a few lines replaced with ***
|
||||
# making it useless for importing the signing key. Instead, we dump it as
|
||||
# non-armored and hex-encode it so that its printable.
|
||||
echo "PGP Signing key, in raw PGP format in hex. Import with cat ... | xxd -r -p - | gpg --import"
|
||||
gpg --export | xxd -p
|
||||
echo '${{ secrets.GNUPG_PASSPHRASE }}' | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --armor --detach-sign cinny-${{ steps.vars.outputs.tag }}.tar.gz
|
||||
- name: Upload tagged release
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844
|
||||
with:
|
||||
files: |
|
||||
cinny-${{ steps.vars.outputs.tag }}.tar.gz
|
||||
cinny-${{ steps.vars.outputs.tag }}.tar.gz.asc
|
||||
|
||||
publish-image:
|
||||
name: Push Docker image to Docker Hub, ghcr
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.2.0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2.1.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.2.1
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login to the Container registry
|
||||
uses: docker/login-action@v2.1.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4.1.1
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_USERNAME }}/cinny
|
||||
ghcr.io/${{ github.repository }}
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -0,0 +1,3 @@
|
||||
legacy-peer-deps=true
|
||||
save-exact=true
|
||||
@matrix-org:registry=https://gitlab.matrix.org/api/v4/projects/27/packages/npm/
|
||||
@@ -0,0 +1,6 @@
|
||||
dist
|
||||
node_modules
|
||||
package.json
|
||||
package-lock.json
|
||||
LICENSE
|
||||
README.md
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"printWidth": 100,
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
@@ -36,4 +36,9 @@ It is not always possible to phrase every change in such a manner, but it is des
|
||||
|
||||
Also, we use [ESLint](https://eslint.org/) for clean and stylistically consistent code syntax, so make sure your pull request follow it.
|
||||
|
||||
**For any query or design discussion, join our [Matrix room](https://matrix.to/#/#cinny:matrix.org).**
|
||||
**For any query or design discussion, join our [Matrix room](https://matrix.to/#/#cinny:matrix.org).**
|
||||
|
||||
## Helpful links
|
||||
- [BEM methodology](http://getbem.com/introduction/)
|
||||
- [Atomic design](https://bradfrost.com/blog/post/atomic-web-design/)
|
||||
- [Matrix JavaScript SDK documentation](https://matrix-org.github.io/matrix-js-sdk/index.html)
|
||||
@@ -1,16 +1,16 @@
|
||||
## Builder
|
||||
FROM node:17.7.1-alpine3.15 as builder
|
||||
FROM node:18.12.1-alpine3.15 as builder
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY package.json package-lock.json /src/
|
||||
COPY .npmrc package.json package-lock.json /src/
|
||||
RUN npm ci
|
||||
COPY . /src/
|
||||
RUN npm run build
|
||||
|
||||
|
||||
## App
|
||||
FROM nginx:1.21.6-alpine
|
||||
FROM nginx:1.23.3-alpine
|
||||
|
||||
COPY --from=builder /src/dist /app
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Ajay Bura (ajbura) and other contributors
|
||||
Copyright (c) 2021-present Ajay Bura (ajbura)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
@@ -1,65 +1,120 @@
|
||||
# Cinny
|
||||
<p>
|
||||
<a href="https://github.com/ajbura/cinny/releases">
|
||||
<img alt="GitHub release downloads" src="https://img.shields.io/github/downloads/ajbura/cinny/total?logo=github&style=social"></a>
|
||||
<a href="https://hub.docker.com/r/ajbura/cinny">
|
||||
<img alt="DockerHub downloads" src="https://img.shields.io/docker/pulls/ajbura/cinny?logo=docker&style=social"></a>
|
||||
<a href="https://fosstodon.org/@cinnyapp">
|
||||
<img alt="Follow on Mastodon" src="https://img.shields.io/mastodon/follow/106845779685925461?domain=https%3A%2F%2Ffosstodon.org&logo=mastodon&style=social"></a>
|
||||
<a href="https://twitter.com/intent/follow?screen_name=cinnyapp">
|
||||
<img alt="Follow on Twitter" src="https://img.shields.io/twitter/follow/cinnyapp?logo=twitter&style=social"></a>
|
||||
<a href="https://cinny.in/#sponsor">
|
||||
<img alt="Sponsor Cinny" src="https://img.shields.io/opencollective/all/cinny?logo=opencollective&style=social"></a>
|
||||
</p>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [About](#about)
|
||||
- [Getting Started](https://cinny.in)
|
||||
- [Contributing](./CONTRIBUTING.md)
|
||||
A Matrix client focusing primarily on simple, elegant and secure interface. The main goal is to have an instant messaging application that is easy on people and has a modern touch.
|
||||
- [Roadmap](https://github.com/ajbura/cinny/projects/11)
|
||||
- [Contributing](./CONTRIBUTING.md)
|
||||
|
||||
## About <a name = "about"></a>
|
||||
## Getting started
|
||||
Web app is available at https://app.cinny.in and gets updated on each new release. The `dev` branch is continuously deployed at https://dev.cinny.in but keep in mind that it could have things broken.
|
||||
|
||||
Cinny is a [Matrix](https://matrix.org) client focusing primarily on simple, elegant and secure interface.
|
||||
You can also download our desktop app from [cinny-desktop repository](https://github.com/cinnyapp/cinny-desktop).
|
||||
|
||||

|
||||
To host Cinny on your own, download tarball of the app from [GitHub release](https://github.com/cinnyapp/cinny/releases/latest).
|
||||
You can serve the application with a webserver of your choice by simply copying `dist/` directory to the webroot.
|
||||
To set default Homeserver on login and register page, place a customized [`config.json`](config.json) in webroot of your choice.
|
||||
|
||||
## Building and Running
|
||||
Alternatively you can just pull the [DockerHub image](https://hub.docker.com/r/ajbura/cinny) by:
|
||||
```
|
||||
docker pull ajbura/cinny
|
||||
```
|
||||
or [ghcr image](https://github.com/cinnyapp/cinny/pkgs/container/cinny) by:
|
||||
```
|
||||
docker pull ghcr.io/cinnyapp/cinny:latest
|
||||
```
|
||||
|
||||
### Running pre-compiled
|
||||
<details>
|
||||
<summary>PGP Public Key to verify tarball</summary>
|
||||
|
||||
A tarball of pre-compiled version of the app is provided with each [release](https://github.com/ajbura/cinny/releases).
|
||||
You can serve the application with a webserver of your choosing by simply copying `dist/` directory to the webroot.
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
### Building from source
|
||||
mQGNBGJw/g0BDAC8qQeLqDMzYzfPyOmRlHVEoguVTo+eo1aVdQH2X7OELdjjBlyj
|
||||
6d6c1adv/uF2g83NNMoQY7GEeHjRnXE4m8kYSaarb840pxrYUagDc0dAbJOGaCBY
|
||||
FKTo7U1Kvg0vdiaRuus0pvc1NVdXSxRNQbFXBSwduD+zn66TI3HfcEHNN62FG1cE
|
||||
K1jWDwLAU0P3kKmj8+CAc3h9ZklPu0k/+t5bf/LJkvdBJAUzGZpehbPL5f3u3BZ0
|
||||
leZLIrR8uV7PiV5jKFahxlKR5KQHld8qQm+qVhYbUzpuMBGmh419I6UvTzxuRcvU
|
||||
Frn9ttCEzV55Y+so4X2e4ZnB+5gOnNw+ecifGVdj/+UyWnqvqqDvLrEjjK890nLb
|
||||
Pil4siecNMEpiwAN6WSmKpWaCwQAHEGDVeZCc/kT0iYfj5FBcsTVqWiO6eaxkUlm
|
||||
jnulqWqRrlB8CJQQvih/g//uSEBdzIibo+ro+3Jpe120U/XVUH62i9HoRQEm6ADG
|
||||
4zS5hIq4xyA8fL8AEQEAAbQdQ2lubnlBcHAgPGNpbm55YXBwQGdtYWlsLmNvbT6J
|
||||
AdQEEwEIAD4WIQSRri2MHidaaZv+vvuUMwx6UK/M8wUCYnD+DQIbAwUJA8JnAAUL
|
||||
CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCUMwx6UK/M88ApC/9HAdbum1lYBC0s
|
||||
1k7GwP2A7B4sQtBWjy771BzybWlHeaeG+BGJwg4YiuowXZMm5dubFJFoI/CfeY07
|
||||
B5aK40/bmT6Xcfkp0VA74c1wUpubBUEJN7tH5HG/OGd9BKeq9E/HHtVaJLVT1k3w
|
||||
Rhv9VuHO6nR30EEp7IDthftotl5S4lio3+W0pKk4TAKV8vjaCNp3y/lAHzoP1BU9
|
||||
bUSao+7GXVeArKBjuqxN+t1uuiaxPH4L0oe2pMVjTig04zGJM5fTVoly859MEcC/
|
||||
R7Taq9RWGfXFmgCXy8Dviz3eOD90vqpCzhX4+ypK0cp2X0UwhMH4dpKUzExmdbhl
|
||||
eBO5GcHB4VxvloRBNf9/Lr7YOTgWejMUw+MlhZE2RE8unfW1LnM/cjL4dhXzO/XB
|
||||
FUHHNq8d6d4e02rfWqw7mZo2/NVJgFRcvzw2rgx7w7CKtCNwF4lNjUetB2waZzDb
|
||||
fAE0kwhK4Iuwvy12JOBzL0Yy9MxANtwUryr/LQz9AmdT4Rwnp0S5AY0EYnD+DQEM
|
||||
ANOu/d6ZMF8bW+Df9RDCUQKytbaZfa+ZbIHBus7whCD/SQMOhPKntv3HX7SmMCs+
|
||||
5i27kJMu4YN623JCS7hdCoXVO1R5kXCEcneW/rPBMDutaM472YvIWMIqK9Wwl5+0
|
||||
Piu2N+uTkKhe9uS2u7eN+Khef3d7xfjGRxoppM+xI9dZO+jhYiy8LuC0oBohTjJq
|
||||
QPqfGDpowBwRkkOsGz/XVcesJ1Pzg4bKivTS9kZjZSyT9RRSY8As0sVUN57AwYul
|
||||
s1+eh00n/tVpi2Jj9pCm7S0csSXvXj8v2OTdK1jt4YjpzR0/rwh4+/xlOjDjZEqH
|
||||
vMPhpzpbgnwkxZ3X8BFne9dJ3maC5zQ3LAeCP5m1W0hXzagYhfyjo74slJgD1O8c
|
||||
LDf2Oxc5MyM8Y/UK497zfqSPfgT3NhQmhHzk83DjXw3I6Z3A3U+Jp61w0eBRI1nx
|
||||
H1UIG+gldcAKUTcfwL0lghoT3nmi9JAbvek0Smhz00Bbo8/dx8vwQRxDUxlt7Exx
|
||||
NwARAQABiQG8BBgBCAAmFiEEka4tjB4nWmmb/r77lDMMelCvzPMFAmJw/g0CGwwF
|
||||
CQPCZwAACgkQlDMMelCvzPPT7Qv8CjXUEhphZFLwpBfaNOzRNfIXJST9aDit8zHW
|
||||
IMmfSpORVfpU71IyIB3o/DtTUPwCeb8nvNJs7aj1QT1ZUSsqFa3yY2S16V/g8+WN
|
||||
sHca6oDSc1J+A0eEpEL1HbG1b5OPBC0AeGvvMOoqrbqThBZVKg1Jc/0SD3cvKElv
|
||||
aHeCZCNNmfcZ2Ib4HYhhc8//ZtC9TeI+5J/YesctY1M12EoWMxMrc27Y3P5Pa0BI
|
||||
Uc3qxWggPq1vOFYsEshL0w99HyJvREJmQA7Fa0crV+rICxyrBxJeNnEvjH/0KCBU
|
||||
LCkEonLY1QwrxyeeV3VpxGE3zHHE3azOdAjTIoAdzX5f/qhbgYlM68GL2f8xdDkp
|
||||
O0igSGHWhO4F8BfmE7IOTx1Bi7daczp8nCFxh73cKpKB0RUsd9xxrqYpovjmEAlo
|
||||
w7aHpdzt64NQcsrbK10OSVDF3gFa9Vz20/NQvdUrp8jGmAb/8+nYqI94Jsc28H36
|
||||
UeGsouhyuITLwEhScounZDqop+Dx
|
||||
=Zg+6
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
</details>
|
||||
|
||||
Execute the following commands to compile the app from its source code:
|
||||
## Local development
|
||||
> We recommend using a version manager as versions change very quickly. You will likely need to switch
|
||||
between multiple Node.js versions based on the needs of different projects you're working on. [NVM on windows](https://github.com/coreybutler/nvm-windows#installation--upgrades) on Windows and [nvm](https://github.com/nvm-sh/nvm) on Linux/macOS are pretty good choices. Also recommended nodejs version Hydrogen LTS (v18).
|
||||
|
||||
Execute the following commands to start a development server:
|
||||
```sh
|
||||
npm ci # Installs all dependencies
|
||||
npm start # Serve a development version
|
||||
```
|
||||
|
||||
To build the app:
|
||||
```sh
|
||||
npm run build # Compiles the app into the dist/ directory
|
||||
```
|
||||
|
||||
You can then copy the files to a webserver's webroot of your choice.
|
||||
|
||||
To serve a development version of the app locally for testing, you may also use the command `npm start`.
|
||||
|
||||
### Running with Docker
|
||||
|
||||
This repository includes a Dockerfile, which builds the application from source and serves it with Nginx on port 80. To
|
||||
use this locally, you can build the container like so:
|
||||
|
||||
```
|
||||
docker build -t cinny:latest .
|
||||
```
|
||||
|
||||
You can then run the container you've built with a command similar to this:
|
||||
|
||||
```
|
||||
docker run -p 8080:80 cinny:latest
|
||||
```
|
||||
|
||||
This will forward your `localhost` port 8080 to the container's port 80. You can visit the app in your browser by
|
||||
navigating to `http://localhost:8080`.
|
||||
This will forward your `localhost` port 8080 to the container's port 80. You can visit the app in your browser by navigating to `http://localhost:8080`.
|
||||
|
||||
Alternatively you can just pull the [DockerHub image](https://hub.docker.com/r/ajbura/cinny) by `docker pull ajbura/cinny`.
|
||||
|
||||
### Configuring default Homeserver
|
||||
|
||||
To set default Homeserver on login and register page, place a customized [`config.json`](config.json) in webroot of your choice.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2021 Ajay Bura (ajbura) and contributors
|
||||
Copyright (c) 2021-present Ajay Bura (ajbura)
|
||||
|
||||
Code licensed under the MIT License: <http://opensource.org/licenses/MIT>
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"defaultHomeserver": 4,
|
||||
"defaultHomeserver": 3,
|
||||
"homeserverList": [
|
||||
"converser.eu",
|
||||
"envs.net",
|
||||
"halogen.city",
|
||||
"kde.org",
|
||||
"matrix.icarus.today",
|
||||
"matrix.org",
|
||||
"chat.mozilla.org"
|
||||
]
|
||||
"mozilla.org"
|
||||
],
|
||||
"allowCustomHomeservers": true
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Cinny</title>
|
||||
<meta name="name" content="Cinny" />
|
||||
<meta name="author" content="Ajay Bura" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A Matrix client where you can enjoy the conversation using simple, elegant and secure interface protected by e2ee with the power of open source."
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="cinny, cinnyapp, cinnychat, matrix, matrix client, matrix.org, element"
|
||||
/>
|
||||
|
||||
<meta property="og:title" content="Cinny" />
|
||||
<meta property="og:url" content="https://cinny.in" />
|
||||
<meta property="og:image" content="https://cinny.in/assets/favicon-48x48.png" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="A Matrix client where you can enjoy the conversation using simple, elegant and secure interface protected by e2ee with the power of open source."
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
<link id="favicon" rel="shortcut icon" href="./public/favicon.ico" />
|
||||
|
||||
<link rel="manifest" href="./manifest.json" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="application-name" content="Cinny" />
|
||||
<meta name="apple-mobile-web-app-title" content="Cinny" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="57x57"
|
||||
href="./public/res/apple/apple-touch-icon-57x57.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="60x60"
|
||||
href="./public/res/apple/apple-touch-icon-60x60.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="72x72"
|
||||
href="./public/res/apple/apple-touch-icon-72x72.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="76x76"
|
||||
href="./public/res/apple/apple-touch-icon-76x76.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="114x114"
|
||||
href="./public/res/apple/apple-touch-icon-114x114.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="120x120"
|
||||
href="./public/res/apple/apple-touch-icon-120x120.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="144x144"
|
||||
href="./public/res/apple/apple-touch-icon-144x144.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="152x152"
|
||||
href="./public/res/apple/apple-touch-icon-152x152.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="167x167"
|
||||
href="./public/res/apple/apple-touch-icon-167x167.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="./public/res/apple/apple-touch-icon-180x180.png"
|
||||
/>
|
||||
</head>
|
||||
<body id="appBody">
|
||||
<script>
|
||||
window.global ||= window;
|
||||
</script>
|
||||
<div id="root"></div>
|
||||
<audio id="notificationSound">
|
||||
<source src="./public/sound/notification.ogg" type="audio/ogg" />
|
||||
</audio>
|
||||
<audio id="inviteSound">
|
||||
<source src="./public/sound/invite.ogg" type="audio/ogg" />
|
||||
</audio>
|
||||
<script type="module" src="./src/index.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,85 +1,83 @@
|
||||
{
|
||||
"name": "cinny",
|
||||
"version": "1.8.1",
|
||||
"version": "2.2.4",
|
||||
"description": "Yet another matrix client",
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
"npm": ">=6.14.11",
|
||||
"node": ">=14.6.0"
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack serve --config ./webpack.dev.js --open",
|
||||
"build": "webpack --config ./webpack.prod.js"
|
||||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "yarn check:eslint && yarn check:prettier",
|
||||
"check:eslint": "eslint src/*",
|
||||
"check:prettier": "prettier --check .",
|
||||
"fix:prettier": "prettier --write .",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Ajay Bura",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^4.5.5",
|
||||
"@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",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"browser-encrypt-attachment": "^0.3.0",
|
||||
"dateformat": "^5.0.3",
|
||||
"emojibase-data": "^7.0.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"flux": "^4.0.3",
|
||||
"formik": "^2.2.9",
|
||||
"html-react-parser": "^1.4.8",
|
||||
"linkifyjs": "^2.1.9",
|
||||
"matrix-js-sdk": "^15.6.0",
|
||||
"micromark": "^3.0.10",
|
||||
"micromark-extension-gfm": "^2.0.1",
|
||||
"micromark-util-chunked": "^1.0.0",
|
||||
"micromark-util-resolve-all": "^1.0.0",
|
||||
"micromark-util-symbol": "^1.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^17.0.2",
|
||||
"react-autosize-textarea": "^7.1.0",
|
||||
"react-dnd": "^15.1.1",
|
||||
"react-dnd-html5-backend": "^15.1.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-google-recaptcha": "^2.1.0",
|
||||
"react-modal": "^3.14.4",
|
||||
"sanitize-html": "^2.7.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"twemoji": "^14.0.1"
|
||||
"@fontsource/inter": "4.5.14",
|
||||
"@fontsource/roboto": "4.5.8",
|
||||
"@khanacademy/simple-markdown": "0.8.6",
|
||||
"@lzy1960/google-translate": "0.2.6",
|
||||
"@matrix-org/olm": "3.2.14",
|
||||
"@tippyjs/react": "4.2.6",
|
||||
"blurhash": "2.0.4",
|
||||
"browser-encrypt-attachment": "0.3.0",
|
||||
"dateformat": "5.0.3",
|
||||
"emojibase-data": "7.0.1",
|
||||
"file-saver": "2.0.5",
|
||||
"flux": "4.0.3",
|
||||
"formik": "2.2.9",
|
||||
"google-translate-api-browser": "3.0.0",
|
||||
"html-react-parser": "3.0.4",
|
||||
"i18next": "22.4.9",
|
||||
"i18next-browser-languagedetector": "7.0.1",
|
||||
"i18next-http-backend": "2.1.1",
|
||||
"katex": "0.16.4",
|
||||
"linkify-html": "4.0.2",
|
||||
"linkifyjs": "4.0.2",
|
||||
"matrix-js-sdk": "22.0.0",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "17.0.2",
|
||||
"react-autosize-textarea": "7.1.0",
|
||||
"react-blurhash": "0.2.0",
|
||||
"react-dnd": "15.1.2",
|
||||
"react-dnd-html5-backend": "15.1.3",
|
||||
"react-dom": "17.0.2",
|
||||
"react-google-recaptcha": "2.1.0",
|
||||
"react-i18next": "12.1.5",
|
||||
"react-modal": "3.16.1",
|
||||
"sanitize-html": "2.8.0",
|
||||
"tippy.js": "6.3.7",
|
||||
"twemoji": "14.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.7",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"assert": "^2.0.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"browserify-fs": "^1.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.29.4",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"favicons": "^6.2.2",
|
||||
"favicons-webpack-plugin": "^5.0.2",
|
||||
"html-loader": "^3.1.0",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"mini-css-extract-plugin": "^2.6.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"sass": "^1.49.9",
|
||||
"sass-loader": "^12.6.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"url": "^0.11.0",
|
||||
"util": "^0.12.4",
|
||||
"webpack": "^5.70.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.7.4",
|
||||
"webpack-merge": "^5.7.3"
|
||||
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
|
||||
"@rollup/plugin-inject": "5.0.3",
|
||||
"@rollup/plugin-wasm": "6.1.1",
|
||||
"@types/node": "18.11.18",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "5.46.1",
|
||||
"@typescript-eslint/parser": "5.46.1",
|
||||
"@vitejs/plugin-react": "3.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"eslint": "8.29.0",
|
||||
"eslint-config-airbnb": "19.0.4",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "6.6.1",
|
||||
"eslint-plugin-react": "7.31.11",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"mini-svg-data-uri": "1.4.4",
|
||||
"prettier": "2.8.1",
|
||||
"sass": "1.56.2",
|
||||
"typescript": "4.9.4",
|
||||
"vite": "4.0.1",
|
||||
"vite-plugin-static-copy": "0.13.0"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 33 KiB |
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Cinny</title>
|
||||
<meta name="name" content="Cinny">
|
||||
<meta name="author" content="Ajay Bura">
|
||||
<meta name="description" content="A Matrix client where you can enjoy the conversation using simple, elegant and secure interface protected by e2ee with the power of open source.">
|
||||
<meta name="keywords" content="cinny, cinnyapp, cinnychat, matrix, matrix client, matrix.org, element">
|
||||
|
||||
<meta property="og:title" content="Cinny">
|
||||
<meta property="og:url" content="https://cinny.in">
|
||||
<meta property="og:image" content="https://cinny.in/assets/favicon-48x48.png">
|
||||
<meta property="og:description" content="A Matrix client where you can enjoy the conversation using simple, elegant and secure interface protected by e2ee with the power of open source.">
|
||||
<meta name="theme-color" content="#000000">
|
||||
</head>
|
||||
<body id="appBody">
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,889 @@
|
||||
{
|
||||
"translation": {
|
||||
"common": {
|
||||
"close": "Close",
|
||||
"open": "Open",
|
||||
"leave": "Leave",
|
||||
"options": "Options",
|
||||
"cinny": "Cinny",
|
||||
"slogan": "Yet another matrix client",
|
||||
"source_code": "Source code",
|
||||
"sponsor": "Support",
|
||||
"retry": "Retry",
|
||||
"delete": "Delete",
|
||||
"continue": "Continue",
|
||||
"cancel": "Cancel",
|
||||
"save": "Save",
|
||||
"view_more": "View more",
|
||||
"view_less": "View less",
|
||||
"copy": "Copy",
|
||||
"upload": "Upload",
|
||||
"download": "Download",
|
||||
"or": "Or",
|
||||
"reset": "Reset",
|
||||
"setup": "Setup",
|
||||
"search": "Search",
|
||||
"loading": "Loading...",
|
||||
"joining": "Joining...",
|
||||
"join": "Join",
|
||||
"remove": "Remove",
|
||||
"send": "Send",
|
||||
"homeserver": "Homeserver",
|
||||
"invite": "Invite",
|
||||
"uninvite": "Uninvite",
|
||||
"invited": "Invited",
|
||||
"inviting": "Inviting...",
|
||||
"uninviting": "Uninviting...",
|
||||
"change": "Change",
|
||||
"edit": "Edit",
|
||||
"message_prompt": "Message"
|
||||
},
|
||||
"errors": {
|
||||
"browser_not_supported": "Not supported in this browser",
|
||||
"generic": "Something went wrong!"
|
||||
},
|
||||
"Organisms": {
|
||||
"Welcome": {
|
||||
"heading": "Welcome to Cinny!",
|
||||
"subheading": "Yet another Matrix client"
|
||||
},
|
||||
"ViewSource": {
|
||||
"title": "View Source",
|
||||
"original_source": "Original source",
|
||||
"decrypted_source": "Decrypted source"
|
||||
},
|
||||
"SpaceManage": {
|
||||
"subtitle": "manage rooms",
|
||||
"load_more": "Load more",
|
||||
"rooms_and_spaces": "Rooms and spaces",
|
||||
"private_rooms_message": "Either the space contains private rooms or you need to join space to view it's rooms.",
|
||||
"items_selected_zero": "No selected items",
|
||||
"items_selected_one": "{{count}} selected item",
|
||||
"items_selected_other": "{{count}} selected items",
|
||||
"room_members_zero": "No room members",
|
||||
"room_members_one": "{{count}} room member",
|
||||
"room_members_other": "{{count}} room members",
|
||||
"mark_suggested_zero": "Marking no rooms as suggested",
|
||||
"mark_suggested_one": "Marking {{count}} room as suggested",
|
||||
"mark_suggested_other": "Marking {{count}} rooms as suggested",
|
||||
"mark_not_suggested_zero": "Marking no rooms as suggested",
|
||||
"mark_not_suggested_one": "Marking {{count}} room as suggested",
|
||||
"mark_not_suggested_other": "Marking {{count}} rooms as suggested",
|
||||
"remove_zero": "Removing no items",
|
||||
"remove_one": "Removing {{count}} item",
|
||||
"remove_other": "Removing {{count}} items.",
|
||||
"suggested": "Suggested",
|
||||
"mark_as_suggested": "Mark as suggested",
|
||||
"mark_as_not_suggested": "Mark as not suggested"
|
||||
},
|
||||
"SpaceSettings": {
|
||||
"subtitle": "space settings",
|
||||
"leave": {
|
||||
"leave_space": "Leave Space",
|
||||
"leave_dialog_title": "Leave Space",
|
||||
"leave_dialog_message": "Are you sure you want to leave {{space}}?"
|
||||
},
|
||||
"visibility": {
|
||||
"header": "Space visibility (who can join)"
|
||||
},
|
||||
"addresses": {
|
||||
"header": "Space addresses"
|
||||
},
|
||||
"categorize_subspaces": "Categorize subspaces",
|
||||
"uncategorize_subspaces": "Uncategorize subspaces",
|
||||
"pin_sidebar": "Pin to sidebar",
|
||||
"unpin_sidebar": "Unpin from sidebar"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "Settings",
|
||||
"theme": {
|
||||
"follow_system": {
|
||||
"title": "Follow system theme",
|
||||
"description": "Use light or dark mode based on the system settings."
|
||||
},
|
||||
"title": "Theme",
|
||||
"theme_light": "Light",
|
||||
"theme_silver": "Silver",
|
||||
"theme_dark": "Dark",
|
||||
"theme_butter": "Butter"
|
||||
},
|
||||
"markdown": {
|
||||
"title": "Markdown formatting",
|
||||
"description": "Format messages with markdown before sending"
|
||||
},
|
||||
"hide_membership_events": {
|
||||
"title": "Hide membership events",
|
||||
"description": "Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)"
|
||||
},
|
||||
"hide_nickname_avatar_events": {
|
||||
"title": "Hide nick/avatar events",
|
||||
"description": "Hide nickname and avatar change messages from the room timeline."
|
||||
},
|
||||
"notifications_and_sound": {
|
||||
"title": "Notifications & Sound",
|
||||
"desktop": {
|
||||
"title": "Desktop notifications",
|
||||
"description": "Show desktop notifications when new messages arrive."
|
||||
},
|
||||
"sound": {
|
||||
"title": "Notification sound",
|
||||
"description": "Play a sound when new messages arrive."
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"cross_signing": {
|
||||
"title": "Cross signing and backup"
|
||||
},
|
||||
"export_import_encryption_keys": {
|
||||
"title": "Export / Import encryption keys"
|
||||
},
|
||||
"export_encryption_keys": {
|
||||
"title": "Export E2E room keys",
|
||||
"description": "Export end-to-end encryption room keys to decrypt old messages in other session. In order to encrypt keys you need to set a password, which will be used while importing."
|
||||
},
|
||||
"import_encryption_keys": {
|
||||
"title": "Import E2E room keys",
|
||||
"description": "To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you\\'ll have to enter the password you set in order to decrypt it."
|
||||
}
|
||||
},
|
||||
"logout": {
|
||||
"title": "Logout",
|
||||
"dialog": {
|
||||
"title": "Logout",
|
||||
"description": "Are you sure that you want to logout your session?",
|
||||
"confirm": "Logout"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"application": "Application",
|
||||
"credits": "Credits"
|
||||
}
|
||||
},
|
||||
"ShortcutSpaces": {
|
||||
"header": "Pin Spaces",
|
||||
"pinned_spaces": "Pinned spaces",
|
||||
"no_pinned_spaces": "No pinned spaces",
|
||||
"unpinned_spaces": "Unpinned spaces",
|
||||
"no_unpinned_spaces": "No unpinned spaces",
|
||||
"spaces_selected_zero": "No selected spaces",
|
||||
"spaces_selected_one": "{{count}} selected space",
|
||||
"spaces_selected_other": "{{count}} selected spaces",
|
||||
"pin_button": "Pin"
|
||||
},
|
||||
"SecretStorageAccess": {
|
||||
"incorrect_security_key": "Incorrect security key",
|
||||
"incorrect_security_phrase": "Incorrect security phrase",
|
||||
"security_phrase": "Security Phrase",
|
||||
"security_key": "Security Key",
|
||||
"use_security_key": "Use Security Key",
|
||||
"use_security_phrase": "Use Security Phrase"
|
||||
},
|
||||
"KeyBackup": {
|
||||
"create_backup_title": "Create key backup",
|
||||
"create_backup_tooltip": "Create backup",
|
||||
"creating_backup": "Creating Backup...",
|
||||
"backup_created": "Successfully created backup",
|
||||
"backup_failed": "Failed to create backup",
|
||||
"restoring": "Restoring backup keys...",
|
||||
"restoring_progress": "Restoring backup keys... ({{progress}}/{{total}}",
|
||||
"restore_backup_title": "Restore Key Backup",
|
||||
"restore_backup_tooltip": "Restore Key Backup",
|
||||
"restore_complete": "Successfully restored backup keys ({{progress}}/{{total}})",
|
||||
"restore_failed_bad_key": "Failed to restore backup. Key is invalid!",
|
||||
"restore_failed_unknown": "Failed to restore backup.",
|
||||
"delete_key_backup_title": "Delete key backup",
|
||||
"delete_key_backup_tooltip": "Delete backup",
|
||||
"delete_key_backup_subtitle": "Deleting key backup is permanent.",
|
||||
"delete_key_backup_message": "All encrypted message keys stored on the server will be permanently deleted.",
|
||||
"encrypted_messages_backup_description": "Online backup your encrypted messages keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.",
|
||||
"encrypted_messages_backup_title": "Encrypted messages backup",
|
||||
"encrypted_messages_backup_cross_signing_disabled": "Setup cross signing to backup your encrypted messages."
|
||||
},
|
||||
"DeviceManage": {
|
||||
"edit_session_name_title": "Edit session name",
|
||||
"edit_session_name_subtitle": "Session name",
|
||||
"edit_session_name_tooltip": "Edit session name",
|
||||
"current_device_label": "Current",
|
||||
"verify_session_button": "Verify",
|
||||
"unverified_sessions_title": "Unverified sessions",
|
||||
"unverified_sessions_none": "No unverified sessions",
|
||||
"unencrypted_sessions_title": "Sessions without encryption support",
|
||||
"verified_sessions_title": "Verified sessions",
|
||||
"verified_sessions_none": "No verified sessions",
|
||||
"setup_cross_signing_message": "Setup cross signing in case you lose all your sessions",
|
||||
"loading_devices": "Loading devices...",
|
||||
"logout_device_title": "Logout {{device}}",
|
||||
"logout_device_message": "You are about to log out the session for {{device}}",
|
||||
"logout_device_confirm": "Logout",
|
||||
"logout_device_tooltip": "Remove session",
|
||||
"session_verification_title": "Session Verification",
|
||||
"session_name_privacy_message": "Session names are visible to everyone, so do not put any private info here."
|
||||
},
|
||||
"CrossSigning": {
|
||||
"title": "Cross Signing",
|
||||
"setup_failed": "Failed to setup cross signing. Please try again",
|
||||
"setup": "Setup cross signing",
|
||||
"save_security_key_message": "Please save this security key somewhere safe",
|
||||
"security_key_dialog_title": "Security Key",
|
||||
"security_key_generation_message": "We will generate a Security Key, which you can use to manage message backups and session verification.",
|
||||
"security_key_generation_button": "Generate Key",
|
||||
"security_phrase_message": "Alternatively you can set a 'Security Phrase' so you don't have to remember the long Security Key, and optionally save the key as a backup",
|
||||
"security_phrase_label": "Security Phrase",
|
||||
"security_phrase_confirm_label": "Confirm Security Phrase",
|
||||
"security_phrase_set_button": "Set Phrase & Generate Key",
|
||||
"setup_dialog_title": "Setup cross signing",
|
||||
"setup_message": "Setup to verify and keep track of all your sessions. Also required to backup encrypted message.",
|
||||
"reset_keys_subtitle": "Resetting cross-signing keys is permanent.",
|
||||
"reset_keys_message": "Anyone you have verified with will see security alerts and your message backup will lost. You almost certainly do not want to do this, unless you have lost Security Key or Phrase and every session you can cross-sign from."
|
||||
},
|
||||
"AuthRequest": {
|
||||
"wrong_password": "Wrong password. Please enter the correct password",
|
||||
"request_failed": "Request failed!",
|
||||
"password_label": "Account password"
|
||||
},
|
||||
"Search": {
|
||||
"description": "Type # for rooms, @ for DMs and * for spaces. Hotkey: Ctrl + k"
|
||||
},
|
||||
"RoomViewInput": {
|
||||
"upload_progress": "Uploading: {{progress}}/{{total}} ({{percent}}%)",
|
||||
"tombstone_replaced": "This room has been replaced, and is no longer active.",
|
||||
"tombstone_permission_denied": "You do not have permission to post to this room",
|
||||
"send_message_placeholder": "Send a message...",
|
||||
"emoji_tooltip": "Emoji",
|
||||
"file_size": "Size: {{size}}",
|
||||
"cancel_reply_tooltip": "Cancel reply"
|
||||
},
|
||||
"RoomViewHeader": {
|
||||
"search_tooltip": "Search",
|
||||
"people_tooltip": "People",
|
||||
"members_tooltip": "Members"
|
||||
},
|
||||
"RoomViewFloating": {
|
||||
"jump_unread": "Jump to unread messages",
|
||||
"mark_read": "Mark as read",
|
||||
"jump_latest": "Jump to latest",
|
||||
"user_typing_1": "<bold><user_one/></bold> is typing...",
|
||||
"user_typing_2": "<bold><user_one/></bold> and <bold><user_two/></bold> are typing...",
|
||||
"user_typing_3": "<bold><user_one/></bold>, <bold><user_two/></bold> and <bold><user_three/></bold> are typing...",
|
||||
"user_typing_4": "<bold><user_one/></bold>, <bold><user_two/></bold>, <bold><user_three/></bold> and <bold><user_four/></bold> are typing...",
|
||||
"user_typing_other": "<bold>Several people</bold> are typing..."
|
||||
},
|
||||
"RoomViewContent": {
|
||||
"welcome_to_room": "Welcome to {{room_name}}!",
|
||||
"beginning_room": "This is the beginning of the <bold>{{room_name}}</bold> room.",
|
||||
"beginning_dm": "This is the beginning of your direct message history with <bold>@{{user_name}}</bold>.",
|
||||
"created_on": "Created on {{date, datetime}}",
|
||||
"new_messages": "New messages"
|
||||
},
|
||||
"RoomSettings": {
|
||||
"leave_room": "Leave room",
|
||||
"leave_room_confirm_message": "Are you sure you want to leave {{room_name}}?",
|
||||
"leave_room_confirm_button": "Leave",
|
||||
"notification_header": "Notifications (Changing this will only affect you)",
|
||||
"visibility_header": "Room visibility (Who can join)",
|
||||
"address_header": "Room addresses",
|
||||
"encryption_header": "Encryption",
|
||||
"message_history_header": "Message history visibility",
|
||||
"room_settings_subtitle": "room settings"
|
||||
},
|
||||
"RoomCommon": {
|
||||
"user_joined": "<bold><user/></bold> joined the room",
|
||||
"user_left": "<bold><user/></bold> left the room",
|
||||
"user_left_reason": "<bold><user/></bold> left the room: <reason/>",
|
||||
"user_invited": "<bold><inviter/></bold> invited <bold><user/></bold>",
|
||||
"invite_cancelled": "<bold><inviter/></bold> cancelled <bold><user/>'s</bold> invite",
|
||||
"invite_rejected": "<bold><user/></bold> rejected the invitation",
|
||||
"user_kicked": "<bold><actor/></bold> kicked <bold><user/></bold>",
|
||||
"user_kicked_reason": "<bold><actor/></bold> kicked <bold><user/></bold>: <reason/>",
|
||||
"user_banned": "<bold><actor/></bold> banned <bold><user/></bold>",
|
||||
"user_banned_reason": "<bold><actor/></bold> banned <bold><user/></bold>: <reason/>",
|
||||
"user_unbanned": "<bold><actor/> unbanned <bold><user/></bold>",
|
||||
"avatar_set": "<bold><user/></bold> set an avatar",
|
||||
"avatar_changed": "<bold><user/></bold> changed their avatar",
|
||||
"avatar_removed": "<bold><user/></bold> removed their avatar",
|
||||
"name_set": "<bold><user/></bold> set their display name to <bold><new_name/></bold>",
|
||||
"name_changed": "<bold><user/></bold> changed their display name to <bold><new_name/></bold>",
|
||||
"name_removed": "<bold><user/></bold> removed their display name <bold><last_name/></bold>"
|
||||
},
|
||||
"PublicRooms": {
|
||||
"could_not_join_alias": "Unable to join {{alias}}. Either the room is private or doesn't exist",
|
||||
"try_joining_alias": "Try joining {{alias}}",
|
||||
"joining_alias": "Joining {{alias}}...",
|
||||
"no_public_rooms": "No public rooms on {{homeserver}}",
|
||||
"no_result_found": "No result found for '{{input}}' on {{homeserver}}",
|
||||
"title": "Public Rooms",
|
||||
"search_room_name_alias": "Room name or alias",
|
||||
"search_button": "Search",
|
||||
"loading": "Loading public rooms from {{homeserver}}...",
|
||||
"searching": "Searching for '{{query}}' on {{homeserver}}...",
|
||||
"result_title": "Public rooms on {{homeserver}}",
|
||||
"search_result_title": "Search result for '{{query}}' on {{homeserver}}"
|
||||
},
|
||||
"ProfileViewer": {
|
||||
"kick_button": "Kick",
|
||||
"kick_reason_label": "Kick Reason",
|
||||
"ban_button": "Ban",
|
||||
"ban_reason_label": "Ban reason",
|
||||
"loading_sessions": "Loading sessions...",
|
||||
"no_sessions_found": "No sessions found.",
|
||||
"view_sessions_one": "View session",
|
||||
"view_sessions_other": "View {{count}} sessions",
|
||||
"send_direct_message_button": "Message",
|
||||
"creating_dm_room": "Creating room...",
|
||||
"ignore": "Ignore",
|
||||
"ignoring": "Ignoring...",
|
||||
"unignore": "Unignore",
|
||||
"unignoring": "Unignoring...",
|
||||
"change_power_level": "Change power level",
|
||||
"shared_power_message": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?",
|
||||
"demoting_self_message": "You will not be able to undo this change as you are demoting yourself. Are you sure?"
|
||||
},
|
||||
"PeopleDrawer": {
|
||||
"title": "People",
|
||||
"members_one": "{{count}} member",
|
||||
"members_other": "{{count}} members",
|
||||
"joined": "Joined",
|
||||
"invited": "Invited",
|
||||
"banned": "Banned",
|
||||
"search_no_results": "No results found",
|
||||
"view_more": "View more",
|
||||
"placeholder": "Search",
|
||||
"invite_tooltip": "Invite"
|
||||
},
|
||||
"ProfileEditor": {
|
||||
"remove_avatar": "Remove avatar",
|
||||
"remove_avatar_confirmation": "Are you sure that you want to remove your avatar?",
|
||||
"display_name_message": "Display name of {{user_name}}"
|
||||
},
|
||||
"DrawerBreadcrumb": {
|
||||
"home": "Home"
|
||||
},
|
||||
"DrawerHeader": {
|
||||
"add_rooms_or_spaces": "Add rooms or spaces",
|
||||
"create_new_space": "Create new space",
|
||||
"create_new_room": "Create new room",
|
||||
"join_public_room": "Join public room",
|
||||
"join_with_address": "Join with address",
|
||||
"add_existing": "Add existing",
|
||||
"manage_rooms": "Manage rooms",
|
||||
"home": "Home",
|
||||
"direct_messages": "Direct messages",
|
||||
"start_dm_tooltip": "Start DM",
|
||||
"add_rooms_spaces_tooltip": "Add rooms/spaces"
|
||||
},
|
||||
"SideBar": {
|
||||
"settings_tooltip": "Settings",
|
||||
"unverified_sessions_one": "{{count}} unverified session",
|
||||
"unverified_sessions_other": "{{count}} unverified sessions",
|
||||
"home_tooltip": "Home",
|
||||
"direct_messages_tooltip": "People",
|
||||
"pin_spaces_tooltip": "Pin spaces",
|
||||
"search_tooltip": "Search",
|
||||
"invites_tooltip": "Invites"
|
||||
},
|
||||
"JoinAlias": {
|
||||
"invalid_address": "Invalid address.",
|
||||
"looking_for_address": "Looking for address...",
|
||||
"joining_alias": "Joining {{alias_name}}...",
|
||||
"couldnt_find_room_or_space_alias": "Unable to find room/space with {{alias_name}}. Either the room/space is private or doesn't exist.",
|
||||
"couldnt_find_room_or_space": "Unable to join {{alias_name}}. Either the room/space is private or doesn't exist.",
|
||||
"address_label": "Address",
|
||||
"title": "Join with address"
|
||||
},
|
||||
"InviteUser": {
|
||||
"user_not_found": "{{user_name}} not found!",
|
||||
"no_matches_found": "No matches found for {{user_name}}",
|
||||
"invite_result": {
|
||||
"invited": "Invited",
|
||||
"already_joined": "Already joined",
|
||||
"already_invited": "Already invited",
|
||||
"banned": "Banned"
|
||||
},
|
||||
"search_label": "Name or User ID",
|
||||
"search_result_title": "Search result for user {{user_name}}",
|
||||
"searching_for_user": "Searching for user {{user_name}}...",
|
||||
"invite_to_room": "Invite to {{room}}",
|
||||
"invite_to_dm": "Direct Message"
|
||||
},
|
||||
"InviteList": {
|
||||
"accept_invite": "Accept",
|
||||
"reject_invite": "Reject",
|
||||
"direct_messages_title": "Direct Messages",
|
||||
"rooms_title": "Rooms",
|
||||
"spaces_title": "Spaces",
|
||||
"title": "Invites"
|
||||
},
|
||||
"EmojiVerification": {
|
||||
"waiting_for_response": "Waiting for response from other device...",
|
||||
"confirmation_prompt": "Confirm the emoji below are displayed on both devices, in the same order:",
|
||||
"emojis_match_button": "They match",
|
||||
"emojis_dont_match_button": "They don't match",
|
||||
"accept_request_from_other_device_message": "Please accept the request from other device.",
|
||||
"begin_verification_process_message": "Click accept to start the verification process.",
|
||||
"begin_verification_button_text": "Accept",
|
||||
"title": "Emoji Verification"
|
||||
},
|
||||
"DragDrop": {
|
||||
"drop_file_to_upload_prompt": "Drop file to upload"
|
||||
},
|
||||
"CreateRoom": {
|
||||
"private_room_short": "Private",
|
||||
"restricted_room_short": "Restricted",
|
||||
"public_room_short": "Public",
|
||||
"private_room_long": "Private (invite only)",
|
||||
"restricted_room_long": "Restricted (space member can join)",
|
||||
"public_room_long": "Public (anyone can join)",
|
||||
"visibility_title": "Visibility",
|
||||
"visibility_message": "Visibility (who can join)",
|
||||
"select_who_can_join_space": "Select who can join this space",
|
||||
"select_who_can_join_room": "Select who can join this room",
|
||||
"space_address": "Space address",
|
||||
"room_address": "Room address",
|
||||
"room_address_already_in_use": "{{room_address}} is already in use",
|
||||
"e2e_title": "Enable end-to-end encryption",
|
||||
"e2e_message": "You can’t disable this later. Bridges & most bots won’t work yet.",
|
||||
"role_title": "Select your role",
|
||||
"role_message": "Selecting 'Admin' sets your power level to 100 whereas 'Founder' sets it to 101.",
|
||||
"creating_room": "Creating room...",
|
||||
"creating_space": "Creating space...",
|
||||
"topic_label": "Topic (optional)",
|
||||
"space_name": "Space name",
|
||||
"room_name": "Room name",
|
||||
"role_admin": "Admin",
|
||||
"role_founder": "Founder",
|
||||
"create_room": "Create room",
|
||||
"create_space": "Create space",
|
||||
"home": "Home"
|
||||
}
|
||||
},
|
||||
"Molecules": {
|
||||
"ConfirmDialog": {
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"ReusableDialog": {
|
||||
"close_tooltip": "Close"
|
||||
},
|
||||
"FollowingMembers": {
|
||||
"users_following_1": "<bold><user_one/></bold> is following the conversation",
|
||||
"users_following_2": "<bold><user_one/></bold> and <bold><user_two/></bold> are following the conversation",
|
||||
"users_following_3": "<bold><user_one/></bold>, <bold><user_two/></bold>, and <bold><user_three/></bold> are following the conversation",
|
||||
"users_following_other": "<bold><user_one/></bold>, <bold><user_two/></bold>, <bold><user_three/></bold> and {{other_count}} others are following the conversation"
|
||||
},
|
||||
"ImageUpload": {
|
||||
"prompt": "Upload",
|
||||
"cancel": "Cancel",
|
||||
"remove": "Remove"
|
||||
},
|
||||
"ExportE2ERoomKeys": {
|
||||
"getting_keys": "Getting keys...",
|
||||
"encrypting_keys": "Encrypting Keys...",
|
||||
"password_does_not_match": "Password does not match.",
|
||||
"export_success": "Successfully exported all keys.",
|
||||
"export_failed": "Failed to export keys. Please try again.",
|
||||
"button_text": "Export"
|
||||
},
|
||||
"ImportE2ERoomKeys": {
|
||||
"decrypting_file": "Decrypting file...",
|
||||
"decrypting_messages": "Decrypting messages...",
|
||||
"import_success": "Successfully imported all keys",
|
||||
"import_failed": "Failed tp decrypt keys. Please try again",
|
||||
"import_keys_button": "Import Keys",
|
||||
"decrypt_button": "Decrypt"
|
||||
},
|
||||
"Media": {
|
||||
"open_new_tab": "Open in new tab",
|
||||
"download": "Download",
|
||||
"play_audio": "Play audio",
|
||||
"play_video": "Play video"
|
||||
},
|
||||
"Message": {
|
||||
"message_deleted": "*** This message has been deleted ***",
|
||||
"unable_to_load_reply": "*** Unable to load reply ***",
|
||||
"unknown_user": "** Unknown user **",
|
||||
"edited": "(edited)",
|
||||
"edit_placeholder": "Edit message",
|
||||
"user_reacted_1": "<bold><user_one/></bold> reacted with <emoji/>",
|
||||
"user_reacted_2": "<bold><user_one/></bold> and <bold><user_two/></bold> reacted with <emoji/>",
|
||||
"user_reacted_3": "<bold><user_one/></bold>, <bold><user_two/></bold> and <bold><user_three/></bold> reacted with <emoji/>",
|
||||
"user_reacted_other": "<bold><user_one/></bold>, <bold><user_two/></bold>, <bold><user_three/></bold> and {{other_count}} others reacted with <emoji/>",
|
||||
"add_reaction_tooltip": "Add reaction",
|
||||
"reply_tooltip": "Reply",
|
||||
"edit_tooltip": "Edit",
|
||||
"options_header": "Options",
|
||||
"options_tooltip": "Options",
|
||||
"read_receipts": "Read receipts",
|
||||
"view_source": "View source",
|
||||
"delete_message_prompt": "Delete message",
|
||||
"delete_message_confirmation": "Are you sure that you want to delete this message?",
|
||||
"delete_message_button": "Delete"
|
||||
},
|
||||
"PopupWindow": {
|
||||
"close_tooltip": "Back"
|
||||
},
|
||||
"PowerLevelSelector": {
|
||||
"placeholder": "Power level"
|
||||
},
|
||||
"RoomAliases": {
|
||||
"invalid_characters": "Invalid character: only letter, numbers and _- are allowed.",
|
||||
"validating_alias": "validating {{alias}}...",
|
||||
"alias_available": "{{alias}} is available.",
|
||||
"alias_unavailable": "{{alias}} is unavailable.",
|
||||
"deleting_alias": "Deleting...",
|
||||
"set_main_alias": "Set as Main",
|
||||
"publish_alias": "Publish",
|
||||
"unpublish_alias": "Unpublish",
|
||||
"delete_alias": "Delete",
|
||||
"main_alias": "Main",
|
||||
"publish_to_room_directory": {
|
||||
"title": "Publish to room directory",
|
||||
"publish_room_message": "Publish this room to the {{homeserver}} publish directory?",
|
||||
"publish_space_message": "Publish this space to the {{homeserver}} publish directory?"
|
||||
},
|
||||
"published_addresses": {
|
||||
"title": "Published addresses",
|
||||
"none": "No published addresses",
|
||||
"no_main_address": "No main address (select one from below)",
|
||||
"message_room": "Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first.",
|
||||
"message_space": "Published addresses can be used by anyone on any server to join your space. To publish an address, it needs to be set as a local address first."
|
||||
},
|
||||
"local_addresses": {
|
||||
"title": "Local addresses",
|
||||
"none": "No local addresses",
|
||||
"message_room": "Set local addresses for this room so users can find this room through your homeserver.",
|
||||
"message_space": "Set local addresses for this space so users can find this space through your homeserver.",
|
||||
"add": "Add local address",
|
||||
"add_button": "Add",
|
||||
"placeholder_room": "my_room_address",
|
||||
"placeholder_space": "my_space_address",
|
||||
"hide": "Hide local addresses",
|
||||
"view": "View local addresses"
|
||||
}
|
||||
},
|
||||
"RoomEncryption": {
|
||||
"encryption_public_room_message": "It is not recommended to add encryption in public room. Anyone can find and join public rooms, so anyone should be able to read messages in them.",
|
||||
"encryption_message": "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly",
|
||||
"encryption_cannot_be_disabled": "Once enabled, encryption cannot be disabled.",
|
||||
"enable_room_encryption": "Enable room encryption",
|
||||
"enable_encryption_prompt": "Enable Encryption",
|
||||
"enable_encryption_button": "Enable",
|
||||
"continue_button": "Continue"
|
||||
},
|
||||
"RoomHistoryVisibility": {
|
||||
"world_readable": "Anyone (including guests)",
|
||||
"shared": "Members (all messages)",
|
||||
"invited": "Members (messages after invite)",
|
||||
"joined": "Members (messages after join)",
|
||||
"changes_only_affect_future": "Changes to history visibility will only apply to future messages. The visibility of existing history will have no effect."
|
||||
},
|
||||
"RoomMembers": {
|
||||
"search_title": "Search member",
|
||||
"found_members_one": "Found — one member",
|
||||
"found_members_other": "Found — {{count}} members",
|
||||
"no_results": "No results found for {{term}}",
|
||||
"no_members": "No members to display",
|
||||
"view_more": "View more",
|
||||
"joined": "Joined",
|
||||
"invited": "Invited",
|
||||
"banned": "Banned",
|
||||
"search_placeholder": "Search for name"
|
||||
},
|
||||
"RoomNotification": {
|
||||
"default": "Global",
|
||||
"all_messages": "All messages",
|
||||
"mentions_and_keywords": "Mentions & Keywords",
|
||||
"mute": "Mute"
|
||||
},
|
||||
"RoomOptions": {
|
||||
"title": "Options for {{room_name}}",
|
||||
"leave": {
|
||||
"title": "Leave room",
|
||||
"subtitle": "Are you sure you want to leave the {{room_name}} room?",
|
||||
"button_text": "Leave"
|
||||
},
|
||||
"mark_as_read": "Mark as read",
|
||||
"notifications_heading": "Notifications",
|
||||
"invite": "Invite"
|
||||
},
|
||||
"RoomPermissions": {
|
||||
"default_role": {
|
||||
"name": "Default role",
|
||||
"description": "Set default role for all members"
|
||||
},
|
||||
"send_messages": {
|
||||
"name": "Send messages",
|
||||
"description": "Set minimum power level to send messages in a room"
|
||||
},
|
||||
"reactions": {
|
||||
"name": "Send reactions",
|
||||
"description": "Set minimum power level to send reactions in a room"
|
||||
},
|
||||
"delete": {
|
||||
"name": "Delete messages sent by others",
|
||||
"description": "Set minumum power level to delete messages in a room"
|
||||
},
|
||||
"notifications": {
|
||||
"name": "Ping room",
|
||||
"description": "Set minimum power level to ping room"
|
||||
},
|
||||
"manage_rooms": {
|
||||
"name": "Manage rooms in space",
|
||||
"description": "Set minimum power level to manage rooms in space"
|
||||
},
|
||||
"invite": {
|
||||
"name": "Invite",
|
||||
"description": "Set minimum power level to invite members"
|
||||
},
|
||||
"kick": {
|
||||
"name": "Kick",
|
||||
"description": "Set minimum power level to kick members"
|
||||
},
|
||||
"ban": {
|
||||
"name": "Ban",
|
||||
"description": "Set minimum power level to ban members"
|
||||
},
|
||||
"change_avatar": {
|
||||
"name": "Change avatar",
|
||||
"description": "Set minimum power level to change room/space avatar"
|
||||
},
|
||||
"change_name": {
|
||||
"name": "Change name",
|
||||
"description": "Set minimum power evel to change room/space name"
|
||||
},
|
||||
"change_topic": {
|
||||
"name": "Change topic",
|
||||
"description": "Set minimum power level to change room/space topic"
|
||||
},
|
||||
"change_settings": {
|
||||
"name": "Change settings",
|
||||
"description": "Set minimum power level to change settings"
|
||||
},
|
||||
"change_published_address": {
|
||||
"name": "Change published address",
|
||||
"description": "Set minimum power level to publish and set main address"
|
||||
},
|
||||
"change_permissions": {
|
||||
"name": "Change permissions",
|
||||
"description": "Set minimum power level to change permissions"
|
||||
},
|
||||
"enable_room_encryption": {
|
||||
"name": "Enable room encryption",
|
||||
"description": "Set minimum power level to enable room encryption"
|
||||
},
|
||||
"change_history_visibility": {
|
||||
"name": "Change history visibility",
|
||||
"description": "Set minimum power level to change room message history visibility"
|
||||
},
|
||||
"upgrade_room": {
|
||||
"name": "Upgrade room",
|
||||
"description": "Set minimum power level to upgrade room"
|
||||
},
|
||||
"pin_messages": {
|
||||
"name": "Pin messages",
|
||||
"description": "Set minimum power level to pin messages in a room"
|
||||
},
|
||||
"change_acls": {
|
||||
"name": "Change server ACLs",
|
||||
"description": "Set minimum power level to change server ACLs"
|
||||
},
|
||||
"modify_widgets": {
|
||||
"name": "Modify widgets",
|
||||
"description": "Set minimum power level to modify room widgets"
|
||||
},
|
||||
"groups": {
|
||||
"general": "General permissions",
|
||||
"manage_members": "Manage members permissions",
|
||||
"room": "Room profile permissions",
|
||||
"space": "Space profile permissions",
|
||||
"other": "Other permissions",
|
||||
"settings": "Settings permissions"
|
||||
}
|
||||
},
|
||||
"RoomProfile": {
|
||||
"saving_room_name": "Saving room name...",
|
||||
"saving_room_topic": "Saving room topic...",
|
||||
"save_success": "Saved successfully",
|
||||
"save_failed": "Unable to save",
|
||||
"remove_avatar_title": "Remove avatar",
|
||||
"remove_avatar_subtitle": "Are you sure that you want to remove room avatar?",
|
||||
"remove_avatar_button": "Remove",
|
||||
"permission_change_room_name": "You have permission to change room name only",
|
||||
"permission_change_room_topic": "You have permission to change room topic only",
|
||||
"permission_change_space_name": "You have permission to change space name only",
|
||||
"permission_change_space_topic": "You have permission to change space topic only",
|
||||
"name_label": "Name",
|
||||
"topic_label": "Topic"
|
||||
},
|
||||
"RoomSearch": {
|
||||
"title": "Room search",
|
||||
"placeholder": "Search for keywords",
|
||||
"search_button": "Search",
|
||||
"searching": "Searching room messages...",
|
||||
"subtitle": "Search room messages",
|
||||
"failed": "Failed to search messages",
|
||||
"no_results": "No results found",
|
||||
"encrypted_room": "Search does not work in encrypted room",
|
||||
"load_more": "Load more",
|
||||
"results_one": "{{count}} result for {{term}}",
|
||||
"results_other": "{{count}} results for {{term}}"
|
||||
},
|
||||
"RoomTile": {
|
||||
"invited_by_user_zero": "Invited by {{inviter}} to {{id}}",
|
||||
"invited_by_user_one": "Invited by {{inviter}} to {{id}} • {{member_count}} member",
|
||||
"invited_by_user_other": "Invited by {{inviter}} to {{id}} • {{member_count}} members",
|
||||
"invited_zero": "{{id}}",
|
||||
"invited_one": "{{id}} • {{member_count}} member",
|
||||
"invited_other": "{{id}} • {{member_count}} members"
|
||||
},
|
||||
"RoomVisibility": {
|
||||
"private": "Private (invite only)",
|
||||
"restricted": "Restricted (space members can join)",
|
||||
"restricted_unsupported": "Restricted (Unsupported: room required upgrade)",
|
||||
"public": "Public (anyone can join)"
|
||||
},
|
||||
"RoomEmojis": {
|
||||
"header": "Create Pack",
|
||||
"submit_button": "Create pack",
|
||||
"name_placeholder": "Pack Name",
|
||||
"no_packs": "No emoji or sticker packs."
|
||||
},
|
||||
"ImagePack": {
|
||||
"rename_title": "Rename",
|
||||
"rename_button": "Rename",
|
||||
"shortcode_label": "Shortcode",
|
||||
"delete_item_title": "Delete",
|
||||
"delete_item_message": "Are you sure that you want to delete {{key}}?",
|
||||
"delete_item_button": "Delete",
|
||||
"delete_pack_title": "Delete Pack",
|
||||
"delete_pack_message": "Are you sure that you want to delete {{pack_name}}?",
|
||||
"delete_pack_button": "Delete",
|
||||
"image_title": "Image",
|
||||
"image_shortcode": "Shortcode",
|
||||
"image_usage": "Usage",
|
||||
"view_more": "View {{count}} more",
|
||||
"view_less": "View less",
|
||||
"use_globally": "Use globally",
|
||||
"use_globally_description": "Add this pack to your account to use in all rooms.",
|
||||
"no_global_packs": "No global packs",
|
||||
"default_personal_pack_name": "Personal"
|
||||
},
|
||||
"ImagePackItem": {
|
||||
"rename_tooltip": "Rename",
|
||||
"delete_tooltip": "Delete"
|
||||
},
|
||||
"ImagePackUsageSelector": {
|
||||
"header": "Usage",
|
||||
"type_emoji": "Emoji",
|
||||
"type_sticker": "Sticker",
|
||||
"type_both": "Both"
|
||||
},
|
||||
"ImagePackUpload": {
|
||||
"remove_file_tooltip": "Remove file",
|
||||
"import_image_button": "Import image",
|
||||
"shortcode_placeholder": "shortcode",
|
||||
"upload_button": "Upload",
|
||||
"upload_button_progress": "Uploading..."
|
||||
},
|
||||
"ImagePackProfile": {
|
||||
"attribution_prompt": "Attribution",
|
||||
"name_prompt": "Name",
|
||||
"edit_tooltip": "Edit",
|
||||
"usage_header": "Usage",
|
||||
"pack_usage": "Pack usage"
|
||||
},
|
||||
"SpaceAddExisting": {
|
||||
"adding_items_one": "Adding one item...",
|
||||
"adding_items_other": "Adding {{count}} items...",
|
||||
"items_selected_one": "{{count}} item selected",
|
||||
"items_selected_other": "{{count}} items selected",
|
||||
"search_rooms_placeholder": "Search rooms",
|
||||
"no_results": "No results found",
|
||||
"add_button": "Add",
|
||||
"subtitle": "add existing rooms"
|
||||
},
|
||||
"SpaceOptions": {
|
||||
"leave_space": "Leave Space",
|
||||
"leave_space_confirmation": "Are you sure that you want to leave the {{space}} space?",
|
||||
"leave_space_confirm": "Leave",
|
||||
"invite": "Invite",
|
||||
"manage_rooms": "Manage rooms",
|
||||
"settings": "Settings",
|
||||
"leave": "Leave"
|
||||
},
|
||||
"SSOButtons": {
|
||||
"login_with": "Login with {{idp_name}}"
|
||||
}
|
||||
},
|
||||
"Templates": {
|
||||
"Auth": {
|
||||
"loading_homeserver_list": "Loading homeserver list...",
|
||||
"looking_for_homeserver": "Looking for homeserver...",
|
||||
"connecting_to_homeserver": "Connecting to {{homeserver}}...",
|
||||
"unable_to_connect": "Unable to connect. Please check your input",
|
||||
"username_taken": "Username is already taken",
|
||||
"registration_in_progress": "Registration in progress...",
|
||||
"link_about": "About",
|
||||
"link_twitter": "Twitter",
|
||||
"link_matrix": "Powered by Matrix",
|
||||
"dont_have_an_account": "Dont have an account?",
|
||||
"already_have_an_account": "Already have an account?",
|
||||
"register_link": "Register",
|
||||
"login_link": "Login",
|
||||
"redirecting": "Redirecting...",
|
||||
"login_in_progress": "Login in progress...",
|
||||
"bad_localpart_error": "Username can only contain characters a-z, 0-9, or '=_-./'",
|
||||
"user_id_too_long_error": "Your user ID, including the hostname, can't be more than 255 characters long.",
|
||||
"bad_password_error": "Password must contain at least 1 lowercase, 1 uppercase, 1 number, 1 non-alphanumeric character, 8-127 characters with no space.",
|
||||
"confirm_password_error": "Passwords don't match.",
|
||||
"bad_email_error": "Invalid email address",
|
||||
"invalid_password": "Invalid password",
|
||||
"check_credentials": "Please check your credentials",
|
||||
"login_types": {
|
||||
"username": "Username",
|
||||
"email": "Email"
|
||||
},
|
||||
"homeserver_form": {
|
||||
"title": "Homeserver",
|
||||
"header": "Homeserver list"
|
||||
},
|
||||
"login_form": {
|
||||
"title": "Login",
|
||||
"prompt_email": "Email",
|
||||
"prompt_username": "Username",
|
||||
"prompt_password": "Password",
|
||||
"login_button": "Login"
|
||||
},
|
||||
"register_form": {
|
||||
"title": "Register",
|
||||
"prompt_email_required": "Email (Required)",
|
||||
"prompt_email_optional": "Email",
|
||||
"prompt_username": "Username",
|
||||
"prompt_password": "Password",
|
||||
"prompt_confirm_password": "Confirm password",
|
||||
"register_button": "Register"
|
||||
},
|
||||
"terms_and_conditions": {
|
||||
"title": "Agree with terms",
|
||||
"description": "In order to complete registration, you need to agree to the terms and conditions.",
|
||||
"accept": "I accept <a>Terms and Conditions</a>",
|
||||
"submit_button": "Submit"
|
||||
},
|
||||
"validate_email": {
|
||||
"title": "Verify email",
|
||||
"continue_button": "Continue",
|
||||
"message": "Please check your email <bold>{{email_address}}</bold> and validate before continuing further"
|
||||
},
|
||||
"captcha": {
|
||||
"message": "Pleace check the box below to proceed"
|
||||
}
|
||||
},
|
||||
"Client": {
|
||||
"loading_messages": {
|
||||
"default": "Heating up",
|
||||
"message_one": "Almost there...",
|
||||
"message_two": "Looks like you have a lot of stuff to heat up!"
|
||||
},
|
||||
"logout_prompt": "Logout"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,889 @@
|
||||
{
|
||||
"translation": {
|
||||
"common": {
|
||||
"close": "Close",
|
||||
"open": "Open",
|
||||
"leave": "Leave",
|
||||
"options": "Options",
|
||||
"cinny": "Cinny",
|
||||
"slogan": "Yet another matrix client",
|
||||
"source_code": "Source code",
|
||||
"sponsor": "Support",
|
||||
"retry": "Retry",
|
||||
"delete": "Delete",
|
||||
"continue": "Continue",
|
||||
"cancel": "Cancel",
|
||||
"save": "Save",
|
||||
"view_more": "View more",
|
||||
"view_less": "View less",
|
||||
"copy": "Copy",
|
||||
"upload": "Upload",
|
||||
"download": "Download",
|
||||
"or": "Or",
|
||||
"reset": "Reset",
|
||||
"setup": "Setup",
|
||||
"search": "Search",
|
||||
"loading": "Loading...",
|
||||
"joining": "Joining...",
|
||||
"join": "Join",
|
||||
"remove": "Remove",
|
||||
"send": "Send",
|
||||
"homeserver": "Homeserver",
|
||||
"invite": "Invite",
|
||||
"uninvite": "Uninvite",
|
||||
"invited": "Invited",
|
||||
"inviting": "Inviting...",
|
||||
"uninviting": "Uninviting...",
|
||||
"change": "Change",
|
||||
"edit": "Edit",
|
||||
"message_prompt": "Message"
|
||||
},
|
||||
"errors": {
|
||||
"browser_not_supported": "Not supported in this browser",
|
||||
"generic": "Something went wrong!"
|
||||
},
|
||||
"Organisms": {
|
||||
"Welcome": {
|
||||
"heading": "Welcome to Cinny!",
|
||||
"subheading": "Yet another Matrix client"
|
||||
},
|
||||
"ViewSource": {
|
||||
"title": "View Source",
|
||||
"original_source": "Original source",
|
||||
"decrypted_source": "Decrypted source"
|
||||
},
|
||||
"SpaceManage": {
|
||||
"subtitle": "manage rooms",
|
||||
"load_more": "Load more",
|
||||
"rooms_and_spaces": "Rooms and spaces",
|
||||
"private_rooms_message": "Either the space contains private rooms or you need to join space to view it's rooms.",
|
||||
"items_selected_zero": "No selected items",
|
||||
"items_selected_one": "{{count}} selected item",
|
||||
"items_selected_other": "{{count}} selected items",
|
||||
"room_members_zero": "No room members",
|
||||
"room_members_one": "{{count}} room member",
|
||||
"room_members_other": "{{count}} room members",
|
||||
"mark_suggested_zero": "Marking no rooms as suggested",
|
||||
"mark_suggested_one": "Marking {{count}} room as suggested",
|
||||
"mark_suggested_other": "Marking {{count}} rooms as suggested",
|
||||
"mark_not_suggested_zero": "Marking no rooms as suggested",
|
||||
"mark_not_suggested_one": "Marking {{count}} room as suggested",
|
||||
"mark_not_suggested_other": "Marking {{count}} rooms as suggested",
|
||||
"remove_zero": "Removing no items",
|
||||
"remove_one": "Removing {{count}} item",
|
||||
"remove_other": "Removing {{count}} items.",
|
||||
"suggested": "Suggested",
|
||||
"mark_as_suggested": "Mark as suggested",
|
||||
"mark_as_not_suggested": "Mark as not suggested"
|
||||
},
|
||||
"SpaceSettings": {
|
||||
"subtitle": "space settings",
|
||||
"leave": {
|
||||
"leave_space": "Leave Space",
|
||||
"leave_dialog_title": "Leave Space",
|
||||
"leave_dialog_message": "Are you sure you want to leave {{space}}?"
|
||||
},
|
||||
"visibility": {
|
||||
"header": "Space visibility (who can join)"
|
||||
},
|
||||
"addresses": {
|
||||
"header": "Space addresses"
|
||||
},
|
||||
"categorize_subspaces": "Categorize subspaces",
|
||||
"uncategorize_subspaces": "Uncategorize subspaces",
|
||||
"pin_sidebar": "Pin to sidebar",
|
||||
"unpin_sidebar": "Unpin from sidebar"
|
||||
},
|
||||
"Settings": {
|
||||
"title": "设置",
|
||||
"theme": {
|
||||
"follow_system": {
|
||||
"title": "Follow system theme",
|
||||
"description": "Use light or dark mode based on the system settings."
|
||||
},
|
||||
"title": "主题",
|
||||
"theme_light": "Light",
|
||||
"theme_silver": "Silver",
|
||||
"theme_dark": "Dark",
|
||||
"theme_butter": "Butter"
|
||||
},
|
||||
"markdown": {
|
||||
"title": "Markdown formatting",
|
||||
"description": "Format messages with markdown before sending"
|
||||
},
|
||||
"hide_membership_events": {
|
||||
"title": "Hide membership events",
|
||||
"description": "Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)"
|
||||
},
|
||||
"hide_nickname_avatar_events": {
|
||||
"title": "Hide nick/avatar events",
|
||||
"description": "Hide nickname and avatar change messages from the room timeline."
|
||||
},
|
||||
"notifications_and_sound": {
|
||||
"title": "Notifications & Sound",
|
||||
"desktop": {
|
||||
"title": "Desktop notifications",
|
||||
"description": "Show desktop notifications when new messages arrive."
|
||||
},
|
||||
"sound": {
|
||||
"title": "Notification sound",
|
||||
"description": "Play a sound when new messages arrive."
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"cross_signing": {
|
||||
"title": "Cross signing and backup"
|
||||
},
|
||||
"export_import_encryption_keys": {
|
||||
"title": "Export / Import encryption keys"
|
||||
},
|
||||
"export_encryption_keys": {
|
||||
"title": "Export E2E room keys",
|
||||
"description": "Export end-to-end encryption room keys to decrypt old messages in other session. In order to encrypt keys you need to set a password, which will be used while importing."
|
||||
},
|
||||
"import_encryption_keys": {
|
||||
"title": "Import E2E room keys",
|
||||
"description": "To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you\\'ll have to enter the password you set in order to decrypt it."
|
||||
}
|
||||
},
|
||||
"logout": {
|
||||
"title": "Logout",
|
||||
"dialog": {
|
||||
"title": "Logout",
|
||||
"description": "Are you sure that you want to logout your session?",
|
||||
"confirm": "Logout"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"application": "Application",
|
||||
"credits": "Credits"
|
||||
}
|
||||
},
|
||||
"ShortcutSpaces": {
|
||||
"header": "Pin Spaces",
|
||||
"pinned_spaces": "Pinned spaces",
|
||||
"no_pinned_spaces": "No pinned spaces",
|
||||
"unpinned_spaces": "Unpinned spaces",
|
||||
"no_unpinned_spaces": "No unpinned spaces",
|
||||
"spaces_selected_zero": "No selected spaces",
|
||||
"spaces_selected_one": "{{count}} selected space",
|
||||
"spaces_selected_other": "{{count}} selected spaces",
|
||||
"pin_button": "Pin"
|
||||
},
|
||||
"SecretStorageAccess": {
|
||||
"incorrect_security_key": "Incorrect security key",
|
||||
"incorrect_security_phrase": "Incorrect security phrase",
|
||||
"security_phrase": "Security Phrase",
|
||||
"security_key": "Security Key",
|
||||
"use_security_key": "Use Security Key",
|
||||
"use_security_phrase": "Use Security Phrase"
|
||||
},
|
||||
"KeyBackup": {
|
||||
"create_backup_title": "Create key backup",
|
||||
"create_backup_tooltip": "Create backup",
|
||||
"creating_backup": "Creating Backup...",
|
||||
"backup_created": "Successfully created backup",
|
||||
"backup_failed": "Failed to create backup",
|
||||
"restoring": "Restoring backup keys...",
|
||||
"restoring_progress": "Restoring backup keys... ({{progress}}/{{total}}",
|
||||
"restore_backup_title": "Restore Key Backup",
|
||||
"restore_backup_tooltip": "Restore Key Backup",
|
||||
"restore_complete": "Successfully restored backup keys ({{progress}}/{{total}})",
|
||||
"restore_failed_bad_key": "Failed to restore backup. Key is invalid!",
|
||||
"restore_failed_unknown": "Failed to restore backup.",
|
||||
"delete_key_backup_title": "Delete key backup",
|
||||
"delete_key_backup_tooltip": "Delete backup",
|
||||
"delete_key_backup_subtitle": "Deleting key backup is permanent.",
|
||||
"delete_key_backup_message": "All encrypted message keys stored on the server will be permanently deleted.",
|
||||
"encrypted_messages_backup_description": "Online backup your encrypted messages keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.",
|
||||
"encrypted_messages_backup_title": "Encrypted messages backup",
|
||||
"encrypted_messages_backup_cross_signing_disabled": "Setup cross signing to backup your encrypted messages."
|
||||
},
|
||||
"DeviceManage": {
|
||||
"edit_session_name_title": "Edit session name",
|
||||
"edit_session_name_subtitle": "Session name",
|
||||
"edit_session_name_tooltip": "Edit session name",
|
||||
"current_device_label": "Current",
|
||||
"verify_session_button": "Verify",
|
||||
"unverified_sessions_title": "Unverified sessions",
|
||||
"unverified_sessions_none": "No unverified sessions",
|
||||
"unencrypted_sessions_title": "Sessions without encryption support",
|
||||
"verified_sessions_title": "Verified sessions",
|
||||
"verified_sessions_none": "No verified sessions",
|
||||
"setup_cross_signing_message": "Setup cross signing in case you lose all your sessions",
|
||||
"loading_devices": "Loading devices...",
|
||||
"logout_device_title": "Logout {{device}}",
|
||||
"logout_device_message": "You are about to log out the session for {{device}}",
|
||||
"logout_device_confirm": "Logout",
|
||||
"logout_device_tooltip": "Remove session",
|
||||
"session_verification_title": "Session Verification",
|
||||
"session_name_privacy_message": "Session names are visible to everyone, so do not put any private info here."
|
||||
},
|
||||
"CrossSigning": {
|
||||
"title": "Cross Signing",
|
||||
"setup_failed": "Failed to setup cross signing. Please try again",
|
||||
"setup": "Setup cross signing",
|
||||
"save_security_key_message": "Please save this security key somewhere safe",
|
||||
"security_key_dialog_title": "Security Key",
|
||||
"security_key_generation_message": "We will generate a Security Key, which you can use to manage message backups and session verification.",
|
||||
"security_key_generation_button": "Generate Key",
|
||||
"security_phrase_message": "Alternatively you can set a 'Security Phrase' so you don't have to remember the long Security Key, and optionally save the key as a backup",
|
||||
"security_phrase_label": "Security Phrase",
|
||||
"security_phrase_confirm_label": "Confirm Security Phrase",
|
||||
"security_phrase_set_button": "Set Phrase & Generate Key",
|
||||
"setup_dialog_title": "Setup cross signing",
|
||||
"setup_message": "Setup to verify and keep track of all your sessions. Also required to backup encrypted message.",
|
||||
"reset_keys_subtitle": "Resetting cross-signing keys is permanent.",
|
||||
"reset_keys_message": "Anyone you have verified with will see security alerts and your message backup will lost. You almost certainly do not want to do this, unless you have lost Security Key or Phrase and every session you can cross-sign from."
|
||||
},
|
||||
"AuthRequest": {
|
||||
"wrong_password": "Wrong password. Please enter the correct password",
|
||||
"request_failed": "Request failed!",
|
||||
"password_label": "Account password"
|
||||
},
|
||||
"Search": {
|
||||
"description": "Type # for rooms, @ for DMs and * for spaces. Hotkey: Ctrl + k"
|
||||
},
|
||||
"RoomViewInput": {
|
||||
"upload_progress": "Uploading: {{progress}}/{{total}} ({{percent}}%)",
|
||||
"tombstone_replaced": "This room has been replaced, and is no longer active.",
|
||||
"tombstone_permission_denied": "You do not have permission to post to this room",
|
||||
"send_message_placeholder": "Send a message...",
|
||||
"emoji_tooltip": "Emoji",
|
||||
"file_size": "Size: {{size}}",
|
||||
"cancel_reply_tooltip": "Cancel reply"
|
||||
},
|
||||
"RoomViewHeader": {
|
||||
"search_tooltip": "Search",
|
||||
"people_tooltip": "People",
|
||||
"members_tooltip": "Members"
|
||||
},
|
||||
"RoomViewFloating": {
|
||||
"jump_unread": "Jump to unread messages",
|
||||
"mark_read": "Mark as read",
|
||||
"jump_latest": "Jump to latest",
|
||||
"user_typing_1": "<bold><user_one/></bold> is typing...",
|
||||
"user_typing_2": "<bold><user_one/></bold> and <bold><user_two/></bold> are typing...",
|
||||
"user_typing_3": "<bold><user_one/></bold>, <bold><user_two/></bold> and <bold><user_three/></bold> are typing...",
|
||||
"user_typing_4": "<bold><user_one/></bold>, <bold><user_two/></bold>, <bold><user_three/></bold> and <bold><user_four/></bold> are typing...",
|
||||
"user_typing_other": "<bold>Several people</bold> are typing..."
|
||||
},
|
||||
"RoomViewContent": {
|
||||
"welcome_to_room": "Welcome to {{room_name}}!",
|
||||
"beginning_room": "This is the beginning of the <bold>{{room_name}}</bold> room.",
|
||||
"beginning_dm": "This is the beginning of your direct message history with <bold>@{{user_name}}</bold>.",
|
||||
"created_on": "Created on {{date, datetime}}",
|
||||
"new_messages": "New messages"
|
||||
},
|
||||
"RoomSettings": {
|
||||
"leave_room": "Leave room",
|
||||
"leave_room_confirm_message": "Are you sure you want to leave {{room_name}}?",
|
||||
"leave_room_confirm_button": "Leave",
|
||||
"notification_header": "Notifications (Changing this will only affect you)",
|
||||
"visibility_header": "Room visibility (Who can join)",
|
||||
"address_header": "Room addresses",
|
||||
"encryption_header": "Encryption",
|
||||
"message_history_header": "Message history visibility",
|
||||
"room_settings_subtitle": "room settings"
|
||||
},
|
||||
"RoomCommon": {
|
||||
"user_joined": "<bold><user/></bold> joined the room",
|
||||
"user_left": "<bold><user/></bold> left the room",
|
||||
"user_left_reason": "<bold><user/></bold> left the room: <reason/>",
|
||||
"user_invited": "<bold><inviter/></bold> invited <bold><user/></bold>",
|
||||
"invite_cancelled": "<bold><inviter/></bold> cancelled <bold><user/>'s</bold> invite",
|
||||
"invite_rejected": "<bold><user/></bold> rejected the invitation",
|
||||
"user_kicked": "<bold><actor/></bold> kicked <bold><user/></bold>",
|
||||
"user_kicked_reason": "<bold><actor/></bold> kicked <bold><user/></bold>: <reason/>",
|
||||
"user_banned": "<bold><actor/></bold> banned <bold><user/></bold>",
|
||||
"user_banned_reason": "<bold><actor/></bold> banned <bold><user/></bold>: <reason/>",
|
||||
"user_unbanned": "<bold><actor/> unbanned <bold><user/></bold>",
|
||||
"avatar_set": "<bold><user/></bold> set an avatar",
|
||||
"avatar_changed": "<bold><user/></bold> changed their avatar",
|
||||
"avatar_removed": "<bold><user/></bold> removed their avatar",
|
||||
"name_set": "<bold><user/></bold> set their display name to <bold><new_name/></bold>",
|
||||
"name_changed": "<bold><user/></bold> changed their display name to <bold><new_name/></bold>",
|
||||
"name_removed": "<bold><user/></bold> removed their display name <bold><last_name/></bold>"
|
||||
},
|
||||
"PublicRooms": {
|
||||
"could_not_join_alias": "Unable to join {{alias}}. Either the room is private or doesn't exist",
|
||||
"try_joining_alias": "Try joining {{alias}}",
|
||||
"joining_alias": "Joining {{alias}}...",
|
||||
"no_public_rooms": "No public rooms on {{homeserver}}",
|
||||
"no_result_found": "No result found for '{{input}}' on {{homeserver}}",
|
||||
"title": "Public Rooms",
|
||||
"search_room_name_alias": "Room name or alias",
|
||||
"search_button": "Search",
|
||||
"loading": "Loading public rooms from {{homeserver}}...",
|
||||
"searching": "Searching for '{{query}}' on {{homeserver}}...",
|
||||
"result_title": "Public rooms on {{homeserver}}",
|
||||
"search_result_title": "Search result for '{{query}}' on {{homeserver}}"
|
||||
},
|
||||
"ProfileViewer": {
|
||||
"kick_button": "Kick",
|
||||
"kick_reason_label": "Kick Reason",
|
||||
"ban_button": "Ban",
|
||||
"ban_reason_label": "Ban reason",
|
||||
"loading_sessions": "Loading sessions...",
|
||||
"no_sessions_found": "No sessions found.",
|
||||
"view_sessions_one": "View session",
|
||||
"view_sessions_other": "View {{count}} sessions",
|
||||
"send_direct_message_button": "Message",
|
||||
"creating_dm_room": "Creating room...",
|
||||
"ignore": "Ignore",
|
||||
"ignoring": "Ignoring...",
|
||||
"unignore": "Unignore",
|
||||
"unignoring": "Unignoring...",
|
||||
"change_power_level": "Change power level",
|
||||
"shared_power_message": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?",
|
||||
"demoting_self_message": "You will not be able to undo this change as you are demoting yourself. Are you sure?"
|
||||
},
|
||||
"PeopleDrawer": {
|
||||
"title": "People",
|
||||
"members_one": "{{count}} member",
|
||||
"members_other": "{{count}} members",
|
||||
"joined": "Joined",
|
||||
"invited": "Invited",
|
||||
"banned": "Banned",
|
||||
"search_no_results": "No results found",
|
||||
"view_more": "View more",
|
||||
"placeholder": "Search",
|
||||
"invite_tooltip": "Invite"
|
||||
},
|
||||
"ProfileEditor": {
|
||||
"remove_avatar": "Remove avatar",
|
||||
"remove_avatar_confirmation": "Are you sure that you want to remove your avatar?",
|
||||
"display_name_message": "Display name of {{user_name}}"
|
||||
},
|
||||
"DrawerBreadcrumb": {
|
||||
"home": "Home"
|
||||
},
|
||||
"DrawerHeader": {
|
||||
"add_rooms_or_spaces": "Add rooms or spaces",
|
||||
"create_new_space": "Create new space",
|
||||
"create_new_room": "Create new room",
|
||||
"join_public_room": "Join public room",
|
||||
"join_with_address": "Join with address",
|
||||
"add_existing": "Add existing",
|
||||
"manage_rooms": "Manage rooms",
|
||||
"home": "Home",
|
||||
"direct_messages": "Direct messages",
|
||||
"start_dm_tooltip": "Start DM",
|
||||
"add_rooms_spaces_tooltip": "Add rooms/spaces"
|
||||
},
|
||||
"SideBar": {
|
||||
"settings_tooltip": "Settings",
|
||||
"unverified_sessions_one": "{{count}} unverified session",
|
||||
"unverified_sessions_other": "{{count}} unverified sessions",
|
||||
"home_tooltip": "Home",
|
||||
"direct_messages_tooltip": "People",
|
||||
"pin_spaces_tooltip": "Pin spaces",
|
||||
"search_tooltip": "Search",
|
||||
"invites_tooltip": "Invites"
|
||||
},
|
||||
"JoinAlias": {
|
||||
"invalid_address": "Invalid address.",
|
||||
"looking_for_address": "Looking for address...",
|
||||
"joining_alias": "Joining {{alias_name}}...",
|
||||
"couldnt_find_room_or_space_alias": "Unable to find room/space with {{alias_name}}. Either the room/space is private or doesn't exist.",
|
||||
"couldnt_find_room_or_space": "Unable to join {{alias_name}}. Either the room/space is private or doesn't exist.",
|
||||
"address_label": "Address",
|
||||
"title": "Join with address"
|
||||
},
|
||||
"InviteUser": {
|
||||
"user_not_found": "{{user_name}} not found!",
|
||||
"no_matches_found": "No matches found for {{user_name}}",
|
||||
"invite_result": {
|
||||
"invited": "Invited",
|
||||
"already_joined": "Already joined",
|
||||
"already_invited": "Already invited",
|
||||
"banned": "Banned"
|
||||
},
|
||||
"search_label": "Name or User ID",
|
||||
"search_result_title": "Search result for user {{user_name}}",
|
||||
"searching_for_user": "Searching for user {{user_name}}...",
|
||||
"invite_to_room": "Invite to {{room}}",
|
||||
"invite_to_dm": "Direct Message"
|
||||
},
|
||||
"InviteList": {
|
||||
"accept_invite": "Accept",
|
||||
"reject_invite": "Reject",
|
||||
"direct_messages_title": "Direct Messages",
|
||||
"rooms_title": "Rooms",
|
||||
"spaces_title": "Spaces",
|
||||
"title": "Invites"
|
||||
},
|
||||
"EmojiVerification": {
|
||||
"waiting_for_response": "Waiting for response from other device...",
|
||||
"confirmation_prompt": "Confirm the emoji below are displayed on both devices, in the same order:",
|
||||
"emojis_match_button": "They match",
|
||||
"emojis_dont_match_button": "They don't match",
|
||||
"accept_request_from_other_device_message": "Please accept the request from other device.",
|
||||
"begin_verification_process_message": "Click accept to start the verification process.",
|
||||
"begin_verification_button_text": "Accept",
|
||||
"title": "Emoji Verification"
|
||||
},
|
||||
"DragDrop": {
|
||||
"drop_file_to_upload_prompt": "Drop file to upload"
|
||||
},
|
||||
"CreateRoom": {
|
||||
"private_room_short": "Private",
|
||||
"restricted_room_short": "Restricted",
|
||||
"public_room_short": "Public",
|
||||
"private_room_long": "Private (invite only)",
|
||||
"restricted_room_long": "Restricted (space member can join)",
|
||||
"public_room_long": "Public (anyone can join)",
|
||||
"visibility_title": "Visibility",
|
||||
"visibility_message": "Visibility (who can join)",
|
||||
"select_who_can_join_space": "Select who can join this space",
|
||||
"select_who_can_join_room": "Select who can join this room",
|
||||
"space_address": "Space address",
|
||||
"room_address": "Room address",
|
||||
"room_address_already_in_use": "{{room_address}} is already in use",
|
||||
"e2e_title": "Enable end-to-end encryption",
|
||||
"e2e_message": "You can’t disable this later. Bridges & most bots won’t work yet.",
|
||||
"role_title": "Select your role",
|
||||
"role_message": "Selecting 'Admin' sets your power level to 100 whereas 'Founder' sets it to 101.",
|
||||
"creating_room": "Creating room...",
|
||||
"creating_space": "Creating space...",
|
||||
"topic_label": "Topic (optional)",
|
||||
"space_name": "Space name",
|
||||
"room_name": "Room name",
|
||||
"role_admin": "Admin",
|
||||
"role_founder": "Founder",
|
||||
"create_room": "Create room",
|
||||
"create_space": "Create space",
|
||||
"home": "Home"
|
||||
}
|
||||
},
|
||||
"Molecules": {
|
||||
"ConfirmDialog": {
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"ReusableDialog": {
|
||||
"close_tooltip": "Close"
|
||||
},
|
||||
"FollowingMembers": {
|
||||
"users_following_1": "<bold><user_one/></bold> is following the conversation",
|
||||
"users_following_2": "<bold><user_one/></bold> and <bold><user_two/></bold> are following the conversation",
|
||||
"users_following_3": "<bold><user_one/></bold>, <bold><user_two/></bold>, and <bold><user_three/></bold> are following the conversation",
|
||||
"users_following_other": "<bold><user_one/></bold>, <bold><user_two/></bold>, <bold><user_three/></bold> and {{other_count}} others are following the conversation"
|
||||
},
|
||||
"ImageUpload": {
|
||||
"prompt": "Upload",
|
||||
"cancel": "Cancel",
|
||||
"remove": "Remove"
|
||||
},
|
||||
"ExportE2ERoomKeys": {
|
||||
"getting_keys": "Getting keys...",
|
||||
"encrypting_keys": "Encrypting Keys...",
|
||||
"password_does_not_match": "Password does not match.",
|
||||
"export_success": "Successfully exported all keys.",
|
||||
"export_failed": "Failed to export keys. Please try again.",
|
||||
"button_text": "Export"
|
||||
},
|
||||
"ImportE2ERoomKeys": {
|
||||
"decrypting_file": "Decrypting file...",
|
||||
"decrypting_messages": "Decrypting messages...",
|
||||
"import_success": "Successfully imported all keys",
|
||||
"import_failed": "Failed tp decrypt keys. Please try again",
|
||||
"import_keys_button": "Import Keys",
|
||||
"decrypt_button": "Decrypt"
|
||||
},
|
||||
"Media": {
|
||||
"open_new_tab": "Open in new tab",
|
||||
"download": "Download",
|
||||
"play_audio": "Play audio",
|
||||
"play_video": "Play video"
|
||||
},
|
||||
"Message": {
|
||||
"message_deleted": "*** This message has been deleted ***",
|
||||
"unable_to_load_reply": "*** Unable to load reply ***",
|
||||
"unknown_user": "** Unknown user **",
|
||||
"edited": "(edited)",
|
||||
"edit_placeholder": "Edit message",
|
||||
"user_reacted_1": "<bold><user_one/></bold> reacted with <emoji/>",
|
||||
"user_reacted_2": "<bold><user_one/></bold> and <bold><user_two/></bold> reacted with <emoji/>",
|
||||
"user_reacted_3": "<bold><user_one/></bold>, <bold><user_two/></bold> and <bold><user_three/></bold> reacted with <emoji/>",
|
||||
"user_reacted_other": "<bold><user_one/></bold>, <bold><user_two/></bold>, <bold><user_three/></bold> and {{other_count}} others reacted with <emoji/>",
|
||||
"add_reaction_tooltip": "Add reaction",
|
||||
"reply_tooltip": "Reply",
|
||||
"edit_tooltip": "Edit",
|
||||
"options_header": "Options",
|
||||
"options_tooltip": "Options",
|
||||
"read_receipts": "Read receipts",
|
||||
"view_source": "View source",
|
||||
"delete_message_prompt": "Delete message",
|
||||
"delete_message_confirmation": "Are you sure that you want to delete this message?",
|
||||
"delete_message_button": "Delete"
|
||||
},
|
||||
"PopupWindow": {
|
||||
"close_tooltip": "Back"
|
||||
},
|
||||
"PowerLevelSelector": {
|
||||
"placeholder": "Power level"
|
||||
},
|
||||
"RoomAliases": {
|
||||
"invalid_characters": "Invalid character: only letter, numbers and _- are allowed.",
|
||||
"validating_alias": "validating {{alias}}...",
|
||||
"alias_available": "{{alias}} is available.",
|
||||
"alias_unavailable": "{{alias}} is unavailable.",
|
||||
"deleting_alias": "Deleting...",
|
||||
"set_main_alias": "Set as Main",
|
||||
"publish_alias": "Publish",
|
||||
"unpublish_alias": "Unpublish",
|
||||
"delete_alias": "Delete",
|
||||
"main_alias": "Main",
|
||||
"publish_to_room_directory": {
|
||||
"title": "Publish to room directory",
|
||||
"publish_room_message": "Publish this room to the {{homeserver}} publish directory?",
|
||||
"publish_space_message": "Publish this space to the {{homeserver}} publish directory?"
|
||||
},
|
||||
"published_addresses": {
|
||||
"title": "Published addresses",
|
||||
"none": "No published addresses",
|
||||
"no_main_address": "No main address (select one from below)",
|
||||
"message_room": "Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first.",
|
||||
"message_space": "Published addresses can be used by anyone on any server to join your space. To publish an address, it needs to be set as a local address first."
|
||||
},
|
||||
"local_addresses": {
|
||||
"title": "Local addresses",
|
||||
"none": "No local addresses",
|
||||
"message_room": "Set local addresses for this room so users can find this room through your homeserver.",
|
||||
"message_space": "Set local addresses for this space so users can find this space through your homeserver.",
|
||||
"add": "Add local address",
|
||||
"add_button": "Add",
|
||||
"placeholder_room": "my_room_address",
|
||||
"placeholder_space": "my_space_address",
|
||||
"hide": "Hide local addresses",
|
||||
"view": "View local addresses"
|
||||
}
|
||||
},
|
||||
"RoomEncryption": {
|
||||
"encryption_public_room_message": "It is not recommended to add encryption in public room. Anyone can find and join public rooms, so anyone should be able to read messages in them.",
|
||||
"encryption_message": "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly",
|
||||
"encryption_cannot_be_disabled": "Once enabled, encryption cannot be disabled.",
|
||||
"enable_room_encryption": "Enable room encryption",
|
||||
"enable_encryption_prompt": "Enable Encryption",
|
||||
"enable_encryption_button": "Enable",
|
||||
"continue_button": "Continue"
|
||||
},
|
||||
"RoomHistoryVisibility": {
|
||||
"world_readable": "Anyone (including guests)",
|
||||
"shared": "Members (all messages)",
|
||||
"invited": "Members (messages after invite)",
|
||||
"joined": "Members (messages after join)",
|
||||
"changes_only_affect_future": "Changes to history visibility will only apply to future messages. The visibility of existing history will have no effect."
|
||||
},
|
||||
"RoomMembers": {
|
||||
"search_title": "Search member",
|
||||
"found_members_one": "Found — one member",
|
||||
"found_members_other": "Found — {{count}} members",
|
||||
"no_results": "No results found for {{term}}",
|
||||
"no_members": "No members to display",
|
||||
"view_more": "View more",
|
||||
"joined": "Joined",
|
||||
"invited": "Invited",
|
||||
"banned": "Banned",
|
||||
"search_placeholder": "Search for name"
|
||||
},
|
||||
"RoomNotification": {
|
||||
"default": "Global",
|
||||
"all_messages": "All messages",
|
||||
"mentions_and_keywords": "Mentions & Keywords",
|
||||
"mute": "Mute"
|
||||
},
|
||||
"RoomOptions": {
|
||||
"title": "Options for {{room_name}}",
|
||||
"leave": {
|
||||
"title": "Leave room",
|
||||
"subtitle": "Are you sure you want to leave the {{room_name}} room?",
|
||||
"button_text": "Leave"
|
||||
},
|
||||
"mark_as_read": "Mark as read",
|
||||
"notifications_heading": "Notifications",
|
||||
"invite": "Invite"
|
||||
},
|
||||
"RoomPermissions": {
|
||||
"default_role": {
|
||||
"name": "Default role",
|
||||
"description": "Set default role for all members"
|
||||
},
|
||||
"send_messages": {
|
||||
"name": "Send messages",
|
||||
"description": "Set minimum power level to send messages in a room"
|
||||
},
|
||||
"reactions": {
|
||||
"name": "Send reactions",
|
||||
"description": "Set minimum power level to send reactions in a room"
|
||||
},
|
||||
"delete": {
|
||||
"name": "Delete messages sent by others",
|
||||
"description": "Set minumum power level to delete messages in a room"
|
||||
},
|
||||
"notifications": {
|
||||
"name": "Ping room",
|
||||
"description": "Set minimum power level to ping room"
|
||||
},
|
||||
"manage_rooms": {
|
||||
"name": "Manage rooms in space",
|
||||
"description": "Set minimum power level to manage rooms in space"
|
||||
},
|
||||
"invite": {
|
||||
"name": "Invite",
|
||||
"description": "Set minimum power level to invite members"
|
||||
},
|
||||
"kick": {
|
||||
"name": "Kick",
|
||||
"description": "Set minimum power level to kick members"
|
||||
},
|
||||
"ban": {
|
||||
"name": "Ban",
|
||||
"description": "Set minimum power level to ban members"
|
||||
},
|
||||
"change_avatar": {
|
||||
"name": "Change avatar",
|
||||
"description": "Set minimum power level to change room/space avatar"
|
||||
},
|
||||
"change_name": {
|
||||
"name": "Change name",
|
||||
"description": "Set minimum power evel to change room/space name"
|
||||
},
|
||||
"change_topic": {
|
||||
"name": "Change topic",
|
||||
"description": "Set minimum power level to change room/space topic"
|
||||
},
|
||||
"change_settings": {
|
||||
"name": "Change settings",
|
||||
"description": "Set minimum power level to change settings"
|
||||
},
|
||||
"change_published_address": {
|
||||
"name": "Change published address",
|
||||
"description": "Set minimum power level to publish and set main address"
|
||||
},
|
||||
"change_permissions": {
|
||||
"name": "Change permissions",
|
||||
"description": "Set minimum power level to change permissions"
|
||||
},
|
||||
"enable_room_encryption": {
|
||||
"name": "Enable room encryption",
|
||||
"description": "Set minimum power level to enable room encryption"
|
||||
},
|
||||
"change_history_visibility": {
|
||||
"name": "Change history visibility",
|
||||
"description": "Set minimum power level to change room message history visibility"
|
||||
},
|
||||
"upgrade_room": {
|
||||
"name": "Upgrade room",
|
||||
"description": "Set minimum power level to upgrade room"
|
||||
},
|
||||
"pin_messages": {
|
||||
"name": "Pin messages",
|
||||
"description": "Set minimum power level to pin messages in a room"
|
||||
},
|
||||
"change_acls": {
|
||||
"name": "Change server ACLs",
|
||||
"description": "Set minimum power level to change server ACLs"
|
||||
},
|
||||
"modify_widgets": {
|
||||
"name": "Modify widgets",
|
||||
"description": "Set minimum power level to modify room widgets"
|
||||
},
|
||||
"groups": {
|
||||
"general": "General permissions",
|
||||
"manage_members": "Manage members permissions",
|
||||
"room": "Room profile permissions",
|
||||
"space": "Space profile permissions",
|
||||
"other": "Other permissions",
|
||||
"settings": "Settings permissions"
|
||||
}
|
||||
},
|
||||
"RoomProfile": {
|
||||
"saving_room_name": "Saving room name...",
|
||||
"saving_room_topic": "Saving room topic...",
|
||||
"save_success": "Saved successfully",
|
||||
"save_failed": "Unable to save",
|
||||
"remove_avatar_title": "Remove avatar",
|
||||
"remove_avatar_subtitle": "Are you sure that you want to remove room avatar?",
|
||||
"remove_avatar_button": "Remove",
|
||||
"permission_change_room_name": "You have permission to change room name only",
|
||||
"permission_change_room_topic": "You have permission to change room topic only",
|
||||
"permission_change_space_name": "You have permission to change space name only",
|
||||
"permission_change_space_topic": "You have permission to change space topic only",
|
||||
"name_label": "Name",
|
||||
"topic_label": "Topic"
|
||||
},
|
||||
"RoomSearch": {
|
||||
"title": "Room search",
|
||||
"placeholder": "Search for keywords",
|
||||
"search_button": "Search",
|
||||
"searching": "Searching room messages...",
|
||||
"subtitle": "Search room messages",
|
||||
"failed": "Failed to search messages",
|
||||
"no_results": "No results found",
|
||||
"encrypted_room": "Search does not work in encrypted room",
|
||||
"load_more": "Load more",
|
||||
"results_one": "{{count}} result for {{term}}",
|
||||
"results_other": "{{count}} results for {{term}}"
|
||||
},
|
||||
"RoomTile": {
|
||||
"invited_by_user_zero": "Invited by {{inviter}} to {{id}}",
|
||||
"invited_by_user_one": "Invited by {{inviter}} to {{id}} • {{member_count}} member",
|
||||
"invited_by_user_other": "Invited by {{inviter}} to {{id}} • {{member_count}} members",
|
||||
"invited_zero": "{{id}}",
|
||||
"invited_one": "{{id}} • {{member_count}} member",
|
||||
"invited_other": "{{id}} • {{member_count}} members"
|
||||
},
|
||||
"RoomVisibility": {
|
||||
"private": "Private (invite only)",
|
||||
"restricted": "Restricted (space members can join)",
|
||||
"restricted_unsupported": "Restricted (Unsupported: room required upgrade)",
|
||||
"public": "Public (anyone can join)"
|
||||
},
|
||||
"RoomEmojis": {
|
||||
"header": "Create Pack",
|
||||
"submit_button": "Create pack",
|
||||
"name_placeholder": "Pack Name",
|
||||
"no_packs": "No emoji or sticker packs."
|
||||
},
|
||||
"ImagePack": {
|
||||
"rename_title": "Rename",
|
||||
"rename_button": "Rename",
|
||||
"shortcode_label": "Shortcode",
|
||||
"delete_item_title": "Delete",
|
||||
"delete_item_message": "Are you sure that you want to delete {{key}}?",
|
||||
"delete_item_button": "Delete",
|
||||
"delete_pack_title": "Delete Pack",
|
||||
"delete_pack_message": "Are you sure that you want to delete {{pack_name}}?",
|
||||
"delete_pack_button": "Delete",
|
||||
"image_title": "Image",
|
||||
"image_shortcode": "Shortcode",
|
||||
"image_usage": "Usage",
|
||||
"view_more": "View {{count}} more",
|
||||
"view_less": "View less",
|
||||
"use_globally": "Use globally",
|
||||
"use_globally_description": "Add this pack to your account to use in all rooms.",
|
||||
"no_global_packs": "No global packs",
|
||||
"default_personal_pack_name": "Personal"
|
||||
},
|
||||
"ImagePackItem": {
|
||||
"rename_tooltip": "Rename",
|
||||
"delete_tooltip": "Delete"
|
||||
},
|
||||
"ImagePackUsageSelector": {
|
||||
"header": "Usage",
|
||||
"type_emoji": "Emoji",
|
||||
"type_sticker": "Sticker",
|
||||
"type_both": "Both"
|
||||
},
|
||||
"ImagePackUpload": {
|
||||
"remove_file_tooltip": "Remove file",
|
||||
"import_image_button": "Import image",
|
||||
"shortcode_placeholder": "shortcode",
|
||||
"upload_button": "Upload",
|
||||
"upload_button_progress": "Uploading..."
|
||||
},
|
||||
"ImagePackProfile": {
|
||||
"attribution_prompt": "Attribution",
|
||||
"name_prompt": "Name",
|
||||
"edit_tooltip": "Edit",
|
||||
"usage_header": "Usage",
|
||||
"pack_usage": "Pack usage"
|
||||
},
|
||||
"SpaceAddExisting": {
|
||||
"adding_items_one": "Adding one item...",
|
||||
"adding_items_other": "Adding {{count}} items...",
|
||||
"items_selected_one": "{{count}} item selected",
|
||||
"items_selected_other": "{{count}} items selected",
|
||||
"search_rooms_placeholder": "Search rooms",
|
||||
"no_results": "No results found",
|
||||
"add_button": "Add",
|
||||
"subtitle": "add existing rooms"
|
||||
},
|
||||
"SpaceOptions": {
|
||||
"leave_space": "Leave Space",
|
||||
"leave_space_confirmation": "Are you sure that you want to leave the {{space}} space?",
|
||||
"leave_space_confirm": "Leave",
|
||||
"invite": "Invite",
|
||||
"manage_rooms": "Manage rooms",
|
||||
"settings": "Settings",
|
||||
"leave": "Leave"
|
||||
},
|
||||
"SSOButtons": {
|
||||
"login_with": "Login with {{idp_name}}"
|
||||
}
|
||||
},
|
||||
"Templates": {
|
||||
"Auth": {
|
||||
"loading_homeserver_list": "Loading homeserver list...",
|
||||
"looking_for_homeserver": "Looking for homeserver...",
|
||||
"connecting_to_homeserver": "Connecting to {{homeserver}}...",
|
||||
"unable_to_connect": "Unable to connect. Please check your input",
|
||||
"username_taken": "Username is already taken",
|
||||
"registration_in_progress": "Registration in progress...",
|
||||
"link_about": "About",
|
||||
"link_twitter": "Twitter",
|
||||
"link_matrix": "Powered by Matrix",
|
||||
"dont_have_an_account": "Dont have an account?",
|
||||
"already_have_an_account": "Already have an account?",
|
||||
"register_link": "Register",
|
||||
"login_link": "Login",
|
||||
"redirecting": "Redirecting...",
|
||||
"login_in_progress": "Login in progress...",
|
||||
"bad_localpart_error": "Username can only contain characters a-z, 0-9, or '=_-./'",
|
||||
"user_id_too_long_error": "Your user ID, including the hostname, can't be more than 255 characters long.",
|
||||
"bad_password_error": "Password must contain at least 1 lowercase, 1 uppercase, 1 number, 1 non-alphanumeric character, 8-127 characters with no space.",
|
||||
"confirm_password_error": "Passwords don't match.",
|
||||
"bad_email_error": "Invalid email address",
|
||||
"invalid_password": "Invalid password",
|
||||
"check_credentials": "Please check your credentials",
|
||||
"login_types": {
|
||||
"username": "Username",
|
||||
"email": "Email"
|
||||
},
|
||||
"homeserver_form": {
|
||||
"title": "Homeserver",
|
||||
"header": "Homeserver list"
|
||||
},
|
||||
"login_form": {
|
||||
"title": "Login",
|
||||
"prompt_email": "Email",
|
||||
"prompt_username": "Username",
|
||||
"prompt_password": "Password",
|
||||
"login_button": "Login"
|
||||
},
|
||||
"register_form": {
|
||||
"title": "Register",
|
||||
"prompt_email_required": "Email (Required)",
|
||||
"prompt_email_optional": "Email",
|
||||
"prompt_username": "Username",
|
||||
"prompt_password": "Password",
|
||||
"prompt_confirm_password": "Confirm password",
|
||||
"register_button": "Register"
|
||||
},
|
||||
"terms_and_conditions": {
|
||||
"title": "Agree with terms",
|
||||
"description": "In order to complete registration, you need to agree to the terms and conditions.",
|
||||
"accept": "I accept <a>Terms and Conditions</a>",
|
||||
"submit_button": "Submit"
|
||||
},
|
||||
"validate_email": {
|
||||
"title": "Verify email",
|
||||
"continue_button": "Continue",
|
||||
"message": "Please check your email <bold>{{email_address}}</bold> and validate before continuing further"
|
||||
},
|
||||
"captcha": {
|
||||
"message": "Pleace check the box below to proceed"
|
||||
}
|
||||
},
|
||||
"Client": {
|
||||
"loading_messages": {
|
||||
"default": "Heating up",
|
||||
"message_one": "Almost there...",
|
||||
"message_two": "Looks like you have a lot of stuff to heat up!"
|
||||
},
|
||||
"logout_prompt": "Logout"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "Cinny",
|
||||
"short_name": "Cinny",
|
||||
"description": "Yet another matrix client",
|
||||
"dir": "auto",
|
||||
"lang": "en-US",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"start_url": "/",
|
||||
"background_color": "#fff",
|
||||
"theme_color": "#fff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/public/android/android-chrome-36x36.png",
|
||||
"sizes": "36x36",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/public/android/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
Graphics (c) by Ajay Bura (ajbura)
|
||||
Copyright (c) 2021-present Ajay Bura (ajbura)
|
||||
|
||||
Graphic content is licensed under a
|
||||
Creative Commons Attribution 4.0 International License.
|
||||
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,12 +1,4 @@
|
||||
<?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 points="14,3 14,5 17.8,5 12.9,9.9 14.3,11.3 19,6.6 19,10.2 21,10.2 21,3 "/>
|
||||
<path d="M3,10.2h2V5h5V3H5C3.9,3,3,3.9,3,5V10.2z"/>
|
||||
<path d="M5,14.2H3V19c0,1.1,0.9,2,2,2h5v-2H5V14.2z"/>
|
||||
<path d="M19,19h-5v2h5c1.1,0,2-0.9,2-2v-4.8h-2V19z"/>
|
||||
</g>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 3V5H17.8L12.9 9.9L14.3 11.3L19 6.6V10.2H21V3H14Z" fill="black"/>
|
||||
<path d="M5 5H10V3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V14.2H19V19H5V5Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 703 B After Width: | Height: | Size: 297 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92896 3.51471L3.51474 4.92892L5.97515 7.38933C4.46742 8.5776 3.32116 9.93994 2.7 10.8C2.1 11.5 2.1 12.5 2.7 13.2C4 15 7.6 19 12 19C13.5709 19 15.0398 18.4902 16.3384 17.7526L19.0711 20.4853L20.4853 19.0711L4.92896 3.51471ZM4.2 12C4.68291 11.3561 5.85678 9.9637 7.39721 8.81139L9.29238 10.7066C9.10496 11.0982 9 11.5368 9 12C9 13.6569 10.3431 15 12 15C12.4632 15 12.9018 14.895 13.2934 14.7076L14.8573 16.2715C13.9566 16.7128 12.9896 17 12 17C8.4 17 5.1 13.2 4.2 12Z" fill="black"/>
|
||||
<path d="M9.6226 5.37995L11.2906 7.04797C11.5254 7.01661 11.762 7 12 7C15.6 7 18.9 10.8 19.8 12C19.493 12.4094 18.9066 13.1213 18.1244 13.8817L19.5194 15.2768C20.2973 14.4974 20.9049 13.7471 21.3 13.2C21.9 12.5 21.9 11.5 21.3 10.8C20 9 16.4 5 12 5C11.1762 5 10.3805 5.14021 9.6226 5.37995Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 943 B |
@@ -1,13 +1,4 @@
|
||||
<?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>
|
||||
<g>
|
||||
<path d="M12,19c-4.4,0-8-4-9.3-5.8c-0.6-0.7-0.6-1.7,0-2.4C4,9,7.6,5,12,5s8,4,9.3,5.8c0.6,0.7,0.6,1.7,0,2.4C20,15,16.4,19,12,19
|
||||
z M12,7c-3.6,0-6.9,3.8-7.8,5c0.9,1.2,4.2,5,7.8,5s6.9-3.8,7.8-5C18.9,10.8,15.6,7,12,7z"/>
|
||||
</g>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</g>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 19C7.6 19 4 15 2.7 13.2C2.1 12.5 2.1 11.5 2.7 10.8C4 9 7.6 5 12 5C16.4 5 20 9 21.3 10.8C21.9 11.5 21.9 12.5 21.3 13.2C20 15 16.4 19 12 19ZM12 7C8.4 7 5.1 10.8 4.2 12C5.1 13.2 8.4 17 12 17C15.6 17 18.9 13.2 19.8 12C18.9 10.8 15.6 7 12 7Z" fill="black"/>
|
||||
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 718 B After Width: | Height: | Size: 508 B |
@@ -0,0 +1,15 @@
|
||||
<?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>
|
||||
<rect x="7" y="12" fill="#010101" width="10" height="2"/>
|
||||
<g>
|
||||
<circle fill="#010101" cx="19" cy="6" r="4"/>
|
||||
</g>
|
||||
<path fill="#010101" d="M13.3,8H7v2h7.5C14,9.4,13.6,8.7,13.3,8z"/>
|
||||
<path fill="#010101" d="M19,12v5.6l-2.4-1.3L16.1,16h-0.5H5V6h8c0-0.7,0.1-1.4,0.3-2H4.8C3.8,4,3,4.9,3,6v10c0,1.1,0.8,2,1.8,2
|
||||
h10.8l5.4,3v-9.3C20.4,11.9,19.7,12,19,12z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 811 B |
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
<path fill="#010101" d="M19,6v11.6l-2.4-1.3L16.1,16h-0.5H5V6H19 M19.2,4H4.8C3.8,4,3,4.9,3,6v10c0,1.1,0.8,2,1.8,2h10.8l5.4,3V6
|
||||
C21,4.9,20.2,4,19.2,4L19.2,4z"/>
|
||||
<rect x="7" y="8" fill="#010101" width="10" height="2"/>
|
||||
<rect x="7" y="12" fill="#010101" width="10" height="2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 732 B |
@@ -4,13 +4,14 @@
|
||||
<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"/>
|
||||
<polygon points="11,7 11,13 14.5,16.5 15.9,15.1 13,12.2 13,7 "/>
|
||||
<path d="M12,2C8.7,2,5.8,3.6,4,6V4H2.5v5h5V7.5H5.4C6.9,5.4,9.3,4,12,4c4.4,0,8,3.6,8,8s-3.6,8-8,8s-8-3.6-8-8H2
|
||||
c0,5.5,4.5,10,10,10s10-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"/>
|
||||
<polygon points="49,44 49,50 53.2,54.2 54.7,52.8 51,49.2 51,44 "/>
|
||||
<polygon points="45.5,47 40.5,47 40.5,42 42,42 42,45.5 45.5,45.5 "/>
|
||||
<path d="M50,40c-4.1,0-7.6,2.5-9.2,6h2.2c1.4-2.4,4-4,6.9-4c4.4,0,8,3.6,8,8s-3.6,8-8,8s-8-3.6-8-8h-2c0,5.5,4.5,10,10,10
|
||||
s10-4.5,10-10S55.5,40,50,40z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 985 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 3L21 8V19C21 20.1046 20.1046 21 19 21H5C3.89543 21 3 20.1046 3 19V5C3 3.89543 3.89543 3 5 3H16ZM19 9H17C15.8954 9 15 8.10457 15 7V5H5V19H19V9Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 12C9 13.6569 10.3431 15 12 15C13.6569 15 15 13.6569 15 12H17C17 14.7614 14.7614 17 12 17C9.23858 17 7 14.7614 7 12H9Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 501 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 8L10 13M4 14L10 8L12 5M2 5H14M7 2H8M12.913 17H20.087M12.913 17L11 21M12.913 17L15.7783 11.009C16.0092 10.5263 16.1246 10.2849 16.2826 10.2086C16.4199 10.1423 16.5801 10.1423 16.7174 10.2086C16.8754 10.2849 16.9908 10.5263 17.2217 11.009L20.087 17M20.087 17L22 21" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 586 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2707_1961)">
|
||||
<path d="M10.5867 17.3522C10.0727 17.4492 9.54226 17.5 9 17.5C4.30558 17.5 0.5 13.6944 0.5 9C0.5 4.30558 4.30558 0.5 9 0.5C13.6944 0.5 17.5 4.30558 17.5 9C17.5 9.54226 17.4492 10.0727 17.3522 10.5867C16.6511 10.2123 15.8503 10 15 10C12.2386 10 10 12.2386 10 15C10 15.8503 10.2123 16.6511 10.5867 17.3522Z" fill="white"/>
|
||||
<path d="M10 6.39999C10 6.67614 9.77614 6.89999 9.5 6.89999C9.22386 6.89999 9 6.67614 9 6.39999C9 6.12385 9.22386 5.89999 9.5 5.89999C9.77614 5.89999 10 6.12385 10 6.39999Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 0C4 0 0 4 0 9C0 14 4 18 9 18C9.63967 18 10.263 17.9345 10.8636 17.8099C10.3186 17.0091 10 16.0417 10 15C10 12.2386 12.2386 10 15 10C16.0417 10 17.0091 10.3186 17.8099 10.8636C17.9345 10.263 18 9.63967 18 9C18 4 14 0 9 0ZM1.2 10.8L4.7 8.5V8.2C4.7 6.4 6 5 7.8 4.8H8.2C9.4 4.8 10.5 5.4 11.1 6.4C11.4 6.3 11.7 6.3 12 6.3C12.4 6.3 12.8 6.3 13.2 6.4C13.9 6.6 14.6 6.9 15.2 7.3C14.6 7.1 14 7 13.3 7C12.1 7 11.1 7.4 10.4 8.4C9.7 9.3 9.3 10.4 9.3 11.6C9.3 13.1 8.9 14.5 8 15.8C7.93744 15.8834 7.87923 15.9625 7.82356 16.0381C7.6123 16.325 7.43739 16.5626 7.2 16.8C4.2 16.1 1.9 13.8 1.2 10.8Z" fill="black"/>
|
||||
<path d="M18 15C18 16.6569 16.6569 18 15 18C13.3431 18 12 16.6569 12 15C12 13.3431 13.3431 12 15 12C16.6569 12 18 13.3431 18 15Z" fill="#45B83B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2707_1961">
|
||||
<rect width="18" height="18" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2707_2015)">
|
||||
<path d="M10.5867 17.3522C10.0727 17.4492 9.54226 17.5 9 17.5C4.30558 17.5 0.5 13.6944 0.5 9C0.5 4.30558 4.30558 0.5 9 0.5C13.6944 0.5 17.5 4.30558 17.5 9C17.5 9.54226 17.4492 10.0727 17.3522 10.5867C16.6511 10.2123 15.8503 10 15 10C12.2386 10 10 12.2386 10 15C10 15.8503 10.2123 16.6511 10.5867 17.3522Z" fill="white"/>
|
||||
<path d="M10 6.39999C10 6.67614 9.77614 6.89999 9.5 6.89999C9.22386 6.89999 9 6.67614 9 6.39999C9 6.12385 9.22386 5.89999 9.5 5.89999C9.77614 5.89999 10 6.12385 10 6.39999Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 0C4 0 0 4 0 9C0 14 4 18 9 18C9.63967 18 10.263 17.9345 10.8636 17.8099C10.3186 17.0091 10 16.0417 10 15C10 12.2386 12.2386 10 15 10C16.0417 10 17.0091 10.3186 17.8099 10.8636C17.9345 10.263 18 9.63967 18 9C18 4 14 0 9 0ZM1.2 10.8L4.7 8.5V8.2C4.7 6.4 6 5 7.8 4.8H8.2C9.4 4.8 10.5 5.4 11.1 6.4C11.4 6.3 11.7 6.3 12 6.3C12.4 6.3 12.8 6.3 13.2 6.4C13.9 6.6 14.6 6.9 15.2 7.3C14.6 7.1 14 7 13.3 7C12.1 7 11.1 7.4 10.4 8.4C9.7 9.3 9.3 10.4 9.3 11.6C9.3 13.1 8.9 14.5 8 15.8C7.93744 15.8834 7.87923 15.9625 7.82356 16.0381C7.6123 16.325 7.43739 16.5626 7.2 16.8C4.2 16.1 1.9 13.8 1.2 10.8Z" fill="black"/>
|
||||
<path d="M18 15C18 16.6569 16.6569 18 15 18C13.3431 18 12 16.6569 12 15C12 13.3431 13.3431 12 15 12C16.6569 12 18 13.3431 18 15Z" fill="#989898"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2707_2015">
|
||||
<rect width="18" height="18" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,8 +1,4 @@
|
||||
import { avatarInitials } from '../../../util/common';
|
||||
|
||||
function cssVar(name) {
|
||||
return getComputedStyle(document.body).getPropertyValue(name);
|
||||
}
|
||||
import { avatarInitials, cssVar } from '../../../util/common';
|
||||
|
||||
// renders the avatar and returns it as an URL
|
||||
export default async function renderAvatar({
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
&--icon {
|
||||
@include dir.side(padding, var(--sp-tight), var(--sp-loose));
|
||||
|
||||
.ic-raw {
|
||||
@include dir.side(margin, 0, var(--sp-extra-tight));
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.ic-raw {
|
||||
@include dir.side(margin, 0, var(--sp-extra-tight));
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,12 @@ const IconButton = React.forwardRef(({
|
||||
variant, size, type,
|
||||
tooltip, tooltipPlacement, src,
|
||||
onClick, tabIndex, disabled, isImage,
|
||||
className,
|
||||
}, ref) => {
|
||||
const btn = (
|
||||
<button
|
||||
ref={ref}
|
||||
className={`ic-btn ic-btn-${variant}`}
|
||||
className={`ic-btn ic-btn-${variant} ${className}`}
|
||||
onMouseUp={(e) => blurOnBubbling(e, `.ic-btn-${variant}`)}
|
||||
onClick={onClick}
|
||||
// eslint-disable-next-line react/button-has-type
|
||||
@@ -47,6 +48,7 @@ IconButton.defaultProps = {
|
||||
tabIndex: 0,
|
||||
disabled: false,
|
||||
isImage: false,
|
||||
className: '',
|
||||
};
|
||||
|
||||
IconButton.propTypes = {
|
||||
@@ -60,6 +62,7 @@ IconButton.propTypes = {
|
||||
tabIndex: PropTypes.number,
|
||||
disabled: PropTypes.bool,
|
||||
isImage: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default IconButton;
|
||||
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './InfoCard.scss';
|
||||
|
||||
import Text from '../text/Text';
|
||||
import RawIcon from '../system-icons/RawIcon';
|
||||
import IconButton from '../button/IconButton';
|
||||
|
||||
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||
|
||||
function InfoCard({
|
||||
className, style,
|
||||
variant, iconSrc,
|
||||
title, content,
|
||||
rounded, requestClose,
|
||||
}) {
|
||||
const classes = [`info-card info-card--${variant}`];
|
||||
if (rounded) classes.push('info-card--rounded');
|
||||
if (className) classes.push(className);
|
||||
return (
|
||||
<div className={classes.join(' ')} style={style}>
|
||||
{iconSrc && (
|
||||
<div className="info-card__icon">
|
||||
<RawIcon color={`var(--ic-${variant}-high)`} src={iconSrc} />
|
||||
</div>
|
||||
)}
|
||||
<div className="info-card__content">
|
||||
<Text>{title}</Text>
|
||||
{content}
|
||||
</div>
|
||||
{requestClose && (
|
||||
<IconButton src={CrossIC} variant={variant} onClick={requestClose} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
InfoCard.defaultProps = {
|
||||
className: null,
|
||||
style: null,
|
||||
variant: 'surface',
|
||||
iconSrc: null,
|
||||
content: null,
|
||||
rounded: false,
|
||||
requestClose: null,
|
||||
};
|
||||
|
||||
InfoCard.propTypes = {
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.shape({}),
|
||||
variant: PropTypes.oneOf(['surface', 'primary', 'positive', 'caution', 'danger']),
|
||||
iconSrc: PropTypes.string,
|
||||
title: PropTypes.string.isRequired,
|
||||
content: PropTypes.node,
|
||||
rounded: PropTypes.bool,
|
||||
requestClose: PropTypes.func,
|
||||
};
|
||||
|
||||
export default InfoCard;
|
||||
@@ -0,0 +1,79 @@
|
||||
@use '.././../partials/flex';
|
||||
@use '.././../partials/dir';
|
||||
|
||||
.info-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
line-height: 0;
|
||||
padding: var(--sp-tight);
|
||||
@include dir.prop(border-left, 4px solid transparent, none);
|
||||
@include dir.prop(border-right, none, 4px solid transparent);
|
||||
|
||||
& > .ic-btn {
|
||||
padding: 0;
|
||||
border-radius: 4;
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin: 0 var(--sp-tight);
|
||||
@extend .cp-fx__item-one;
|
||||
|
||||
& > *:nth-child(2) {
|
||||
margin-top: var(--sp-ultra-tight);
|
||||
}
|
||||
}
|
||||
|
||||
&--rounded {
|
||||
@include dir.prop(
|
||||
border-radius,
|
||||
0 var(--bo-radius) var(--bo-radius) 0,
|
||||
var(--bo-radius) 0 0 var(--bo-radius)
|
||||
);
|
||||
}
|
||||
|
||||
&--surface {
|
||||
border-color: var(--bg-surface-border);
|
||||
background-color: var(--bg-surface-hover);
|
||||
|
||||
}
|
||||
&--primary {
|
||||
border-color: var(--bg-primary);
|
||||
background-color: var(--bg-primary-hover);
|
||||
& .text {
|
||||
color: var(--tc-primary-high);
|
||||
&-b3 {
|
||||
color: var(--tc-primary-normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
&--positive {
|
||||
border-color: var(--bg-positive-border);
|
||||
background-color: var(--bg-positive-hover);
|
||||
& .text {
|
||||
color: var(--tc-positive-high);
|
||||
&-b3 {
|
||||
color: var(--tc-positive-normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
&--caution {
|
||||
border-color: var(--bg-caution-border);
|
||||
background-color: var(--bg-caution-hover);
|
||||
& .text {
|
||||
color: var(--tc-caution-high);
|
||||
&-b3 {
|
||||
color: var(--tc-caution-normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
&--danger {
|
||||
border-color: var(--bg-danger-border);
|
||||
background-color: var(--bg-danger-hover);
|
||||
& .text {
|
||||
color: var(--tc-danger-high);
|
||||
&-b3 {
|
||||
color: var(--tc-danger-normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ function Input({
|
||||
{ resizable
|
||||
? (
|
||||
<TextareaAutosize
|
||||
dir="auto"
|
||||
style={{ minHeight: `${minHeight}px` }}
|
||||
name={name}
|
||||
id={id}
|
||||
@@ -34,6 +35,7 @@ function Input({
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
dir="auto"
|
||||
ref={forwardRef}
|
||||
id={id}
|
||||
name={name}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './Math.scss';
|
||||
|
||||
import katex from 'katex';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import 'katex/dist/contrib/copy-tex';
|
||||
|
||||
const Math = React.memo(({
|
||||
content, throwOnError, errorColor, displayMode,
|
||||
}) => {
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
katex.render(content, ref.current, { throwOnError, errorColor, displayMode });
|
||||
}, [content, throwOnError, errorColor, displayMode]);
|
||||
|
||||
return <span ref={ref} />;
|
||||
});
|
||||
Math.defaultProps = {
|
||||
throwOnError: null,
|
||||
errorColor: null,
|
||||
displayMode: null,
|
||||
};
|
||||
Math.propTypes = {
|
||||
content: PropTypes.string.isRequired,
|
||||
throwOnError: PropTypes.bool,
|
||||
errorColor: PropTypes.string,
|
||||
displayMode: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Math;
|
||||
@@ -0,0 +1,3 @@
|
||||
.katex-display {
|
||||
margin: 0 !important;
|
||||
}
|
||||
@@ -42,7 +42,6 @@ function RawModal({
|
||||
shouldCloseOnEsc={closeFromOutside}
|
||||
shouldCloseOnOverlayClick={closeFromOutside}
|
||||
shouldReturnFocusAfterClose={false}
|
||||
closeTimeoutMS={300}
|
||||
>
|
||||
{children}
|
||||
</Modal>
|
||||
|
||||
@@ -1,33 +1,9 @@
|
||||
.ReactModal__Overlay {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms var(--fluid-slide-up);
|
||||
}
|
||||
.ReactModal__Overlay--after-open{
|
||||
opacity: 1;
|
||||
}
|
||||
.ReactModal__Overlay--before-close{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ReactModal__Content {
|
||||
transform: translateY(100%);
|
||||
transition: transform 200ms var(--fluid-slide-up);
|
||||
}
|
||||
|
||||
.ReactModal__Content--after-open{
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.ReactModal__Content--before-close{
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.raw-modal {
|
||||
--small-modal-width: 525px;
|
||||
--medium-modal-width: 712px;
|
||||
--large-modal-width: 1024px;
|
||||
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: var(--bo-radius);
|
||||
@@ -60,4 +36,31 @@
|
||||
height: 100%;
|
||||
background-color: var(--bg-overlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ReactModal__Overlay {
|
||||
animation: raw-modal--overlay 150ms;
|
||||
}
|
||||
|
||||
.ReactModal__Content {
|
||||
animation: raw-modal--content 150ms;
|
||||
}
|
||||
|
||||
@keyframes raw-modal--content {
|
||||
0% {
|
||||
transform: translateY(100px);
|
||||
opacity: .5;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes raw-modal--overlay {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,18 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './RawIcon.scss';
|
||||
|
||||
function RawIcon({
|
||||
color, size, src, isImage,
|
||||
}) {
|
||||
function RawIcon({ color, size, src, isImage }) {
|
||||
const style = {};
|
||||
if (color !== null) style.backgroundColor = color;
|
||||
if (isImage) {
|
||||
style.backgroundColor = 'transparent';
|
||||
style.backgroundImage = `url(${src})`;
|
||||
style.backgroundImage = `url("${src}")`;
|
||||
} else {
|
||||
style.WebkitMaskImage = `url(${src})`;
|
||||
style.maskImage = `url(${src})`;
|
||||
style.WebkitMaskImage = `url("${src}")`;
|
||||
style.maskImage = `url("${src}")`;
|
||||
}
|
||||
|
||||
return <span className={`ic-raw ic-raw-${size}`} style={style}> </span>;
|
||||
return <span className={`ic-raw ic-raw-${size}`} style={style} />;
|
||||
}
|
||||
|
||||
RawIcon.defaultProps = {
|
||||
|
||||
@@ -74,7 +74,7 @@ Tabs.defaultProps = {
|
||||
|
||||
Tabs.propTypes = {
|
||||
items: PropTypes.arrayOf(
|
||||
PropTypes.exact({
|
||||
PropTypes.shape({
|
||||
iconSrc: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
@@ -84,4 +84,4 @@ Tabs.propTypes = {
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export { Tabs as default };
|
||||
export default Tabs;
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import dateFormat from 'dateformat';
|
||||
import { isInSameDay } from '../../../util/common';
|
||||
|
||||
function Time({ timestamp, fullTime }) {
|
||||
const date = new Date(timestamp);
|
||||
|
||||
const formattedFullTime = dateFormat(date, 'dd mmmm yyyy, hh:MM TT');
|
||||
let formattedDate = formattedFullTime;
|
||||
|
||||
if (!fullTime) {
|
||||
const compareDate = new Date();
|
||||
const isToday = isInSameDay(date, compareDate);
|
||||
compareDate.setDate(compareDate.getDate() - 1);
|
||||
const isYesterday = isInSameDay(date, compareDate);
|
||||
|
||||
formattedDate = dateFormat(date, isToday || isYesterday ? 'hh:MM TT' : 'dd/mm/yyyy');
|
||||
if (isYesterday) {
|
||||
formattedDate = `Yesterday, ${formattedDate}`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<time
|
||||
dateTime={date.toISOString()}
|
||||
title={formattedFullTime}
|
||||
>
|
||||
{formattedDate}
|
||||
</time>
|
||||
);
|
||||
}
|
||||
|
||||
Time.defaultProps = {
|
||||
fullTime: false,
|
||||
};
|
||||
|
||||
Time.propTypes = {
|
||||
timestamp: PropTypes.number.isRequired,
|
||||
fullTime: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Time;
|
||||
@@ -0,0 +1,22 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import initMatrix from '../../client/initMatrix';
|
||||
|
||||
export function useAccountData(eventType) {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const [event, setEvent] = useState(mx.getAccountData(eventType));
|
||||
|
||||
useEffect(() => {
|
||||
const handleChange = (mEvent) => {
|
||||
if (mEvent.getType() !== eventType) return;
|
||||
setEvent(mEvent);
|
||||
};
|
||||
mx.on('accountData', handleChange);
|
||||
return () => {
|
||||
mx.removeListener('accountData', handleChange);
|
||||
};
|
||||
}, [eventType]);
|
||||
|
||||
return event;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import initMatrix from '../../client/initMatrix';
|
||||
import { hasCrossSigningAccountData } from '../../util/matrixUtil';
|
||||
|
||||
export function useCrossSigningStatus() {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const [isCSEnabled, setIsCSEnabled] = useState(hasCrossSigningAccountData());
|
||||
|
||||
useEffect(() => {
|
||||
if (isCSEnabled) return null;
|
||||
const handleAccountData = (event) => {
|
||||
if (event.getType() === 'm.cross_signing.master') {
|
||||
setIsCSEnabled(true);
|
||||
}
|
||||
};
|
||||
|
||||
mx.on('accountData', handleAccountData);
|
||||
return () => {
|
||||
mx.removeListener('accountData', handleAccountData);
|
||||
};
|
||||
}, [isCSEnabled === false]);
|
||||
return isCSEnabled;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import initMatrix from '../../client/initMatrix';
|
||||
|
||||
export function useDeviceList() {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const [deviceList, setDeviceList] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const updateDevices = () => mx.getDevices().then((data) => {
|
||||
if (!isMounted) return;
|
||||
setDeviceList(data.devices || []);
|
||||
});
|
||||
updateDevices();
|
||||
|
||||
const handleDevicesUpdate = (users) => {
|
||||
if (users.includes(mx.getUserId())) {
|
||||
updateDevices();
|
||||
}
|
||||
};
|
||||
|
||||
mx.on('crypto.devicesUpdated', handleDevicesUpdate);
|
||||
return () => {
|
||||
mx.removeListener('crypto.devicesUpdated', handleDevicesUpdate);
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
return deviceList;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import Backend from 'i18next-http-backend'
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
// detect user language
|
||||
// learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
.use(LanguageDetector)
|
||||
// pass the i18n instance to react-i18next.
|
||||
.use(initReactI18next)
|
||||
// init i18next
|
||||
// for all options read: https://www.i18next.com/overview/configuration-options
|
||||
.init({
|
||||
debug: true,
|
||||
fallbackLng: 'en',
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
backend: {
|
||||
loadPath: '/public/locales/{{lng}}/translation.json',
|
||||
}
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './ConfirmDialog.scss';
|
||||
|
||||
import { openReusableDialog } from '../../../client/action/navigation';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import Button from '../../atoms/button/Button';
|
||||
|
||||
function ConfirmDialog({
|
||||
desc, actionTitle, actionType, onComplete,
|
||||
}) {
|
||||
return (
|
||||
<div className="confirm-dialog">
|
||||
<Text>{desc}</Text>
|
||||
<div className="confirm-dialog__btn">
|
||||
<Button variant={actionType} onClick={() => onComplete(true)}>{actionTitle}</Button>
|
||||
<Button onClick={() => onComplete(false)}>Cancel</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
ConfirmDialog.propTypes = {
|
||||
desc: PropTypes.string.isRequired,
|
||||
actionTitle: PropTypes.string.isRequired,
|
||||
actionType: PropTypes.oneOf(['primary', 'positive', 'danger', 'caution']).isRequired,
|
||||
onComplete: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} title title of confirm dialog
|
||||
* @param {string} desc description of confirm dialog
|
||||
* @param {string} actionTitle title of main action to take
|
||||
* @param {'primary' | 'positive' | 'danger' | 'caution'} actionType type of action. default=primary
|
||||
* @return {Promise<boolean>} does it get's confirmed or not
|
||||
*/
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const confirmDialog = (title, desc, actionTitle, actionType = 'primary') => new Promise((resolve) => {
|
||||
let isCompleted = false;
|
||||
openReusableDialog(
|
||||
<Text variant="s1" weight="medium">{title}</Text>,
|
||||
(requestClose) => (
|
||||
<ConfirmDialog
|
||||
desc={desc}
|
||||
actionTitle={actionTitle}
|
||||
actionType={actionType}
|
||||
onComplete={(isConfirmed) => {
|
||||
isCompleted = true;
|
||||
resolve(isConfirmed);
|
||||
requestClose();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
() => {
|
||||
if (!isCompleted) resolve(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
.confirm-dialog {
|
||||
padding: var(--sp-normal);
|
||||
|
||||
& > .text {
|
||||
padding-bottom: var(--sp-normal);
|
||||
}
|
||||
&__btn {
|
||||
display: flex;
|
||||
gap: var(--sp-normal);
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,11 @@ import RawModal from '../../atoms/modal/RawModal';
|
||||
function Dialog({
|
||||
className, isOpen, title, onAfterOpen, onAfterClose,
|
||||
contentOptions, onRequestClose, closeFromOutside, children,
|
||||
invisibleScroll,
|
||||
}) {
|
||||
return (
|
||||
<RawModal
|
||||
className={`${className === null ? '' : `${className} `}dialog-model`}
|
||||
className={`${className === null ? '' : `${className} `}dialog-modal`}
|
||||
isOpen={isOpen}
|
||||
onAfterOpen={onAfterOpen}
|
||||
onAfterClose={onAfterClose}
|
||||
@@ -36,7 +37,7 @@ function Dialog({
|
||||
{contentOptions}
|
||||
</Header>
|
||||
<div className="dialog__content__wrapper">
|
||||
<ScrollView autoHide>
|
||||
<ScrollView autoHide={!invisibleScroll} invisible={invisibleScroll}>
|
||||
<div className="dialog__content-container">
|
||||
{children}
|
||||
</div>
|
||||
@@ -55,6 +56,7 @@ Dialog.defaultProps = {
|
||||
onAfterClose: null,
|
||||
onRequestClose: null,
|
||||
closeFromOutside: true,
|
||||
invisibleScroll: false,
|
||||
};
|
||||
|
||||
Dialog.propTypes = {
|
||||
@@ -67,6 +69,7 @@ Dialog.propTypes = {
|
||||
onRequestClose: PropTypes.func,
|
||||
closeFromOutside: PropTypes.bool,
|
||||
children: PropTypes.node.isRequired,
|
||||
invisibleScroll: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Dialog;
|
||||
@@ -1,4 +1,4 @@
|
||||
.dialog-model {
|
||||
.dialog-modal {
|
||||
--modal-height: 656px;
|
||||
max-height: min(100%, var(--modal-height));
|
||||
display: flex;
|
||||
@@ -21,8 +21,3 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog__content-container {
|
||||
padding-top: var(--sp-extra-tight);
|
||||
padding-bottom: var(--sp-extra-loose);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import cons from '../../../client/state/cons';
|
||||
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import IconButton from '../../atoms/button/IconButton';
|
||||
import Dialog from './Dialog';
|
||||
|
||||
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||
|
||||
function ReusableDialog() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleOpen = (title, render, afterClose) => {
|
||||
setIsOpen(true);
|
||||
setData({ title, render, afterClose });
|
||||
};
|
||||
navigation.on(cons.events.navigation.REUSABLE_DIALOG_OPENED, handleOpen);
|
||||
return () => {
|
||||
navigation.removeListener(cons.events.navigation.REUSABLE_DIALOG_OPENED, handleOpen);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleAfterClose = () => {
|
||||
data.afterClose?.();
|
||||
setData(null);
|
||||
};
|
||||
|
||||
const handleRequestClose = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
isOpen={isOpen}
|
||||
title={data?.title || ''}
|
||||
onAfterClose={handleAfterClose}
|
||||
onRequestClose={handleRequestClose}
|
||||
contentOptions={<IconButton src={CrossIC} onClick={handleRequestClose} tooltip="Close" />}
|
||||
invisibleScroll
|
||||
>
|
||||
{data?.render(handleRequestClose) || <div />}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReusableDialog;
|
||||
@@ -0,0 +1,174 @@
|
||||
import React from 'react';
|
||||
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
import { openReusableContextMenu } from '../../../client/action/navigation';
|
||||
import { getEventCords } from '../../../util/common';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import Button from '../../atoms/button/Button';
|
||||
import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
|
||||
import SettingTile from '../setting-tile/SettingTile';
|
||||
|
||||
import NotificationSelector from './NotificationSelector';
|
||||
|
||||
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
|
||||
|
||||
import { useAccountData } from '../../hooks/useAccountData';
|
||||
|
||||
export const notifType = {
|
||||
ON: 'on',
|
||||
OFF: 'off',
|
||||
NOISY: 'noisy',
|
||||
};
|
||||
export const typeToLabel = {
|
||||
[notifType.ON]: 'On',
|
||||
[notifType.OFF]: 'Off',
|
||||
[notifType.NOISY]: 'Noisy',
|
||||
};
|
||||
Object.freeze(notifType);
|
||||
|
||||
const DM = '.m.rule.room_one_to_one';
|
||||
const ENC_DM = '.m.rule.encrypted_room_one_to_one';
|
||||
const ROOM = '.m.rule.message';
|
||||
const ENC_ROOM = '.m.rule.encrypted';
|
||||
|
||||
export function getActionType(rule) {
|
||||
const { actions } = rule;
|
||||
if (actions.find((action) => action?.set_tweak === 'sound')) return notifType.NOISY;
|
||||
if (actions.find((action) => action?.set_tweak === 'highlight')) return notifType.ON;
|
||||
if (actions.find((action) => action === 'dont_notify')) return notifType.OFF;
|
||||
return notifType.OFF;
|
||||
}
|
||||
|
||||
export function getTypeActions(type, highlightValue = false) {
|
||||
if (type === notifType.OFF) return ['dont_notify'];
|
||||
|
||||
const highlight = { set_tweak: 'highlight' };
|
||||
if (typeof highlightValue === 'boolean') highlight.value = highlightValue;
|
||||
if (type === notifType.ON) return ['notify', highlight];
|
||||
|
||||
const sound = { set_tweak: 'sound', value: 'default' };
|
||||
return ['notify', sound, highlight];
|
||||
}
|
||||
|
||||
function useGlobalNotif() {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const pushRules = useAccountData('m.push_rules')?.getContent();
|
||||
const underride = pushRules?.global?.underride ?? [];
|
||||
const rulesToType = {
|
||||
[DM]: notifType.ON,
|
||||
[ENC_DM]: notifType.ON,
|
||||
[ROOM]: notifType.NOISY,
|
||||
[ENC_ROOM]: notifType.NOISY,
|
||||
};
|
||||
|
||||
const getRuleCondition = (rule) => {
|
||||
const condition = [];
|
||||
if (rule === DM || rule === ENC_DM) {
|
||||
condition.push({ kind: 'room_member_count', is: '2' });
|
||||
}
|
||||
condition.push({
|
||||
kind: 'event_match',
|
||||
key: 'type',
|
||||
pattern: [ENC_DM, ENC_ROOM].includes(rule) ? 'm.room.encrypted' : 'm.room.message',
|
||||
});
|
||||
return condition;
|
||||
};
|
||||
|
||||
const setRule = (rule, type) => {
|
||||
const content = pushRules ?? {};
|
||||
if (!content.global) content.global = {};
|
||||
if (!content.global.underride) content.global.underride = [];
|
||||
const ur = content.global.underride;
|
||||
let ruleContent = ur.find((action) => action?.rule_id === rule);
|
||||
if (!ruleContent) {
|
||||
ruleContent = {
|
||||
conditions: getRuleCondition(type),
|
||||
actions: [],
|
||||
rule_id: rule,
|
||||
default: true,
|
||||
enabled: true,
|
||||
};
|
||||
ur.push(ruleContent);
|
||||
}
|
||||
ruleContent.actions = getTypeActions(type);
|
||||
|
||||
mx.setAccountData('m.push_rules', content);
|
||||
};
|
||||
|
||||
const dmRule = underride.find((rule) => rule.rule_id === DM);
|
||||
const encDmRule = underride.find((rule) => rule.rule_id === ENC_DM);
|
||||
const roomRule = underride.find((rule) => rule.rule_id === ROOM);
|
||||
const encRoomRule = underride.find((rule) => rule.rule_id === ENC_ROOM);
|
||||
|
||||
if (dmRule) rulesToType[DM] = getActionType(dmRule);
|
||||
if (encDmRule) rulesToType[ENC_DM] = getActionType(encDmRule);
|
||||
if (roomRule) rulesToType[ROOM] = getActionType(roomRule);
|
||||
if (encRoomRule) rulesToType[ENC_ROOM] = getActionType(encRoomRule);
|
||||
|
||||
return [rulesToType, setRule];
|
||||
}
|
||||
|
||||
function GlobalNotification() {
|
||||
const [rulesToType, setRule] = useGlobalNotif();
|
||||
|
||||
const onSelect = (evt, rule) => {
|
||||
openReusableContextMenu(
|
||||
'bottom',
|
||||
getEventCords(evt, '.btn-surface'),
|
||||
(requestClose) => (
|
||||
<NotificationSelector
|
||||
value={rulesToType[rule]}
|
||||
onSelect={(value) => {
|
||||
if (rulesToType[rule] !== value) setRule(rule, value);
|
||||
requestClose();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="global-notification">
|
||||
<MenuHeader>Global Notifications</MenuHeader>
|
||||
<SettingTile
|
||||
title="Direct messages"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, DM)} iconSrc={ChevronBottomIC}>
|
||||
{ typeToLabel[rulesToType[DM]] }
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all direct message.</Text>}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Encrypted direct messages"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, ENC_DM)} iconSrc={ChevronBottomIC}>
|
||||
{typeToLabel[rulesToType[ENC_DM]]}
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all encrypted direct message.</Text>}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Rooms messages"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, ROOM)} iconSrc={ChevronBottomIC}>
|
||||
{typeToLabel[rulesToType[ROOM]]}
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all room message.</Text>}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Encrypted rooms messages"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, ENC_ROOM)} iconSrc={ChevronBottomIC}>
|
||||
{typeToLabel[rulesToType[ENC_ROOM]]}
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all encrypted room message.</Text>}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GlobalNotification;
|
||||
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import './IgnoreUserList.scss';
|
||||
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
import * as roomActions from '../../../client/action/room';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import Chip from '../../atoms/chip/Chip';
|
||||
import Input from '../../atoms/input/Input';
|
||||
import Button from '../../atoms/button/Button';
|
||||
import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
|
||||
import SettingTile from '../setting-tile/SettingTile';
|
||||
|
||||
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||
|
||||
import { useAccountData } from '../../hooks/useAccountData';
|
||||
|
||||
function IgnoreUserList() {
|
||||
useAccountData('m.ignored_user_list');
|
||||
const ignoredUsers = initMatrix.matrixClient.getIgnoredUsers();
|
||||
|
||||
const handleSubmit = (evt) => {
|
||||
evt.preventDefault();
|
||||
const { ignoreInput } = evt.target.elements;
|
||||
const value = ignoreInput.value.trim();
|
||||
const userIds = value.split(' ').filter((v) => v.match(/^@\S+:\S+$/));
|
||||
if (userIds.length === 0) return;
|
||||
ignoreInput.value = '';
|
||||
roomActions.ignore(userIds);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ignore-user-list">
|
||||
<MenuHeader>Ignored users</MenuHeader>
|
||||
<SettingTile
|
||||
title="Ignore user"
|
||||
content={(
|
||||
<div className="ignore-user-list__users">
|
||||
<Text variant="b3">Ignore userId if you do not want to receive their messages or invites.</Text>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Input name="ignoreInput" required />
|
||||
<Button variant="primary" type="submit">Ignore</Button>
|
||||
</form>
|
||||
{ignoredUsers.length > 0 && (
|
||||
<div>
|
||||
{ignoredUsers.map((uId) => (
|
||||
<Chip
|
||||
iconSrc={CrossIC}
|
||||
key={uId}
|
||||
text={uId}
|
||||
iconColor={CrossIC}
|
||||
onClick={() => roomActions.unignore([uId])}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default IgnoreUserList;
|
||||
@@ -0,0 +1,17 @@
|
||||
.ignore-user-list {
|
||||
&__users {
|
||||
& form,
|
||||
& > div:last-child {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--sp-tight);
|
||||
}
|
||||
|
||||
& form {
|
||||
margin: var(--sp-extra-tight) 0 var(--sp-normal);
|
||||
.input-container {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
import React from 'react';
|
||||
import './KeywordNotification.scss';
|
||||
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
import { openReusableContextMenu } from '../../../client/action/navigation';
|
||||
import { getEventCords } from '../../../util/common';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import Chip from '../../atoms/chip/Chip';
|
||||
import Input from '../../atoms/input/Input';
|
||||
import Button from '../../atoms/button/Button';
|
||||
import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
|
||||
import SettingTile from '../setting-tile/SettingTile';
|
||||
|
||||
import NotificationSelector from './NotificationSelector';
|
||||
|
||||
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
|
||||
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||
|
||||
import { useAccountData } from '../../hooks/useAccountData';
|
||||
import {
|
||||
notifType, typeToLabel, getActionType, getTypeActions,
|
||||
} from './GlobalNotification';
|
||||
|
||||
const DISPLAY_NAME = '.m.rule.contains_display_name';
|
||||
const ROOM_PING = '.m.rule.roomnotif';
|
||||
const USERNAME = '.m.rule.contains_user_name';
|
||||
const KEYWORD = 'keyword';
|
||||
|
||||
function useKeywordNotif() {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const pushRules = useAccountData('m.push_rules')?.getContent();
|
||||
const override = pushRules?.global?.override ?? [];
|
||||
const content = pushRules?.global?.content ?? [];
|
||||
|
||||
const rulesToType = {
|
||||
[DISPLAY_NAME]: notifType.NOISY,
|
||||
[ROOM_PING]: notifType.NOISY,
|
||||
[USERNAME]: notifType.NOISY,
|
||||
};
|
||||
|
||||
const setRule = (rule, type) => {
|
||||
const evtContent = pushRules ?? {};
|
||||
if (!evtContent.global) evtContent.global = {};
|
||||
if (!evtContent.global.override) evtContent.global.override = [];
|
||||
if (!evtContent.global.content) evtContent.global.content = [];
|
||||
const or = evtContent.global.override;
|
||||
const ct = evtContent.global.content;
|
||||
|
||||
if (rule === DISPLAY_NAME || rule === ROOM_PING) {
|
||||
let orRule = or.find((r) => r?.rule_id === rule);
|
||||
if (!orRule) {
|
||||
orRule = {
|
||||
conditions: [],
|
||||
actions: [],
|
||||
rule_id: rule,
|
||||
default: true,
|
||||
enabled: true,
|
||||
};
|
||||
or.push(orRule);
|
||||
}
|
||||
if (rule === DISPLAY_NAME) {
|
||||
orRule.conditions = [{ kind: 'contains_display_name' }];
|
||||
orRule.actions = getTypeActions(type, true);
|
||||
} else {
|
||||
orRule.conditions = [
|
||||
{ kind: 'event_match', key: 'content.body', pattern: '@room' },
|
||||
{ kind: 'sender_notification_permission', key: 'room' },
|
||||
];
|
||||
orRule.actions = getTypeActions(type, true);
|
||||
}
|
||||
} else if (rule === USERNAME) {
|
||||
let usernameRule = ct.find((r) => r?.rule_id === rule);
|
||||
if (!usernameRule) {
|
||||
const userId = mx.getUserId();
|
||||
const username = userId.match(/^@?(\S+):(\S+)$/)?.[1] ?? userId;
|
||||
usernameRule = {
|
||||
actions: [],
|
||||
default: true,
|
||||
enabled: true,
|
||||
pattern: username,
|
||||
rule_id: rule,
|
||||
};
|
||||
ct.push(usernameRule);
|
||||
}
|
||||
usernameRule.actions = getTypeActions(type, true);
|
||||
} else {
|
||||
const keyRules = ct.filter((r) => r.rule_id !== USERNAME);
|
||||
keyRules.forEach((r) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
r.actions = getTypeActions(type, true);
|
||||
});
|
||||
}
|
||||
|
||||
mx.setAccountData('m.push_rules', evtContent);
|
||||
};
|
||||
|
||||
const addKeyword = (keyword) => {
|
||||
if (content.find((r) => r.rule_id === keyword)) return;
|
||||
content.push({
|
||||
rule_id: keyword,
|
||||
pattern: keyword,
|
||||
enabled: true,
|
||||
default: false,
|
||||
actions: getTypeActions(rulesToType[KEYWORD] ?? notifType.NOISY, true),
|
||||
});
|
||||
mx.setAccountData('m.push_rules', pushRules);
|
||||
};
|
||||
const removeKeyword = (rule) => {
|
||||
pushRules.global.content = content.filter((r) => r.rule_id !== rule.rule_id);
|
||||
mx.setAccountData('m.push_rules', pushRules);
|
||||
};
|
||||
|
||||
const dsRule = override.find((rule) => rule.rule_id === DISPLAY_NAME);
|
||||
const roomRule = override.find((rule) => rule.rule_id === ROOM_PING);
|
||||
const usernameRule = content.find((rule) => rule.rule_id === USERNAME);
|
||||
const keywordRule = content.find((rule) => rule.rule_id !== USERNAME);
|
||||
|
||||
if (dsRule) rulesToType[DISPLAY_NAME] = getActionType(dsRule);
|
||||
if (roomRule) rulesToType[ROOM_PING] = getActionType(roomRule);
|
||||
if (usernameRule) rulesToType[USERNAME] = getActionType(usernameRule);
|
||||
if (keywordRule) rulesToType[KEYWORD] = getActionType(keywordRule);
|
||||
|
||||
return {
|
||||
rulesToType,
|
||||
pushRules,
|
||||
setRule,
|
||||
addKeyword,
|
||||
removeKeyword,
|
||||
};
|
||||
}
|
||||
|
||||
function GlobalNotification() {
|
||||
const {
|
||||
rulesToType,
|
||||
pushRules,
|
||||
setRule,
|
||||
addKeyword,
|
||||
removeKeyword,
|
||||
} = useKeywordNotif();
|
||||
|
||||
const keywordRules = pushRules?.global?.content.filter((r) => r.rule_id !== USERNAME) ?? [];
|
||||
|
||||
const onSelect = (evt, rule) => {
|
||||
openReusableContextMenu(
|
||||
'bottom',
|
||||
getEventCords(evt, '.btn-surface'),
|
||||
(requestClose) => (
|
||||
<NotificationSelector
|
||||
value={rulesToType[rule]}
|
||||
onSelect={(value) => {
|
||||
if (rulesToType[rule] !== value) setRule(rule, value);
|
||||
requestClose();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const handleSubmit = (evt) => {
|
||||
evt.preventDefault();
|
||||
const { keywordInput } = evt.target.elements;
|
||||
const value = keywordInput.value.trim();
|
||||
if (value === '') return;
|
||||
addKeyword(value);
|
||||
keywordInput.value = '';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="keyword-notification">
|
||||
<MenuHeader>Mentions & keywords</MenuHeader>
|
||||
<SettingTile
|
||||
title="Message containing my display name"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, DISPLAY_NAME)} iconSrc={ChevronBottomIC}>
|
||||
{ typeToLabel[rulesToType[DISPLAY_NAME]] }
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all message containing your display name.</Text>}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Message containing my username"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, USERNAME)} iconSrc={ChevronBottomIC}>
|
||||
{ typeToLabel[rulesToType[USERNAME]] }
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all message containing your username.</Text>}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Message containing @room"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, ROOM_PING)} iconSrc={ChevronBottomIC}>
|
||||
{typeToLabel[rulesToType[ROOM_PING]]}
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all messages containing @room.</Text>}
|
||||
/>
|
||||
{ rulesToType[KEYWORD] && (
|
||||
<SettingTile
|
||||
title="Message containing keywords"
|
||||
options={(
|
||||
<Button onClick={(evt) => onSelect(evt, KEYWORD)} iconSrc={ChevronBottomIC}>
|
||||
{typeToLabel[rulesToType[KEYWORD]]}
|
||||
</Button>
|
||||
)}
|
||||
content={<Text variant="b3">Default notification settings for all message containing keywords.</Text>}
|
||||
/>
|
||||
)}
|
||||
<SettingTile
|
||||
title="Keywords"
|
||||
content={(
|
||||
<div className="keyword-notification__keyword">
|
||||
<Text variant="b3">Get notification when a message contains keyword.</Text>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Input name="keywordInput" required />
|
||||
<Button variant="primary" type="submit">Add</Button>
|
||||
</form>
|
||||
{keywordRules.length > 0 && (
|
||||
<div>
|
||||
{keywordRules.map((rule) => (
|
||||
<Chip
|
||||
iconSrc={CrossIC}
|
||||
key={rule.rule_id}
|
||||
text={rule.pattern}
|
||||
iconColor={CrossIC}
|
||||
onClick={() => removeKeyword(rule)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GlobalNotification;
|
||||
@@ -0,0 +1,17 @@
|
||||
.keyword-notification {
|
||||
&__keyword {
|
||||
& form,
|
||||
& > div:last-child {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--sp-tight);
|
||||
}
|
||||
|
||||
& form {
|
||||
margin: var(--sp-extra-tight) 0 var(--sp-normal);
|
||||
.input-container {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
|
||||
|
||||
import CheckIC from '../../../../public/res/ic/outlined/check.svg';
|
||||
|
||||
function NotificationSelector({
|
||||
value, onSelect,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<MenuHeader>Notification</MenuHeader>
|
||||
<MenuItem iconSrc={value === 'off' ? CheckIC : null} variant={value === 'off' ? 'positive' : 'surface'} onClick={() => onSelect('off')}>Off</MenuItem>
|
||||
<MenuItem iconSrc={value === 'on' ? CheckIC : null} variant={value === 'on' ? 'positive' : 'surface'} onClick={() => onSelect('on')}>On</MenuItem>
|
||||
<MenuItem iconSrc={value === 'noisy' ? CheckIC : null} variant={value === 'noisy' ? 'positive' : 'surface'} onClick={() => onSelect('noisy')}>Noisy</MenuItem>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
NotificationSelector.propTypes = {
|
||||
value: PropTypes.oneOf(['off', 'on', 'noisy']).isRequired,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default NotificationSelector;
|
||||
@@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './ImageLightbox.scss';
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import RawModal from '../../atoms/modal/RawModal';
|
||||
import IconButton from '../../atoms/button/IconButton';
|
||||
|
||||
import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
|
||||
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
|
||||
|
||||
function ImageLightbox({
|
||||
url, alt, isOpen, onRequestClose,
|
||||
}) {
|
||||
const handleDownload = () => {
|
||||
FileSaver.saveAs(url, alt);
|
||||
};
|
||||
|
||||
return (
|
||||
<RawModal
|
||||
className="image-lightbox__modal"
|
||||
overlayClassName="image-lightbox__overlay"
|
||||
isOpen={isOpen}
|
||||
onRequestClose={onRequestClose}
|
||||
size="large"
|
||||
>
|
||||
<div className="image-lightbox__header">
|
||||
<Text variant="b2" weight="medium">{alt}</Text>
|
||||
<IconButton onClick={() => window.open(url)} size="small" src={ExternalSVG} />
|
||||
<IconButton onClick={handleDownload} size="small" src={DownloadSVG} />
|
||||
</div>
|
||||
<div className="image-lightbox__content">
|
||||
<img src={url} alt={alt} />
|
||||
</div>
|
||||
</RawModal>
|
||||
);
|
||||
}
|
||||
|
||||
ImageLightbox.propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
alt: PropTypes.string.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onRequestClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ImageLightbox;
|
||||
@@ -0,0 +1,50 @@
|
||||
@use '../../partials/flex';
|
||||
@use '../../partials/text';
|
||||
|
||||
.image-lightbox__modal {
|
||||
box-shadow: none;
|
||||
width: unset;
|
||||
gap: var(--sp-normal);
|
||||
|
||||
border-radius: 0;
|
||||
pointer-events: none;
|
||||
|
||||
& .text {
|
||||
color: white;
|
||||
}
|
||||
& .ic-raw {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.image-lightbox__overlay {
|
||||
background-color: var(--bg-overlay-low);
|
||||
}
|
||||
|
||||
|
||||
.image-lightbox__header > *,
|
||||
.image-lightbox__content > * {
|
||||
pointer-events: all;
|
||||
}
|
||||
.image-lightbox__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > .text {
|
||||
@extend .cp-fx__item-one;
|
||||
@extend .cp-txt__ellipsis;
|
||||
}
|
||||
}
|
||||
.image-lightbox__content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-height: 80vh;
|
||||
|
||||
& img {
|
||||
background-color: var(--bg-surface-low);
|
||||
object-fit: contain;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: var(--bo-radius);
|
||||
}
|
||||
}
|
||||