diff --git a/package-lock.json b/package-lock.json
index 1b65135..3dc4e73 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,7 +36,6 @@
"immer": "9.0.16",
"is-hotkey": "0.2.0",
"jotai": "1.12.0",
- "katex": "0.16.4",
"linkify-html": "4.0.2",
"linkify-react": "4.1.1",
"linkifyjs": "4.0.2",
@@ -61,7 +60,6 @@
"slate-history": "0.93.0",
"slate-react": "0.98.4",
"tippy.js": "6.3.7",
- "twemoji": "14.0.2",
"ua-parser-js": "1.0.35"
},
"devDependencies": {
@@ -275,9 +273,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz",
- "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
+ "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
"engines": {
"node": ">=6.9.0"
}
@@ -366,11 +364,11 @@
}
},
"node_modules/@babel/plugin-syntax-typescript": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz",
- "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==",
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz",
+ "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.19.0"
+ "@babel/helper-plugin-utils": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
@@ -505,9 +503,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.9.tgz",
- "integrity": "sha512-kW5ccqWHVOOTGUkkJbtfoImtqu3kA1PFkivM+9QPFSHphPfPBlBalX9eDRqPK+wHCqKhU48/78T791qPgC9e9A==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.6.tgz",
+ "integrity": "sha512-bSC9YVUjADDy1gae8RrioINU6e1lCkg3VGVwm0QQ2E1CWcC4gnMce9+B6RpxuSsrsXsk1yojn7sp1fnG8erE2g==",
"cpu": [
"arm"
],
@@ -520,9 +518,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.9.tgz",
- "integrity": "sha512-ndIAZJUeLx4O+4AJbFQCurQW4VRUXjDsUvt1L+nP8bVELOWdmdCEOtlIweCUE6P+hU0uxYbEK2AEP0n5IVQvhg==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.6.tgz",
+ "integrity": "sha512-YnYSCceN/dUzUr5kdtUzB+wZprCafuD89Hs0Aqv9QSdwhYQybhXTaSTcrl6X/aWThn1a/j0eEpUBGOE7269REg==",
"cpu": [
"arm64"
],
@@ -535,9 +533,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.9.tgz",
- "integrity": "sha512-UbMcJB4EHrAVOnknQklREPgclNU2CPet2h+sCBCXmF2mfoYWopBn/CfTfeyOkb/JglOcdEADqAljFndMKnFtOw==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.6.tgz",
+ "integrity": "sha512-MVcYcgSO7pfu/x34uX9u2QIZHmXAB7dEiLQC5bBl5Ryqtpj9lT2sg3gNDEsrPEmimSJW2FXIaxqSQ501YLDsZQ==",
"cpu": [
"x64"
],
@@ -550,9 +548,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.9.tgz",
- "integrity": "sha512-d7D7/nrt4CxPul98lx4PXhyNZwTYtbdaHhOSdXlZuu5zZIznjqtMqLac8Bv+IuT6SVHiHUwrkL6ywD7mOgLW+A==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.6.tgz",
+ "integrity": "sha512-bsDRvlbKMQMt6Wl08nHtFz++yoZHsyTOxnjfB2Q95gato+Yi4WnRl13oC2/PJJA9yLCoRv9gqT/EYX0/zDsyMA==",
"cpu": [
"arm64"
],
@@ -565,9 +563,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.9.tgz",
- "integrity": "sha512-LZc+Wlz06AkJYtwWsBM3x2rSqTG8lntDuftsUNQ3fCx9ZttYtvlDcVtgb+NQ6t9s6K5No5zutN3pcjZEC2a4iQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.6.tgz",
+ "integrity": "sha512-xh2A5oPrYRfMFz74QXIQTQo8uA+hYzGWJFoeTE8EvoZGHb+idyV4ATaukaUvnnxJiauhs/fPx3vYhU4wiGfosg==",
"cpu": [
"x64"
],
@@ -580,9 +578,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.9.tgz",
- "integrity": "sha512-gIj0UQZlQo93CHYouHKkpzP7AuruSaMIm1etcWIxccFEVqCN1xDr6BWlN9bM+ol/f0W9w3hx3HDuEwcJVtGneQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.6.tgz",
+ "integrity": "sha512-EnUwjRc1inT4ccZh4pB3v1cIhohE2S4YXlt1OvI7sw/+pD+dIE4smwekZlEPIwY6PhU6oDWwITrQQm5S2/iZgg==",
"cpu": [
"arm64"
],
@@ -595,9 +593,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.9.tgz",
- "integrity": "sha512-GNors4vaMJ7lzGOuhzNc7jvgsQZqErGA8rsW+nck8N1nYu86CvsJW2seigVrQQWOV4QzEP8Zf3gm+QCjA2hnBQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.6.tgz",
+ "integrity": "sha512-Uh3HLWGzH6FwpviUcLMKPCbZUAFzv67Wj5MTwK6jn89b576SR2IbEp+tqUHTr8DIl0iDmBAf51MVaP7pw6PY5Q==",
"cpu": [
"x64"
],
@@ -610,9 +608,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.9.tgz",
- "integrity": "sha512-cNx1EF99c2t1Ztn0lk9N+MuwBijGF8mH6nx9GFsB3e0lpUpPkCE/yt5d+7NP9EwJf5uzqdjutgVYoH1SNqzudA==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.6.tgz",
+ "integrity": "sha512-7YdGiurNt7lqO0Bf/U9/arrPWPqdPqcV6JCZda4LZgEn+PTQ5SMEI4MGR52Bfn3+d6bNEGcWFzlIxiQdS48YUw==",
"cpu": [
"arm"
],
@@ -625,9 +623,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.9.tgz",
- "integrity": "sha512-YPxQunReYp8RQ1FvexFrOEqqf+nLbS3bKVZF5FRT2uKM7Wio7BeATqAwO02AyrdSEntt3I5fhFsujUChIa8CZg==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.6.tgz",
+ "integrity": "sha512-bUR58IFOMJX523aDVozswnlp5yry7+0cRLCXDsxnUeQYJik1DukMY+apBsLOZJblpH+K7ox7YrKrHmJoWqVR9w==",
"cpu": [
"arm64"
],
@@ -640,9 +638,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.9.tgz",
- "integrity": "sha512-zb12ixDIKNwFpIqR00J88FFitVwOEwO78EiUi8wi8FXlmSc3GtUuKV/BSO+730Kglt0B47+ZrJN1BhhOxZaVrw==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.6.tgz",
+ "integrity": "sha512-ujp8uoQCM9FRcbDfkqECoARsLnLfCUhKARTP56TFPog8ie9JG83D5GVKjQ6yVrEVdMie1djH86fm98eY3quQkQ==",
"cpu": [
"ia32"
],
@@ -655,9 +653,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.9.tgz",
- "integrity": "sha512-X8te4NLxtHiNT6H+4Pfm5RklzItA1Qy4nfyttihGGX+Koc53Ar20ViC+myY70QJ8PDEOehinXZj/F7QK3A+MKQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.6.tgz",
+ "integrity": "sha512-y2NX1+X/Nt+izj9bLoiaYB9YXT/LoaQFYvCkVD77G/4F+/yuVXYCWz4SE9yr5CBMbOxOfBcy/xFL4LlOeNlzYQ==",
"cpu": [
"loong64"
],
@@ -670,9 +668,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.9.tgz",
- "integrity": "sha512-ZqyMDLt02c5smoS3enlF54ndK5zK4IpClLTxF0hHfzHJlfm4y8IAkIF8LUW0W7zxcKy7oAwI7BRDqeVvC120SA==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.6.tgz",
+ "integrity": "sha512-09AXKB1HDOzXD+j3FdXCiL/MWmZP0Ex9eR8DLMBVcHorrWJxWmY8Nms2Nm41iRM64WVx7bA/JVHMv081iP2kUA==",
"cpu": [
"mips64el"
],
@@ -685,9 +683,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.9.tgz",
- "integrity": "sha512-k+ca5W5LDBEF3lfDwMV6YNXwm4wEpw9krMnNvvlNz3MrKSD2Eb2c861O0MaKrZkG/buTQAP4vkavbLwgIe6xjg==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.6.tgz",
+ "integrity": "sha512-AmLhMzkM8JuqTIOhxnX4ubh0XWJIznEynRnZAVdA2mMKE6FAfwT2TWKTwdqMG+qEaeyDPtfNoZRpJbD4ZBv0Tg==",
"cpu": [
"ppc64"
],
@@ -700,9 +698,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.9.tgz",
- "integrity": "sha512-GuInVdogjmg9DhgkEmNipHkC+3tzkanPJzgzTC2ihsvrruLyFoR1YrTGixblNSMPudQLpiqkcwGwwe0oqfrvfA==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.6.tgz",
+ "integrity": "sha512-Y4Ri62PfavhLQhFbqucysHOmRamlTVK10zPWlqjNbj2XMea+BOs4w6ASKwQwAiqf9ZqcY9Ab7NOU4wIgpxwoSQ==",
"cpu": [
"riscv64"
],
@@ -715,9 +713,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.9.tgz",
- "integrity": "sha512-49wQ0aYkvwXonGsxc7LuuLNICMX8XtO92Iqmug5Qau0kpnV6SP34jk+jIeu4suHwAbSbRhVFtDv75yRmyfQcHw==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.6.tgz",
+ "integrity": "sha512-SPUiz4fDbnNEm3JSdUW8pBJ/vkop3M1YwZAVwvdwlFLoJwKEZ9L98l3tzeyMzq27CyepDQ3Qgoba44StgbiN5Q==",
"cpu": [
"s390x"
],
@@ -730,9 +728,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.9.tgz",
- "integrity": "sha512-Nx4oKEAJ6EcQlt4dK7qJyuZUoXZG7CAeY22R7rqZijFzwFfMOD+gLP56uV7RrV86jGf8PeRY8TBsRmOcZoG42w==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.6.tgz",
+ "integrity": "sha512-a3yHLmOodHrzuNgdpB7peFGPx1iJ2x6m+uDvhP2CKdr2CwOaqEFMeSqYAHU7hG+RjCq8r2NFujcd/YsEsFgTGw==",
"cpu": [
"x64"
],
@@ -745,9 +743,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.9.tgz",
- "integrity": "sha512-d0WnpgJ+FTiMZXEQ1NOv9+0gvEhttbgKEvVqWWAtl1u9AvlspKXbodKHzQ5MLP6YV1y52Xp+p8FMYqj8ykTahg==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.6.tgz",
+ "integrity": "sha512-EanJqcU/4uZIBreTrnbnre2DXgXSa+Gjap7ifRfllpmyAU7YMvaXmljdArptTHmjrkkKm9BK6GH5D5Yo+p6y5A==",
"cpu": [
"x64"
],
@@ -760,9 +758,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.9.tgz",
- "integrity": "sha512-jccK11278dvEscHFfMk5EIPjF4wv1qGD0vps7mBV1a6TspdR36O28fgPem/SA/0pcsCPHjww5ouCLwP+JNAFlw==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.6.tgz",
+ "integrity": "sha512-xaxeSunhQRsTNGFanoOkkLtnmMn5QbA0qBhNet/XLVsc+OVkpIWPHcr3zTW2gxVU5YOHFbIHR9ODuaUdNza2Vw==",
"cpu": [
"x64"
],
@@ -775,9 +773,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.9.tgz",
- "integrity": "sha512-OetwTSsv6mIDLqN7I7I2oX9MmHGwG+AP+wKIHvq+6sIHwcPPJqRx+DJB55jy9JG13CWcdcQno/7V5MTJ5a0xfQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.6.tgz",
+ "integrity": "sha512-gnMnMPg5pfMkZvhHee21KbKdc6W3GR8/JuE0Da1kjwpK6oiFU3nqfHuVPgUX2rsOx9N2SadSQTIYV1CIjYG+xw==",
"cpu": [
"x64"
],
@@ -790,9 +788,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.9.tgz",
- "integrity": "sha512-tKSSSK6unhxbGbHg+Cc+JhRzemkcsX0tPBvG0m5qsWbkShDK9c+/LSb13L18LWVdOQZwuA55Vbakxmt6OjBDOQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.6.tgz",
+ "integrity": "sha512-G95n7vP1UnGJPsVdKXllAJPtqjMvFYbN20e8RK8LVLhlTiSOH1sd7+Gt7rm70xiG+I5tM58nYgwWrLs6I1jHqg==",
"cpu": [
"arm64"
],
@@ -805,9 +803,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.9.tgz",
- "integrity": "sha512-ZTQ5vhNS5gli0KK8I6/s6+LwXmNEfq1ftjnSVyyNm33dBw8zDpstqhGXYUbZSWWLvkqiRRjgxgmoncmi6Yy7Ng==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.6.tgz",
+ "integrity": "sha512-96yEFzLhq5bv9jJo5JhTs1gI+1cKQ83cUpyxHuGqXVwQtY5Eq54ZEsKs8veKtiKwlrNimtckHEkj4mRh4pPjsg==",
"cpu": [
"ia32"
],
@@ -820,9 +818,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.9.tgz",
- "integrity": "sha512-C4ZX+YFIp6+lPrru3tpH6Gaapy8IBRHw/e7l63fzGDhn/EaiGpQgbIlT5paByyy+oMvRFQoxxyvC4LE0AjJMqQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.6.tgz",
+ "integrity": "sha512-n6d8MOyUrNp6G4VSpRcgjs5xj4A91svJSaiwLIDWVWEsZtpN5FA9NlBbZHDmAJc2e8e6SF4tkBD3HAvPF+7igA==",
"cpu": [
"x64"
],
@@ -2747,8 +2745,7 @@
"node_modules/@types/node": {
"version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
- "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
- "dev": true
+ "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA=="
},
"node_modules/@types/prismjs": {
"version": "1.26.0",
@@ -3066,9 +3063,9 @@
}
},
"node_modules/@vanilla-extract/babel-plugin-debug-ids": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/babel-plugin-debug-ids/-/babel-plugin-debug-ids-1.0.1.tgz",
- "integrity": "sha512-ynyKqsJiMzM1/yiIJ6QdqpWKlK4IMJJWREpPtaemZrE1xG1B4E/Nfa6YazuDWjDkCJC1tRIpEGnVs+pMIjUxyw==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/babel-plugin-debug-ids/-/babel-plugin-debug-ids-1.0.3.tgz",
+ "integrity": "sha512-vm4jYu1xhSa6ofQ9AhIpR3DkAp4c+eoR1Rpm8/TQI4DmWbmGbOjYRcqV0aWsfaIlNhN4kFuxFMKBNN9oG6iRzA==",
"dependencies": {
"@babel/core": "^7.20.7"
}
@@ -3164,22 +3161,126 @@
}
},
"node_modules/@vanilla-extract/integration": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/integration/-/integration-6.0.2.tgz",
- "integrity": "sha512-LwfXlh0THeNvVXdA3iWFYvJs1mvEP1PkfQD/7S6Purry7L8iDizDV/87FgWBJ79FnTmYIvMrc7BOQsUajNj9VQ==",
+ "version": "6.2.4",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/integration/-/integration-6.2.4.tgz",
+ "integrity": "sha512-+AfymNMVq9sEUe0OJpdCokmPZg4Zi6CqKaW/PnUOfDwEn53ighHOMOBl5hAgxYR8Kiz9NG43Bn00mkjWlFi+ng==",
"dependencies": {
"@babel/core": "^7.20.7",
"@babel/plugin-syntax-typescript": "^7.20.0",
- "@vanilla-extract/babel-plugin-debug-ids": "^1.0.1",
- "@vanilla-extract/css": "^1.9.3",
- "esbuild": "^0.16.3",
- "eval": "0.1.6",
+ "@vanilla-extract/babel-plugin-debug-ids": "^1.0.2",
+ "@vanilla-extract/css": "^1.14.0",
+ "esbuild": "0.17.6",
+ "eval": "0.1.8",
"find-up": "^5.0.0",
"javascript-stringify": "^2.0.1",
"lodash": "^4.17.21",
+ "mlly": "^1.1.0",
+ "outdent": "^0.8.0",
+ "vite": "^4.1.4",
+ "vite-node": "^0.28.5"
+ }
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/@vanilla-extract/css": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/css/-/css-1.14.0.tgz",
+ "integrity": "sha512-rYfm7JciWZ8PFzBM/HDiE2GLnKI3xJ6/vdmVJ5BSgcCZ5CxRlM9Cjqclni9lGzF3eMOijnUhCd/KV8TOzyzbMA==",
+ "dependencies": {
+ "@emotion/hash": "^0.9.0",
+ "@vanilla-extract/private": "^1.0.3",
+ "chalk": "^4.1.1",
+ "css-what": "^6.1.0",
+ "cssesc": "^3.0.0",
+ "csstype": "^3.0.7",
+ "deep-object-diff": "^1.1.9",
+ "deepmerge": "^4.2.2",
+ "media-query-parser": "^2.0.2",
+ "modern-ahocorasick": "^1.0.0",
"outdent": "^0.8.0"
}
},
+ "node_modules/@vanilla-extract/integration/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@vanilla-extract/integration/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@vanilla-extract/private": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@vanilla-extract/private/-/private-1.0.3.tgz",
@@ -3233,10 +3334,9 @@
"optional": true
},
"node_modules/acorn": {
- "version": "8.8.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
- "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
- "dev": true,
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"bin": {
"acorn": "bin/acorn"
},
@@ -3608,6 +3708,19 @@
"ieee754": "^1.2.1"
}
},
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -4173,9 +4286,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.16.9",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.9.tgz",
- "integrity": "sha512-gkH83yHyijMSZcZFs1IWew342eMdFuWXmQo3zkDPTre25LIPBJsXryg02M3u8OpTwCJdBkdaQwqKkDLnAsAeLQ==",
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.6.tgz",
+ "integrity": "sha512-TKFRp9TxrJDdRWfSsSERKEovm6v30iHnrjlcGhLBOtReE28Yp1VSBRfO3GTaOFMoxsNerx4TjrhzSuma9ha83Q==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@@ -4184,28 +4297,28 @@
"node": ">=12"
},
"optionalDependencies": {
- "@esbuild/android-arm": "0.16.9",
- "@esbuild/android-arm64": "0.16.9",
- "@esbuild/android-x64": "0.16.9",
- "@esbuild/darwin-arm64": "0.16.9",
- "@esbuild/darwin-x64": "0.16.9",
- "@esbuild/freebsd-arm64": "0.16.9",
- "@esbuild/freebsd-x64": "0.16.9",
- "@esbuild/linux-arm": "0.16.9",
- "@esbuild/linux-arm64": "0.16.9",
- "@esbuild/linux-ia32": "0.16.9",
- "@esbuild/linux-loong64": "0.16.9",
- "@esbuild/linux-mips64el": "0.16.9",
- "@esbuild/linux-ppc64": "0.16.9",
- "@esbuild/linux-riscv64": "0.16.9",
- "@esbuild/linux-s390x": "0.16.9",
- "@esbuild/linux-x64": "0.16.9",
- "@esbuild/netbsd-x64": "0.16.9",
- "@esbuild/openbsd-x64": "0.16.9",
- "@esbuild/sunos-x64": "0.16.9",
- "@esbuild/win32-arm64": "0.16.9",
- "@esbuild/win32-ia32": "0.16.9",
- "@esbuild/win32-x64": "0.16.9"
+ "@esbuild/android-arm": "0.17.6",
+ "@esbuild/android-arm64": "0.17.6",
+ "@esbuild/android-x64": "0.17.6",
+ "@esbuild/darwin-arm64": "0.17.6",
+ "@esbuild/darwin-x64": "0.17.6",
+ "@esbuild/freebsd-arm64": "0.17.6",
+ "@esbuild/freebsd-x64": "0.17.6",
+ "@esbuild/linux-arm": "0.17.6",
+ "@esbuild/linux-arm64": "0.17.6",
+ "@esbuild/linux-ia32": "0.17.6",
+ "@esbuild/linux-loong64": "0.17.6",
+ "@esbuild/linux-mips64el": "0.17.6",
+ "@esbuild/linux-ppc64": "0.17.6",
+ "@esbuild/linux-riscv64": "0.17.6",
+ "@esbuild/linux-s390x": "0.17.6",
+ "@esbuild/linux-x64": "0.17.6",
+ "@esbuild/netbsd-x64": "0.17.6",
+ "@esbuild/openbsd-x64": "0.17.6",
+ "@esbuild/sunos-x64": "0.17.6",
+ "@esbuild/win32-arm64": "0.17.6",
+ "@esbuild/win32-ia32": "0.17.6",
+ "@esbuild/win32-x64": "0.17.6"
}
},
"node_modules/escalade": {
@@ -4734,10 +4847,11 @@
}
},
"node_modules/eval": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.6.tgz",
- "integrity": "sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==",
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz",
+ "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==",
"dependencies": {
+ "@types/node": "*",
"require-like": ">= 0.1.1"
},
"engines": {
@@ -4988,27 +5102,6 @@
"react": ">=16.8.0"
}
},
- "node_modules/fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- },
- "engines": {
- "node": ">=6 <7 || >=8"
- }
- },
- "node_modules/fs-extra/node_modules/jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
@@ -5043,7 +5136,6 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@@ -5227,7 +5319,8 @@
"node_modules/graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
},
"node_modules/grapheme-splitter": {
"version": "1.0.4",
@@ -5870,16 +5963,10 @@
"node": ">=6"
}
},
- "node_modules/jsonfile": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz",
- "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==",
- "dependencies": {
- "universalify": "^0.1.2"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
+ "node_modules/jsonc-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
+ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="
},
"node_modules/jsx-ast-utils": {
"version": "3.3.3",
@@ -5894,29 +5981,6 @@
"node": ">=4.0"
}
},
- "node_modules/katex": {
- "version": "0.16.4",
- "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.4.tgz",
- "integrity": "sha512-WudRKUj8yyBeVDI4aYMNxhx5Vhh2PjpzQw1GRu/LVGqL4m1AxwD1GcUp0IMbdJaf5zsjtj8ghP0DOQRYhroNkw==",
- "funding": [
- "https://opencollective.com/katex",
- "https://github.com/sponsors/katex"
- ],
- "dependencies": {
- "commander": "^8.0.0"
- },
- "bin": {
- "katex": "cli.js"
- }
- },
- "node_modules/katex/node_modules/commander": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
- "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
- "engines": {
- "node": ">= 12"
- }
- },
"node_modules/language-subtag-registry": {
"version": "0.3.22",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
@@ -5960,9 +6024,9 @@
}
},
"node_modules/lilconfig": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
- "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+ "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
"engines": {
"node": ">=10"
}
@@ -6257,6 +6321,22 @@
"node": ">=10"
}
},
+ "node_modules/mlly": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz",
+ "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==",
+ "dependencies": {
+ "acorn": "^8.11.3",
+ "pathe": "^1.1.2",
+ "pkg-types": "^1.0.3",
+ "ufo": "^1.3.2"
+ }
+ },
+ "node_modules/modern-ahocorasick": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/modern-ahocorasick/-/modern-ahocorasick-1.0.1.tgz",
+ "integrity": "sha512-yoe+JbhTClckZ67b2itRtistFKf8yPYelHLc7e5xAwtNAXxM6wJTUx2C7QeVSJFDzKT7bCIFyBVybPMKvmB9AA=="
+ },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -6600,6 +6680,11 @@
"node": ">=8"
}
},
+ "node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
+ },
"node_modules/pdfjs-dist": {
"version": "3.10.111",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.10.111.tgz",
@@ -6629,6 +6714,16 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pkg-types": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz",
+ "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
+ "dependencies": {
+ "jsonc-parser": "^3.2.0",
+ "mlly": "^1.2.0",
+ "pathe": "^1.1.0"
+ }
+ },
"node_modules/postcss": {
"version": "8.4.24",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
@@ -7128,7 +7223,6 @@
"version": "3.25.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.1.tgz",
"integrity": "sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==",
- "dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@@ -7418,6 +7512,14 @@
"resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz",
"integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ=="
},
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -7426,6 +7528,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -7686,22 +7797,6 @@
"typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
}
},
- "node_modules/twemoji": {
- "version": "14.0.2",
- "resolved": "https://registry.npmjs.org/twemoji/-/twemoji-14.0.2.tgz",
- "integrity": "sha512-BzOoXIe1QVdmsUmZ54xbEH+8AgtOKUiG53zO5vVP2iUu6h5u9lN15NcuS6te4OY96qx0H7JK9vjjl9WQbkTRuA==",
- "dependencies": {
- "fs-extra": "^8.0.1",
- "jsonfile": "^5.0.0",
- "twemoji-parser": "14.0.0",
- "universalify": "^0.1.2"
- }
- },
- "node_modules/twemoji-parser": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-14.0.0.tgz",
- "integrity": "sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA=="
- },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -7757,6 +7852,11 @@
"node": "*"
}
},
+ "node_modules/ufo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.2.tgz",
+ "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA=="
+ },
"node_modules/unbox-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
@@ -7777,14 +7877,6 @@
"resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz",
"integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg=="
},
- "node_modules/universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
- "engines": {
- "node": ">= 4.0.0"
- }
- },
"node_modules/update-browserslist-db": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
@@ -7837,7 +7929,6 @@
"version": "4.3.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
"integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
- "dev": true,
"dependencies": {
"esbuild": "^0.17.5",
"postcss": "^8.4.23",
@@ -7881,6 +7972,30 @@
}
}
},
+ "node_modules/vite-node": {
+ "version": "0.28.5",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.28.5.tgz",
+ "integrity": "sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.4",
+ "mlly": "^1.1.0",
+ "pathe": "^1.1.0",
+ "picocolors": "^1.0.0",
+ "source-map": "^0.6.1",
+ "source-map-support": "^0.5.21",
+ "vite": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": ">=v14.16.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/vite-plugin-static-copy": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.13.0.tgz",
@@ -7941,7 +8056,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -7957,7 +8071,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -7973,7 +8086,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -7989,7 +8101,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -8005,7 +8116,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -8021,7 +8131,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -8037,7 +8146,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -8053,7 +8161,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8069,7 +8176,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8085,7 +8191,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8101,7 +8206,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8117,7 +8221,6 @@
"cpu": [
"mips64el"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8133,7 +8236,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8149,7 +8251,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8165,7 +8266,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8181,7 +8281,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -8197,7 +8296,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"netbsd"
@@ -8213,7 +8311,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"openbsd"
@@ -8229,7 +8326,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"sunos"
@@ -8245,7 +8341,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -8261,7 +8356,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -8277,7 +8371,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -8290,7 +8383,6 @@
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
"integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
- "dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
diff --git a/package.json b/package.json
index c4d0170..4ae4f9c 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,6 @@
"immer": "9.0.16",
"is-hotkey": "0.2.0",
"jotai": "1.12.0",
- "katex": "0.16.4",
"linkify-html": "4.0.2",
"linkify-react": "4.1.1",
"linkifyjs": "4.0.2",
@@ -71,7 +70,6 @@
"slate-history": "0.93.0",
"slate-react": "0.98.4",
"tippy.js": "6.3.7",
- "twemoji": "14.0.2",
"ua-parser-js": "1.0.35"
},
"devDependencies": {
diff --git a/src/app/atoms/divider/Divider.jsx b/src/app/atoms/divider/Divider.jsx
deleted file mode 100644
index 7672124..0000000
--- a/src/app/atoms/divider/Divider.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import './Divider.scss';
-
-import Text from '../text/Text';
-
-function Divider({ text, variant, align }) {
- const dividerClass = ` divider--${variant} divider--${align}`;
- return (
-
- {text !== null && {text}}
-
- );
-}
-
-Divider.defaultProps = {
- text: null,
- variant: 'surface',
- align: 'center',
-};
-
-Divider.propTypes = {
- text: PropTypes.string,
- variant: PropTypes.oneOf(['surface', 'primary', 'positive', 'caution', 'danger']),
- align: PropTypes.oneOf(['left', 'center', 'right']),
-};
-
-export default Divider;
diff --git a/src/app/atoms/math/Math.jsx b/src/app/atoms/math/Math.jsx
deleted file mode 100644
index ab52a47..0000000
--- a/src/app/atoms/math/Math.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-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 ;
-});
-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;
diff --git a/src/app/components/editor/Editor.preview.tsx b/src/app/components/editor/Editor.preview.tsx
deleted file mode 100644
index ad67dc1..0000000
--- a/src/app/components/editor/Editor.preview.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import React, { useState } from 'react';
-import FocusTrap from 'focus-trap-react';
-import {
- config,
- Icon,
- IconButton,
- Icons,
- Line,
- Modal,
- Overlay,
- OverlayBackdrop,
- OverlayCenter,
-} from 'folds';
-
-import { CustomEditor, useEditor } from './Editor';
-import { Toolbar } from './Toolbar';
-
-export function EditorPreview() {
- const [open, setOpen] = useState(false);
- const editor = useEditor();
- const [toolbar, setToolbar] = useState(false);
-
- return (
- <>
- setOpen(!open)}>
-
-
- }>
-
- setOpen(false),
- clickOutsideDeactivates: true,
- }}
- >
-
-
-
-
-
- }
- after={
- <>
- setToolbar(!toolbar)}
- aria-pressed={toolbar}
- >
-
-
-
-
-
-
-
-
- >
- }
- bottom={
- toolbar && (
-
-
-
-
- )
- }
- />
-
-
-
-
-
- >
- );
-}
diff --git a/src/app/components/sidebar/Sidebar.css.ts b/src/app/components/sidebar/Sidebar.css.ts
deleted file mode 100644
index e62ed6f..0000000
--- a/src/app/components/sidebar/Sidebar.css.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import { style } from '@vanilla-extract/css';
-import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
-import { color, config, DefaultReset, toRem } from 'folds';
-
-export const Sidebar = style([
- DefaultReset,
- {
- width: toRem(66),
- backgroundColor: color.Background.Container,
- borderRight: `${config.borderWidth.B300} solid ${color.Background.ContainerLine}`,
-
- display: 'flex',
- flexDirection: 'column',
- color: color.Background.OnContainer,
- },
-]);
-
-export const SidebarStack = style([
- DefaultReset,
- {
- width: '100%',
- display: 'flex',
- flexDirection: 'column',
- justifyContent: 'center',
- alignItems: 'center',
- gap: config.space.S300,
- padding: `${config.space.S300} 0`,
- },
-]);
-
-const PUSH_X = 2;
-export const SidebarAvatarBox = recipe({
- base: [
- DefaultReset,
- {
- display: 'flex',
- alignItems: 'center',
- position: 'relative',
- transition: 'transform 200ms cubic-bezier(0, 0.8, 0.67, 0.97)',
-
- selectors: {
- '&:hover': {
- transform: `translateX(${toRem(PUSH_X)})`,
- },
- '&::before': {
- content: '',
- display: 'none',
- position: 'absolute',
- left: toRem(-11.5 - PUSH_X),
- width: toRem(3 + PUSH_X),
- height: toRem(16),
- borderRadius: `0 ${toRem(4)} ${toRem(4)} 0`,
- background: 'CurrentColor',
- transition: 'height 200ms linear',
- },
- '&:hover::before': {
- display: 'block',
- width: toRem(3),
- },
- },
- },
- ],
- variants: {
- active: {
- true: {
- selectors: {
- '&::before': {
- display: 'block',
- height: toRem(24),
- },
- '&:hover::before': {
- width: toRem(3 + PUSH_X),
- },
- },
- },
- },
- },
-});
-
-export type SidebarAvatarBoxVariants = RecipeVariants;
-
-export const SidebarBadgeBox = recipe({
- base: [
- DefaultReset,
- {
- position: 'absolute',
- zIndex: 1,
- },
- ],
- variants: {
- hasCount: {
- true: {
- top: toRem(-6),
- right: toRem(-6),
- },
- false: {
- top: toRem(-2),
- right: toRem(-2),
- },
- },
- },
- defaultVariants: {
- hasCount: false,
- },
-});
-
-export type SidebarBadgeBoxVariants = RecipeVariants;
-
-export const SidebarBadgeOutline = style({
- boxShadow: `0 0 0 ${config.borderWidth.B500} ${color.Background.Container}`,
-});
diff --git a/src/app/components/sidebar/Sidebar.tsx b/src/app/components/sidebar/Sidebar.tsx
deleted file mode 100644
index 7caf1b2..0000000
--- a/src/app/components/sidebar/Sidebar.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import classNames from 'classnames';
-import { as } from 'folds';
-import React from 'react';
-import * as css from './Sidebar.css';
-
-export const Sidebar = as<'div'>(({ as: AsSidebar = 'div', className, ...props }, ref) => (
-
-));
diff --git a/src/app/components/sidebar/SidebarAvatar.tsx b/src/app/components/sidebar/SidebarAvatar.tsx
deleted file mode 100644
index 86665f7..0000000
--- a/src/app/components/sidebar/SidebarAvatar.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import classNames from 'classnames';
-import { as, Avatar, Box, color, config, Text, Tooltip, TooltipProvider } from 'folds';
-import React, { forwardRef, MouseEventHandler, ReactNode } from 'react';
-import * as css from './Sidebar.css';
-
-const SidebarAvatarBox = as<'div', css.SidebarAvatarBoxVariants>(
- ({ as: AsSidebarAvatarBox = 'div', className, active, ...props }, ref) => (
-
- )
-);
-
-export const SidebarAvatar = forwardRef<
- HTMLDivElement,
- css.SidebarAvatarBoxVariants &
- css.SidebarBadgeBoxVariants & {
- outlined?: boolean;
- avatarChildren: ReactNode;
- tooltip: ReactNode | string;
- notificationBadge?: (badgeClassName: string) => ReactNode;
- onClick?: MouseEventHandler;
- onContextMenu?: MouseEventHandler;
- }
->(
- (
- {
- active,
- hasCount,
- outlined,
- avatarChildren,
- tooltip,
- notificationBadge,
- onClick,
- onContextMenu,
- },
- ref
- ) => (
-
-
- {tooltip}
-
- }
- >
- {(avRef) => (
-
- {avatarChildren}
-
- )}
-
- {notificationBadge && (
-
- {notificationBadge(css.SidebarBadgeOutline)}
-
- )}
-
- )
-);
diff --git a/src/app/components/sidebar/SidebarContent.tsx b/src/app/components/sidebar/SidebarContent.tsx
deleted file mode 100644
index 4f40587..0000000
--- a/src/app/components/sidebar/SidebarContent.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React, { ReactNode } from 'react';
-import { Box, Scroll } from 'folds';
-
-type SidebarContentProps = {
- scrollable: ReactNode;
- sticky: ReactNode;
-};
-export function SidebarContent({ scrollable, sticky }: SidebarContentProps) {
- return (
- <>
-
-
- {scrollable}
-
-
-
- {sticky}
-
- >
- );
-}
diff --git a/src/app/components/sidebar/SidebarStack.tsx b/src/app/components/sidebar/SidebarStack.tsx
deleted file mode 100644
index c0e976c..0000000
--- a/src/app/components/sidebar/SidebarStack.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import React from 'react';
-import classNames from 'classnames';
-import { as } from 'folds';
-import * as css from './Sidebar.css';
-
-export const SidebarStack = as<'div'>(
- ({ as: AsSidebarStack = 'div', className, ...props }, ref) => (
-
- )
-);
diff --git a/src/app/components/sidebar/SidebarStackSeparator.tsx b/src/app/components/sidebar/SidebarStackSeparator.tsx
deleted file mode 100644
index 110341c..0000000
--- a/src/app/components/sidebar/SidebarStackSeparator.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-import { Line, toRem } from 'folds';
-
-export function SidebarStackSeparator() {
- return (
-
- );
-}
diff --git a/src/app/components/sidebar/index.ts b/src/app/components/sidebar/index.ts
deleted file mode 100644
index f744628..0000000
--- a/src/app/components/sidebar/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from './Sidebar';
-export * from './SidebarAvatar';
-export * from './SidebarContent';
-export * from './SidebarStack';
-export * from './SidebarStackSeparator';
diff --git a/src/app/hooks/useForceUpdate.ts b/src/app/hooks/useForceUpdate.ts
deleted file mode 100644
index 0691aa9..0000000
--- a/src/app/hooks/useForceUpdate.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { useReducer } from 'react';
-
-const reducer = (prevCount: number): number => prevCount + 1;
-
-export const useForceUpdate = (): [number, () => void] => {
- const [state, dispatch] = useReducer(reducer, 0);
-
- return [state, dispatch];
-};
diff --git a/src/app/hooks/useStateEvents.ts b/src/app/hooks/useStateEvents.ts
deleted file mode 100644
index dd08569..0000000
--- a/src/app/hooks/useStateEvents.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useCallback, useMemo } from 'react';
-import { Room } from 'matrix-js-sdk';
-import { StateEvent } from '../../types/matrix/room';
-import { useForceUpdate } from './useForceUpdate';
-import { useStateEventCallback } from './useStateEventCallback';
-import { getStateEvents } from '../utils/room';
-
-export const useStateEvents = (room: Room, eventType: StateEvent) => {
- const [updateCount, forceUpdate] = useForceUpdate();
-
- useStateEventCallback(
- room.client,
- useCallback(
- (event) => {
- if (event.getRoomId() === room.roomId && event.getType() === eventType) {
- forceUpdate();
- }
- },
- [room, eventType, forceUpdate]
- )
- );
-
- return useMemo(
- () => getStateEvents(room, eventType),
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [room, eventType, updateCount]
- );
-};
diff --git a/src/app/molecules/following-members/FollowingMembers.jsx b/src/app/molecules/following-members/FollowingMembers.jsx
deleted file mode 100644
index 949dac7..0000000
--- a/src/app/molecules/following-members/FollowingMembers.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/* eslint-disable react/prop-types */
-import React, { useState, useEffect } from 'react';
-import PropTypes from 'prop-types';
-import './FollowingMembers.scss';
-
-import initMatrix from '../../../client/initMatrix';
-import cons from '../../../client/state/cons';
-import { openReadReceipts } from '../../../client/action/navigation';
-
-import Text from '../../atoms/text/Text';
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
-
-import { getUsersActionJsx } from '../../organisms/room/common';
-
-function FollowingMembers({ roomTimeline }) {
- const [followingMembers, setFollowingMembers] = useState([]);
- const { roomId } = roomTimeline;
- const mx = initMatrix.matrixClient;
- const myUserId = mx.getUserId();
-
- useEffect(() => {
- const updateFollowingMembers = () => {
- setFollowingMembers(roomTimeline.getLiveReaders());
- };
- const updateOnEvent = (event, room) => {
- if (room.roomId !== roomId) return;
- setFollowingMembers(roomTimeline.getLiveReaders());
- };
- updateFollowingMembers();
- roomTimeline.on(cons.events.roomTimeline.LIVE_RECEIPT, updateFollowingMembers);
- mx.on('Room.timeline', updateOnEvent);
- return () => {
- roomTimeline.removeListener(cons.events.roomTimeline.LIVE_RECEIPT, updateFollowingMembers);
- mx.removeListener('Room.timeline', updateOnEvent);
- };
- }, [roomTimeline, roomId]);
-
- const filteredM = followingMembers.filter((userId) => userId !== myUserId);
-
- return (
- filteredM.length !== 0 && (
-
- )
- );
-}
-
-FollowingMembers.propTypes = {
- roomTimeline: PropTypes.shape({}).isRequired,
-};
-
-export default FollowingMembers;
diff --git a/src/app/molecules/message/TimelineChange.jsx b/src/app/molecules/message/TimelineChange.jsx
deleted file mode 100644
index bc6e913..0000000
--- a/src/app/molecules/message/TimelineChange.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import './TimelineChange.scss';
-
-import Text from '../../atoms/text/Text';
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import Time from '../../atoms/time/Time';
-
-import JoinArraowIC from '../../../../public/res/ic/outlined/join-arrow.svg';
-import LeaveArraowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
-import InviteArraowIC from '../../../../public/res/ic/outlined/invite-arrow.svg';
-import InviteCancelArraowIC from '../../../../public/res/ic/outlined/invite-cancel-arrow.svg';
-import UserIC from '../../../../public/res/ic/outlined/user.svg';
-
-function TimelineChange({
- variant, content, timestamp, onClick,
-}) {
- let iconSrc;
-
- switch (variant) {
- case 'join':
- iconSrc = JoinArraowIC;
- break;
- case 'leave':
- iconSrc = LeaveArraowIC;
- break;
- case 'invite':
- iconSrc = InviteArraowIC;
- break;
- case 'invite-cancel':
- iconSrc = InviteCancelArraowIC;
- break;
- case 'avatar':
- iconSrc = UserIC;
- break;
- default:
- iconSrc = JoinArraowIC;
- break;
- }
-
- return (
-
- );
-}
-
-TimelineChange.defaultProps = {
- variant: 'other',
- onClick: null,
-};
-
-TimelineChange.propTypes = {
- variant: PropTypes.oneOf([
- 'join', 'leave', 'invite',
- 'invite-cancel', 'avatar', 'other',
- ]),
- content: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.node,
- ]).isRequired,
- timestamp: PropTypes.number.isRequired,
- onClick: PropTypes.func,
-};
-
-export default TimelineChange;
diff --git a/src/app/molecules/room-intro/RoomIntro.jsx b/src/app/molecules/room-intro/RoomIntro.jsx
deleted file mode 100644
index 2ec46cb..0000000
--- a/src/app/molecules/room-intro/RoomIntro.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import './RoomIntro.scss';
-
-import colorMXID from '../../../util/colorMXID';
-
-import Text from '../../atoms/text/Text';
-import Avatar from '../../atoms/avatar/Avatar';
-
-function RoomIntro({
- roomId, avatarSrc, name, heading, desc, time,
-}) {
- return (
-
-
-
- {heading}
- {desc}
- { time !== null && {time}}
-
-
- );
-}
-
-RoomIntro.defaultProps = {
- avatarSrc: null,
- time: null,
-};
-
-RoomIntro.propTypes = {
- roomId: PropTypes.string.isRequired,
- avatarSrc: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.bool,
- ]),
- name: PropTypes.string.isRequired,
- heading: PropTypes.node.isRequired,
- desc: PropTypes.node.isRequired,
- time: PropTypes.node,
-};
-
-export default RoomIntro;
diff --git a/src/app/organisms/emoji-board/EmojiBoard.jsx b/src/app/organisms/emoji-board/EmojiBoard.jsx
deleted file mode 100644
index 84c4130..0000000
--- a/src/app/organisms/emoji-board/EmojiBoard.jsx
+++ /dev/null
@@ -1,356 +0,0 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-import React, { useState, useEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import './EmojiBoard.scss';
-
-import parse from 'html-react-parser';
-import twemoji from 'twemoji';
-import { emojiGroups, emojis } from './emoji';
-import { getRelevantPacks } from './custom-emoji';
-import initMatrix from '../../../client/initMatrix';
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import AsyncSearch from '../../../util/AsyncSearch';
-import { addRecentEmoji, getRecentEmojis } from './recent';
-import { TWEMOJI_BASE_URL } from '../../../util/twemojify';
-
-import Text from '../../atoms/text/Text';
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import IconButton from '../../atoms/button/IconButton';
-import Input from '../../atoms/input/Input';
-import ScrollView from '../../atoms/scroll/ScrollView';
-
-import SearchIC from '../../../../public/res/ic/outlined/search.svg';
-import RecentClockIC from '../../../../public/res/ic/outlined/recent-clock.svg';
-import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
-import DogIC from '../../../../public/res/ic/outlined/dog.svg';
-import CupIC from '../../../../public/res/ic/outlined/cup.svg';
-import BallIC from '../../../../public/res/ic/outlined/ball.svg';
-import PhotoIC from '../../../../public/res/ic/outlined/photo.svg';
-import BulbIC from '../../../../public/res/ic/outlined/bulb.svg';
-import PeaceIC from '../../../../public/res/ic/outlined/peace.svg';
-import FlagIC from '../../../../public/res/ic/outlined/flag.svg';
-
-const ROW_EMOJIS_COUNT = 7;
-
-const EmojiGroup = React.memo(({ name, groupEmojis }) => {
- function getEmojiBoard() {
- const emojiBoard = [];
- const totalEmojis = groupEmojis.length;
-
- for (let r = 0; r < totalEmojis; r += ROW_EMOJIS_COUNT) {
- const emojiRow = [];
- for (let c = r; c < r + ROW_EMOJIS_COUNT; c += 1) {
- const emojiIndex = c;
- if (emojiIndex >= totalEmojis) break;
- const emoji = groupEmojis[emojiIndex];
- emojiRow.push(
-
- {emoji.hexcode ? (
- // This is a unicode emoji, and should be rendered with twemoji
- parse(
- twemoji.parse(emoji.unicode, {
- attributes: () => ({
- unicode: emoji.unicode,
- shortcodes: emoji.shortcodes?.toString(),
- hexcode: emoji.hexcode,
- loading: 'lazy',
- }),
- base: TWEMOJI_BASE_URL,
- })
- )
- ) : (
- // This is a custom emoji, and should be render as an mxc
-
- )}
-
- );
- }
- emojiBoard.push(
-
- {emojiRow}
-
- );
- }
- return emojiBoard;
- }
-
- return (
-
-
- {name}
-
- {groupEmojis.length !== 0 &&
{getEmojiBoard()}
}
-
- );
-});
-
-EmojiGroup.propTypes = {
- name: PropTypes.string.isRequired,
- groupEmojis: PropTypes.arrayOf(
- PropTypes.shape({
- length: PropTypes.number,
- unicode: PropTypes.string,
- hexcode: PropTypes.string,
- mxc: PropTypes.string,
- shortcode: PropTypes.string,
- shortcodes: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
- })
- ).isRequired,
-};
-
-const asyncSearch = new AsyncSearch();
-asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 40 });
-function SearchedEmoji() {
- const [searchedEmojis, setSearchedEmojis] = useState(null);
-
- function handleSearchEmoji(resultEmojis, term) {
- if (term === '' || resultEmojis.length === 0) {
- if (term === '') setSearchedEmojis(null);
- else setSearchedEmojis({ emojis: [] });
- return;
- }
- setSearchedEmojis({ emojis: resultEmojis });
- }
-
- useEffect(() => {
- asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchEmoji);
- return () => {
- asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchEmoji);
- };
- }, []);
-
- if (searchedEmojis === null) return false;
-
- return (
-
- );
-}
-
-function EmojiBoard({ onSelect, searchRef }) {
- const scrollEmojisRef = useRef(null);
- const emojiInfo = useRef(null);
-
- function isTargetNotEmoji(target) {
- return target.classList.contains('emoji') === false;
- }
- function getEmojiDataFromTarget(target) {
- const unicode = target.getAttribute('unicode');
- const hexcode = target.getAttribute('hexcode');
- const mxc = target.getAttribute('data-mx-emoticon');
- let shortcodes = target.getAttribute('shortcodes');
- if (typeof shortcodes === 'undefined') shortcodes = undefined;
- else shortcodes = shortcodes.split(',');
- return {
- unicode,
- hexcode,
- shortcodes,
- mxc,
- };
- }
-
- function selectEmoji(e) {
- if (isTargetNotEmoji(e.target)) return;
-
- const emoji = getEmojiDataFromTarget(e.target);
- onSelect(emoji);
- if (emoji.hexcode) addRecentEmoji(emoji.unicode);
- }
-
- function setEmojiInfo(emoji) {
- const infoEmoji = emojiInfo.current.firstElementChild.firstElementChild;
- const infoShortcode = emojiInfo.current.lastElementChild;
-
- infoEmoji.src = emoji.src;
- infoEmoji.alt = emoji.unicode;
- infoShortcode.textContent = `:${emoji.shortcode}:`;
- }
-
- function hoverEmoji(e) {
- if (isTargetNotEmoji(e.target)) return;
-
- const emoji = e.target;
- const { shortcodes, unicode } = getEmojiDataFromTarget(emoji);
- const { src } = e.target;
-
- if (typeof shortcodes === 'undefined') {
- searchRef.current.placeholder = 'Search';
- setEmojiInfo({
- unicode: '🙂',
- shortcode: 'slight_smile',
- src: 'https://twemoji.maxcdn.com/v/13.1.0/72x72/1f642.png',
- });
- return;
- }
- if (searchRef.current.placeholder === shortcodes[0]) return;
- searchRef.current.setAttribute('placeholder', shortcodes[0]);
- setEmojiInfo({ shortcode: shortcodes[0], src, unicode });
- }
-
- function handleSearchChange() {
- const term = searchRef.current.value;
- asyncSearch.search(term);
- scrollEmojisRef.current.scrollTop = 0;
- }
-
- const [availableEmojis, setAvailableEmojis] = useState([]);
- const [recentEmojis, setRecentEmojis] = useState([]);
-
- const recentOffset = recentEmojis.length > 0 ? 1 : 0;
-
- useEffect(() => {
- const updateAvailableEmoji = (selectedRoomId) => {
- if (!selectedRoomId) {
- setAvailableEmojis([]);
- return;
- }
-
- const mx = initMatrix.matrixClient;
- const room = mx.getRoom(selectedRoomId);
- const parentIds = initMatrix.roomList.getAllParentSpaces(room.roomId);
- const parentRooms = [...parentIds].map((id) => mx.getRoom(id));
- if (room) {
- const packs = getRelevantPacks(room.client, [room, ...parentRooms]).filter(
- (pack) => pack.getEmojis().length !== 0
- );
-
- // Set an index for each pack so that we know where to jump when the user uses the nav
- for (let i = 0; i < packs.length; i += 1) {
- packs[i].packIndex = i;
- }
- setAvailableEmojis(packs);
- }
- };
-
- const onOpen = () => {
- searchRef.current.value = '';
- handleSearchChange();
-
- // only update when board is getting opened to prevent shifting UI
- setRecentEmojis(getRecentEmojis(3 * ROW_EMOJIS_COUNT));
- };
-
- navigation.on(cons.events.navigation.ROOM_SELECTED, updateAvailableEmoji);
- navigation.on(cons.events.navigation.EMOJIBOARD_OPENED, onOpen);
- return () => {
- navigation.removeListener(cons.events.navigation.ROOM_SELECTED, updateAvailableEmoji);
- navigation.removeListener(cons.events.navigation.EMOJIBOARD_OPENED, onOpen);
- };
- }, []);
-
- function openGroup(groupOrder) {
- let tabIndex = groupOrder;
- const $emojiContent = scrollEmojisRef.current.firstElementChild;
- const groupCount = $emojiContent.childElementCount;
- if (groupCount > emojiGroups.length) {
- tabIndex += groupCount - emojiGroups.length - availableEmojis.length - recentOffset;
- }
- $emojiContent.children[tabIndex].scrollIntoView();
- }
-
- return (
-
-
-
- {recentEmojis.length > 0 && (
-
openGroup(0)}
- src={RecentClockIC}
- tooltip="Recent"
- tooltipPlacement="left"
- />
- )}
-
- {availableEmojis.map((pack) => {
- const src = initMatrix.matrixClient.mxcUrlToHttp(
- pack.avatarUrl ?? pack.getEmojis()[0].mxc
- );
- return (
- openGroup(recentOffset + pack.packIndex)}
- src={src}
- key={pack.packIndex}
- tooltip={pack.displayName ?? 'Unknown'}
- tooltipPlacement="left"
- isImage
- />
- );
- })}
-
-
- {[
- [0, EmojiIC, 'Smilies'],
- [1, DogIC, 'Animals'],
- [2, CupIC, 'Food'],
- [3, BallIC, 'Activities'],
- [4, PhotoIC, 'Travel'],
- [5, BulbIC, 'Objects'],
- [6, PeaceIC, 'Symbols'],
- [7, FlagIC, 'Flags'],
- ].map(([indx, ico, name]) => (
- openGroup(recentOffset + availableEmojis.length + indx)}
- key={indx}
- src={ico}
- tooltip={name}
- tooltipPlacement="left"
- />
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
- {recentEmojis.length > 0 && (
-
- )}
- {availableEmojis.map((pack) => (
-
- ))}
- {emojiGroups.map((group) => (
-
- ))}
-
-
-
-
-
{parse(twemoji.parse('🙂', { base: TWEMOJI_BASE_URL }))}
-
:slight_smile:
-
-
-
- );
-}
-
-EmojiBoard.propTypes = {
- onSelect: PropTypes.func.isRequired,
- searchRef: PropTypes.shape({}).isRequired,
-};
-
-export default EmojiBoard;
diff --git a/src/app/organisms/emoji-board/EmojiBoardOpener.jsx b/src/app/organisms/emoji-board/EmojiBoardOpener.jsx
deleted file mode 100644
index 32b7a83..0000000
--- a/src/app/organisms/emoji-board/EmojiBoardOpener.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React, { useEffect, useRef } from 'react';
-
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import settings from '../../../client/state/settings';
-
-import ContextMenu from '../../atoms/context-menu/ContextMenu';
-import EmojiBoard from './EmojiBoard';
-
-let requestCallback = null;
-let isEmojiBoardVisible = false;
-function EmojiBoardOpener() {
- const openerRef = useRef(null);
- const searchRef = useRef(null);
-
- function openEmojiBoard(cords, requestEmojiCallback) {
- if (requestCallback !== null || isEmojiBoardVisible) {
- requestCallback = null;
- if (cords.detail === 0) openerRef.current.click();
- return;
- }
-
- openerRef.current.style.transform = `translate(${cords.x}px, ${cords.y}px)`;
- requestCallback = requestEmojiCallback;
- openerRef.current.click();
- }
-
- function afterEmojiBoardToggle(isVisible) {
- isEmojiBoardVisible = isVisible;
- if (isVisible) {
- if (!settings.isTouchScreenDevice) searchRef.current.focus();
- } else {
- setTimeout(() => {
- if (!isEmojiBoardVisible) requestCallback = null;
- }, 500);
- }
- }
-
- function addEmoji(emoji) {
- requestCallback(emoji);
- }
-
- useEffect(() => {
- navigation.on(cons.events.navigation.EMOJIBOARD_OPENED, openEmojiBoard);
- return () => {
- navigation.removeListener(cons.events.navigation.EMOJIBOARD_OPENED, openEmojiBoard);
- };
- }, []);
-
- return (
-
- )}
- afterToggle={afterEmojiBoardToggle}
- render={(toggleMenu) => (
-
- )}
- />
- );
-}
-
-export default EmojiBoardOpener;
diff --git a/src/app/organisms/emoji-board/recent.js b/src/app/organisms/emoji-board/recent.js
deleted file mode 100644
index dff67fb..0000000
--- a/src/app/organisms/emoji-board/recent.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import initMatrix from '../../../client/initMatrix';
-import { emojis } from './emoji';
-
-const eventType = 'io.element.recent_emoji';
-
-function getRecentEmojisRaw() {
- return initMatrix.matrixClient.getAccountData(eventType)?.getContent().recent_emoji ?? [];
-}
-
-export function getRecentEmojis(limit) {
- const res = [];
- getRecentEmojisRaw()
- .sort((a, b) => b[1] - a[1])
- .find(([unicode]) => {
- const emoji = emojis.find((e) => e.unicode === unicode);
- if (emoji) return res.push(emoji) >= limit;
- return false;
- });
- return res;
-}
-
-export function addRecentEmoji(unicode) {
- const recent = getRecentEmojisRaw();
- const i = recent.findIndex(([u]) => u === unicode);
- let entry;
- if (i < 0) {
- entry = [unicode, 1];
- } else {
- [entry] = recent.splice(i, 1);
- entry[1] += 1;
- }
- recent.unshift(entry);
- initMatrix.matrixClient.setAccountData(eventType, {
- recent_emoji: recent.slice(0, 100),
- });
-}
diff --git a/src/app/organisms/navigation/Sidebar1.tsx b/src/app/organisms/navigation/Sidebar1.tsx
deleted file mode 100644
index d9ee466..0000000
--- a/src/app/organisms/navigation/Sidebar1.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import React from 'react';
-import { Icon, Icons, Badge, AvatarFallback, Text } from 'folds';
-import { useAtom } from 'jotai';
-
-import {
- Sidebar,
- SidebarContent,
- SidebarStackSeparator,
- SidebarStack,
- SidebarAvatar,
-} from '../../components/sidebar';
-import { selectedTabAtom, SidebarTab } from '../../state/selectedTab';
-
-export function Sidebar1() {
- const [selectedTab, setSelectedTab] = useAtom(selectedTabAtom);
-
- return (
-
-
-
- }
- onClick={() => setSelectedTab(SidebarTab.Home)}
- />
- }
- onClick={() => setSelectedTab(SidebarTab.People)}
- />
-
-
-
- (
-
- )}
- avatarChildren={
-
- B
-
- }
- />
- (
-
- 64
-
- )}
- avatarChildren={
-
- C
-
- }
- />
-
-
-
- }
- />
- }
- />
-
- >
- }
- sticky={
- <>
-
-
- }
- />
-
- A
-
- }
- />
-
- >
- }
- />
-
- );
-}
diff --git a/src/app/organisms/room/EventLimit.js b/src/app/organisms/room/EventLimit.js
deleted file mode 100644
index de87da3..0000000
--- a/src/app/organisms/room/EventLimit.js
+++ /dev/null
@@ -1,35 +0,0 @@
-class EventLimit {
- constructor() {
- this._from = 0;
-
- this.SMALLEST_EVT_HEIGHT = 32;
- this.PAGES_COUNT = 4;
- }
-
- get maxEvents() {
- return Math.round(document.body.clientHeight / this.SMALLEST_EVT_HEIGHT) * this.PAGES_COUNT;
- }
-
- get from() {
- return this._from;
- }
-
- get length() {
- return this._from + this.maxEvents;
- }
-
- setFrom(from) {
- this._from = from < 0 ? 0 : from;
- }
-
- paginate(backwards, limit, timelineLength) {
- this._from = backwards ? this._from - limit : this._from + limit;
-
- if (!backwards && this.length > timelineLength) {
- this._from = timelineLength - this.maxEvents;
- }
- if (this._from < 0) this._from = 0;
- }
-}
-
-export default EventLimit;
diff --git a/src/app/organisms/room/PeopleDrawer.jsx b/src/app/organisms/room/PeopleDrawer.jsx
deleted file mode 100644
index 8f98324..0000000
--- a/src/app/organisms/room/PeopleDrawer.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-import React, {
- useState, useEffect, useCallback, useRef,
-} from 'react';
-import PropTypes from 'prop-types';
-import './PeopleDrawer.scss';
-
-import initMatrix from '../../../client/initMatrix';
-import { getPowerLabel, getUsernameOfRoomMember } from '../../../util/matrixUtil';
-import colorMXID from '../../../util/colorMXID';
-import { openInviteUser, openProfileViewer } from '../../../client/action/navigation';
-import AsyncSearch from '../../../util/AsyncSearch';
-import { memberByAtoZ, memberByPowerLevel } from '../../../util/sort';
-
-import Text from '../../atoms/text/Text';
-import Header, { TitleWrapper } from '../../atoms/header/Header';
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import IconButton from '../../atoms/button/IconButton';
-import Button from '../../atoms/button/Button';
-import ScrollView from '../../atoms/scroll/ScrollView';
-import Input from '../../atoms/input/Input';
-import SegmentedControl from '../../atoms/segmented-controls/SegmentedControls';
-import PeopleSelector from '../../molecules/people-selector/PeopleSelector';
-
-import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
-import SearchIC from '../../../../public/res/ic/outlined/search.svg';
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-
-function simplyfiMembers(members) {
- const mx = initMatrix.matrixClient;
- return members.map((member) => ({
- userId: member.userId,
- name: getUsernameOfRoomMember(member),
- username: member.userId.slice(1, member.userId.indexOf(':')),
- avatarSrc: member.getAvatarUrl(mx.baseUrl, 24, 24, 'crop'),
- peopleRole: getPowerLabel(member.powerLevel),
- powerLevel: members.powerLevel,
- }));
-}
-
-const asyncSearch = new AsyncSearch();
-function PeopleDrawer({ roomId }) {
- const PER_PAGE_MEMBER = 50;
- const mx = initMatrix.matrixClient;
- const room = mx.getRoom(roomId);
- const canInvite = room?.canInvite(mx.getUserId());
-
- const [itemCount, setItemCount] = useState(PER_PAGE_MEMBER);
- const [membership, setMembership] = useState('join');
- const [memberList, setMemberList] = useState([]);
- const [searchedMembers, setSearchedMembers] = useState(null);
- const searchRef = useRef(null);
-
- const getMembersWithMembership = useCallback(
- (mship) => room.getMembersWithMembership(mship),
- [roomId, membership],
- );
-
- function loadMorePeople() {
- setItemCount(itemCount + PER_PAGE_MEMBER);
- }
-
- function handleSearchData(data) {
- // NOTICE: data is passed as object property
- // because react sucks at handling state update with array.
- setSearchedMembers({ data });
- setItemCount(PER_PAGE_MEMBER);
- }
-
- function handleSearch(e) {
- const term = e.target.value;
- if (term === '' || term === undefined) {
- searchRef.current.value = '';
- searchRef.current.focus();
- setSearchedMembers(null);
- setItemCount(PER_PAGE_MEMBER);
- } else asyncSearch.search(term);
- }
-
- useEffect(() => {
- asyncSearch.setup(memberList, {
- keys: ['name', 'username', 'userId'],
- limit: PER_PAGE_MEMBER,
- });
- }, [memberList]);
-
- useEffect(() => {
- let isLoadingMembers = false;
- let isRoomChanged = false;
- const updateMemberList = (event) => {
- if (isLoadingMembers) return;
- if (event && event?.getRoomId() !== roomId) return;
- setMemberList(
- simplyfiMembers(
- getMembersWithMembership(membership)
- .sort(memberByAtoZ).sort(memberByPowerLevel),
- ),
- );
- };
- searchRef.current.value = '';
- updateMemberList();
- isLoadingMembers = true;
- room.loadMembersIfNeeded().then(() => {
- isLoadingMembers = false;
- if (isRoomChanged) return;
- updateMemberList();
- });
-
- asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchData);
- mx.on('RoomMember.membership', updateMemberList);
- mx.on('RoomMember.powerLevel', updateMemberList);
- return () => {
- isRoomChanged = true;
- setMemberList([]);
- setSearchedMembers(null);
- setItemCount(PER_PAGE_MEMBER);
- asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchData);
- mx.removeListener('RoomMember.membership', updateMemberList);
- mx.removeListener('RoomMember.powerLevel', updateMemberList);
- };
- }, [roomId, membership]);
-
- useEffect(() => {
- setMembership('join');
- }, [roomId]);
-
- const mList = searchedMembers !== null ? searchedMembers.data : memberList.slice(0, itemCount);
- return (
-
-
-
-
- People
- {`${room.getJoinedMemberCount()} members`}
-
-
- openInviteUser(roomId)} tooltip="Invite" src={AddUserIC} disabled={!canInvite} />
-
-
-
-
-
-
{
- const getSegmentIndex = {
- join: 0,
- invite: 1,
- ban: 2,
- };
- return getSegmentIndex[membership];
- })()
- }
- segments={[{ text: 'Joined' }, { text: 'Invited' }, { text: 'Banned' }]}
- onSelect={(index) => {
- const selectSegment = [
- () => setMembership('join'),
- () => setMembership('invite'),
- () => setMembership('ban'),
- ];
- selectSegment[index]?.();
- }}
- />
- {
- mList.map((member) => (
- openProfileViewer(member.userId, roomId)}
- avatarSrc={member.avatarSrc}
- name={member.name}
- color={colorMXID(member.userId)}
- peopleRole={member.peopleRole}
- />
- ))
- }
- {
- (searchedMembers?.data.length === 0 || memberList.length === 0)
- && (
-
- No results found!
-
- )
- }
-
- {
- mList.length !== 0
- && memberList.length > itemCount
- && searchedMembers === null
- && (
-
- )
- }
-
-
-
-
-
-
-
-
-
- );
-}
-
-PeopleDrawer.propTypes = {
- roomId: PropTypes.string.isRequired,
-};
-
-export default PeopleDrawer;
diff --git a/src/app/organisms/room/RoomViewCmdBar.jsx b/src/app/organisms/room/RoomViewCmdBar.jsx
deleted file mode 100644
index 0d21123..0000000
--- a/src/app/organisms/room/RoomViewCmdBar.jsx
+++ /dev/null
@@ -1,297 +0,0 @@
-/* eslint-disable react/prop-types */
-import React, { useState, useEffect } from 'react';
-import PropTypes from 'prop-types';
-import './RoomViewCmdBar.scss';
-import parse from 'html-react-parser';
-import twemoji from 'twemoji';
-
-import { twemojify, TWEMOJI_BASE_URL } from '../../../util/twemojify';
-
-import initMatrix from '../../../client/initMatrix';
-import { getEmojiForCompletion } from '../emoji-board/custom-emoji';
-import AsyncSearch from '../../../util/AsyncSearch';
-
-import Text from '../../atoms/text/Text';
-import ScrollView from '../../atoms/scroll/ScrollView';
-import FollowingMembers from '../../molecules/following-members/FollowingMembers';
-import { addRecentEmoji, getRecentEmojis } from '../emoji-board/recent';
-import commands from './commands';
-
-function CmdItem({ onClick, children }) {
- return (
-
- );
-}
-CmdItem.propTypes = {
- onClick: PropTypes.func.isRequired,
- children: PropTypes.node.isRequired,
-};
-
-function renderSuggestions({ prefix, option, suggestions }, fireCmd) {
- function renderCmdSuggestions(cmdPrefix, cmds) {
- const cmdOptString = typeof option === 'string' ? `/${option}` : '/?';
- return cmds.map((cmd) => (
- {
- fireCmd({
- prefix: cmdPrefix,
- option,
- result: commands[cmd],
- });
- }}
- >
- {`${cmd}${cmd.isOptions ? cmdOptString : ''}`}
-
- ));
- }
-
- function renderEmojiSuggestion(emPrefix, emos) {
- const mx = initMatrix.matrixClient;
-
- // Renders a small Twemoji
- function renderTwemoji(emoji) {
- return parse(
- twemoji.parse(emoji.unicode, {
- attributes: () => ({
- unicode: emoji.unicode,
- shortcodes: emoji.shortcodes?.toString(),
- }),
- base: TWEMOJI_BASE_URL,
- })
- );
- }
-
- // Render a custom emoji
- function renderCustomEmoji(emoji) {
- return (
-
- );
- }
-
- // Dynamically render either a custom emoji or twemoji based on what the input is
- function renderEmoji(emoji) {
- if (emoji.mxc) {
- return renderCustomEmoji(emoji);
- }
- return renderTwemoji(emoji);
- }
-
- return emos.map((emoji) => (
-
- fireCmd({
- prefix: emPrefix,
- result: emoji,
- })
- }
- >
- {renderEmoji(emoji)}
- {`:${emoji.shortcode}:`}
-
- ));
- }
-
- function renderNameSuggestion(namePrefix, members) {
- return members.map((member) => (
- {
- fireCmd({
- prefix: namePrefix,
- result: member,
- });
- }}
- >
- {twemojify(member.name)}
-
- ));
- }
-
- const cmd = {
- '/': (cmds) => renderCmdSuggestions(prefix, cmds),
- ':': (emos) => renderEmojiSuggestion(prefix, emos),
- '@': (members) => renderNameSuggestion(prefix, members),
- };
- return cmd[prefix]?.(suggestions);
-}
-
-const asyncSearch = new AsyncSearch();
-let cmdPrefix;
-let cmdOption;
-function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
- const [cmd, setCmd] = useState(null);
-
- function displaySuggestions(suggestions) {
- if (suggestions.length === 0) {
- setCmd({ prefix: cmd?.prefix || cmdPrefix, error: 'No suggestion found.' });
- viewEvent.emit('cmd_error');
- return;
- }
- setCmd({ prefix: cmd?.prefix || cmdPrefix, suggestions, option: cmdOption });
- }
-
- function processCmd(prefix, slug) {
- let searchTerm = slug;
- cmdOption = undefined;
- cmdPrefix = prefix;
- if (prefix === '/') {
- const cmdSlugParts = slug.split('/');
- [searchTerm, cmdOption] = cmdSlugParts;
- }
- if (prefix === ':') {
- if (searchTerm.length <= 3) {
- if (searchTerm.match(/^[-]?(\))$/)) searchTerm = 'smile';
- else if (searchTerm.match(/^[-]?(s|S)$/)) searchTerm = 'confused';
- else if (searchTerm.match(/^[-]?(o|O|0)$/)) searchTerm = 'astonished';
- else if (searchTerm.match(/^[-]?(\|)$/)) searchTerm = 'neutral_face';
- else if (searchTerm.match(/^[-]?(d|D)$/)) searchTerm = 'grin';
- else if (searchTerm.match(/^[-]?(\/)$/)) searchTerm = 'frown';
- else if (searchTerm.match(/^[-]?(p|P)$/)) searchTerm = 'stuck_out_tongue';
- else if (searchTerm.match(/^'[-]?(\()$/)) searchTerm = 'cry';
- else if (searchTerm.match(/^[-]?(x|X)$/)) searchTerm = 'dizzy_face';
- else if (searchTerm.match(/^[-]?(\()$/)) searchTerm = 'pleading_face';
- else if (searchTerm.match(/^[-]?(\$)$/)) searchTerm = 'money';
- else if (searchTerm.match(/^(<3)$/)) searchTerm = 'heart';
- else if (searchTerm.match(/^(c|ca|cat)$/)) searchTerm = '_cat';
- }
- }
-
- asyncSearch.search(searchTerm);
- }
- function activateCmd(prefix) {
- cmdPrefix = prefix;
- cmdPrefix = undefined;
-
- const mx = initMatrix.matrixClient;
- const setupSearch = {
- '/': () => {
- asyncSearch.setup(Object.keys(commands), { isContain: true });
- setCmd({ prefix, suggestions: Object.keys(commands) });
- },
- ':': () => {
- const parentIds = initMatrix.roomList.getAllParentSpaces(roomId);
- const parentRooms = [...parentIds].map((id) => mx.getRoom(id));
- const emojis = getEmojiForCompletion(mx, [mx.getRoom(roomId), ...parentRooms]);
- const recentEmoji = getRecentEmojis(20);
- asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 20 });
- setCmd({
- prefix,
- suggestions: recentEmoji.length > 0 ? recentEmoji : emojis.slice(26, 46),
- });
- },
- '@': () => {
- const members = mx
- .getRoom(roomId)
- .getJoinedMembers()
- .map((member) => ({
- name: member.name,
- userId: member.userId.slice(1),
- }));
- asyncSearch.setup(members, { keys: ['name', 'userId'], limit: 20 });
- const endIndex = members.length > 20 ? 20 : members.length;
- setCmd({ prefix, suggestions: members.slice(0, endIndex) });
- },
- };
- setupSearch[prefix]?.();
- }
- function deactivateCmd() {
- setCmd(null);
- cmdOption = undefined;
- cmdPrefix = undefined;
- }
- function fireCmd(myCmd) {
- if (myCmd.prefix === '/') {
- viewEvent.emit('cmd_fired', {
- replace: `/${myCmd.result.name}`,
- });
- }
- if (myCmd.prefix === ':') {
- if (!myCmd.result.mxc) addRecentEmoji(myCmd.result.unicode);
- viewEvent.emit('cmd_fired', {
- replace: myCmd.result.mxc ? `:${myCmd.result.shortcode}: ` : myCmd.result.unicode,
- });
- }
- if (myCmd.prefix === '@') {
- viewEvent.emit('cmd_fired', {
- replace: `@${myCmd.result.userId}`,
- });
- }
- deactivateCmd();
- }
-
- function listenKeyboard(event) {
- const { activeElement } = document;
- const lastCmdItem = document.activeElement.parentNode.lastElementChild;
- if (event.key === 'Escape') {
- if (activeElement.className !== 'cmd-item') return;
- viewEvent.emit('focus_msg_input');
- }
- if (event.key === 'Tab') {
- if (lastCmdItem.className !== 'cmd-item') return;
- if (lastCmdItem !== activeElement) return;
- if (event.shiftKey) return;
- viewEvent.emit('focus_msg_input');
- event.preventDefault();
- }
- }
-
- useEffect(() => {
- viewEvent.on('cmd_activate', activateCmd);
- viewEvent.on('cmd_deactivate', deactivateCmd);
- return () => {
- deactivateCmd();
- viewEvent.removeListener('cmd_activate', activateCmd);
- viewEvent.removeListener('cmd_deactivate', deactivateCmd);
- };
- }, [roomId]);
-
- useEffect(() => {
- if (cmd !== null) document.body.addEventListener('keydown', listenKeyboard);
- viewEvent.on('cmd_process', processCmd);
- asyncSearch.on(asyncSearch.RESULT_SENT, displaySuggestions);
- return () => {
- if (cmd !== null) document.body.removeEventListener('keydown', listenKeyboard);
-
- viewEvent.removeListener('cmd_process', processCmd);
- asyncSearch.removeListener(asyncSearch.RESULT_SENT, displaySuggestions);
- };
- }, [cmd]);
-
- const isError = typeof cmd?.error === 'string';
- if (cmd === null || isError) {
- return (
-
-
-
- );
- }
-
- return (
-
-
- TAB
-
-
-
- {renderSuggestions(cmd, fireCmd)}
-
-
-
- );
-}
-RoomViewCmdBar.propTypes = {
- roomId: PropTypes.string.isRequired,
- roomTimeline: PropTypes.shape({}).isRequired,
- viewEvent: PropTypes.shape({}).isRequired,
-};
-
-export default RoomViewCmdBar;
diff --git a/src/app/organisms/room/RoomViewCmdBar.scss b/src/app/organisms/room/RoomViewCmdBar.scss
deleted file mode 100644
index 3f03fb0..0000000
--- a/src/app/organisms/room/RoomViewCmdBar.scss
+++ /dev/null
@@ -1,57 +0,0 @@
-@use '../../partials/flex';
-@use '../../partials/text';
-@use '../../partials/dir';
-
-.cmd-bar {
- --cmd-bar-height: 28px;
- min-height: var(--cmd-bar-height);
- display: flex;
-
- &__info {
- display: flex;
- width: 40px;
- @include dir.side(margin, 14px, 10px);
-
- & > * {
- margin: auto;
- }
- }
-
- &__content {
- @extend .cp-fx__item-one;
- display: flex;
-
- &-suggestions {
- height: 100%;
- white-space: nowrap;
- display: flex;
- align-items: center;
-
- & > .text {
- @extend .cp-txt__ellipsis;
- }
- }
- }
-}
-
-.cmd-item {
- --cmd-item-bar: inset 0 -2px 0 0 var(--bg-caution);
- height: 100%;
- @include dir.side(margin, 0, var(--sp-extra-tight));
- padding: 0 var(--sp-extra-tight);
- border-radius: var(--bo-radius) var(--bo-radius) 0 0;
- cursor: pointer;
-
- display: inline-flex;
- align-items: center;
-
- &:hover {
- background-color: var(--bg-caution-hover);
- }
- &:focus {
- background-color: var(--bg-caution-active);
- box-shadow: var(--cmd-item-bar);
- border-bottom: 2px solid transparent;
- outline: none;
- }
-}
\ No newline at end of file
diff --git a/src/app/organisms/room/RoomViewContent.jsx b/src/app/organisms/room/RoomViewContent.jsx
deleted file mode 100644
index 5726fe1..0000000
--- a/src/app/organisms/room/RoomViewContent.jsx
+++ /dev/null
@@ -1,644 +0,0 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-/* eslint-disable react/prop-types */
-import React, {
- useState, useEffect, useLayoutEffect, useCallback, useRef,
-} from 'react';
-import PropTypes from 'prop-types';
-import './RoomViewContent.scss';
-
-import dateFormat from 'dateformat';
-import { twemojify } from '../../../util/twemojify';
-
-import initMatrix from '../../../client/initMatrix';
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import { openProfileViewer } from '../../../client/action/navigation';
-import { diffMinutes, isInSameDay, Throttle } from '../../../util/common';
-import { markAsRead } from '../../../client/action/notifications';
-
-import Divider from '../../atoms/divider/Divider';
-import ScrollView from '../../atoms/scroll/ScrollView';
-import { Message, PlaceholderMessage } from '../../molecules/message/Message';
-import RoomIntro from '../../molecules/room-intro/RoomIntro';
-import TimelineChange from '../../molecules/message/TimelineChange';
-
-import { useStore } from '../../hooks/useStore';
-import { useForceUpdate } from '../../hooks/useForceUpdate';
-import { parseTimelineChange } from './common';
-import TimelineScroll from './TimelineScroll';
-import EventLimit from './EventLimit';
-import { getResizeObserverEntry, useResizeObserver } from '../../hooks/useResizeObserver';
-
-const PAG_LIMIT = 30;
-const MAX_MSG_DIFF_MINUTES = 5;
-const PLACEHOLDER_COUNT = 2;
-const PLACEHOLDERS_HEIGHT = 96 * PLACEHOLDER_COUNT;
-const SCROLL_TRIGGER_POS = PLACEHOLDERS_HEIGHT * 4;
-
-function loadingMsgPlaceholders(key, count = 2) {
- const pl = [];
- const genPlaceholders = () => {
- for (let i = 0; i < count; i += 1) {
- pl.push();
- }
- return pl;
- };
-
- return (
-
- {genPlaceholders()}
-
- );
-}
-
-function RoomIntroContainer({ event, timeline }) {
- const [, nameForceUpdate] = useForceUpdate();
- const mx = initMatrix.matrixClient;
- const { roomList } = initMatrix;
- const { room } = timeline;
- const roomTopic = room.currentState.getStateEvents('m.room.topic')[0]?.getContent().topic;
- const isDM = roomList.directs.has(timeline.roomId);
- let avatarSrc = room.getAvatarUrl(mx.baseUrl, 80, 80, 'crop');
- avatarSrc = isDM ? room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 80, 80, 'crop') : avatarSrc;
-
- const heading = isDM ? room.name : `Welcome to ${room.name}`;
- const topic = twemojify(roomTopic || '', undefined, true);
- const nameJsx = twemojify(room.name);
- const desc = isDM
- ? (
- <>
- This is the beginning of your direct message history with @
- {nameJsx}
- {'. '}
- {topic}
- >
- )
- : (
- <>
- {'This is the beginning of the '}
- {nameJsx}
- {' room. '}
- {topic}
- >
- );
-
- useEffect(() => {
- const handleUpdate = () => nameForceUpdate();
-
- roomList.on(cons.events.roomList.ROOM_PROFILE_UPDATED, handleUpdate);
- return () => {
- roomList.removeListener(cons.events.roomList.ROOM_PROFILE_UPDATED, handleUpdate);
- };
- }, []);
-
- return (
-
- );
-}
-
-function handleOnClickCapture(e) {
- const { target, nativeEvent } = e;
-
- const userId = target.getAttribute('data-mx-pill');
- if (userId) {
- const roomId = navigation.selectedRoomId;
- openProfileViewer(userId, roomId);
- }
-
- const spoiler = nativeEvent.composedPath().find((el) => el?.hasAttribute?.('data-mx-spoiler'));
- if (spoiler) {
- if (!spoiler.classList.contains('data-mx-spoiler--visible')) e.preventDefault();
- spoiler.classList.toggle('data-mx-spoiler--visible');
- }
-}
-
-function renderEvent(
- roomTimeline,
- mEvent,
- prevMEvent,
- isFocus,
- isEdit,
- setEdit,
- cancelEdit,
-) {
- const isBodyOnly = (prevMEvent !== null
- && prevMEvent.getSender() === mEvent.getSender()
- && prevMEvent.getType() !== 'm.room.member'
- && prevMEvent.getType() !== 'm.room.create'
- && diffMinutes(mEvent.getDate(), prevMEvent.getDate()) <= MAX_MSG_DIFF_MINUTES
- );
- const timestamp = mEvent.getTs();
-
- if (mEvent.getType() === 'm.room.member') {
- const timelineChange = parseTimelineChange(mEvent);
- if (timelineChange === null) return ;
- return (
-
- );
- }
- return (
-
- );
-}
-
-function useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef) {
- const [timelineInfo, setTimelineInfo] = useState(null);
-
- const setEventTimeline = async (eId) => {
- if (typeof eId === 'string') {
- const isLoaded = await roomTimeline.loadEventTimeline(eId);
- if (isLoaded) return;
- // if eventTimeline failed to load,
- // we will load live timeline as fallback.
- }
- roomTimeline.loadLiveTimeline();
- };
-
- useEffect(() => {
- const limit = eventLimitRef.current;
- const initTimeline = (eId) => {
- // NOTICE: eId can be id of readUpto, reply or specific event.
- // readUpTo: when user click jump to unread message button.
- // reply: when user click reply from timeline.
- // specific event when user open a link of event. behave same as ^^^^
- const readUpToId = roomTimeline.getReadUpToEventId();
- let focusEventIndex = -1;
- const isSpecificEvent = eId && eId !== readUpToId;
-
- if (isSpecificEvent) {
- focusEventIndex = roomTimeline.getEventIndex(eId);
- }
- if (!readUptoEvtStore.getItem() && roomTimeline.hasEventInTimeline(readUpToId)) {
- // either opening live timeline or jump to unread.
- readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
- }
- if (readUptoEvtStore.getItem() && !isSpecificEvent) {
- focusEventIndex = roomTimeline.getUnreadEventIndex(readUptoEvtStore.getItem().getId());
- }
-
- if (focusEventIndex > -1) {
- limit.setFrom(focusEventIndex - Math.round(limit.maxEvents / 2));
- } else {
- limit.setFrom(roomTimeline.timeline.length - limit.maxEvents);
- }
- setTimelineInfo({ focusEventId: isSpecificEvent ? eId : null });
- };
-
- roomTimeline.on(cons.events.roomTimeline.READY, initTimeline);
- setEventTimeline(eventId);
- return () => {
- roomTimeline.removeListener(cons.events.roomTimeline.READY, initTimeline);
- limit.setFrom(0);
- };
- }, [roomTimeline, eventId]);
-
- return timelineInfo;
-}
-
-function usePaginate(
- roomTimeline,
- readUptoEvtStore,
- forceUpdateLimit,
- timelineScrollRef,
- eventLimitRef,
-) {
- const [info, setInfo] = useState(null);
-
- useEffect(() => {
- const handlePaginatedFromServer = (backwards, loaded) => {
- const limit = eventLimitRef.current;
- if (loaded === 0) return;
- if (!readUptoEvtStore.getItem()) {
- const readUpToId = roomTimeline.getReadUpToEventId();
- readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
- }
- limit.paginate(backwards, PAG_LIMIT, roomTimeline.timeline.length);
- setTimeout(() => setInfo({
- backwards,
- loaded,
- }));
- };
- roomTimeline.on(cons.events.roomTimeline.PAGINATED, handlePaginatedFromServer);
- return () => {
- roomTimeline.removeListener(cons.events.roomTimeline.PAGINATED, handlePaginatedFromServer);
- };
- }, [roomTimeline]);
-
- const autoPaginate = useCallback(async () => {
- const timelineScroll = timelineScrollRef.current;
- const limit = eventLimitRef.current;
- if (roomTimeline.isOngoingPagination) return;
- const tLength = roomTimeline.timeline.length;
-
- if (timelineScroll.bottom < SCROLL_TRIGGER_POS) {
- if (limit.length < tLength) {
- // paginate from memory
- limit.paginate(false, PAG_LIMIT, tLength);
- forceUpdateLimit();
- } else if (roomTimeline.canPaginateForward()) {
- // paginate from server.
- await roomTimeline.paginateTimeline(false, PAG_LIMIT);
- return;
- }
- }
- if (timelineScroll.top < SCROLL_TRIGGER_POS) {
- if (limit.from > 0) {
- // paginate from memory
- limit.paginate(true, PAG_LIMIT, tLength);
- forceUpdateLimit();
- } else if (roomTimeline.canPaginateBackward()) {
- // paginate from server.
- await roomTimeline.paginateTimeline(true, PAG_LIMIT);
- }
- }
- }, [roomTimeline]);
-
- return [info, autoPaginate];
-}
-
-function useHandleScroll(
- roomTimeline,
- autoPaginate,
- readUptoEvtStore,
- forceUpdateLimit,
- timelineScrollRef,
- eventLimitRef,
-) {
- const handleScroll = useCallback(() => {
- const timelineScroll = timelineScrollRef.current;
- const limit = eventLimitRef.current;
- requestAnimationFrame(() => {
- // emit event to toggle scrollToBottom button visibility
- const isAtBottom = (
- timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()
- && limit.length >= roomTimeline.timeline.length
- );
- roomTimeline.emit(cons.events.roomTimeline.AT_BOTTOM, isAtBottom);
- if (isAtBottom && readUptoEvtStore.getItem()) {
- requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
- }
- });
- autoPaginate();
- }, [roomTimeline]);
-
- const handleScrollToLive = useCallback(() => {
- const timelineScroll = timelineScrollRef.current;
- const limit = eventLimitRef.current;
- if (readUptoEvtStore.getItem()) {
- requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
- }
- if (roomTimeline.isServingLiveTimeline()) {
- limit.setFrom(roomTimeline.timeline.length - limit.maxEvents);
- timelineScroll.scrollToBottom();
- forceUpdateLimit();
- return;
- }
- roomTimeline.loadLiveTimeline();
- }, [roomTimeline]);
-
- return [handleScroll, handleScrollToLive];
-}
-
-function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef) {
- const myUserId = initMatrix.matrixClient.getUserId();
- const [newEvent, setEvent] = useState(null);
-
- useEffect(() => {
- const timelineScroll = timelineScrollRef.current;
- const limit = eventLimitRef.current;
- const trySendReadReceipt = (event) => {
- if (myUserId === event.getSender()) {
- requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
- return;
- }
- const readUpToEvent = readUptoEvtStore.getItem();
- const readUpToId = roomTimeline.getReadUpToEventId();
- const isUnread = readUpToEvent ? readUpToEvent?.getId() === readUpToId : true;
-
- if (isUnread === false) {
- if (document.visibilityState === 'visible' && timelineScroll.bottom < 16) {
- requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
- } else {
- readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
- }
- return;
- }
-
- const { timeline } = roomTimeline;
- const unreadMsgIsLast = timeline[timeline.length - 2].getId() === readUpToId;
- if (unreadMsgIsLast) {
- requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
- }
- };
-
- const handleEvent = (event) => {
- const tLength = roomTimeline.timeline.length;
- const isViewingLive = roomTimeline.isServingLiveTimeline() && limit.length >= tLength - 1;
- const isAttached = timelineScroll.bottom < SCROLL_TRIGGER_POS;
-
- if (isViewingLive && isAttached && document.hasFocus()) {
- limit.setFrom(tLength - limit.maxEvents);
- trySendReadReceipt(event);
- setEvent(event);
- return;
- }
- const isRelates = (event.getType() === 'm.reaction' || event.getRelation()?.rel_type === 'm.replace');
- if (isRelates) {
- setEvent(event);
- return;
- }
-
- if (isViewingLive) {
- // This stateUpdate will help to put the
- // loading msg placeholder at bottom
- setEvent(event);
- }
- };
-
- const handleEventRedact = (event) => setEvent(event);
-
- roomTimeline.on(cons.events.roomTimeline.EVENT, handleEvent);
- roomTimeline.on(cons.events.roomTimeline.EVENT_REDACTED, handleEventRedact);
- return () => {
- roomTimeline.removeListener(cons.events.roomTimeline.EVENT, handleEvent);
- roomTimeline.removeListener(cons.events.roomTimeline.EVENT_REDACTED, handleEventRedact);
- };
- }, [roomTimeline]);
-
- return newEvent;
-}
-
-let jumpToItemIndex = -1;
-
-function RoomViewContent({ roomInputRef, eventId, roomTimeline }) {
- const [throttle] = useState(new Throttle());
-
- const timelineSVRef = useRef(null);
- const timelineScrollRef = useRef(null);
- const eventLimitRef = useRef(null);
- const [editEventId, setEditEventId] = useState(null);
- const cancelEdit = () => setEditEventId(null);
-
- const readUptoEvtStore = useStore(roomTimeline);
- const [onLimitUpdate, forceUpdateLimit] = useForceUpdate();
-
- const timelineInfo = useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef);
- const [paginateInfo, autoPaginate] = usePaginate(
- roomTimeline,
- readUptoEvtStore,
- forceUpdateLimit,
- timelineScrollRef,
- eventLimitRef,
- );
- const [handleScroll, handleScrollToLive] = useHandleScroll(
- roomTimeline,
- autoPaginate,
- readUptoEvtStore,
- forceUpdateLimit,
- timelineScrollRef,
- eventLimitRef,
- );
- const newEvent = useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef);
-
- const { timeline } = roomTimeline;
-
- useLayoutEffect(() => {
- if (!roomTimeline.initialized) {
- timelineScrollRef.current = new TimelineScroll(timelineSVRef.current);
- eventLimitRef.current = new EventLimit();
- }
- });
-
- // when active timeline changes
- useEffect(() => {
- if (!roomTimeline.initialized) return undefined;
- const timelineScroll = timelineScrollRef.current;
-
- if (timeline.length > 0) {
- if (jumpToItemIndex === -1) {
- timelineScroll.scrollToBottom();
- } else {
- timelineScroll.scrollToIndex(jumpToItemIndex, 80);
- }
- if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()) {
- const readUpToId = roomTimeline.getReadUpToEventId();
- if (readUptoEvtStore.getItem()?.getId() === readUpToId || readUpToId === null) {
- requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
- }
- }
- jumpToItemIndex = -1;
- }
- autoPaginate();
-
- roomTimeline.on(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
- return () => {
- if (timelineSVRef.current === null) return;
- roomTimeline.removeListener(cons.events.roomTimeline.SCROLL_TO_LIVE, handleScrollToLive);
- };
- }, [timelineInfo]);
-
- // when paginating from server
- useEffect(() => {
- if (!roomTimeline.initialized) return;
- const timelineScroll = timelineScrollRef.current;
- timelineScroll.tryRestoringScroll();
- autoPaginate();
- }, [paginateInfo]);
-
- // when paginating locally
- useEffect(() => {
- if (!roomTimeline.initialized) return;
- const timelineScroll = timelineScrollRef.current;
- timelineScroll.tryRestoringScroll();
- }, [onLimitUpdate]);
-
- useEffect(() => {
- const timelineScroll = timelineScrollRef.current;
- if (!roomTimeline.initialized) return;
- if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward() && document.visibilityState === 'visible') {
- timelineScroll.scrollToBottom();
- } else {
- timelineScroll.tryRestoringScroll();
- }
- }, [newEvent]);
-
- useResizeObserver(
- useCallback((entries) => {
- if (!roomInputRef.current) return;
- const editorBaseEntry = getResizeObserverEntry(roomInputRef.current, entries);
- if (!editorBaseEntry) return;
-
- const timelineScroll = timelineScrollRef.current;
- if (!roomTimeline.initialized) return;
- if (timelineScroll.bottom < 40 && !roomTimeline.canPaginateForward() && document.visibilityState === 'visible') {
- timelineScroll.scrollToBottom();
- }
- }, [roomInputRef]),
- useCallback(() => roomInputRef.current, [roomInputRef]),
- );
-
- const listenKeyboard = useCallback((event) => {
- if (event.ctrlKey || event.altKey || event.metaKey) return;
- if (event.key !== 'ArrowUp') return;
- if (navigation.isRawModalVisible) return;
-
- if (document.activeElement.id !== 'message-textarea') return;
- if (document.activeElement.value !== '') return;
-
- const {
- timeline: tl, activeTimeline, liveTimeline, matrixClient: mx,
- } = roomTimeline;
- const limit = eventLimitRef.current;
- if (activeTimeline !== liveTimeline) return;
- if (tl.length > limit.length) return;
-
- const mTypes = ['m.text'];
- for (let i = tl.length - 1; i >= 0; i -= 1) {
- const mE = tl[i];
- if (
- mE.getSender() === mx.getUserId()
- && mE.getType() === 'm.room.message'
- && mTypes.includes(mE.getContent()?.msgtype)
- ) {
- setEditEventId(mE.getId());
- return;
- }
- }
- }, [roomTimeline]);
-
- useEffect(() => {
- document.body.addEventListener('keydown', listenKeyboard);
- return () => {
- document.body.removeEventListener('keydown', listenKeyboard);
- };
- }, [listenKeyboard]);
-
- const handleTimelineScroll = (event) => {
- const timelineScroll = timelineScrollRef.current;
- if (!event.target) return;
-
- throttle._(() => {
- const backwards = timelineScroll?.calcScroll();
- if (typeof backwards !== 'boolean') return;
- handleScroll(backwards);
- }, 200)();
- };
-
- const renderTimeline = () => {
- const tl = [];
- const limit = eventLimitRef.current;
-
- let itemCountIndex = 0;
- jumpToItemIndex = -1;
- const readUptoEvent = readUptoEvtStore.getItem();
- let unreadDivider = false;
-
- if (roomTimeline.canPaginateBackward() || limit.from > 0) {
- tl.push(loadingMsgPlaceholders(1, PLACEHOLDER_COUNT));
- itemCountIndex += PLACEHOLDER_COUNT;
- }
- for (let i = limit.from; i < limit.length; i += 1) {
- if (i >= timeline.length) break;
- const mEvent = timeline[i];
- const prevMEvent = timeline[i - 1] ?? null;
-
- if (i === 0 && !roomTimeline.canPaginateBackward()) {
- if (mEvent.getType() === 'm.room.create') {
- tl.push(
- ,
- );
- itemCountIndex += 1;
- // eslint-disable-next-line no-continue
- continue;
- } else {
- tl.push();
- itemCountIndex += 1;
- }
- }
-
- let isNewEvent = false;
- if (!unreadDivider) {
- unreadDivider = (readUptoEvent
- && prevMEvent?.getTs() <= readUptoEvent.getTs()
- && readUptoEvent.getTs() < mEvent.getTs());
- if (unreadDivider) {
- isNewEvent = true;
- tl.push();
- itemCountIndex += 1;
- if (jumpToItemIndex === -1) jumpToItemIndex = itemCountIndex;
- }
- }
- const dayDivider = prevMEvent && !isInSameDay(mEvent.getDate(), prevMEvent.getDate());
- if (dayDivider) {
- tl.push();
- itemCountIndex += 1;
- }
-
- const focusId = timelineInfo.focusEventId;
- const isFocus = focusId === mEvent.getId();
- if (isFocus) jumpToItemIndex = itemCountIndex;
-
- tl.push(renderEvent(
- roomTimeline,
- mEvent,
- isNewEvent ? null : prevMEvent,
- isFocus,
- editEventId === mEvent.getId(),
- setEditEventId,
- cancelEdit,
- ));
- itemCountIndex += 1;
- }
- if (roomTimeline.canPaginateForward() || limit.length < timeline.length) {
- tl.push(loadingMsgPlaceholders(2, PLACEHOLDER_COUNT));
- }
-
- return tl;
- };
-
- return (
-
-
-
- { roomTimeline.initialized ? renderTimeline() : loadingMsgPlaceholders('loading', 3) }
-
-
-
- );
-}
-
-RoomViewContent.defaultProps = {
- eventId: null,
-};
-RoomViewContent.propTypes = {
- eventId: PropTypes.string,
- roomTimeline: PropTypes.shape({}).isRequired,
- roomInputRef: PropTypes.shape({
- current: PropTypes.shape({})
- }).isRequired
-};
-
-export default RoomViewContent;
diff --git a/src/app/organisms/room/RoomViewFloating.jsx b/src/app/organisms/room/RoomViewFloating.jsx
deleted file mode 100644
index d027aff..0000000
--- a/src/app/organisms/room/RoomViewFloating.jsx
+++ /dev/null
@@ -1,125 +0,0 @@
-/* eslint-disable react/prop-types */
-import React, { useState, useEffect } from 'react';
-import PropTypes from 'prop-types';
-import './RoomViewFloating.scss';
-
-import initMatrix from '../../../client/initMatrix';
-import cons from '../../../client/state/cons';
-import { markAsRead } from '../../../client/action/notifications';
-
-import Text from '../../atoms/text/Text';
-import Button from '../../atoms/button/Button';
-
-import MessageIC from '../../../../public/res/ic/outlined/message.svg';
-import MessageUnreadIC from '../../../../public/res/ic/outlined/message-unread.svg';
-import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
-
-import { getUsersActionJsx } from './common';
-
-function useJumpToEvent(roomTimeline) {
- const [eventId, setEventId] = useState(null);
-
- const jumpToEvent = () => {
- roomTimeline.loadEventTimeline(eventId);
- };
-
- const cancelJumpToEvent = () => {
- markAsRead(roomTimeline.roomId);
- setEventId(null);
- };
-
- useEffect(() => {
- const readEventId = roomTimeline.getReadUpToEventId();
- // we only show "Jump to unread" btn only if the event is not in timeline.
- // if event is in timeline
- // we will automatically open the timeline from that event position
- if (!readEventId?.startsWith('~') && !roomTimeline.hasEventInTimeline(readEventId)) {
- setEventId(readEventId);
- }
-
- const { notifications } = initMatrix;
- const handleMarkAsRead = () => setEventId(null);
- notifications.on(cons.events.notifications.FULL_READ, handleMarkAsRead);
-
- return () => {
- notifications.removeListener(cons.events.notifications.FULL_READ, handleMarkAsRead);
- setEventId(null);
- };
- }, [roomTimeline]);
-
- return [!!eventId, jumpToEvent, cancelJumpToEvent];
-}
-
-function useTypingMembers(roomTimeline) {
- const [typingMembers, setTypingMembers] = useState(new Set());
-
- const updateTyping = (members) => {
- const mx = initMatrix.matrixClient;
- members.delete(mx.getUserId());
- setTypingMembers(members);
- };
-
- useEffect(() => {
- setTypingMembers(new Set());
- roomTimeline.on(cons.events.roomTimeline.TYPING_MEMBERS_UPDATED, updateTyping);
- return () => {
- roomTimeline?.removeListener(cons.events.roomTimeline.TYPING_MEMBERS_UPDATED, updateTyping);
- };
- }, [roomTimeline]);
-
- return [typingMembers];
-}
-
-function useScrollToBottom(roomTimeline) {
- const [isAtBottom, setIsAtBottom] = useState(true);
- const handleAtBottom = (atBottom) => setIsAtBottom(atBottom);
-
- useEffect(() => {
- setIsAtBottom(true);
- roomTimeline.on(cons.events.roomTimeline.AT_BOTTOM, handleAtBottom);
- return () => roomTimeline.removeListener(cons.events.roomTimeline.AT_BOTTOM, handleAtBottom);
- }, [roomTimeline]);
-
- return [isAtBottom, setIsAtBottom];
-}
-
-function RoomViewFloating({
- roomId, roomTimeline,
-}) {
- const [isJumpToEvent, jumpToEvent, cancelJumpToEvent] = useJumpToEvent(roomTimeline);
- const [typingMembers] = useTypingMembers(roomTimeline);
- const [isAtBottom, setIsAtBottom] = useScrollToBottom(roomTimeline);
-
- const handleScrollToBottom = () => {
- roomTimeline.emit(cons.events.roomTimeline.SCROLL_TO_LIVE);
- setIsAtBottom(true);
- };
-
- return (
- <>
-
-
-
-
- 0 ? ' room-view__typing--open' : ''}`}>
-
-
{getUsersActionJsx(roomId, [...typingMembers], 'typing...')}
-
-
-
-
- >
- );
-}
-RoomViewFloating.propTypes = {
- roomId: PropTypes.string.isRequired,
- roomTimeline: PropTypes.shape({}).isRequired,
-};
-
-export default RoomViewFloating;
diff --git a/src/app/organisms/room/RoomViewInput.jsx b/src/app/organisms/room/RoomViewInput.jsx
deleted file mode 100644
index 3fb780a..0000000
--- a/src/app/organisms/room/RoomViewInput.jsx
+++ /dev/null
@@ -1,491 +0,0 @@
-/* eslint-disable react/prop-types */
-import React, { useState, useEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import './RoomViewInput.scss';
-
-import TextareaAutosize from 'react-autosize-textarea';
-
-import initMatrix from '../../../client/initMatrix';
-import cons from '../../../client/state/cons';
-import settings from '../../../client/state/settings';
-import { openEmojiBoard, openReusableContextMenu } from '../../../client/action/navigation';
-import navigation from '../../../client/state/navigation';
-import { bytesToSize, getEventCords } from '../../../util/common';
-import { getUsername } from '../../../util/matrixUtil';
-import colorMXID from '../../../util/colorMXID';
-
-import Text from '../../atoms/text/Text';
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import IconButton from '../../atoms/button/IconButton';
-import ScrollView from '../../atoms/scroll/ScrollView';
-import { MessageReply } from '../../molecules/message/Message';
-
-import StickerBoard from '../sticker-board/StickerBoard';
-import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
-
-import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
-import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
-import SendIC from '../../../../public/res/ic/outlined/send.svg';
-import StickerIC from '../../../../public/res/ic/outlined/sticker.svg';
-import ShieldIC from '../../../../public/res/ic/outlined/shield.svg';
-import VLCIC from '../../../../public/res/ic/outlined/vlc.svg';
-import VolumeFullIC from '../../../../public/res/ic/outlined/volume-full.svg';
-import FileIC from '../../../../public/res/ic/outlined/file.svg';
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-
-import commands from './commands';
-
-const CMD_REGEX = /(^\/|:|@)(\S*)$/;
-let isTyping = false;
-let isCmdActivated = false;
-let cmdCursorPos = null;
-function RoomViewInput({
- roomId, roomTimeline, viewEvent,
-}) {
- const [attachment, setAttachment] = useState(null);
- const [replyTo, setReplyTo] = useState(null);
-
- const textAreaRef = useRef(null);
- const inputBaseRef = useRef(null);
- const uploadInputRef = useRef(null);
- const uploadProgressRef = useRef(null);
- const rightOptionsRef = useRef(null);
-
- const TYPING_TIMEOUT = 5000;
- const mx = initMatrix.matrixClient;
- const { roomsInput } = initMatrix;
-
- function requestFocusInput() {
- if (textAreaRef === null) return;
- textAreaRef.current.focus();
- }
-
- useEffect(() => {
- roomsInput.on(cons.events.roomsInput.ATTACHMENT_SET, setAttachment);
- viewEvent.on('focus_msg_input', requestFocusInput);
- return () => {
- roomsInput.removeListener(cons.events.roomsInput.ATTACHMENT_SET, setAttachment);
- viewEvent.removeListener('focus_msg_input', requestFocusInput);
- };
- }, []);
-
- const sendIsTyping = (isT) => {
- mx.sendTyping(roomId, isT, isT ? TYPING_TIMEOUT : undefined);
- isTyping = isT;
-
- if (isT === true) {
- setTimeout(() => {
- if (isTyping) sendIsTyping(false);
- }, TYPING_TIMEOUT);
- }
- };
-
- function uploadingProgress(myRoomId, { loaded, total }) {
- if (myRoomId !== roomId) return;
- const progressPer = Math.round((loaded * 100) / total);
- uploadProgressRef.current.textContent = `Uploading: ${bytesToSize(loaded)}/${bytesToSize(total)} (${progressPer}%)`;
- inputBaseRef.current.style.backgroundImage = `linear-gradient(90deg, var(--bg-surface-hover) ${progressPer}%, var(--bg-surface-low) ${progressPer}%)`;
- }
- function clearAttachment(myRoomId) {
- if (roomId !== myRoomId) return;
- setAttachment(null);
- inputBaseRef.current.style.backgroundImage = 'unset';
- uploadInputRef.current.value = null;
- }
-
- function rightOptionsA11Y(A11Y) {
- const rightOptions = rightOptionsRef.current.children;
- for (let index = 0; index < rightOptions.length; index += 1) {
- rightOptions[index].tabIndex = A11Y ? 0 : -1;
- }
- }
-
- function activateCmd(prefix) {
- isCmdActivated = true;
- rightOptionsA11Y(false);
- viewEvent.emit('cmd_activate', prefix);
- }
- function deactivateCmd() {
- isCmdActivated = false;
- cmdCursorPos = null;
- rightOptionsA11Y(true);
- }
- function deactivateCmdAndEmit() {
- deactivateCmd();
- viewEvent.emit('cmd_deactivate');
- }
- function setCursorPosition(pos) {
- setTimeout(() => {
- textAreaRef.current.focus();
- textAreaRef.current.setSelectionRange(pos, pos);
- }, 0);
- }
- function replaceCmdWith(msg, cursor, replacement) {
- if (msg === null) return null;
- const targetInput = msg.slice(0, cursor);
- const cmdParts = targetInput.match(CMD_REGEX);
- const leadingInput = msg.slice(0, cmdParts.index);
- if (replacement.length > 0) setCursorPosition(leadingInput.length + replacement.length);
- return leadingInput + replacement + msg.slice(cursor);
- }
- function firedCmd(cmdData) {
- const msg = textAreaRef.current.value;
- textAreaRef.current.value = replaceCmdWith(
- msg,
- cmdCursorPos,
- typeof cmdData?.replace !== 'undefined' ? cmdData.replace : '',
- );
- deactivateCmd();
- }
-
- function focusInput() {
- if (settings.isTouchScreenDevice) return;
- textAreaRef.current.focus();
- }
-
- function setUpReply(userId, eventId, body, formattedBody) {
- setReplyTo({ userId, eventId, body });
- roomsInput.setReplyTo(roomId, {
- userId, eventId, body, formattedBody,
- });
- focusInput();
- }
-
- useEffect(() => {
- roomsInput.on(cons.events.roomsInput.UPLOAD_PROGRESS_CHANGES, uploadingProgress);
- roomsInput.on(cons.events.roomsInput.ATTACHMENT_CANCELED, clearAttachment);
- roomsInput.on(cons.events.roomsInput.FILE_UPLOADED, clearAttachment);
- viewEvent.on('cmd_fired', firedCmd);
- navigation.on(cons.events.navigation.REPLY_TO_CLICKED, setUpReply);
- if (textAreaRef?.current !== null) {
- isTyping = false;
- textAreaRef.current.value = roomsInput.getMessage(roomId);
- setAttachment(roomsInput.getAttachment(roomId));
- setReplyTo(roomsInput.getReplyTo(roomId));
- }
- return () => {
- roomsInput.removeListener(cons.events.roomsInput.UPLOAD_PROGRESS_CHANGES, uploadingProgress);
- roomsInput.removeListener(cons.events.roomsInput.ATTACHMENT_CANCELED, clearAttachment);
- roomsInput.removeListener(cons.events.roomsInput.FILE_UPLOADED, clearAttachment);
- viewEvent.removeListener('cmd_fired', firedCmd);
- navigation.removeListener(cons.events.navigation.REPLY_TO_CLICKED, setUpReply);
- if (isCmdActivated) deactivateCmd();
- if (textAreaRef?.current === null) return;
-
- const msg = textAreaRef.current.value;
- textAreaRef.current.style.height = 'unset';
- inputBaseRef.current.style.backgroundImage = 'unset';
- if (msg.trim() === '') {
- roomsInput.setMessage(roomId, '');
- return;
- }
- roomsInput.setMessage(roomId, msg);
- };
- }, [roomId]);
-
- const sendBody = async (body, options) => {
- const opt = options ?? {};
- if (!opt.msgType) opt.msgType = 'm.text';
- if (typeof opt.autoMarkdown !== 'boolean') opt.autoMarkdown = true;
- if (roomsInput.isSending(roomId)) return;
- sendIsTyping(false);
-
- roomsInput.setMessage(roomId, body);
- if (attachment !== null) {
- roomsInput.setAttachment(roomId, attachment);
- }
- textAreaRef.current.disabled = true;
- textAreaRef.current.style.cursor = 'not-allowed';
- await roomsInput.sendInput(roomId, opt);
- textAreaRef.current.disabled = false;
- textAreaRef.current.style.cursor = 'unset';
- focusInput();
-
- textAreaRef.current.value = roomsInput.getMessage(roomId);
- textAreaRef.current.style.height = 'unset';
- if (replyTo !== null) setReplyTo(null);
- };
-
- /** Return true if a command was executed. */
- const processCommand = async (cmdBody) => {
- const spaceIndex = cmdBody.indexOf(' ');
- const cmdName = cmdBody.slice(1, spaceIndex > -1 ? spaceIndex : undefined);
- const cmdData = spaceIndex > -1 ? cmdBody.slice(spaceIndex + 1) : '';
- if (!commands[cmdName]) {
- const sendAsMessage = await confirmDialog('Invalid Command', `"${cmdName}" is not a valid command. Did you mean to send this as a message?`, 'Send as message');
- if (sendAsMessage) {
- sendBody(cmdBody);
- return true;
- }
- return false;
- }
- if (['me', 'shrug', 'plain'].includes(cmdName)) {
- commands[cmdName].exe(roomId, cmdData, sendBody);
- return true;
- }
- commands[cmdName].exe(roomId, cmdData);
- return true;
- };
-
- const sendMessage = async () => {
- requestAnimationFrame(() => deactivateCmdAndEmit());
- const msgBody = textAreaRef.current.value.trim();
- if (msgBody.startsWith('/')) {
- const executed = await processCommand(msgBody.trim());
- if (executed) {
- textAreaRef.current.value = '';
- textAreaRef.current.style.height = 'unset';
- }
- return;
- }
- if (msgBody === '' && attachment === null) return;
- sendBody(msgBody);
- };
-
- const handleSendSticker = async (data) => {
- roomsInput.sendSticker(roomId, data);
- };
-
- function processTyping(msg) {
- const isEmptyMsg = msg === '';
-
- if (isEmptyMsg && isTyping) {
- sendIsTyping(false);
- return;
- }
- if (!isEmptyMsg && !isTyping) {
- sendIsTyping(true);
- }
- }
-
- function getCursorPosition() {
- return textAreaRef.current.selectionStart;
- }
-
- function recognizeCmd(rawInput) {
- const cursor = getCursorPosition();
- const targetInput = rawInput.slice(0, cursor);
-
- const cmdParts = targetInput.match(CMD_REGEX);
- if (cmdParts === null) {
- if (isCmdActivated) deactivateCmdAndEmit();
- return;
- }
- const cmdPrefix = cmdParts[1];
- const cmdSlug = cmdParts[2];
-
- if (cmdPrefix === ':') {
- // skip emoji autofill command if link is suspected.
- const checkForLink = targetInput.slice(0, cmdParts.index);
- if (checkForLink.match(/(http|https|mailto|matrix|ircs|irc)$/)) {
- deactivateCmdAndEmit();
- return;
- }
- }
-
- cmdCursorPos = cursor;
- if (cmdSlug === '') {
- activateCmd(cmdPrefix);
- return;
- }
- if (!isCmdActivated) activateCmd(cmdPrefix);
- viewEvent.emit('cmd_process', cmdPrefix, cmdSlug);
- }
-
- const handleMsgTyping = (e) => {
- const msg = e.target.value;
- recognizeCmd(e.target.value);
- if (!isCmdActivated) processTyping(msg);
- };
-
- const handleKeyDown = (e) => {
- if (e.key === 'Escape') {
- e.preventDefault();
- roomsInput.cancelReplyTo(roomId);
- setReplyTo(null);
- }
- if (e.key === 'Enter' && e.shiftKey === false) {
- e.preventDefault();
- sendMessage();
- }
- };
-
- const handlePaste = (e) => {
- if (e.clipboardData === false) {
- return;
- }
-
- if (e.clipboardData.items === undefined) {
- return;
- }
-
- for (let i = 0; i < e.clipboardData.items.length; i += 1) {
- const item = e.clipboardData.items[i];
- if (item.type.indexOf('image') !== -1) {
- const image = item.getAsFile();
- if (attachment === null) {
- setAttachment(image);
- if (image !== null) {
- roomsInput.setAttachment(roomId, image);
- return;
- }
- } else {
- return;
- }
- }
- }
- };
-
- function addEmoji(emoji) {
- textAreaRef.current.value += emoji.unicode;
- textAreaRef.current.focus();
- }
-
- const handleUploadClick = () => {
- if (attachment === null) uploadInputRef.current.click();
- else {
- roomsInput.cancelAttachment(roomId);
- }
- };
- function uploadFileChange(e) {
- const file = e.target.files.item(0);
- setAttachment(file);
- if (file !== null) roomsInput.setAttachment(roomId, file);
- }
-
- function renderInputs() {
- const canISend = roomTimeline.room.currentState.maySendMessage(mx.getUserId());
- const tombstoneEvent = roomTimeline.room.currentState.getStateEvents('m.room.tombstone')[0];
- if (!canISend || tombstoneEvent) {
- return (
-
- {
- tombstoneEvent
- ? tombstoneEvent.getContent()?.body ?? 'This room has been replaced and is no longer active.'
- : 'You do not have permission to post to this room'
- }
-
- );
- }
- return (
- <>
-
-
-
-
-
- {roomTimeline.isEncrypted() && }
-
-
-
-
-
-
-
- {
- openReusableContextMenu(
- 'top',
- (() => {
- const cords = getEventCords(e);
- cords.y -= 20;
- return cords;
- })(),
- (closeMenu) => (
- {
- handleSendSticker(data);
- closeMenu();
- }}
- />
- ),
- );
- }}
- tooltip="Sticker"
- src={StickerIC}
- />
- {
- const cords = getEventCords(e);
- cords.x += (document.dir === 'rtl' ? -80 : 80);
- cords.y -= 250;
- openEmojiBoard(cords, addEmoji);
- }}
- tooltip="Emoji"
- src={EmojiIC}
- />
-
-
- >
- );
- }
-
- function attachFile() {
- const fileType = attachment.type.slice(0, attachment.type.indexOf('/'));
- return (
-
-
- {fileType === 'image' &&
}
- {fileType === 'video' &&
}
- {fileType === 'audio' &&
}
- {fileType !== 'image' && fileType !== 'video' && fileType !== 'audio' &&
}
-
-
- {attachment.name}
- {`size: ${bytesToSize(attachment.size)}`}
-
-
- );
- }
-
- function attachReply() {
- return (
-
- {
- roomsInput.cancelReplyTo(roomId);
- setReplyTo(null);
- }}
- src={CrossIC}
- tooltip="Cancel reply"
- size="extra-small"
- />
-
-
- );
- }
-
- return (
- <>
- { replyTo !== null && attachReply()}
- { attachment !== null && attachFile() }
-
- >
- );
-}
-RoomViewInput.propTypes = {
- roomId: PropTypes.string.isRequired,
- roomTimeline: PropTypes.shape({}).isRequired,
- viewEvent: PropTypes.shape({}).isRequired,
-};
-
-export default RoomViewInput;
diff --git a/src/app/organisms/room/TimelineScroll.js b/src/app/organisms/room/TimelineScroll.js
deleted file mode 100644
index ccdc9a9..0000000
--- a/src/app/organisms/room/TimelineScroll.js
+++ /dev/null
@@ -1,136 +0,0 @@
-import { getScrollInfo } from '../../../util/common';
-
-class TimelineScroll {
- constructor(target) {
- if (target === null) {
- throw new Error('Can not initialize TimelineScroll, target HTMLElement in null');
- }
- this.scroll = target;
-
- this.backwards = false;
- this.inTopHalf = false;
-
- this.isScrollable = false;
- this.top = 0;
- this.bottom = 0;
- this.height = 0;
- this.viewHeight = 0;
-
- this.topMsg = null;
- this.bottomMsg = null;
- this.diff = 0;
- }
-
- scrollToBottom() {
- const scrollInfo = getScrollInfo(this.scroll);
- const maxScrollTop = scrollInfo.height - scrollInfo.viewHeight;
-
- this._scrollTo(scrollInfo, maxScrollTop);
- }
-
- // use previous calc by this._updateTopBottomMsg() & this._calcDiff.
- tryRestoringScroll() {
- const scrollInfo = getScrollInfo(this.scroll);
-
- let scrollTop = 0;
- const ot = this.inTopHalf ? this.topMsg?.offsetTop : this.bottomMsg?.offsetTop;
- if (!ot) scrollTop = Math.round(this.height - this.viewHeight);
- else scrollTop = ot - this.diff;
-
- this._scrollTo(scrollInfo, scrollTop);
- }
-
- scrollToIndex(index, offset = 0) {
- const scrollInfo = getScrollInfo(this.scroll);
- const msgs = this.scroll.lastElementChild.lastElementChild.children;
- const offsetTop = msgs[index]?.offsetTop;
-
- if (offsetTop === undefined) return;
- // if msg is already in visible are we don't need to scroll to that
- if (offsetTop > scrollInfo.top && offsetTop < (scrollInfo.top + scrollInfo.viewHeight)) return;
- const to = offsetTop - offset;
-
- this._scrollTo(scrollInfo, to);
- }
-
- _scrollTo(scrollInfo, scrollTop) {
- this.scroll.scrollTop = scrollTop;
-
- // browser emit 'onscroll' event only if the 'element.scrollTop' value changes.
- // so here we flag that the upcoming 'onscroll' event is
- // emitted as side effect of assigning 'this.scroll.scrollTop' above
- // only if it's changes.
- // by doing so we prevent this._updateCalc() from calc again.
- if (scrollTop !== this.top) {
- this.scrolledByCode = true;
- }
- const sInfo = { ...scrollInfo };
-
- const maxScrollTop = scrollInfo.height - scrollInfo.viewHeight;
-
- sInfo.top = (scrollTop > maxScrollTop) ? maxScrollTop : scrollTop;
- this._updateCalc(sInfo);
- }
-
- // we maintain reference of top and bottom messages
- // to restore the scroll position when
- // messages gets removed from either end and added to other.
- _updateTopBottomMsg() {
- const msgs = this.scroll.lastElementChild.lastElementChild.children;
- const lMsgIndex = msgs.length - 1;
-
- // TODO: classname 'ph-msg' prevent this class from being used
- const PLACEHOLDER_COUNT = 2;
- this.topMsg = msgs[0]?.className === 'ph-msg'
- ? msgs[PLACEHOLDER_COUNT]
- : msgs[0];
- this.bottomMsg = msgs[lMsgIndex]?.className === 'ph-msg'
- ? msgs[lMsgIndex - PLACEHOLDER_COUNT]
- : msgs[lMsgIndex];
- }
-
- // we calculate the difference between first/last message and current scrollTop.
- // if we are going above we calc diff between first and scrollTop
- // else otherwise.
- // NOTE: This will help to restore the scroll when msgs get's removed
- // from one end and added to other end
- _calcDiff(scrollInfo) {
- if (!this.topMsg || !this.bottomMsg) return 0;
- if (this.inTopHalf) {
- return this.topMsg.offsetTop - scrollInfo.top;
- }
- return this.bottomMsg.offsetTop - scrollInfo.top;
- }
-
- _updateCalc(scrollInfo) {
- const halfViewHeight = Math.round(scrollInfo.viewHeight / 2);
- const scrollMiddle = scrollInfo.top + halfViewHeight;
- const lastMiddle = this.top + halfViewHeight;
-
- this.backwards = scrollMiddle < lastMiddle;
- this.inTopHalf = scrollMiddle < scrollInfo.height / 2;
-
- this.isScrollable = scrollInfo.isScrollable;
- this.top = scrollInfo.top;
- this.bottom = scrollInfo.height - (scrollInfo.top + scrollInfo.viewHeight);
- this.height = scrollInfo.height;
- this.viewHeight = scrollInfo.viewHeight;
-
- this._updateTopBottomMsg();
- this.diff = this._calcDiff(scrollInfo);
- }
-
- calcScroll() {
- if (this.scrolledByCode) {
- this.scrolledByCode = false;
- return undefined;
- }
-
- const scrollInfo = getScrollInfo(this.scroll);
- this._updateCalc(scrollInfo);
-
- return this.backwards;
- }
-}
-
-export default TimelineScroll;
diff --git a/src/app/organisms/room/commands.jsx b/src/app/organisms/room/commands.jsx
deleted file mode 100644
index 463f9d9..0000000
--- a/src/app/organisms/room/commands.jsx
+++ /dev/null
@@ -1,220 +0,0 @@
-import React from 'react';
-import './commands.scss';
-
-import initMatrix from '../../../client/initMatrix';
-import * as roomActions from '../../../client/action/room';
-import { hasDMWith, hasDevices } from '../../../util/matrixUtil';
-import { selectRoom, openReusableDialog } from '../../../client/action/navigation';
-
-import Text from '../../atoms/text/Text';
-import SettingTile from '../../molecules/setting-tile/SettingTile';
-
-const MXID_REG = /^@\S+:\S+$/;
-const ROOM_ID_ALIAS_REG = /^(#|!)\S+:\S+$/;
-const ROOM_ID_REG = /^!\S+:\S+$/;
-const MXC_REG = /^mxc:\/\/\S+$/;
-
-export function processMxidAndReason(data) {
- let reason;
- let idData = data;
- const reasonMatch = data.match(/\s-r\s/);
- if (reasonMatch) {
- idData = data.slice(0, reasonMatch.index);
- reason = data.slice(reasonMatch.index + reasonMatch[0].length);
- if (reason.trim() === '') reason = undefined;
- }
- const rawIds = idData.split(' ');
- const userIds = rawIds.filter((id) => id.match(MXID_REG));
- return {
- userIds,
- reason,
- };
-}
-
-const commands = {
- me: {
- name: 'me',
- description: 'Display action',
- exe: (roomId, data, onSuccess) => {
- const body = data.trim();
- if (body === '') return;
- onSuccess(body, { msgType: 'm.emote' });
- },
- },
- shrug: {
- name: 'shrug',
- description: 'Send ¯\\_(ツ)_/¯ as message',
- exe: (roomId, data, onSuccess) => onSuccess(
- `¯\\_(ツ)_/¯${data.trim() !== '' ? ` ${data}` : ''}`,
- { msgType: 'm.text' },
- ),
- },
- plain: {
- name: 'plain',
- description: 'Send plain text message',
- exe: (roomId, data, onSuccess) => {
- const body = data.trim();
- if (body === '') return;
- onSuccess(body, { msgType: 'm.text', autoMarkdown: false });
- },
- },
- help: {
- name: 'help',
- description: 'View all commands',
- // eslint-disable-next-line no-use-before-define
- exe: () => openHelpDialog(),
- },
- startdm: {
- name: 'startdm',
- description: 'Start direct message with user. Example: /startdm userId1',
- exe: async (roomId, data) => {
- const mx = initMatrix.matrixClient;
- const rawIds = data.split(' ');
- const userIds = rawIds.filter((id) => id.match(MXID_REG) && id !== mx.getUserId());
- if (userIds.length === 0) return;
- if (userIds.length === 1) {
- const dmRoomId = hasDMWith(userIds[0]);
- if (dmRoomId) {
- selectRoom(dmRoomId);
- return;
- }
- }
- const devices = await Promise.all(userIds.map(hasDevices));
- const isEncrypt = devices.every((hasDevice) => hasDevice);
- const result = await roomActions.createDM(userIds, isEncrypt);
- selectRoom(result.room_id);
- },
- },
- join: {
- name: 'join',
- description: 'Join room with address. Example: /join address1 address2',
- exe: (roomId, data) => {
- const rawIds = data.split(' ');
- const roomIds = rawIds.filter((id) => id.match(ROOM_ID_ALIAS_REG));
- roomIds.map((id) => roomActions.join(id));
- },
- },
- leave: {
- name: 'leave',
- description: 'Leave current room.',
- exe: (roomId, data) => {
- if (data.trim() === '') {
- roomActions.leave(roomId);
- return;
- }
- const rawIds = data.split(' ');
- const roomIds = rawIds.filter((id) => id.match(ROOM_ID_REG));
- roomIds.map((id) => roomActions.leave(id));
- },
- },
- invite: {
- name: 'invite',
- description: 'Invite user to room. Example: /invite userId1 userId2 [-r reason]',
- exe: (roomId, data) => {
- const { userIds, reason } = processMxidAndReason(data);
- userIds.map((id) => roomActions.invite(roomId, id, reason));
- },
- },
- disinvite: {
- name: 'disinvite',
- description: 'Disinvite user to room. Example: /disinvite userId1 userId2 [-r reason]',
- exe: (roomId, data) => {
- const { userIds, reason } = processMxidAndReason(data);
- userIds.map((id) => roomActions.kick(roomId, id, reason));
- },
- },
- kick: {
- name: 'kick',
- description: 'Kick user from room. Example: /kick userId1 userId2 [-r reason]',
- exe: (roomId, data) => {
- const { userIds, reason } = processMxidAndReason(data);
- userIds.map((id) => roomActions.kick(roomId, id, reason));
- },
- },
- ban: {
- name: 'ban',
- description: 'Ban user from room. Example: /ban userId1 userId2 [-r reason]',
- exe: (roomId, data) => {
- const { userIds, reason } = processMxidAndReason(data);
- userIds.map((id) => roomActions.ban(roomId, id, reason));
- },
- },
- unban: {
- name: 'unban',
- description: 'Unban user from room. Example: /unban userId1 userId2',
- exe: (roomId, data) => {
- const rawIds = data.split(' ');
- const userIds = rawIds.filter((id) => id.match(MXID_REG));
- userIds.map((id) => roomActions.unban(roomId, id));
- },
- },
- ignore: {
- name: 'ignore',
- description: 'Ignore user. Example: /ignore userId1 userId2',
- exe: (roomId, data) => {
- const rawIds = data.split(' ');
- const userIds = rawIds.filter((id) => id.match(MXID_REG));
- if (userIds.length > 0) roomActions.ignore(userIds);
- },
- },
- unignore: {
- name: 'unignore',
- description: 'Unignore user. Example: /unignore userId1 userId2',
- exe: (roomId, data) => {
- const rawIds = data.split(' ');
- const userIds = rawIds.filter((id) => id.match(MXID_REG));
- if (userIds.length > 0) roomActions.unignore(userIds);
- },
- },
- myroomnick: {
- name: 'myroomnick',
- description: 'Change nick in current room.',
- exe: (roomId, data) => {
- const nick = data.trim();
- if (nick === '') return;
- roomActions.setMyRoomNick(roomId, nick);
- },
- },
- myroomavatar: {
- name: 'myroomavatar',
- description: 'Change profile picture in current room. Example /myroomavatar mxc://xyzabc',
- exe: (roomId, data) => {
- if (data.match(MXC_REG)) {
- roomActions.setMyRoomAvatar(roomId, data);
- }
- },
- },
- converttodm: {
- name: 'converttodm',
- description: 'Convert room to direct message',
- exe: (roomId) => {
- roomActions.convertToDm(roomId);
- },
- },
- converttoroom: {
- name: 'converttoroom',
- description: 'Convert direct message to room',
- exe: (roomId) => {
- roomActions.convertToRoom(roomId);
- },
- },
-};
-
-function openHelpDialog() {
- openReusableDialog(
- Commands,
- () => (
-
- {Object.keys(commands).map((cmdName) => (
- {commands[cmdName].description}}
- />
- ))}
-
- ),
- );
-}
-
-export default commands;
diff --git a/src/app/organisms/room/common.jsx b/src/app/organisms/room/common.jsx
deleted file mode 100644
index 28974a8..0000000
--- a/src/app/organisms/room/common.jsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import React from 'react';
-
-import { twemojify } from '../../../util/twemojify';
-
-import initMatrix from '../../../client/initMatrix';
-import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil';
-
-function getTimelineJSXMessages() {
- return {
- join(user) {
- return (
- <>
- {twemojify(user)}
- {' joined the room'}
- >
- );
- },
- leave(user, reason) {
- const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : '';
- return (
- <>
- {twemojify(user)}
- {' left the room'}
- {twemojify(reasonMsg)}
- >
- );
- },
- invite(inviter, user) {
- return (
- <>
- {twemojify(inviter)}
- {' invited '}
- {twemojify(user)}
- >
- );
- },
- cancelInvite(inviter, user) {
- return (
- <>
- {twemojify(inviter)}
- {' canceled '}
- {twemojify(user)}
- {'\'s invite'}
- >
- );
- },
- rejectInvite(user) {
- return (
- <>
- {twemojify(user)}
- {' rejected the invitation'}
- >
- );
- },
- kick(actor, user, reason) {
- const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : '';
- return (
- <>
- {twemojify(actor)}
- {' kicked '}
- {twemojify(user)}
- {twemojify(reasonMsg)}
- >
- );
- },
- ban(actor, user, reason) {
- const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : '';
- return (
- <>
- {twemojify(actor)}
- {' banned '}
- {twemojify(user)}
- {twemojify(reasonMsg)}
- >
- );
- },
- unban(actor, user) {
- return (
- <>
- {twemojify(actor)}
- {' unbanned '}
- {twemojify(user)}
- >
- );
- },
- avatarSets(user) {
- return (
- <>
- {twemojify(user)}
- {' set a avatar'}
- >
- );
- },
- avatarChanged(user) {
- return (
- <>
- {twemojify(user)}
- {' changed their avatar'}
- >
- );
- },
- avatarRemoved(user) {
- return (
- <>
- {twemojify(user)}
- {' removed their avatar'}
- >
- );
- },
- nameSets(user, newName) {
- return (
- <>
- {twemojify(user)}
- {' set display name to '}
- {twemojify(newName)}
- >
- );
- },
- nameChanged(user, newName) {
- return (
- <>
- {twemojify(user)}
- {' changed their display name to '}
- {twemojify(newName)}
- >
- );
- },
- nameRemoved(user, lastName) {
- return (
- <>
- {twemojify(user)}
- {' removed their display name '}
- {twemojify(lastName)}
- >
- );
- },
- };
-}
-
-function getUsersActionJsx(roomId, userIds, actionStr) {
- const room = initMatrix.matrixClient.getRoom(roomId);
- const getUserDisplayName = (userId) => {
- if (room?.getMember(userId)) return getUsernameOfRoomMember(room.getMember(userId));
- return getUsername(userId);
- };
- const getUserJSX = (userId) => {twemojify(getUserDisplayName(userId))};
- if (!Array.isArray(userIds)) return 'Idle';
- if (userIds.length === 0) return 'Idle';
- const MAX_VISIBLE_COUNT = 3;
-
- const u1Jsx = getUserJSX(userIds[0]);
- // eslint-disable-next-line react/jsx-one-expression-per-line
- if (userIds.length === 1) return <>{u1Jsx} is {actionStr}>;
-
- const u2Jsx = getUserJSX(userIds[1]);
- // eslint-disable-next-line react/jsx-one-expression-per-line
- if (userIds.length === 2) return <>{u1Jsx} and {u2Jsx} are {actionStr}>;
-
- const u3Jsx = getUserJSX(userIds[2]);
- if (userIds.length === 3) {
- // eslint-disable-next-line react/jsx-one-expression-per-line
- return <>{u1Jsx}, {u2Jsx} and {u3Jsx} are {actionStr}>;
- }
-
- const othersCount = userIds.length - MAX_VISIBLE_COUNT;
- // eslint-disable-next-line react/jsx-one-expression-per-line
- return <>{u1Jsx}, {u2Jsx}, {u3Jsx} and {othersCount} others are {actionStr}>;
-}
-
-function parseTimelineChange(mEvent) {
- const tJSXMsgs = getTimelineJSXMessages();
- const makeReturnObj = (variant, content) => ({
- variant,
- content,
- });
- const content = mEvent.getContent();
- const prevContent = mEvent.getPrevContent();
- const sender = mEvent.getSender();
- const senderName = getUsername(sender);
- const userName = getUsername(mEvent.getStateKey());
-
- switch (content.membership) {
- case 'invite': return makeReturnObj('invite', tJSXMsgs.invite(senderName, userName));
- case 'ban': return makeReturnObj('leave', tJSXMsgs.ban(senderName, userName, content.reason));
- case 'join':
- if (prevContent.membership === 'join') {
- if (content.displayname !== prevContent.displayname) {
- if (typeof content.displayname === 'undefined') return makeReturnObj('avatar', tJSXMsgs.nameRemoved(sender, prevContent.displayname));
- if (typeof prevContent.displayname === 'undefined') return makeReturnObj('avatar', tJSXMsgs.nameSets(sender, content.displayname));
- return makeReturnObj('avatar', tJSXMsgs.nameChanged(prevContent.displayname, content.displayname));
- }
- if (content.avatar_url !== prevContent.avatar_url) {
- if (typeof content.avatar_url === 'undefined') return makeReturnObj('avatar', tJSXMsgs.avatarRemoved(content.displayname));
- if (typeof prevContent.avatar_url === 'undefined') return makeReturnObj('avatar', tJSXMsgs.avatarSets(content.displayname));
- return makeReturnObj('avatar', tJSXMsgs.avatarChanged(content.displayname));
- }
- return null;
- }
- return makeReturnObj('join', tJSXMsgs.join(senderName));
- case 'leave':
- if (sender === mEvent.getStateKey()) {
- switch (prevContent.membership) {
- case 'invite': return makeReturnObj('invite-cancel', tJSXMsgs.rejectInvite(senderName));
- default: return makeReturnObj('leave', tJSXMsgs.leave(senderName, content.reason));
- }
- }
- switch (prevContent.membership) {
- case 'invite': return makeReturnObj('invite-cancel', tJSXMsgs.cancelInvite(senderName, userName));
- case 'ban': return makeReturnObj('other', tJSXMsgs.unban(senderName, userName));
- // sender is not target and made the target leave,
- // if not from invite/ban then this is a kick
- default: return makeReturnObj('leave', tJSXMsgs.kick(senderName, userName, content.reason));
- }
- default: return null;
- }
-}
-
-export {
- getTimelineJSXMessages,
- getUsersActionJsx,
- parseTimelineChange,
-};
diff --git a/src/app/organisms/sticker-board/StickerBoard.jsx b/src/app/organisms/sticker-board/StickerBoard.jsx
deleted file mode 100644
index 91e2591..0000000
--- a/src/app/organisms/sticker-board/StickerBoard.jsx
+++ /dev/null
@@ -1,115 +0,0 @@
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-import React, { useRef } from 'react';
-import PropTypes from 'prop-types';
-import './StickerBoard.scss';
-
-import initMatrix from '../../../client/initMatrix';
-import { getRelevantPacks } from '../emoji-board/custom-emoji';
-
-import Text from '../../atoms/text/Text';
-import ScrollView from '../../atoms/scroll/ScrollView';
-import IconButton from '../../atoms/button/IconButton';
-
-function StickerBoard({ roomId, onSelect }) {
- const mx = initMatrix.matrixClient;
- const room = mx.getRoom(roomId);
- const scrollRef = useRef(null);
-
- const parentIds = initMatrix.roomList.getAllParentSpaces(room.roomId);
- const parentRooms = [...parentIds].map((id) => mx.getRoom(id));
-
- const packs = getRelevantPacks(
- mx,
- [room, ...parentRooms],
- ).filter((pack) => pack.getStickers().length !== 0);
-
- function isTargetNotSticker(target) {
- return target.classList.contains('sticker-board__sticker') === false;
- }
- function getStickerData(target) {
- const mxc = target.getAttribute('data-mx-sticker');
- const body = target.getAttribute('title');
- const httpUrl = target.getAttribute('src');
- return { mxc, body, httpUrl };
- }
- const handleOnSelect = (e) => {
- if (isTargetNotSticker(e.target)) return;
-
- const stickerData = getStickerData(e.target);
- onSelect(stickerData);
- };
-
- const openGroup = (groupIndex) => {
- const scrollContent = scrollRef.current.firstElementChild;
- scrollContent.children[groupIndex].scrollIntoView();
- };
-
- const renderPack = (pack) => (
-
-
{pack.displayName ?? 'Unknown'}
-
- {pack.getStickers().map((sticker) => (
-
- ))}
-
-
- );
-
- return (
-
- {packs.length > 0 && (
-
-
- {packs.map((pack, index) => {
- const src = mx.mxcUrlToHttp(pack.avatarUrl ?? pack.getStickers()[0].mxc);
- return (
- openGroup(index)}
- src={src}
- tooltip={pack.displayName || 'Unknown'}
- tooltipPlacement="left"
- isImage
- />
- );
- })}
-
-
- )}
-
-
-
- {
- packs.length > 0
- ? packs.map(renderPack)
- : (
-
- There is no sticker pack.
-
- )
- }
-
-
-
-
-
- );
-}
-StickerBoard.propTypes = {
- roomId: PropTypes.string.isRequired,
- onSelect: PropTypes.func.isRequired,
-};
-
-export default StickerBoard;
diff --git a/src/app/state/hooks/inviteList.ts b/src/app/state/hooks/inviteList.ts
deleted file mode 100644
index f8b7e05..0000000
--- a/src/app/state/hooks/inviteList.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { useAtomValue, WritableAtom } from 'jotai';
-import { selectAtom } from 'jotai/utils';
-import { MatrixClient } from 'matrix-js-sdk';
-import { useCallback } from 'react';
-import { isDirectInvite, isRoom, isSpace, isUnsupportedRoom } from '../../utils/room';
-import { compareRoomsEqual, RoomsAction } from '../utils';
-import { MDirectAction } from '../mDirectList';
-
-export const useSpaceInvites = (
- mx: MatrixClient,
- allInvitesAtom: WritableAtom
-) => {
- const selector = useCallback(
- (rooms: string[]) => rooms.filter((roomId) => isSpace(mx.getRoom(roomId))),
- [mx]
- );
- return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
-};
-
-export const useRoomInvites = (
- mx: MatrixClient,
- allInvitesAtom: WritableAtom,
- mDirectAtom: WritableAtom, MDirectAction>
-) => {
- const mDirects = useAtomValue(mDirectAtom);
- const selector = useCallback(
- (rooms: string[]) =>
- rooms.filter(
- (roomId) =>
- isRoom(mx.getRoom(roomId)) &&
- !(mDirects.has(roomId) || isDirectInvite(mx.getRoom(roomId), mx.getUserId()))
- ),
- [mx, mDirects]
- );
- return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
-};
-
-export const useDirectInvites = (
- mx: MatrixClient,
- allInvitesAtom: WritableAtom,
- mDirectAtom: WritableAtom, MDirectAction>
-) => {
- const mDirects = useAtomValue(mDirectAtom);
- const selector = useCallback(
- (rooms: string[]) =>
- rooms.filter(
- (roomId) => mDirects.has(roomId) || isDirectInvite(mx.getRoom(roomId), mx.getUserId())
- ),
- [mx, mDirects]
- );
- return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
-};
-
-export const useUnsupportedInvites = (
- mx: MatrixClient,
- allInvitesAtom: WritableAtom
-) => {
- const selector = useCallback(
- (rooms: string[]) => rooms.filter((roomId) => isUnsupportedRoom(mx.getRoom(roomId))),
- [mx]
- );
- return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
-};
diff --git a/src/app/state/hooks/roomList.ts b/src/app/state/hooks/roomList.ts
deleted file mode 100644
index 5d0890b..0000000
--- a/src/app/state/hooks/roomList.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { useAtomValue, WritableAtom } from 'jotai';
-import { selectAtom } from 'jotai/utils';
-import { MatrixClient } from 'matrix-js-sdk';
-import { useCallback } from 'react';
-import { isRoom, isSpace, isUnsupportedRoom } from '../../utils/room';
-import { compareRoomsEqual, RoomsAction } from '../utils';
-import { MDirectAction } from '../mDirectList';
-
-export const useSpaces = (mx: MatrixClient, allRoomsAtom: WritableAtom) => {
- const selector = useCallback(
- (rooms: string[]) => rooms.filter((roomId) => isSpace(mx.getRoom(roomId))),
- [mx]
- );
- return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
-};
-
-export const useRooms = (
- mx: MatrixClient,
- allRoomsAtom: WritableAtom,
- mDirectAtom: WritableAtom, MDirectAction>
-) => {
- const mDirects = useAtomValue(mDirectAtom);
- const selector = useCallback(
- (rooms: string[]) =>
- rooms.filter((roomId) => isRoom(mx.getRoom(roomId)) && !mDirects.has(roomId)),
- [mx, mDirects]
- );
- return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
-};
-
-export const useDirects = (
- mx: MatrixClient,
- allRoomsAtom: WritableAtom,
- mDirectAtom: WritableAtom, MDirectAction>
-) => {
- const mDirects = useAtomValue(mDirectAtom);
- const selector = useCallback(
- (rooms: string[]) =>
- rooms.filter((roomId) => isRoom(mx.getRoom(roomId)) && mDirects.has(roomId)),
- [mx, mDirects]
- );
- return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
-};
-
-export const useUnsupportedRooms = (
- mx: MatrixClient,
- allRoomsAtom: WritableAtom
-) => {
- const selector = useCallback(
- (rooms: string[]) => rooms.filter((roomId) => isUnsupportedRoom(mx.getRoom(roomId))),
- [mx]
- );
- return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
-};
diff --git a/src/app/state/hooks/useBindAtoms.ts b/src/app/state/hooks/useBindAtoms.ts
deleted file mode 100644
index 6dc2a3d..0000000
--- a/src/app/state/hooks/useBindAtoms.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { MatrixClient } from 'matrix-js-sdk';
-import { allInvitesAtom, useBindAllInvitesAtom } from '../inviteList';
-import { allRoomsAtom, useBindAllRoomsAtom } from '../roomList';
-import { mDirectAtom, useBindMDirectAtom } from '../mDirectList';
-import { muteChangesAtom, mutedRoomsAtom, useBindMutedRoomsAtom } from '../mutedRoomList';
-import { roomToUnreadAtom, useBindRoomToUnreadAtom } from '../roomToUnread';
-import { roomToParentsAtom, useBindRoomToParentsAtom } from '../roomToParents';
-
-export const useBindAtoms = (mx: MatrixClient) => {
- useBindMDirectAtom(mx, mDirectAtom);
- useBindAllInvitesAtom(mx, allInvitesAtom);
- useBindAllRoomsAtom(mx, allRoomsAtom);
- useBindRoomToParentsAtom(mx, roomToParentsAtom);
- useBindMutedRoomsAtom(mx, mutedRoomsAtom);
- useBindRoomToUnreadAtom(mx, roomToUnreadAtom, muteChangesAtom);
-};
diff --git a/src/app/state/inviteList.ts b/src/app/state/inviteList.ts
deleted file mode 100644
index 463fd35..0000000
--- a/src/app/state/inviteList.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { atom, WritableAtom } from 'jotai';
-import { MatrixClient } from 'matrix-js-sdk';
-import { useMemo } from 'react';
-import { Membership } from '../../types/matrix/room';
-import { RoomsAction, useBindRoomsWithMembershipsAtom } from './utils';
-
-const baseRoomsAtom = atom([]);
-export const allInvitesAtom = atom(
- (get) => get(baseRoomsAtom),
- (get, set, action) => {
- if (action.type === 'INITIALIZE') {
- set(baseRoomsAtom, action.rooms);
- return;
- }
- set(baseRoomsAtom, (ids) => {
- const newIds = ids.filter((id) => id !== action.roomId);
- if (action.type === 'PUT') newIds.push(action.roomId);
- return newIds;
- });
- }
-);
-
-export const useBindAllInvitesAtom = (
- mx: MatrixClient,
- allRooms: WritableAtom
-) => {
- useBindRoomsWithMembershipsAtom(
- mx,
- allRooms,
- useMemo(() => [Membership.Invite], [])
- );
-};
diff --git a/src/app/state/mDirectList.ts b/src/app/state/mDirectList.ts
deleted file mode 100644
index 96e2f0d..0000000
--- a/src/app/state/mDirectList.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { atom, useSetAtom, WritableAtom } from 'jotai';
-import { ClientEvent, MatrixClient, MatrixEvent } from 'matrix-js-sdk';
-import { useEffect } from 'react';
-import { AccountDataEvent } from '../../types/matrix/accountData';
-import { getAccountData, getMDirects } from '../utils/room';
-
-export type MDirectAction = {
- type: 'INITIALIZE' | 'UPDATE';
- rooms: Set;
-};
-
-const baseMDirectAtom = atom(new Set());
-export const mDirectAtom = atom, MDirectAction>(
- (get) => get(baseMDirectAtom),
- (get, set, action) => {
- set(baseMDirectAtom, action.rooms);
- }
-);
-
-export const useBindMDirectAtom = (
- mx: MatrixClient,
- mDirect: WritableAtom, MDirectAction>
-) => {
- const setMDirect = useSetAtom(mDirect);
-
- useEffect(() => {
- const mDirectEvent = getAccountData(mx, AccountDataEvent.Direct);
- if (mDirectEvent) {
- setMDirect({
- type: 'INITIALIZE',
- rooms: getMDirects(mDirectEvent),
- });
- }
-
- const handleAccountData = (event: MatrixEvent) => {
- setMDirect({
- type: 'UPDATE',
- rooms: getMDirects(event),
- });
- };
-
- mx.on(ClientEvent.AccountData, handleAccountData);
- return () => {
- mx.removeListener(ClientEvent.AccountData, handleAccountData);
- };
- }, [mx, setMDirect]);
-};
diff --git a/src/app/state/mutedRoomList.ts b/src/app/state/mutedRoomList.ts
deleted file mode 100644
index d456f85..0000000
--- a/src/app/state/mutedRoomList.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { atom, WritableAtom, useSetAtom } from 'jotai';
-import { ClientEvent, IPushRule, IPushRules, MatrixClient, MatrixEvent } from 'matrix-js-sdk';
-import { useEffect } from 'react';
-import { MuteChanges } from '../../types/matrix/room';
-import { findMutedRule, isMutedRule } from '../utils/room';
-
-export type MutedRoomsUpdate =
- | {
- type: 'INITIALIZE';
- addRooms: string[];
- }
- | {
- type: 'UPDATE';
- addRooms: string[];
- removeRooms: string[];
- };
-
-export const muteChangesAtom = atom({
- added: [],
- removed: [],
-});
-
-const baseMutedRoomsAtom = atom(new Set());
-export const mutedRoomsAtom = atom, MutedRoomsUpdate>(
- (get) => get(baseMutedRoomsAtom),
- (get, set, action) => {
- const mutedRooms = new Set([...get(mutedRoomsAtom)]);
- if (action.type === 'INITIALIZE') {
- set(baseMutedRoomsAtom, new Set([...action.addRooms]));
- set(muteChangesAtom, {
- added: [...action.addRooms],
- removed: [],
- });
- return;
- }
- if (action.type === 'UPDATE') {
- action.removeRooms.forEach((roomId) => mutedRooms.delete(roomId));
- action.addRooms.forEach((roomId) => mutedRooms.add(roomId));
- set(baseMutedRoomsAtom, mutedRooms);
- set(muteChangesAtom, {
- added: [...action.addRooms],
- removed: [...action.removeRooms],
- });
- }
- }
-);
-
-export const useBindMutedRoomsAtom = (
- mx: MatrixClient,
- mutedAtom: WritableAtom, MutedRoomsUpdate>
-) => {
- const setMuted = useSetAtom(mutedAtom);
-
- useEffect(() => {
- const overrideRules = mx.getAccountData('m.push_rules')?.getContent()
- ?.global?.override;
- if (overrideRules) {
- const mutedRooms = overrideRules.reduce((rooms, rule) => {
- if (isMutedRule(rule)) rooms.push(rule.rule_id);
- return rooms;
- }, []);
- setMuted({
- type: 'INITIALIZE',
- addRooms: mutedRooms,
- });
- }
- }, [mx, setMuted]);
-
- useEffect(() => {
- const handlePushRules = (mEvent: MatrixEvent, oldMEvent?: MatrixEvent) => {
- if (mEvent.getType() === 'm.push_rules') {
- const override = mEvent?.getContent()?.global?.override as IPushRule[] | undefined;
- const oldOverride = oldMEvent?.getContent()?.global?.override as IPushRule[] | undefined;
- if (!override || !oldOverride) return;
-
- const isMuteToggled = (rule: IPushRule, otherOverride: IPushRule[]) => {
- const roomId = rule.rule_id;
-
- const isMuted = isMutedRule(rule);
- if (!isMuted) return false;
- const isOtherMuted = findMutedRule(otherOverride, roomId);
- if (isOtherMuted) return false;
- return true;
- };
-
- const mutedRules = override.filter((rule) => isMuteToggled(rule, oldOverride));
- const unMutedRules = oldOverride.filter((rule) => isMuteToggled(rule, override));
-
- setMuted({
- type: 'UPDATE',
- addRooms: mutedRules.map((rule) => rule.rule_id),
- removeRooms: unMutedRules.map((rule) => rule.rule_id),
- });
- }
- };
- mx.on(ClientEvent.AccountData, handlePushRules);
- return () => {
- mx.removeListener(ClientEvent.AccountData, handlePushRules);
- };
- }, [mx, setMuted]);
-};
diff --git a/src/app/state/roomList.ts b/src/app/state/roomList.ts
deleted file mode 100644
index 7a793d8..0000000
--- a/src/app/state/roomList.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { atom, WritableAtom } from 'jotai';
-import { MatrixClient } from 'matrix-js-sdk';
-import { useMemo } from 'react';
-import { Membership } from '../../types/matrix/room';
-import { RoomsAction, useBindRoomsWithMembershipsAtom } from './utils';
-
-const baseRoomsAtom = atom([]);
-export const allRoomsAtom = atom(
- (get) => get(baseRoomsAtom),
- (get, set, action) => {
- if (action.type === 'INITIALIZE') {
- set(baseRoomsAtom, action.rooms);
- return;
- }
- set(baseRoomsAtom, (ids) => {
- const newIds = ids.filter((id) => id !== action.roomId);
- if (action.type === 'PUT') newIds.push(action.roomId);
- return newIds;
- });
- }
-);
-export const useBindAllRoomsAtom = (
- mx: MatrixClient,
- allRooms: WritableAtom
-) => {
- useBindRoomsWithMembershipsAtom(
- mx,
- allRooms,
- useMemo(() => [Membership.Join], [])
- );
-};
diff --git a/src/app/state/roomToParents.ts b/src/app/state/roomToParents.ts
deleted file mode 100644
index 374ddd5..0000000
--- a/src/app/state/roomToParents.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-import produce from 'immer';
-import { atom, useSetAtom, WritableAtom } from 'jotai';
-import {
- ClientEvent,
- MatrixClient,
- MatrixEvent,
- Room,
- RoomEvent,
- RoomStateEvent,
-} from 'matrix-js-sdk';
-import { useEffect } from 'react';
-import { Membership, RoomToParents, StateEvent } from '../../types/matrix/room';
-import {
- getRoomToParents,
- getSpaceChildren,
- isSpace,
- isValidChild,
- mapParentWithChildren,
-} from '../utils/room';
-
-export type RoomToParentsAction =
- | {
- type: 'INITIALIZE';
- roomToParents: RoomToParents;
- }
- | {
- type: 'PUT';
- parent: string;
- children: string[];
- }
- | {
- type: 'DELETE';
- roomId: string;
- };
-
-const baseRoomToParents = atom(new Map());
-export const roomToParentsAtom = atom(
- (get) => get(baseRoomToParents),
- (get, set, action) => {
- if (action.type === 'INITIALIZE') {
- set(baseRoomToParents, action.roomToParents);
- return;
- }
- if (action.type === 'PUT') {
- set(
- baseRoomToParents,
- produce(get(baseRoomToParents), (draftRoomToParents) => {
- mapParentWithChildren(draftRoomToParents, action.parent, action.children);
- })
- );
- return;
- }
- if (action.type === 'DELETE') {
- set(
- baseRoomToParents,
- produce(get(baseRoomToParents), (draftRoomToParents) => {
- const noParentRooms: string[] = [];
- draftRoomToParents.delete(action.roomId);
- draftRoomToParents.forEach((parents, child) => {
- parents.delete(action.roomId);
- if (parents.size === 0) noParentRooms.push(child);
- });
- noParentRooms.forEach((room) => draftRoomToParents.delete(room));
- })
- );
- }
- }
-);
-
-export const useBindRoomToParentsAtom = (
- mx: MatrixClient,
- roomToParents: WritableAtom
-) => {
- const setRoomToParents = useSetAtom(roomToParents);
-
- useEffect(() => {
- setRoomToParents({ type: 'INITIALIZE', roomToParents: getRoomToParents(mx) });
-
- const handleAddRoom = (room: Room) => {
- if (isSpace(room) && room.getMyMembership() !== Membership.Invite) {
- setRoomToParents({ type: 'PUT', parent: room.roomId, children: getSpaceChildren(room) });
- }
- };
-
- const handleMembershipChange = (room: Room, membership: string) => {
- if (isSpace(room) && membership === Membership.Join) {
- setRoomToParents({ type: 'PUT', parent: room.roomId, children: getSpaceChildren(room) });
- }
- };
-
- const handleStateChange = (mEvent: MatrixEvent) => {
- if (mEvent.getType() === StateEvent.SpaceChild) {
- const childId = mEvent.getStateKey();
- const roomId = mEvent.getRoomId();
- if (childId && roomId) {
- if (isValidChild(mEvent)) {
- setRoomToParents({ type: 'PUT', parent: roomId, children: [childId] });
- } else {
- setRoomToParents({ type: 'DELETE', roomId: childId });
- }
- }
- }
- };
-
- const handleDeleteRoom = (roomId: string) => {
- setRoomToParents({ type: 'DELETE', roomId });
- };
-
- mx.on(ClientEvent.Room, handleAddRoom);
- mx.on(RoomEvent.MyMembership, handleMembershipChange);
- mx.on(RoomStateEvent.Events, handleStateChange);
- mx.on(ClientEvent.DeleteRoom, handleDeleteRoom);
- return () => {
- mx.removeListener(ClientEvent.Room, handleAddRoom);
- mx.removeListener(RoomEvent.MyMembership, handleMembershipChange);
- mx.removeListener(RoomStateEvent.Events, handleStateChange);
- mx.removeListener(ClientEvent.DeleteRoom, handleDeleteRoom);
- };
- }, [mx, setRoomToParents]);
-};
diff --git a/src/app/state/roomToUnread.ts b/src/app/state/roomToUnread.ts
deleted file mode 100644
index 0c7b6bd..0000000
--- a/src/app/state/roomToUnread.ts
+++ /dev/null
@@ -1,219 +0,0 @@
-import produce from 'immer';
-import { atom, useSetAtom, PrimitiveAtom, WritableAtom, useAtomValue } from 'jotai';
-import { IRoomTimelineData, MatrixClient, MatrixEvent, Room, RoomEvent } from 'matrix-js-sdk';
-import { ReceiptContent, ReceiptType } from 'matrix-js-sdk/lib/@types/read_receipts';
-import { useEffect } from 'react';
-import {
- MuteChanges,
- Membership,
- NotificationType,
- RoomToUnread,
- UnreadInfo,
-} from '../../types/matrix/room';
-import {
- getAllParents,
- getNotificationType,
- getUnreadInfo,
- getUnreadInfos,
- isNotificationEvent,
- roomHaveUnread,
-} from '../utils/room';
-import { roomToParentsAtom } from './roomToParents';
-
-export type RoomToUnreadAction =
- | {
- type: 'RESET';
- unreadInfos: UnreadInfo[];
- }
- | {
- type: 'PUT';
- unreadInfo: UnreadInfo;
- }
- | {
- type: 'DELETE';
- roomId: string;
- };
-
-const putUnreadInfo = (
- roomToUnread: RoomToUnread,
- allParents: Set,
- unreadInfo: UnreadInfo
-) => {
- const oldUnread = roomToUnread.get(unreadInfo.roomId) ?? { highlight: 0, total: 0, from: null };
- roomToUnread.set(unreadInfo.roomId, {
- highlight: unreadInfo.highlight,
- total: unreadInfo.total,
- from: null,
- });
-
- const newH = unreadInfo.highlight - oldUnread.highlight;
- const newT = unreadInfo.total - oldUnread.total;
-
- allParents.forEach((parentId) => {
- const oldParentUnread = roomToUnread.get(parentId) ?? { highlight: 0, total: 0, from: null };
- roomToUnread.set(parentId, {
- highlight: (oldParentUnread.highlight += newH),
- total: (oldParentUnread.total += newT),
- from: new Set([...(oldParentUnread.from ?? []), unreadInfo.roomId]),
- });
- });
-};
-
-const deleteUnreadInfo = (roomToUnread: RoomToUnread, allParents: Set, roomId: string) => {
- const oldUnread = roomToUnread.get(roomId);
- if (!oldUnread) return;
- roomToUnread.delete(roomId);
-
- allParents.forEach((parentId) => {
- const oldParentUnread = roomToUnread.get(parentId);
- if (!oldParentUnread) return;
- const newFrom = new Set([...(oldParentUnread.from ?? roomId)]);
- newFrom.delete(roomId);
- if (newFrom.size === 0) {
- roomToUnread.delete(parentId);
- return;
- }
- roomToUnread.set(parentId, {
- highlight: oldParentUnread.highlight - oldUnread.highlight,
- total: oldParentUnread.total - oldUnread.total,
- from: newFrom,
- });
- });
-};
-
-const baseRoomToUnread = atom(new Map());
-export const roomToUnreadAtom = atom(
- (get) => get(baseRoomToUnread),
- (get, set, action) => {
- if (action.type === 'RESET') {
- const draftRoomToUnread: RoomToUnread = new Map();
- action.unreadInfos.forEach((unreadInfo) => {
- putUnreadInfo(
- draftRoomToUnread,
- getAllParents(get(roomToParentsAtom), unreadInfo.roomId),
- unreadInfo
- );
- });
- set(baseRoomToUnread, draftRoomToUnread);
- return;
- }
- if (action.type === 'PUT') {
- set(
- baseRoomToUnread,
- produce(get(baseRoomToUnread), (draftRoomToUnread) =>
- putUnreadInfo(
- draftRoomToUnread,
- getAllParents(get(roomToParentsAtom), action.unreadInfo.roomId),
- action.unreadInfo
- )
- )
- );
- return;
- }
- if (action.type === 'DELETE' && get(baseRoomToUnread).has(action.roomId)) {
- set(
- baseRoomToUnread,
- produce(get(baseRoomToUnread), (draftRoomToUnread) =>
- deleteUnreadInfo(
- draftRoomToUnread,
- getAllParents(get(roomToParentsAtom), action.roomId),
- action.roomId
- )
- )
- );
- }
- }
-);
-
-export const useBindRoomToUnreadAtom = (
- mx: MatrixClient,
- unreadAtom: WritableAtom,
- muteChangesAtom: PrimitiveAtom
-) => {
- const setUnreadAtom = useSetAtom(unreadAtom);
- const muteChanges = useAtomValue(muteChangesAtom);
-
- useEffect(() => {
- setUnreadAtom({
- type: 'RESET',
- unreadInfos: getUnreadInfos(mx),
- });
- }, [mx, setUnreadAtom]);
-
- useEffect(() => {
- const handleTimelineEvent = (
- mEvent: MatrixEvent,
- room: Room | undefined,
- toStartOfTimeline: boolean | undefined,
- removed: boolean,
- data: IRoomTimelineData
- ) => {
- if (!room || !data.liveEvent || room.isSpaceRoom() || !isNotificationEvent(mEvent)) return;
- if (getNotificationType(mx, room.roomId) === NotificationType.Mute) {
- setUnreadAtom({
- type: 'DELETE',
- roomId: room.roomId,
- });
- return;
- }
-
- if (mEvent.getSender() === mx.getUserId()) return;
- setUnreadAtom({ type: 'PUT', unreadInfo: getUnreadInfo(room) });
- };
- mx.on(RoomEvent.Timeline, handleTimelineEvent);
- return () => {
- mx.removeListener(RoomEvent.Timeline, handleTimelineEvent);
- };
- }, [mx, setUnreadAtom]);
-
- useEffect(() => {
- const handleReceipt = (mEvent: MatrixEvent, room: Room) => {
- if (mEvent.getType() === 'm.receipt') {
- const myUserId = mx.getUserId();
- if (!myUserId) return;
- if (room.isSpaceRoom()) return;
- const content = mEvent.getContent();
-
- const isMyReceipt = Object.keys(content).find((eventId) =>
- (Object.keys(content[eventId]) as ReceiptType[]).find(
- (receiptType) => content[eventId][receiptType][myUserId]
- )
- );
- if (isMyReceipt) {
- setUnreadAtom({ type: 'DELETE', roomId: room.roomId });
- }
- }
- };
- mx.on(RoomEvent.Receipt, handleReceipt);
- return () => {
- mx.removeListener(RoomEvent.Receipt, handleReceipt);
- };
- }, [mx, setUnreadAtom]);
-
- useEffect(() => {
- muteChanges.removed.forEach((roomId) => {
- const room = mx.getRoom(roomId);
- if (!room) return;
- if (!roomHaveUnread(mx, room)) return;
- setUnreadAtom({ type: 'PUT', unreadInfo: getUnreadInfo(room) });
- });
- muteChanges.added.forEach((roomId) => {
- setUnreadAtom({ type: 'DELETE', roomId });
- });
- }, [mx, setUnreadAtom, muteChanges]);
-
- useEffect(() => {
- const handleMembershipChange = (room: Room, membership: string) => {
- if (membership !== Membership.Join) {
- setUnreadAtom({
- type: 'DELETE',
- roomId: room.roomId,
- });
- }
- };
- mx.on(RoomEvent.MyMembership, handleMembershipChange);
- return () => {
- mx.removeListener(RoomEvent.MyMembership, handleMembershipChange);
- };
- }, [mx, setUnreadAtom]);
-};
diff --git a/src/app/state/selectedRoom.ts b/src/app/state/selectedRoom.ts
deleted file mode 100644
index 1ef04de..0000000
--- a/src/app/state/selectedRoom.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { atom } from 'jotai';
-
-export const selectedRoomAtom = atom(undefined);
diff --git a/src/app/state/selectedTab.ts b/src/app/state/selectedTab.ts
deleted file mode 100644
index e680ae6..0000000
--- a/src/app/state/selectedTab.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { atom } from 'jotai';
-
-export enum SidebarTab {
- Home = 'Home',
- People = 'People',
-}
-
-export const selectedTabAtom = atom(SidebarTab.Home);
diff --git a/src/app/state/tabToRoom.ts b/src/app/state/tabToRoom.ts
deleted file mode 100644
index 2f4ee92..0000000
--- a/src/app/state/tabToRoom.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import produce from 'immer';
-import { atom } from 'jotai';
-import { MatrixClient } from 'matrix-js-sdk';
-
-type RoomInfo = {
- roomId: string;
- timestamp: number;
-};
-type TabToRoom = Map;
-
-type TabToRoomAction = {
- type: 'PUT';
- tabInfo: { tabId: string; roomInfo: RoomInfo };
-};
-
-const baseTabToRoom = atom(new Map());
-export const tabToRoomAtom = atom(
- (get) => get(baseTabToRoom),
- (get, set, action) => {
- if (action.type === 'PUT') {
- set(
- baseTabToRoom,
- produce(get(baseTabToRoom), (draft) => {
- draft.set(action.tabInfo.tabId, action.tabInfo.roomInfo);
- })
- );
- }
- }
-);
-
-export const useBindTabToRoomAtom = (mx: MatrixClient) => {
- console.log(mx);
- // TODO:
-};
diff --git a/src/app/state/utils.ts b/src/app/state/utils.ts
deleted file mode 100644
index 355c941..0000000
--- a/src/app/state/utils.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { useSetAtom, WritableAtom } from 'jotai';
-import { ClientEvent, MatrixClient, Room, RoomEvent } from 'matrix-js-sdk';
-import { useEffect } from 'react';
-import { Membership } from '../../types/matrix/room';
-
-export type RoomsAction =
- | {
- type: 'INITIALIZE';
- rooms: string[];
- }
- | {
- type: 'PUT' | 'DELETE';
- roomId: string;
- };
-
-export const useBindRoomsWithMembershipsAtom = (
- mx: MatrixClient,
- roomsAtom: WritableAtom,
- memberships: Membership[]
-) => {
- const setRoomsAtom = useSetAtom(roomsAtom);
-
- useEffect(() => {
- const satisfyMembership = (room: Room): boolean =>
- !!memberships.find((membership) => membership === room.getMyMembership());
- setRoomsAtom({
- type: 'INITIALIZE',
- rooms: mx
- .getRooms()
- .filter(satisfyMembership)
- .map((room) => room.roomId),
- });
-
- const handleAddRoom = (room: Room) => {
- if (satisfyMembership(room)) {
- setRoomsAtom({ type: 'PUT', roomId: room.roomId });
- }
- };
-
- const handleMembershipChange = (room: Room) => {
- if (!satisfyMembership(room)) {
- setRoomsAtom({ type: 'DELETE', roomId: room.roomId });
- }
- };
-
- const handleDeleteRoom = (roomId: string) => {
- setRoomsAtom({ type: 'DELETE', roomId });
- };
-
- mx.on(ClientEvent.Room, handleAddRoom);
- mx.on(RoomEvent.MyMembership, handleMembershipChange);
- mx.on(ClientEvent.DeleteRoom, handleDeleteRoom);
- return () => {
- mx.removeListener(ClientEvent.Room, handleAddRoom);
- mx.removeListener(RoomEvent.MyMembership, handleMembershipChange);
- mx.removeListener(ClientEvent.DeleteRoom, handleDeleteRoom);
- };
- }, [mx, memberships, setRoomsAtom]);
-};
-
-export const compareRoomsEqual = (a: string[], b: string[]) => {
- if (a.length !== b.length) return false;
- return a.every((roomId, roomIdIndex) => roomId === b[roomIdIndex]);
-};
diff --git a/src/app/utils/disposable.ts b/src/app/utils/disposable.ts
deleted file mode 100644
index 7840fe4..0000000
--- a/src/app/utils/disposable.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export type DisposeCallback = (...args: Q) => R;
-export type DisposableContext = (
- ...args: P
-) => DisposeCallback;
-
-export const disposable = (
- context: DisposableContext
-) => context;
diff --git a/src/client/mx.ts b/src/client/mx.ts
deleted file mode 100644
index 3090945..0000000
--- a/src/client/mx.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { MatrixClient } from 'matrix-js-sdk';
-import initMatrix from './initMatrix';
-
-export const mx = (): MatrixClient => {
- if (!initMatrix.matrixClient) console.error('Matrix client is used before initialization!');
- return initMatrix.matrixClient!;
-};
diff --git a/src/client/state/RoomTimeline.js b/src/client/state/RoomTimeline.js
deleted file mode 100644
index 57d91c1..0000000
--- a/src/client/state/RoomTimeline.js
+++ /dev/null
@@ -1,407 +0,0 @@
-import EventEmitter from 'events';
-import initMatrix from '../initMatrix';
-import cons from './cons';
-
-import settings from './settings';
-
-function isEdited(mEvent) {
- return mEvent.getRelation()?.rel_type === 'm.replace';
-}
-
-function isReaction(mEvent) {
- return mEvent.getType() === 'm.reaction';
-}
-
-function hideMemberEvents(mEvent) {
- const content = mEvent.getContent();
- const prevContent = mEvent.getPrevContent();
- const { membership } = content;
- if (settings.hideMembershipEvents) {
- if (membership === 'invite' || membership === 'ban' || membership === 'leave') return true;
- if (prevContent.membership !== 'join') return true;
- }
- if (settings.hideNickAvatarEvents) {
- if (membership === 'join' && prevContent.membership === 'join') return true;
- }
- return false;
-}
-
-function getRelateToId(mEvent) {
- const relation = mEvent.getRelation();
- return relation && relation.event_id;
-}
-
-function addToMap(myMap, mEvent) {
- const relateToId = getRelateToId(mEvent);
- if (relateToId === null) return null;
- const mEventId = mEvent.getId();
-
- if (typeof myMap.get(relateToId) === 'undefined') myMap.set(relateToId, []);
- const mEvents = myMap.get(relateToId);
- if (mEvents.find((ev) => ev.getId() === mEventId)) return mEvent;
- mEvents.push(mEvent);
- return mEvent;
-}
-
-function getFirstLinkedTimeline(timeline) {
- let tm = timeline;
- while (tm.prevTimeline) {
- tm = tm.prevTimeline;
- }
- return tm;
-}
-function getLastLinkedTimeline(timeline) {
- let tm = timeline;
- while (tm.nextTimeline) {
- tm = tm.nextTimeline;
- }
- return tm;
-}
-
-function iterateLinkedTimelines(timeline, backwards, callback) {
- let tm = timeline;
- while (tm) {
- callback(tm);
- if (backwards) tm = tm.prevTimeline;
- else tm = tm.nextTimeline;
- }
-}
-
-function isTimelineLinked(tm1, tm2) {
- let tm = getFirstLinkedTimeline(tm1);
- while (tm) {
- if (tm === tm2) return true;
- tm = tm.nextTimeline;
- }
- return false;
-}
-
-class RoomTimeline extends EventEmitter {
- constructor(roomId) {
- super();
- // These are local timelines
- this.timeline = [];
- this.editedTimeline = new Map();
- this.reactionTimeline = new Map();
- this.typingMembers = new Set();
-
- this.matrixClient = initMatrix.matrixClient;
- this.roomId = roomId;
- this.room = this.matrixClient.getRoom(roomId);
-
- this.liveTimeline = this.room.getLiveTimeline();
- this.activeTimeline = this.liveTimeline;
-
- this.isOngoingPagination = false;
- this.ongoingDecryptionCount = 0;
- this.initialized = false;
-
- setTimeout(() => this.room.loadMembersIfNeeded());
-
- // TODO: remove below line
- window.selectedRoom = this;
- }
-
- isServingLiveTimeline() {
- return getLastLinkedTimeline(this.activeTimeline) === this.liveTimeline;
- }
-
- canPaginateBackward() {
- if (this.timeline[0]?.getType() === 'm.room.create') return false;
- const tm = getFirstLinkedTimeline(this.activeTimeline);
- return tm.getPaginationToken('b') !== null;
- }
-
- canPaginateForward() {
- return !this.isServingLiveTimeline();
- }
-
- isEncrypted() {
- return this.matrixClient.isRoomEncrypted(this.roomId);
- }
-
- clearLocalTimelines() {
- this.timeline = [];
- }
-
- addToTimeline(mEvent) {
- if (mEvent.getType() === 'm.room.member' && hideMemberEvents(mEvent)) {
- return;
- }
- if (mEvent.isRedacted()) return;
- if (isReaction(mEvent)) {
- addToMap(this.reactionTimeline, mEvent);
- return;
- }
- if (!cons.supportEventTypes.includes(mEvent.getType())) return;
- if (isEdited(mEvent)) {
- addToMap(this.editedTimeline, mEvent);
- return;
- }
- this.timeline.push(mEvent);
- }
-
- _populateAllLinkedEvents(timeline) {
- const firstTimeline = getFirstLinkedTimeline(timeline);
- iterateLinkedTimelines(firstTimeline, false, (tm) => {
- tm.getEvents().forEach((mEvent) => this.addToTimeline(mEvent));
- });
- }
-
- _populateTimelines() {
- this.clearLocalTimelines();
- this._populateAllLinkedEvents(this.activeTimeline);
- }
-
- async _reset() {
- if (this.isEncrypted()) await this.decryptAllEventsOfTimeline(this.activeTimeline);
- this._populateTimelines();
- if (!this.initialized) {
- this.initialized = true;
- this._listenEvents();
- }
- }
-
- async loadLiveTimeline() {
- this.activeTimeline = this.liveTimeline;
- await this._reset();
- this.emit(cons.events.roomTimeline.READY, null);
- return true;
- }
-
- async loadEventTimeline(eventId) {
- // we use first unfiltered EventTimelineSet for room pagination.
- const timelineSet = this.getUnfilteredTimelineSet();
- try {
- const eventTimeline = await this.matrixClient.getEventTimeline(timelineSet, eventId);
- this.activeTimeline = eventTimeline;
- await this._reset();
- this.emit(cons.events.roomTimeline.READY, eventId);
- return true;
- } catch {
- return false;
- }
- }
-
- async paginateTimeline(backwards = false, limit = 30) {
- if (this.initialized === false) return false;
- if (this.isOngoingPagination) return false;
-
- this.isOngoingPagination = true;
-
- const timelineToPaginate = backwards
- ? getFirstLinkedTimeline(this.activeTimeline)
- : getLastLinkedTimeline(this.activeTimeline);
-
- if (timelineToPaginate.getPaginationToken(backwards ? 'b' : 'f') === null) {
- this.emit(cons.events.roomTimeline.PAGINATED, backwards, 0);
- this.isOngoingPagination = false;
- return false;
- }
-
- const oldSize = this.timeline.length;
- try {
- await this.matrixClient.paginateEventTimeline(timelineToPaginate, { backwards, limit });
-
- if (this.isEncrypted()) await this.decryptAllEventsOfTimeline(this.activeTimeline);
- this._populateTimelines();
-
- const loaded = this.timeline.length - oldSize;
- this.emit(cons.events.roomTimeline.PAGINATED, backwards, loaded);
- this.isOngoingPagination = false;
- return true;
- } catch {
- this.emit(cons.events.roomTimeline.PAGINATED, backwards, 0);
- this.isOngoingPagination = false;
- return false;
- }
- }
-
- decryptAllEventsOfTimeline(eventTimeline) {
- const decryptionPromises = eventTimeline
- .getEvents()
- .filter((event) => event.isEncrypted() && !event.clearEvent)
- .reverse()
- .map((event) => event.attemptDecryption(this.matrixClient.crypto, { isRetry: true }));
-
- return Promise.allSettled(decryptionPromises);
- }
-
- hasEventInTimeline(eventId, timeline = this.activeTimeline) {
- const timelineSet = this.getUnfilteredTimelineSet();
- const eventTimeline = timelineSet.getTimelineForEvent(eventId);
- if (!eventTimeline) return false;
- return isTimelineLinked(eventTimeline, timeline);
- }
-
- getUnfilteredTimelineSet() {
- return this.room.getUnfilteredTimelineSet();
- }
-
- getEventReaders(mEvent) {
- const liveEvents = this.liveTimeline.getEvents();
- const readers = [];
- if (!mEvent) return [];
-
- for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
- readers.splice(readers.length, 0, ...this.room.getUsersReadUpTo(liveEvents[i]));
- if (mEvent === liveEvents[i]) break;
- }
-
- return [...new Set(readers)];
- }
-
- getLiveReaders() {
- const liveEvents = this.liveTimeline.getEvents();
- const getLatestVisibleEvent = () => {
- for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
- const mEvent = liveEvents[i];
- if (mEvent.getType() === 'm.room.member' && hideMemberEvents(mEvent)) {
- // eslint-disable-next-line no-continue
- continue;
- }
- if (!mEvent.isRedacted()
- && !isReaction(mEvent)
- && !isEdited(mEvent)
- && cons.supportEventTypes.includes(mEvent.getType())
- ) return mEvent;
- }
- return liveEvents[liveEvents.length - 1];
- };
-
- return this.getEventReaders(getLatestVisibleEvent());
- }
-
- getUnreadEventIndex(readUpToEventId) {
- if (!this.hasEventInTimeline(readUpToEventId)) return -1;
-
- const readUpToEvent = this.findEventByIdInTimelineSet(readUpToEventId);
- if (!readUpToEvent) return -1;
- const rTs = readUpToEvent.getTs();
-
- const tLength = this.timeline.length;
-
- for (let i = 0; i < tLength; i += 1) {
- const mEvent = this.timeline[i];
- if (mEvent.getTs() > rTs) return i;
- }
- return -1;
- }
-
- getReadUpToEventId() {
- return this.room.getEventReadUpTo(this.matrixClient.getUserId());
- }
-
- getEventIndex(eventId) {
- return this.timeline.findIndex((mEvent) => mEvent.getId() === eventId);
- }
-
- findEventByIdInTimelineSet(eventId, eventTimelineSet = this.getUnfilteredTimelineSet()) {
- return eventTimelineSet.findEventById(eventId);
- }
-
- findEventById(eventId) {
- return this.timeline[this.getEventIndex(eventId)] ?? null;
- }
-
- deleteFromTimeline(eventId) {
- const i = this.getEventIndex(eventId);
- if (i === -1) return undefined;
- return this.timeline.splice(i, 1)[0];
- }
-
- _listenEvents() {
- this._listenRoomTimeline = (event, room, toStartOfTimeline, removed, data) => {
- if (room.roomId !== this.roomId) return;
- if (this.isOngoingPagination) return;
-
- // User is currently viewing the old events probably
- // no need to add new event and emit changes.
- // only add reactions and edited messages
- if (this.isServingLiveTimeline() === false) {
- if (!isReaction(event) && !isEdited(event)) return;
- }
-
- // We only process live events here
- if (!data.liveEvent) return;
-
- if (event.isEncrypted()) {
- // We will add this event after it is being decrypted.
- this.ongoingDecryptionCount += 1;
- return;
- }
-
- // FIXME: An unencrypted plain event can come
- // while previous event is still decrypting
- // and has not been added to timeline
- // causing unordered timeline view.
-
- this.addToTimeline(event);
- this.emit(cons.events.roomTimeline.EVENT, event);
- };
-
- this._listenDecryptEvent = (event) => {
- if (event.getRoomId() !== this.roomId) return;
- if (this.isOngoingPagination) return;
-
- // Not a live event.
- // so we don't need to process it here
- if (this.ongoingDecryptionCount === 0) return;
-
- if (this.ongoingDecryptionCount > 0) {
- this.ongoingDecryptionCount -= 1;
- }
- this.addToTimeline(event);
- this.emit(cons.events.roomTimeline.EVENT, event);
- };
-
- this._listenRedaction = (mEvent, room) => {
- if (room.roomId !== this.roomId) return;
- const rEvent = this.deleteFromTimeline(mEvent.event.redacts);
- this.editedTimeline.delete(mEvent.event.redacts);
- this.reactionTimeline.delete(mEvent.event.redacts);
- this.emit(cons.events.roomTimeline.EVENT_REDACTED, rEvent, mEvent);
- };
-
- this._listenTypingEvent = (event, member) => {
- if (member.roomId !== this.roomId) return;
-
- const isTyping = member.typing;
- if (isTyping) this.typingMembers.add(member.userId);
- else this.typingMembers.delete(member.userId);
- this.emit(cons.events.roomTimeline.TYPING_MEMBERS_UPDATED, new Set([...this.typingMembers]));
- };
- this._listenReciptEvent = (event, room) => {
- // we only process receipt for latest message here.
- if (room.roomId !== this.roomId) return;
- const receiptContent = event.getContent();
-
- const mEvents = this.liveTimeline.getEvents();
- const lastMEvent = mEvents[mEvents.length - 1];
- const lastEventId = lastMEvent.getId();
- const lastEventRecipt = receiptContent[lastEventId];
-
- if (typeof lastEventRecipt === 'undefined') return;
- if (lastEventRecipt['m.read']) {
- this.emit(cons.events.roomTimeline.LIVE_RECEIPT);
- }
- };
-
- this.matrixClient.on('Room.timeline', this._listenRoomTimeline);
- this.matrixClient.on('Room.redaction', this._listenRedaction);
- this.matrixClient.on('Event.decrypted', this._listenDecryptEvent);
- this.matrixClient.on('RoomMember.typing', this._listenTypingEvent);
- this.matrixClient.on('Room.receipt', this._listenReciptEvent);
- }
-
- removeInternalListeners() {
- if (!this.initialized) return;
- this.matrixClient.removeListener('Room.timeline', this._listenRoomTimeline);
- this.matrixClient.removeListener('Room.redaction', this._listenRedaction);
- this.matrixClient.removeListener('Event.decrypted', this._listenDecryptEvent);
- this.matrixClient.removeListener('RoomMember.typing', this._listenTypingEvent);
- this.matrixClient.removeListener('Room.receipt', this._listenReciptEvent);
- }
-}
-
-export default RoomTimeline;