first step
This commit is contained in:
parent
44318d1ecd
commit
4b7f07ef6d
14 changed files with 404 additions and 79 deletions
|
@ -96,6 +96,6 @@
|
||||||
<audio id="inviteSound">
|
<audio id="inviteSound">
|
||||||
<source src="./public/sound/invite.ogg" type="audio/ogg" />
|
<source src="./public/sound/invite.ogg" type="audio/ogg" />
|
||||||
</audio>
|
</audio>
|
||||||
<script type="module" src="./src/index.jsx"></script>
|
<script type="module" src="./src/index.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -46,6 +46,7 @@
|
||||||
"@types/node": "18.11.18",
|
"@types/node": "18.11.18",
|
||||||
"@types/react": "18.0.26",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-dom": "18.0.9",
|
"@types/react-dom": "18.0.9",
|
||||||
|
"@types/sanitize-html": "2.8.0",
|
||||||
"@typescript-eslint/eslint-plugin": "5.46.1",
|
"@typescript-eslint/eslint-plugin": "5.46.1",
|
||||||
"@typescript-eslint/parser": "5.46.1",
|
"@typescript-eslint/parser": "5.46.1",
|
||||||
"@vitejs/plugin-react": "3.0.0",
|
"@vitejs/plugin-react": "3.0.0",
|
||||||
|
@ -1165,6 +1166,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
||||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
|
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/sanitize-html": {
|
||||||
|
"version": "2.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.8.0.tgz",
|
||||||
|
"integrity": "sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"htmlparser2": "^8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/scheduler": {
|
"node_modules/@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
|
@ -3668,9 +3678,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"json5": "lib/cli.js"
|
"json5": "lib/cli.js"
|
||||||
|
@ -4974,9 +4984,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsconfig-paths/node_modules/json5": {
|
"node_modules/tsconfig-paths/node_modules/json5": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
|
@ -5059,9 +5069,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ua-parser-js": {
|
"node_modules/ua-parser-js": {
|
||||||
"version": "0.7.32",
|
"version": "0.7.33",
|
||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
|
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
|
||||||
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==",
|
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
"@types/node": "18.11.18",
|
"@types/node": "18.11.18",
|
||||||
"@types/react": "18.0.26",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-dom": "18.0.9",
|
"@types/react-dom": "18.0.9",
|
||||||
|
"@types/sanitize-html": "2.8.0",
|
||||||
"@typescript-eslint/eslint-plugin": "5.46.1",
|
"@typescript-eslint/eslint-plugin": "5.46.1",
|
||||||
"@typescript-eslint/parser": "5.46.1",
|
"@typescript-eslint/parser": "5.46.1",
|
||||||
"@vitejs/plugin-react": "3.0.0",
|
"@vitejs/plugin-react": "3.0.0",
|
||||||
|
|
4
src/custom.d.ts
vendored
Normal file
4
src/custom.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
declare module '*.svg' {
|
||||||
|
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
|
||||||
|
export default content;
|
||||||
|
}
|
|
@ -1,6 +1,18 @@
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
|
||||||
class AsyncSearch extends EventEmitter {
|
class AsyncSearch extends EventEmitter {
|
||||||
|
RESULT_SENT: string;
|
||||||
|
dataList: (string | object)[];
|
||||||
|
term: any;
|
||||||
|
searchKeys: any;
|
||||||
|
isContain: boolean;
|
||||||
|
isCaseSensitive: boolean;
|
||||||
|
normalizeUnicode: boolean;
|
||||||
|
ignoreWhitespace: boolean;
|
||||||
|
limit: number;
|
||||||
|
findingList: any[];
|
||||||
|
searchUptoIndex: number;
|
||||||
|
sessionStartTimestamp: number;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -44,7 +56,17 @@ class AsyncSearch extends EventEmitter {
|
||||||
* @param {boolean} [opts.ignoreWhitespace=true]
|
* @param {boolean} [opts.ignoreWhitespace=true]
|
||||||
* @param {number} [opts.limit=null] - Stop search after limit
|
* @param {number} [opts.limit=null] - Stop search after limit
|
||||||
*/
|
*/
|
||||||
setup(dataList, opts) {
|
setup(
|
||||||
|
dataList: (string | object)[],
|
||||||
|
opts: {
|
||||||
|
keys?: string | string[];
|
||||||
|
isContain?: boolean;
|
||||||
|
isCaseSensitive?: boolean;
|
||||||
|
normalizeUnicode?: boolean;
|
||||||
|
ignoreWhitespace?: boolean;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
) {
|
||||||
this._reset();
|
this._reset();
|
||||||
this.dataList = dataList;
|
this.dataList = dataList;
|
||||||
this.searchKeys = opts?.keys || null;
|
this.searchKeys = opts?.keys || null;
|
||||||
|
@ -55,7 +77,7 @@ class AsyncSearch extends EventEmitter {
|
||||||
this.limit = opts?.limit || null;
|
this.limit = opts?.limit || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
search(term) {
|
search(term: any) {
|
||||||
this._softReset();
|
this._softReset();
|
||||||
|
|
||||||
this.term = this._normalize(term);
|
this.term = this._normalize(term);
|
||||||
|
@ -67,7 +89,7 @@ class AsyncSearch extends EventEmitter {
|
||||||
this._find(this.sessionStartTimestamp, 0);
|
this._find(this.sessionStartTimestamp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_find(sessionTimestamp, lastFindingCount) {
|
_find(sessionTimestamp: number, lastFindingCount: number) {
|
||||||
if (sessionTimestamp !== this.sessionStartTimestamp) return;
|
if (sessionTimestamp !== this.sessionStartTimestamp) return;
|
||||||
this.sessionStartTimestamp = window.performance.now();
|
this.sessionStartTimestamp = window.performance.now();
|
||||||
|
|
||||||
|
@ -93,12 +115,12 @@ class AsyncSearch extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastFindingCount !== this.findingList.length
|
if (lastFindingCount !== this.findingList.length || lastFindingCount === 0)
|
||||||
|| lastFindingCount === 0) this._sendFindings();
|
this._sendFindings();
|
||||||
this._softReset();
|
this._softReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
_match(item) {
|
_match(item: string | object) {
|
||||||
if (typeof item === 'string') {
|
if (typeof item === 'string') {
|
||||||
return this._compare(item);
|
return this._compare(item);
|
||||||
}
|
}
|
||||||
|
@ -113,14 +135,14 @@ class AsyncSearch extends EventEmitter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_compare(item) {
|
_compare(item: string) {
|
||||||
if (typeof item !== 'string') return false;
|
if (typeof item !== 'string') return false;
|
||||||
const myItem = this._normalize(item);
|
const myItem = this._normalize(item);
|
||||||
if (this.isContain) return myItem.indexOf(this.term) !== -1;
|
if (this.isContain) return myItem.indexOf(this.term) !== -1;
|
||||||
return myItem.startsWith(this.term);
|
return myItem.startsWith(this.term);
|
||||||
}
|
}
|
||||||
|
|
||||||
_normalize(item) {
|
_normalize(item: string) {
|
||||||
let myItem = item.normalize(this.normalizeUnicode ? 'NFKC' : 'NFC');
|
let myItem = item.normalize(this.normalizeUnicode ? 'NFKC' : 'NFC');
|
||||||
if (!this.isCaseSensitive) myItem = myItem.toLocaleLowerCase();
|
if (!this.isCaseSensitive) myItem = myItem.toLocaleLowerCase();
|
||||||
if (this.ignoreWhitespace) myItem = myItem.replace(/\s/g, '');
|
if (this.ignoreWhitespace) myItem = myItem.replace(/\s/g, '');
|
|
@ -1,9 +1,10 @@
|
||||||
class Postie {
|
class Postie {
|
||||||
|
_topics: Map<string, Map<string, Set<Function>>>;
|
||||||
constructor() {
|
constructor() {
|
||||||
this._topics = new Map();
|
this._topics = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSubscribers(topic) {
|
_getSubscribers(topic: string) {
|
||||||
const subscribers = this._topics.get(topic);
|
const subscribers = this._topics.get(topic);
|
||||||
if (subscribers === undefined) {
|
if (subscribers === undefined) {
|
||||||
throw new Error(`Topic:"${topic}" doesn't exist.`);
|
throw new Error(`Topic:"${topic}" doesn't exist.`);
|
||||||
|
@ -11,7 +12,7 @@ class Postie {
|
||||||
return subscribers;
|
return subscribers;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getInboxes(topic, address) {
|
_getInboxes(topic: string, address: string) {
|
||||||
const subscribers = this._getSubscribers(topic);
|
const subscribers = this._getSubscribers(topic);
|
||||||
const inboxes = subscribers.get(address);
|
const inboxes = subscribers.get(address);
|
||||||
if (inboxes === undefined) {
|
if (inboxes === undefined) {
|
||||||
|
@ -20,19 +21,17 @@ class Postie {
|
||||||
return inboxes;
|
return inboxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasTopic(topic) {
|
hasTopic(topic: string) {
|
||||||
return this._topics.get(topic) !== undefined;
|
return this._topics.get(topic) !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSubscriber(topic, address) {
|
hasSubscriber(topic: string, address: string) {
|
||||||
const subscribers = this._getSubscribers(topic);
|
const subscribers = this._getSubscribers(topic);
|
||||||
return subscribers.get(address) !== undefined;
|
return subscribers.get(address) !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasTopicAndSubscriber(topic, address) {
|
hasTopicAndSubscriber(topic: string, address: string) {
|
||||||
return (this.hasTopic(topic))
|
return this.hasTopic(topic) ? this.hasSubscriber(topic, address) : false;
|
||||||
? this.hasSubscriber(topic, address)
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,12 +39,12 @@ class Postie {
|
||||||
* @param {string} address - Address of subscriber
|
* @param {string} address - Address of subscriber
|
||||||
* @param {function} inbox - The inbox function to receive post data
|
* @param {function} inbox - The inbox function to receive post data
|
||||||
*/
|
*/
|
||||||
subscribe(topic, address, inbox) {
|
subscribe(topic: string, address: string, inbox: Set<Function>) {
|
||||||
if (typeof inbox !== 'function') {
|
if (typeof inbox !== 'function') {
|
||||||
throw new TypeError('Inbox must be a function.');
|
throw new TypeError('Inbox must be a function.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._topics.has(topic) === false) {
|
if (!this._topics.has(topic)) {
|
||||||
this._topics.set(topic, new Map());
|
this._topics.set(topic, new Map());
|
||||||
}
|
}
|
||||||
const subscribers = this._topics.get(topic);
|
const subscribers = this._topics.get(topic);
|
||||||
|
@ -57,14 +56,17 @@ class Postie {
|
||||||
return () => this.unsubscribe(topic, address, inbox);
|
return () => this.unsubscribe(topic, address, inbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribe(topic, address, inbox) {
|
unsubscribe(topic: string, address: string, inbox: Function) {
|
||||||
const subscribers = this._getSubscribers(topic);
|
const subscribers = this._getSubscribers(topic);
|
||||||
if (!subscribers) throw new Error(`Unable to unsubscribe. Topic: "${topic}" doesn't exist.`);
|
if (!subscribers) throw new Error(`Unable to unsubscribe. Topic: "${topic}" doesn't exist.`);
|
||||||
|
|
||||||
const inboxes = subscribers.get(address);
|
const inboxes = subscribers.get(address);
|
||||||
if (!inboxes) throw new Error(`Unable to unsubscribe. Subscriber on topic:"${topic}" at address:"${address}" doesn't exist`);
|
if (!inboxes)
|
||||||
|
throw new Error(
|
||||||
|
`Unable to unsubscribe. Subscriber on topic:"${topic}" at address:"${address}" doesn't exist`
|
||||||
|
);
|
||||||
|
|
||||||
if (!inboxes.delete(inbox)) throw new Error('Unable to unsubscribe. Inbox doesn\'t exist');
|
if (!inboxes.delete(inbox)) throw new Error("Unable to unsubscribe. Inbox doesn't exist");
|
||||||
|
|
||||||
if (inboxes.size === 0) subscribers.delete(address);
|
if (inboxes.size === 0) subscribers.delete(address);
|
||||||
if (subscribers.size === 0) this._topics.delete(topic);
|
if (subscribers.size === 0) this._topics.delete(topic);
|
||||||
|
@ -75,10 +77,12 @@ class Postie {
|
||||||
* @param {string|string[]} address - Address of subscriber
|
* @param {string|string[]} address - Address of subscriber
|
||||||
* @param {*} data - Data to deliver to subscriber
|
* @param {*} data - Data to deliver to subscriber
|
||||||
*/
|
*/
|
||||||
post(topic, address, data) {
|
post(topic: string, address: string | string[], data: any) {
|
||||||
const sendPost = (inboxes, addr) => {
|
const sendPost = (inboxes: Set<Function>, addr: string) => {
|
||||||
if (inboxes === undefined) {
|
if (inboxes === undefined) {
|
||||||
throw new Error(`Unable to post on topic:"${topic}" at address:"${addr}". Subscriber doesn't exist.`);
|
throw new Error(
|
||||||
|
`Unable to post on topic:"${topic}" at address:"${addr}". Subscriber doesn't exist.`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
inboxes.forEach((inbox) => inbox(data));
|
inboxes.forEach((inbox) => inbox(data));
|
||||||
};
|
};
|
||||||
|
@ -88,7 +92,7 @@ class Postie {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const subscribers = this._getSubscribers(topic);
|
const subscribers = this._getSubscribers(topic);
|
||||||
address.forEach((addr) => {
|
address.forEach((addr: string) => {
|
||||||
sendPost(subscribers.get(addr), addr);
|
sendPost(subscribers.get(addr), addr);
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -1,27 +1,27 @@
|
||||||
// https://github.com/cloudrac3r/cadencegq/blob/master/pug/mxid.pug
|
// https://github.com/cloudrac3r/cadencegq/blob/master/pug/mxid.pug
|
||||||
|
|
||||||
export function hashCode(str) {
|
export function hashCode(str: string) {
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
let i;
|
let i: number;
|
||||||
let chr;
|
let chr: number;
|
||||||
if (str.length === 0) {
|
if (str.length === 0) {
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
for (i = 0; i < str.length; i += 1) {
|
for (i = 0; i < str.length; i += 1) {
|
||||||
chr = str.charCodeAt(i);
|
chr = str.charCodeAt(i);
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
hash = ((hash << 5) - hash) + chr;
|
hash = (hash << 5) - hash + chr;
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
hash |= 0;
|
hash |= 0;
|
||||||
}
|
}
|
||||||
return Math.abs(hash);
|
return Math.abs(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cssColorMXID(userId) {
|
export function cssColorMXID(userId: string) {
|
||||||
const colorNumber = hashCode(userId) % 8;
|
const colorNumber = hashCode(userId) % 8;
|
||||||
return `--mx-uc-${colorNumber + 1}`;
|
return `--mx-uc-${colorNumber + 1}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function colorMXID(userId) {
|
export default function colorMXID(userId: string) {
|
||||||
return `var(${cssColorMXID(userId)})`;
|
return `var(${cssColorMXID(userId)})`;
|
||||||
}
|
}
|
233
src/util/common.ts
Normal file
233
src/util/common.ts
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
|
export function bytesToSize(bytes: number) {
|
||||||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
if (bytes === 0) return 'n/a';
|
||||||
|
const i = Math.floor(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||||
|
if (i === 0) return `${bytes} ${sizes[i]}`;
|
||||||
|
return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function diffMinutes(dt2: { getTime: () => number }, dt1: { getTime: () => number }) {
|
||||||
|
let diff = (dt2.getTime() - dt1.getTime()) / 1000;
|
||||||
|
diff /= 60;
|
||||||
|
return Math.abs(Math.round(diff));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isInSameDay(dt2: Date, dt1: Date) {
|
||||||
|
return (
|
||||||
|
dt2.getFullYear() === dt1.getFullYear() &&
|
||||||
|
dt2.getMonth() === dt1.getMonth() &&
|
||||||
|
dt2.getDate() === dt1.getDate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} ev
|
||||||
|
* @param {string} [targetSelector] element selector for Element.matches([selector])
|
||||||
|
*/
|
||||||
|
export function getEventCords(ev: Event, targetSelector: string) {
|
||||||
|
let boxInfo: DOMRect;
|
||||||
|
|
||||||
|
const path = ev.composedPath();
|
||||||
|
const target = targetSelector
|
||||||
|
? path.find((element) => (element as HTMLElement).matches?.(targetSelector))
|
||||||
|
: null;
|
||||||
|
if (target) {
|
||||||
|
boxInfo = (target as HTMLElement).getBoundingClientRect();
|
||||||
|
} else {
|
||||||
|
boxInfo = (ev.target as HTMLElement).getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: boxInfo.x,
|
||||||
|
y: boxInfo.y,
|
||||||
|
width: boxInfo.width,
|
||||||
|
height: boxInfo.height,
|
||||||
|
detail: (ev as MouseEvent).detail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function abbreviateNumber(number: number) {
|
||||||
|
if (number > 99) return '99+';
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Debounce {
|
||||||
|
timeoutId: any;
|
||||||
|
constructor() {
|
||||||
|
this.timeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {function} func - callback function
|
||||||
|
* @param {number} wait - wait in milliseconds to call func
|
||||||
|
* @returns {func} debounceCallback - to pass arguments to func callback
|
||||||
|
*/
|
||||||
|
_(func: Function, wait: number) {
|
||||||
|
const debounceCallback = (...args) => {
|
||||||
|
clearTimeout(this.timeoutId);
|
||||||
|
this.timeoutId = setTimeout(() => {
|
||||||
|
func(args);
|
||||||
|
this.timeoutId = null;
|
||||||
|
}, wait);
|
||||||
|
};
|
||||||
|
return debounceCallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Throttle {
|
||||||
|
timeoutId: any;
|
||||||
|
constructor() {
|
||||||
|
this.timeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {function} func - callback function
|
||||||
|
* @param {number} wait - wait in milliseconds to call func
|
||||||
|
* @returns {function} throttleCallback - to pass arguments to func callback
|
||||||
|
*/
|
||||||
|
_(func: Function, wait: number) {
|
||||||
|
const throttleCallback = (...args) => {
|
||||||
|
if (this.timeoutId !== null) return;
|
||||||
|
this.timeoutId = setTimeout(() => {
|
||||||
|
func(args);
|
||||||
|
this.timeoutId = null;
|
||||||
|
}, wait);
|
||||||
|
};
|
||||||
|
return throttleCallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUrlPrams(paramName: string) {
|
||||||
|
const queryString = window.location.search;
|
||||||
|
const urlParams = new URLSearchParams(queryString);
|
||||||
|
return urlParams.get(paramName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getScrollInfo(target: HTMLElement) {
|
||||||
|
const scroll = {
|
||||||
|
top: Math.round(target.scrollTop),
|
||||||
|
height: Math.round(target.scrollHeight),
|
||||||
|
viewHeight: Math.round(target.offsetHeight),
|
||||||
|
isScrollable: this.height > this.viewHeight,
|
||||||
|
};
|
||||||
|
return scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function avatarInitials(text) {
|
||||||
|
return [...text][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cssVar(name: string) {
|
||||||
|
return getComputedStyle(document.body).getPropertyValue(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setFavicon(url: string) {
|
||||||
|
const favicon = document.querySelector('#favicon');
|
||||||
|
if (!favicon) return;
|
||||||
|
favicon.setAttribute('href', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function copyToClipboard(text: string) {
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
} else {
|
||||||
|
const host = document.body;
|
||||||
|
const copyInput = document.createElement('input');
|
||||||
|
copyInput.style.position = 'fixed';
|
||||||
|
copyInput.style.opacity = '0';
|
||||||
|
copyInput.value = text;
|
||||||
|
host.append(copyInput);
|
||||||
|
|
||||||
|
copyInput.select();
|
||||||
|
copyInput.setSelectionRange(0, 99999);
|
||||||
|
document.execCommand('Copy');
|
||||||
|
copyInput.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function suffixRename(name: string | number, validator: Function) {
|
||||||
|
let suffix = 2;
|
||||||
|
let newName = name;
|
||||||
|
do {
|
||||||
|
newName = `name${suffix}`;
|
||||||
|
suffix += 1;
|
||||||
|
} while (validator(newName));
|
||||||
|
|
||||||
|
return newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getImageDimension(file: Blob | MediaSource) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = async () => {
|
||||||
|
resolve({
|
||||||
|
w: img.width,
|
||||||
|
h: img.height,
|
||||||
|
});
|
||||||
|
URL.revokeObjectURL(img.src);
|
||||||
|
};
|
||||||
|
img.src = URL.createObjectURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scaleDownImage(imageFile: Blob, width: number, height: number) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const imgURL = URL.createObjectURL(imageFile);
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
let newWidth = img.width;
|
||||||
|
let newHeight = img.height;
|
||||||
|
if (newHeight <= height && newWidth <= width) {
|
||||||
|
resolve(imageFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newHeight > height) {
|
||||||
|
newWidth = Math.floor(newWidth * (height / newHeight));
|
||||||
|
newHeight = height;
|
||||||
|
}
|
||||||
|
if (newWidth > width) {
|
||||||
|
newHeight = Math.floor(newHeight * (width / newWidth));
|
||||||
|
newWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = newWidth;
|
||||||
|
canvas.height = newHeight;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
||||||
|
|
||||||
|
canvas.toBlob((thumbnail) => {
|
||||||
|
URL.revokeObjectURL(imgURL);
|
||||||
|
resolve(thumbnail);
|
||||||
|
}, imageFile.type);
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = imgURL;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {sigil} string sigil to search for (for example '@', '#' or '$')
|
||||||
|
* @param {flags} string regex flags
|
||||||
|
* @param {prefix} string prefix appended at the beginning of the regex
|
||||||
|
* @returns {RegExp}
|
||||||
|
*/
|
||||||
|
export function idRegex(sigil: string, flags: string, prefix: string): RegExp {
|
||||||
|
const servername = '(?:[a-zA-Z0-9-.]*[a-zA-Z0-9]+|\\[\\S+?\\])(?::\\d+)?';
|
||||||
|
return new RegExp(`${prefix}(${sigil}\\S+:${servername})`, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
const matrixToRegex = /^https?:\/\/matrix.to\/#\/(\S+:\S+)/;
|
||||||
|
/**
|
||||||
|
* Parses a matrix.to URL into an matrix id.
|
||||||
|
* This function can later be extended to support matrix: URIs
|
||||||
|
* @param {string} uri The URI to parse
|
||||||
|
* @returns {string|null} The id or null if the URI does not match
|
||||||
|
*/
|
||||||
|
export function parseIdUri(uri: string): string | null {
|
||||||
|
const res = decodeURIComponent(uri).match(matrixToRegex);
|
||||||
|
if (!res) return null;
|
||||||
|
return res[1];
|
||||||
|
}
|
|
@ -6,10 +6,12 @@ import HashLockIC from '../../public/res/ic/outlined/hash-lock.svg';
|
||||||
import SpaceIC from '../../public/res/ic/outlined/space.svg';
|
import SpaceIC from '../../public/res/ic/outlined/space.svg';
|
||||||
import SpaceGlobeIC from '../../public/res/ic/outlined/space-globe.svg';
|
import SpaceGlobeIC from '../../public/res/ic/outlined/space-globe.svg';
|
||||||
import SpaceLockIC from '../../public/res/ic/outlined/space-lock.svg';
|
import SpaceLockIC from '../../public/res/ic/outlined/space-lock.svg';
|
||||||
|
import { RoomMember } from 'matrix-js-sdk';
|
||||||
|
import { Room } from 'matrix-js-sdk';
|
||||||
|
|
||||||
const WELL_KNOWN_URI = '/.well-known/matrix/client';
|
const WELL_KNOWN_URI = '/.well-known/matrix/client';
|
||||||
|
|
||||||
export async function getBaseUrl(servername) {
|
export async function getBaseUrl(servername: string) {
|
||||||
let protocol = 'https://';
|
let protocol = 'https://';
|
||||||
if (servername.match(/^https?:\/\//) !== null) protocol = '';
|
if (servername.match(/^https?:\/\//) !== null) protocol = '';
|
||||||
const serverDiscoveryUrl = `${protocol}${servername}${WELL_KNOWN_URI}`;
|
const serverDiscoveryUrl = `${protocol}${servername}${WELL_KNOWN_URI}`;
|
||||||
|
@ -24,7 +26,7 @@ export async function getBaseUrl(servername) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUsername(userId) {
|
export function getUsername(userId: string) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const user = mx.getUser(userId);
|
const user = mx.getUser(userId);
|
||||||
if (user === null) return userId;
|
if (user === null) return userId;
|
||||||
|
@ -35,11 +37,11 @@ export function getUsername(userId) {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUsernameOfRoomMember(roomMember) {
|
export function getUsernameOfRoomMember(roomMember: RoomMember) {
|
||||||
return roomMember.name || roomMember.userId;
|
return roomMember.name || roomMember.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isRoomAliasAvailable(alias) {
|
export async function isRoomAliasAvailable(alias: string) {
|
||||||
try {
|
try {
|
||||||
const result = await initMatrix.matrixClient.resolveRoomAlias(alias);
|
const result = await initMatrix.matrixClient.resolveRoomAlias(alias);
|
||||||
if (result.room_id) return false;
|
if (result.room_id) return false;
|
||||||
|
@ -50,7 +52,7 @@ export async function isRoomAliasAvailable(alias) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPowerLabel(powerLevel) {
|
export function getPowerLabel(powerLevel: number) {
|
||||||
if (powerLevel > 9000) return 'Goku';
|
if (powerLevel > 9000) return 'Goku';
|
||||||
if (powerLevel > 100) return 'Founder';
|
if (powerLevel > 100) return 'Founder';
|
||||||
if (powerLevel === 100) return 'Admin';
|
if (powerLevel === 100) return 'Admin';
|
||||||
|
@ -58,7 +60,7 @@ export function getPowerLabel(powerLevel) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseReply(rawBody) {
|
export function parseReply(rawBody: string) {
|
||||||
if (rawBody?.indexOf('>') !== 0) return null;
|
if (rawBody?.indexOf('>') !== 0) return null;
|
||||||
let body = rawBody.slice(rawBody.indexOf('<') + 1);
|
let body = rawBody.slice(rawBody.indexOf('<') + 1);
|
||||||
const user = body.slice(0, body.indexOf('>'));
|
const user = body.slice(0, body.indexOf('>'));
|
||||||
|
@ -79,7 +81,7 @@ export function parseReply(rawBody) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function trimHTMLReply(html) {
|
export function trimHTMLReply(html: string | string[]) {
|
||||||
if (!html) return html;
|
if (!html) return html;
|
||||||
const suffix = '</mx-reply>';
|
const suffix = '</mx-reply>';
|
||||||
const i = html.indexOf(suffix);
|
const i = html.indexOf(suffix);
|
||||||
|
@ -89,7 +91,7 @@ export function trimHTMLReply(html) {
|
||||||
return html.slice(i + suffix.length);
|
return html.slice(i + suffix.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasDMWith(userId) {
|
export function hasDMWith(userId: string) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const directIds = [...initMatrix.roomList.directs];
|
const directIds = [...initMatrix.roomList.directs];
|
||||||
|
|
||||||
|
@ -103,18 +105,22 @@ export function hasDMWith(userId) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function joinRuleToIconSrc(joinRule, isSpace) {
|
export function joinRuleToIconSrc(joinRule: string | number, isSpace: boolean) {
|
||||||
return ({
|
return (
|
||||||
|
{
|
||||||
restricted: () => (isSpace ? SpaceIC : HashIC),
|
restricted: () => (isSpace ? SpaceIC : HashIC),
|
||||||
knock: () => (isSpace ? SpaceLockIC : HashLockIC),
|
knock: () => (isSpace ? SpaceLockIC : HashLockIC),
|
||||||
invite: () => (isSpace ? SpaceLockIC : HashLockIC),
|
invite: () => (isSpace ? SpaceLockIC : HashLockIC),
|
||||||
public: () => (isSpace ? SpaceGlobeIC : HashGlobeIC),
|
public: () => (isSpace ? SpaceGlobeIC : HashGlobeIC),
|
||||||
}[joinRule]?.() || null);
|
}[joinRule]?.() || null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: it gives userId with minimum power level 50;
|
// NOTE: it gives userId with minimum power level 50;
|
||||||
function getHighestPowerUserId(room) {
|
function getHighestPowerUserId(room: Room) {
|
||||||
const userIdToPower = room.currentState.getStateEvents('m.room.power_levels', '')?.getContent().users;
|
const userIdToPower = room.currentState
|
||||||
|
.getStateEvents('m.room.power_levels', '')
|
||||||
|
?.getContent().users;
|
||||||
let powerUserId = null;
|
let powerUserId = null;
|
||||||
if (!userIdToPower) return powerUserId;
|
if (!userIdToPower) return powerUserId;
|
||||||
|
|
||||||
|
@ -131,12 +137,12 @@ function getHighestPowerUserId(room) {
|
||||||
return powerUserId;
|
return powerUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIdServer(userId) {
|
export function getIdServer(userId: string) {
|
||||||
const idParts = userId.split(':');
|
const idParts = userId.split(':');
|
||||||
return idParts[1];
|
return idParts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getServerToPopulation(room) {
|
export function getServerToPopulation(room: Room) {
|
||||||
const members = room.getMembers();
|
const members = room.getMembers();
|
||||||
const serverToPop = {};
|
const serverToPop = {};
|
||||||
|
|
||||||
|
@ -154,7 +160,7 @@ export function getServerToPopulation(room) {
|
||||||
return serverToPop;
|
return serverToPop;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function genRoomVia(room) {
|
export function genRoomVia(room: Room) {
|
||||||
const via = [];
|
const via = [];
|
||||||
const userId = getHighestPowerUserId(room);
|
const userId = getHighestPowerUserId(room);
|
||||||
if (userId) {
|
if (userId) {
|
||||||
|
@ -163,7 +169,7 @@ export function genRoomVia(room) {
|
||||||
}
|
}
|
||||||
const serverToPop = getServerToPopulation(room);
|
const serverToPop = getServerToPopulation(room);
|
||||||
const sortedServers = Object.keys(serverToPop).sort(
|
const sortedServers = Object.keys(serverToPop).sort(
|
||||||
(svrA, svrB) => serverToPop[svrB] - serverToPop[svrA],
|
(svrA, svrB) => serverToPop[svrB] - serverToPop[svrA]
|
||||||
);
|
);
|
||||||
const mostPop3 = sortedServers.slice(0, 3);
|
const mostPop3 = sortedServers.slice(0, 3);
|
||||||
if (via.length === 0) return mostPop3;
|
if (via.length === 0) return mostPop3;
|
||||||
|
@ -173,7 +179,7 @@ export function genRoomVia(room) {
|
||||||
return via.concat(mostPop3.slice(0, 2));
|
return via.concat(mostPop3.slice(0, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCrossVerified(deviceId) {
|
export function isCrossVerified(deviceId: string) {
|
||||||
try {
|
try {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const crossSignInfo = mx.getStoredCrossSigningForUser(mx.getUserId());
|
const crossSignInfo = mx.getStoredCrossSigningForUser(mx.getUserId());
|
||||||
|
@ -201,7 +207,7 @@ export function getDefaultSSKey() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSSKeyInfo(key) {
|
export function getSSKeyInfo(key: string) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
try {
|
try {
|
||||||
return mx.getAccountData(`m.secret_storage.key.${key}`).getContent();
|
return mx.getAccountData(`m.secret_storage.key.${key}`).getContent();
|
||||||
|
@ -210,12 +216,13 @@ export function getSSKeyInfo(key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function hasDevices(userId) {
|
export async function hasDevices(userId: string) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
try {
|
try {
|
||||||
const usersDeviceMap = await mx.downloadKeys([userId, mx.getUserId()]);
|
const usersDeviceMap = await mx.downloadKeys([userId, mx.getUserId()]);
|
||||||
return Object.values(usersDeviceMap)
|
return Object.values(usersDeviceMap).every(
|
||||||
.every((userDevices) => (Object.keys(userDevices).length > 0));
|
(userDevices) => Object.keys(userDevices).length > 0
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error determining if it's possible to encrypt to all users: ", e);
|
console.error("Error determining if it's possible to encrypt to all users: ", e);
|
||||||
return false;
|
return false;
|
|
@ -25,7 +25,7 @@ export const ALLOWED_BLOB_MIMETYPES = [
|
||||||
'audio/x-flac',
|
'audio/x-flac',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getBlobSafeMimeType(mimetype) {
|
export function getBlobSafeMimeType(mimetype: string) {
|
||||||
if (typeof mimetype !== 'string') return 'application/octet-stream';
|
if (typeof mimetype !== 'string') return 'application/octet-stream';
|
||||||
const [type] = mimetype.split(';');
|
const [type] = mimetype.split(';');
|
||||||
if (!ALLOWED_BLOB_MIMETYPES.includes(type)) {
|
if (!ALLOWED_BLOB_MIMETYPES.includes(type)) {
|
|
@ -1,21 +1,64 @@
|
||||||
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
|
import { HTMLAttributes } from 'react';
|
||||||
import sanitizeHtml from 'sanitize-html';
|
import sanitizeHtml from 'sanitize-html';
|
||||||
|
|
||||||
const MAX_TAG_NESTING = 100;
|
const MAX_TAG_NESTING = 100;
|
||||||
let mx = null;
|
let mx = null;
|
||||||
|
|
||||||
const permittedHtmlTags = [
|
const permittedHtmlTags = [
|
||||||
'font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
'font',
|
||||||
'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub',
|
'del',
|
||||||
'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code',
|
'h1',
|
||||||
'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th',
|
'h2',
|
||||||
'td', 'caption', 'pre', 'span', 'img', 'details', 'summary',
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6',
|
||||||
|
'blockquote',
|
||||||
|
'p',
|
||||||
|
'a',
|
||||||
|
'ul',
|
||||||
|
'ol',
|
||||||
|
'sup',
|
||||||
|
'sub',
|
||||||
|
'li',
|
||||||
|
'b',
|
||||||
|
'i',
|
||||||
|
'u',
|
||||||
|
'strong',
|
||||||
|
'em',
|
||||||
|
'strike',
|
||||||
|
'code',
|
||||||
|
'hr',
|
||||||
|
'br',
|
||||||
|
'div',
|
||||||
|
'table',
|
||||||
|
'thead',
|
||||||
|
'tbody',
|
||||||
|
'tr',
|
||||||
|
'th',
|
||||||
|
'td',
|
||||||
|
'caption',
|
||||||
|
'pre',
|
||||||
|
'span',
|
||||||
|
'img',
|
||||||
|
'details',
|
||||||
|
'summary',
|
||||||
];
|
];
|
||||||
|
|
||||||
const urlSchemes = ['https', 'http', 'ftp', 'mailto', 'magnet'];
|
const urlSchemes = ['https', 'http', 'ftp', 'mailto', 'magnet'];
|
||||||
|
|
||||||
const permittedTagToAttributes = {
|
const permittedTagToAttributes = {
|
||||||
font: ['style', 'data-mx-bg-color', 'data-mx-color', 'color'],
|
font: ['style', 'data-mx-bg-color', 'data-mx-color', 'color'],
|
||||||
span: ['style', 'data-mx-bg-color', 'data-mx-color', 'data-mx-spoiler', 'data-mx-maths', 'data-mx-pill', 'data-mx-ping'],
|
span: [
|
||||||
|
'style',
|
||||||
|
'data-mx-bg-color',
|
||||||
|
'data-mx-color',
|
||||||
|
'data-mx-spoiler',
|
||||||
|
'data-mx-maths',
|
||||||
|
'data-mx-pill',
|
||||||
|
'data-mx-ping',
|
||||||
|
],
|
||||||
div: ['data-mx-maths'],
|
div: ['data-mx-maths'],
|
||||||
a: ['name', 'target', 'href', 'rel'],
|
a: ['name', 'target', 'href', 'rel'],
|
||||||
img: ['width', 'height', 'alt', 'title', 'src', 'data-mx-emoticon'],
|
img: ['width', 'height', 'alt', 'title', 'src', 'data-mx-emoticon'],
|
||||||
|
@ -60,7 +103,8 @@ function transformATag(tagName, attribs) {
|
||||||
return pill;
|
return pill;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rex = /[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/ug;
|
const rex =
|
||||||
|
/[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/gu;
|
||||||
const newHref = attribs.href.replace(rex, (match) => `[e-${match.codePointAt(0).toString(16)}]`);
|
const newHref = attribs.href.replace(rex, (match) => `[e-${match.codePointAt(0).toString(16)}]`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -96,7 +140,7 @@ function transformImgTag(tagName, attribs) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeCustomHtml(matrixClient, body) {
|
export function sanitizeCustomHtml(matrixClient: MatrixClient, body: string) {
|
||||||
mx = matrixClient;
|
mx = matrixClient;
|
||||||
return sanitizeHtml(body, {
|
return sanitizeHtml(body, {
|
||||||
allowedTags: permittedHtmlTags,
|
allowedTags: permittedHtmlTags,
|
||||||
|
@ -128,7 +172,7 @@ export function sanitizeCustomHtml(matrixClient, body) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeText(body) {
|
export function sanitizeText(body: string) {
|
||||||
const tagsToReplace = {
|
const tagsToReplace = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'<': '<',
|
'<': '<',
|
|
@ -1,13 +1,13 @@
|
||||||
import initMatrix from '../client/initMatrix';
|
import initMatrix from '../client/initMatrix';
|
||||||
|
|
||||||
export function roomIdByActivity(id1, id2) {
|
export function roomIdByActivity(id1: string, id2: string) {
|
||||||
const room1 = initMatrix.matrixClient.getRoom(id1);
|
const room1 = initMatrix.matrixClient.getRoom(id1);
|
||||||
const room2 = initMatrix.matrixClient.getRoom(id2);
|
const room2 = initMatrix.matrixClient.getRoom(id2);
|
||||||
|
|
||||||
return room2.getLastActiveTimestamp() - room1.getLastActiveTimestamp();
|
return room2.getLastActiveTimestamp() - room1.getLastActiveTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function roomIdByAtoZ(aId, bId) {
|
export function roomIdByAtoZ(aId: string, bId: string) {
|
||||||
let aName = initMatrix.matrixClient.getRoom(aId).name;
|
let aName = initMatrix.matrixClient.getRoom(aId).name;
|
||||||
let bName = initMatrix.matrixClient.getRoom(bId).name;
|
let bName = initMatrix.matrixClient.getRoom(bId).name;
|
||||||
|
|
Loading…
Reference in a new issue