use eslint to format ts files.
This commit is contained in:
parent
641714285a
commit
34489bfb30
14 changed files with 117 additions and 303 deletions
|
@ -57,5 +57,6 @@ module.exports = {
|
|||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
},
|
||||
};
|
||||
|
|
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
|
@ -1,5 +1,11 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import EventEmitter from 'events';
|
||||
import { MatrixClient, MatrixEvent, NotificationCountType, Room } from 'matrix-js-sdk';
|
||||
import renderAvatar from '../../app/atoms/avatar/render';
|
||||
import { cssColorMXID } from '../../util/colorMXID';
|
||||
import { selectRoom } from '../action/navigation';
|
||||
|
@ -11,7 +12,6 @@ import LogoSVG from '../../../public/res/svg/cinny.svg';
|
|||
import LogoUnreadSVG from '../../../public/res/svg/cinny-unread.svg';
|
||||
import LogoHighlightSVG from '../../../public/res/svg/cinny-highlight.svg';
|
||||
import { html, plain } from '../../util/markdown';
|
||||
import { MatrixClient, MatrixEvent, NotificationCountType, Room } from 'matrix-js-sdk';
|
||||
import RoomList from './RoomList';
|
||||
|
||||
function isNotifEvent(mEvent: MatrixEvent) {
|
||||
|
@ -35,14 +35,23 @@ function findMutedRule(overrideRules, roomId) {
|
|||
|
||||
class Notifications extends EventEmitter {
|
||||
initialized: boolean;
|
||||
|
||||
favicon: string;
|
||||
|
||||
matrixClient: MatrixClient;
|
||||
|
||||
roomList: RoomList;
|
||||
|
||||
roomIdToNoti: Map<string, any>;
|
||||
|
||||
roomIdToPopupNotis: Map<string, any>;
|
||||
|
||||
eventIdToPopupNoti: Map<any, any>;
|
||||
|
||||
_notiAudio: any;
|
||||
|
||||
_inviteAudio: any;
|
||||
|
||||
constructor(roomList) {
|
||||
super();
|
||||
|
||||
|
@ -333,7 +342,7 @@ class Notifications extends EventEmitter {
|
|||
}
|
||||
|
||||
_listenEvents() {
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('Room.timeline', (mEvent: MatrixEvent, room: Room) => {
|
||||
if (mEvent.isRedaction()) this._deletePopupNoti(mEvent.event.redacts);
|
||||
|
||||
|
@ -360,7 +369,7 @@ class Notifications extends EventEmitter {
|
|||
this._displayPopupNoti(mEvent, room);
|
||||
}
|
||||
});
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('accountData', (mEvent: MatrixEvent, oldMEvent: MatrixEvent) => {
|
||||
if (mEvent.getType() === 'm.push_rules') {
|
||||
const override = mEvent?.getContent()?.global?.override;
|
||||
|
@ -397,7 +406,7 @@ class Notifications extends EventEmitter {
|
|||
});
|
||||
}
|
||||
});
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('Room.receipt', (mEvent: MatrixEvent, room: Room) => {
|
||||
if (mEvent.getType() === 'm.receipt') {
|
||||
if (room.isSpaceRoom()) return;
|
||||
|
@ -411,7 +420,7 @@ class Notifications extends EventEmitter {
|
|||
this._deletePopupRoomNotis(room.roomId);
|
||||
}
|
||||
});
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('Room.myMembership', (room: Room, membership) => {
|
||||
if (membership === 'leave' && this.hasNoti(room.roomId)) {
|
||||
this.deleteNoti(room.roomId);
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
MatrixClient,
|
||||
MatrixEvent,
|
||||
Room,
|
||||
TimelineIndex,
|
||||
} from 'matrix-js-sdk';
|
||||
import initMatrix from '../initMatrix';
|
||||
import cons from './cons';
|
||||
|
@ -53,18 +52,18 @@ function addToMap(myMap: Map<string, MatrixEvent[]>, mEvent: MatrixEvent) {
|
|||
|
||||
function getFirstLinkedTimeline(timeline: EventTimeline) {
|
||||
let tm = timeline;
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
while (tm.prevTimeline) {
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
tm = tm.prevTimeline;
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
function getLastLinkedTimeline(timeline: EventTimeline) {
|
||||
let tm = timeline;
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
while (tm.nextTimeline) {
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
tm = tm.nextTimeline;
|
||||
}
|
||||
return tm;
|
||||
|
@ -78,9 +77,9 @@ function iterateLinkedTimelines(
|
|||
let tm = timeline;
|
||||
while (tm) {
|
||||
callback(tm);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
if (backwards) tm = tm.prevTimeline;
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
else tm = tm.nextTimeline;
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +88,7 @@ function isTimelineLinked(tm1: EventTimeline, tm2: EventTimeline) {
|
|||
let tm = getFirstLinkedTimeline(tm1);
|
||||
while (tm) {
|
||||
if (tm === tm2) return true;
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
tm = tm.nextTimeline;
|
||||
}
|
||||
return false;
|
||||
|
@ -97,17 +96,29 @@ function isTimelineLinked(tm1: EventTimeline, tm2: EventTimeline) {
|
|||
|
||||
class RoomTimeline extends EventEmitter {
|
||||
timeline: any[];
|
||||
|
||||
editedTimeline: Map<any, any>;
|
||||
|
||||
reactionTimeline: Map<any, any>;
|
||||
|
||||
typingMembers: Set<unknown>;
|
||||
|
||||
matrixClient: MatrixClient;
|
||||
|
||||
roomId: string;
|
||||
|
||||
room: any;
|
||||
|
||||
liveTimeline;
|
||||
|
||||
activeTimeline: any;
|
||||
|
||||
isOngoingPagination: boolean;
|
||||
|
||||
ongoingDecryptionCount: number;
|
||||
|
||||
initialized: boolean;
|
||||
|
||||
_listenRoomTimeline: (
|
||||
event: any,
|
||||
room: any,
|
||||
|
@ -115,10 +126,15 @@ class RoomTimeline extends EventEmitter {
|
|||
removed: any,
|
||||
data: any
|
||||
) => void;
|
||||
|
||||
_listenDecryptEvent: (event: any) => void;
|
||||
|
||||
_listenRedaction: (mEvent: MatrixEvent, room: any) => void;
|
||||
|
||||
_listenTypingEvent: (event: any, member: any) => void;
|
||||
|
||||
_listenReciptEvent: (event: any, room: any) => void;
|
||||
|
||||
constructor(roomId: string) {
|
||||
super();
|
||||
// These are local timelines
|
||||
|
@ -141,7 +157,7 @@ class RoomTimeline extends EventEmitter {
|
|||
setTimeout(() => this.room.loadMembersIfNeeded());
|
||||
|
||||
// TODO: remove below line
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
window.selectedRoom = this;
|
||||
}
|
||||
|
||||
|
@ -440,29 +456,29 @@ class RoomTimeline extends EventEmitter {
|
|||
this.emit(cons.events.roomTimeline.LIVE_RECEIPT);
|
||||
}
|
||||
};
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('Room.timeline', this._listenRoomTimeline);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('Room.redaction', this._listenRedaction);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('Event.decrypted', this._listenDecryptEvent);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('RoomMember.typing', this._listenTypingEvent);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.on('Room.receipt', this._listenReciptEvent);
|
||||
}
|
||||
|
||||
removeInternalListeners() {
|
||||
if (!this.initialized) return;
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.removeListener('Room.timeline', this._listenRoomTimeline);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.removeListener('Room.redaction', this._listenRedaction);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.removeListener('Event.decrypted', this._listenDecryptEvent);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.removeListener('RoomMember.typing', this._listenTypingEvent);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.matrixClient.removeListener('Room.receipt', this._listenReciptEvent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,15 @@ import { RoomHierarchy } from 'matrix-js-sdk/lib/room-hierarchy';
|
|||
|
||||
class RoomsHierarchy {
|
||||
matrixClient: MatrixClient;
|
||||
|
||||
_maxDepth: number;
|
||||
|
||||
_suggestedOnly: boolean;
|
||||
|
||||
_limit: number;
|
||||
|
||||
roomIdToHierarchy: Map<any, any>;
|
||||
|
||||
constructor(matrixClient: MatrixClient, limit = 20, maxDepth = 1, suggestedOnly = false) {
|
||||
this.matrixClient = matrixClient;
|
||||
this._maxDepth = maxDepth;
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import EventEmitter from 'events';
|
||||
import encrypt from 'browser-encrypt-attachment';
|
||||
import { encode } from 'blurhash';
|
||||
import { IContent, MatrixClient, MatrixEvent } from 'matrix-js-sdk';
|
||||
import { getShortcodeToEmoji } from '../../app/organisms/emoji-board/custom-emoji';
|
||||
import { getBlobSafeMimeType } from '../../util/mimetypes';
|
||||
import { sanitizeText } from '../../util/sanitize';
|
||||
import cons from './cons';
|
||||
import settings from './settings';
|
||||
import { markdown, plain } from '../../util/markdown';
|
||||
import { IContent, MatrixClient, MatrixEvent } from 'matrix-js-sdk';
|
||||
import RoomList from './RoomList';
|
||||
|
||||
const blurhashField = 'xyz.amorgan.blurhash';
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
function encodeBlurhash(img: CanvasImageSource) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 100;
|
||||
|
@ -66,6 +67,7 @@ function loadVideo(videoFile: Blob) {
|
|||
});
|
||||
}
|
||||
function getVideoThumbnail(
|
||||
// eslint-disable-next-line no-undef
|
||||
video: CanvasImageSource,
|
||||
width: number,
|
||||
height: number,
|
||||
|
@ -107,8 +109,11 @@ function getVideoThumbnail(
|
|||
|
||||
class RoomsInput extends EventEmitter {
|
||||
matrixClient: MatrixClient;
|
||||
|
||||
roomList: RoomList;
|
||||
|
||||
roomIdToInput: Map<any, any>;
|
||||
|
||||
constructor(mx: MatrixClient, roomList: RoomList) {
|
||||
super();
|
||||
|
||||
|
@ -221,7 +226,7 @@ class RoomsInput extends EventEmitter {
|
|||
const autoMarkdown = options?.autoMarkdown ?? true;
|
||||
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
const userNames = room.currentState.userIdsToDisplayNames;
|
||||
const parentIds = this.roomList.getAllParentSpaces(room.roomId);
|
||||
const parentRooms = [...parentIds].map((id) => this.matrixClient.getRoom(id));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import EventEmitter from 'events';
|
||||
import { MatrixClient, Room } from 'matrix-js-sdk';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import appDispatcher from '../dispatcher';
|
||||
import AccountData from './AccountData';
|
||||
import cons from './cons';
|
||||
|
@ -7,18 +7,27 @@ import RoomList from './RoomList';
|
|||
|
||||
class Navigation extends EventEmitter {
|
||||
initMatrix: { roomList: RoomList; accountData: AccountData; matrixClient: MatrixClient };
|
||||
|
||||
selectedTab: string;
|
||||
|
||||
selectedSpaceId: string;
|
||||
|
||||
selectedSpacePath: string[];
|
||||
|
||||
selectedRoomId: string;
|
||||
|
||||
isRoomSettings: boolean;
|
||||
|
||||
recentRooms: string[];
|
||||
|
||||
spaceToRoom: Map<string, any>;
|
||||
|
||||
rawModelStack: any[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// this will attached by initMatrix
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
this.initMatrix = {};
|
||||
|
||||
this.selectedTab = cons.tabs.HOME;
|
||||
|
|
|
@ -16,7 +16,7 @@ export function getPrivateKey(keyId) {
|
|||
}
|
||||
|
||||
export function deletePrivateKey(keyId) {
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
delete secretStorageKeys.delete(keyId);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import appDispatcher from '../dispatcher';
|
|||
|
||||
import cons from './cons';
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
function getSettings(): Settings {
|
||||
const settings = localStorage.getItem('settings');
|
||||
if (settings === null) return null;
|
||||
|
@ -11,6 +12,7 @@ function getSettings(): Settings {
|
|||
|
||||
function setSettings(key: string, value) {
|
||||
let settings = getSettings();
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
if (settings === null) settings = new Settings();
|
||||
settings[key] = value;
|
||||
localStorage.setItem('settings', JSON.stringify(settings));
|
||||
|
@ -18,15 +20,25 @@ function setSettings(key: string, value) {
|
|||
|
||||
class Settings extends EventEmitter {
|
||||
themes: string[];
|
||||
|
||||
themeIndex: number;
|
||||
|
||||
useSystemTheme: boolean;
|
||||
|
||||
isMarkdown: boolean;
|
||||
|
||||
isPeopleDrawer: boolean;
|
||||
|
||||
hideMembershipEvents: boolean;
|
||||
|
||||
hideNickAvatarEvents: boolean;
|
||||
|
||||
_showNotifications: boolean;
|
||||
|
||||
isNotificationSounds: boolean;
|
||||
|
||||
isTouchScreenDevice: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
|
|
|
@ -2,17 +2,29 @@ import EventEmitter from 'events';
|
|||
|
||||
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() {
|
||||
super();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class Postie {
|
||||
_topics: Map<string, Map<string, Set<Function>>>;
|
||||
|
||||
constructor() {
|
||||
this._topics = new Map();
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ class Postie {
|
|||
* @param {*} data - Data to deliver to subscriber
|
||||
*/
|
||||
post(topic: string, address: string | string[], data: any) {
|
||||
const sendPost = (inboxes: Set<Function>, addr: string) => {
|
||||
const sendPost = (inboxes: Set<Function>, addr: string) => {
|
||||
if (inboxes === undefined) {
|
||||
throw new Error(
|
||||
`Unable to post on topic:"${topic}" at address:"${addr}". Subscriber doesn't exist.`
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
/* eslint-disable max-classes-per-file */
|
||||
export function bytesToSize(bytes) {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes === 0) return 'n/a';
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
|
||||
if (i === 0) return `${bytes} ${sizes[i]}`;
|
||||
return `${(bytes / (1024 ** i)).toFixed(1)} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
export function diffMinutes(dt2, dt1) {
|
||||
let diff = (dt2.getTime() - dt1.getTime()) / 1000;
|
||||
diff /= 60;
|
||||
return Math.abs(Math.round(diff));
|
||||
}
|
||||
|
||||
export function isInSameDay(dt2, dt1) {
|
||||
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, targetSelector) {
|
||||
let boxInfo;
|
||||
|
||||
const path = ev.nativeEvent.composedPath();
|
||||
const target = targetSelector
|
||||
? path.find((element) => element.matches?.(targetSelector))
|
||||
: null;
|
||||
if (target) {
|
||||
boxInfo = target.getBoundingClientRect();
|
||||
} else {
|
||||
boxInfo = ev.target.getBoundingClientRect();
|
||||
}
|
||||
|
||||
return {
|
||||
x: boxInfo.x,
|
||||
y: boxInfo.y,
|
||||
width: boxInfo.width,
|
||||
height: boxInfo.height,
|
||||
detail: ev.detail,
|
||||
};
|
||||
}
|
||||
|
||||
export function abbreviateNumber(number) {
|
||||
if (number > 99) return '99+';
|
||||
return number;
|
||||
}
|
||||
|
||||
export class Debounce {
|
||||
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, wait) {
|
||||
const that = this;
|
||||
return function debounceCallback(...args) {
|
||||
clearTimeout(that.timeoutId);
|
||||
that.timeoutId = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
that.timeoutId = null;
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class Throttle {
|
||||
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, wait) {
|
||||
const that = this;
|
||||
return function throttleCallback(...args) {
|
||||
if (that.timeoutId !== null) return;
|
||||
that.timeoutId = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
that.timeoutId = null;
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getUrlPrams(paramName) {
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
return urlParams.get(paramName);
|
||||
}
|
||||
|
||||
export function getScrollInfo(target) {
|
||||
const scroll = {};
|
||||
scroll.top = Math.round(target.scrollTop);
|
||||
scroll.height = Math.round(target.scrollHeight);
|
||||
scroll.viewHeight = Math.round(target.offsetHeight);
|
||||
scroll.isScrollable = scroll.height > scroll.viewHeight;
|
||||
return scroll;
|
||||
}
|
||||
|
||||
export function avatarInitials(text) {
|
||||
return [...text][0];
|
||||
}
|
||||
|
||||
export function cssVar(name) {
|
||||
return getComputedStyle(document.body).getPropertyValue(name);
|
||||
}
|
||||
|
||||
export function setFavicon(url) {
|
||||
const favicon = document.querySelector('#favicon');
|
||||
if (!favicon) return;
|
||||
favicon.setAttribute('href', url);
|
||||
}
|
||||
|
||||
export function copyToClipboard(text) {
|
||||
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, validator) {
|
||||
let suffix = 2;
|
||||
let newName = name;
|
||||
do {
|
||||
newName = name + suffix;
|
||||
suffix += 1;
|
||||
} while (validator(newName));
|
||||
|
||||
return newName;
|
||||
}
|
||||
|
||||
export function getImageDimension(file) {
|
||||
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, width, height) {
|
||||
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, flags, prefix) {
|
||||
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) {
|
||||
const res = decodeURIComponent(uri).match(matrixToRegex);
|
||||
if (!res) return null;
|
||||
return res[1];
|
||||
}
|
|
@ -54,6 +54,7 @@ export function abbreviateNumber(number: number) {
|
|||
|
||||
export class Debounce {
|
||||
timeoutId: any;
|
||||
|
||||
constructor() {
|
||||
this.timeoutId = null;
|
||||
}
|
||||
|
@ -63,7 +64,7 @@ export class Debounce {
|
|||
* @param {number} wait - wait in milliseconds to call func
|
||||
* @returns {func} debounceCallback - to pass arguments to func callback
|
||||
*/
|
||||
_(func: Function, wait: number) {
|
||||
_(func: (...args) => void, wait: number) {
|
||||
const debounceCallback = (...args) => {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = setTimeout(() => {
|
||||
|
@ -77,6 +78,7 @@ export class Debounce {
|
|||
|
||||
export class Throttle {
|
||||
timeoutId: any;
|
||||
|
||||
constructor() {
|
||||
this.timeoutId = null;
|
||||
}
|
||||
|
@ -86,7 +88,7 @@ export class Throttle {
|
|||
* @param {number} wait - wait in milliseconds to call func
|
||||
* @returns {function} throttleCallback - to pass arguments to func callback
|
||||
*/
|
||||
_(func: Function, wait: number) {
|
||||
_(func: (...args) => void, wait: number) {
|
||||
const throttleCallback = (...args) => {
|
||||
if (this.timeoutId !== null) return;
|
||||
this.timeoutId = setTimeout(() => {
|
||||
|
@ -146,7 +148,7 @@ export function copyToClipboard(text: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export function suffixRename(name: string | number, validator: Function) {
|
||||
export function suffixRename(name: string, validator: (newName: string) => boolean) {
|
||||
let suffix = 2;
|
||||
let newName = name;
|
||||
do {
|
||||
|
|
|
@ -5,45 +5,11 @@ const MAX_TAG_NESTING = 100;
|
|||
let mx = null;
|
||||
|
||||
const permittedHtmlTags = [
|
||||
'font',
|
||||
'del',
|
||||
'h1',
|
||||
'h2',
|
||||
'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',
|
||||
];
|
||||
'font', 'del', 'h1', 'h2', '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'];
|
||||
|
||||
|
|
Loading…
Reference in a new issue