random stuff
This commit is contained in:
parent
0000149de1
commit
00001504db
34 changed files with 483 additions and 137 deletions
|
@ -29,6 +29,7 @@
|
||||||
"@lexical/utils": "^0.5.0",
|
"@lexical/utils": "^0.5.0",
|
||||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
||||||
"discount": "link:../discount",
|
"discount": "link:../discount",
|
||||||
|
"discount.js": "link:../discount",
|
||||||
"emojibase": "^6.1.0",
|
"emojibase": "^6.1.0",
|
||||||
"emojibase-data": "^7.0.1",
|
"emojibase-data": "^7.0.1",
|
||||||
"fuzzysort": "^2.0.3",
|
"fuzzysort": "^2.0.3",
|
||||||
|
|
|
@ -13,6 +13,7 @@ specifiers:
|
||||||
'@types/better-sqlite3': ^7.6.2
|
'@types/better-sqlite3': ^7.6.2
|
||||||
'@types/marked': ^4.0.7
|
'@types/marked': ^4.0.7
|
||||||
discount: link:../discount
|
discount: link:../discount
|
||||||
|
discount.js: link:../discount
|
||||||
emojibase: ^6.1.0
|
emojibase: ^6.1.0
|
||||||
emojibase-data: ^7.0.1
|
emojibase-data: ^7.0.1
|
||||||
fuzzysort: ^2.0.3
|
fuzzysort: ^2.0.3
|
||||||
|
@ -38,6 +39,7 @@ dependencies:
|
||||||
'@lexical/utils': 0.5.0_lexical@0.5.0
|
'@lexical/utils': 0.5.0_lexical@0.5.0
|
||||||
'@matrix-org/olm': '@gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz'
|
'@matrix-org/olm': '@gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz'
|
||||||
discount: link:../discount
|
discount: link:../discount
|
||||||
|
discount.js: link:../discount
|
||||||
emojibase: 6.1.0
|
emojibase: 6.1.0
|
||||||
emojibase-data: 7.0.1_emojibase@6.1.0
|
emojibase-data: 7.0.1_emojibase@6.1.0
|
||||||
fuzzysort: 2.0.3
|
fuzzysort: 2.0.3
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Api from "../matrix/api.js";
|
import Api from "../matrix/api.js";
|
||||||
import Settings from "../matrix/settings.js";
|
import Settings from "../matrix/settings.js";
|
||||||
import PushRules from "../../util/push.js";
|
import PushRules from "../../util/push.js";
|
||||||
import { Client, persist } from "discount";
|
import { Client, persist } from "discount.js";
|
||||||
|
|
||||||
const defaultFilter = {
|
const defaultFilter = {
|
||||||
room: {
|
room: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { writable, get } from "svelte/store";
|
import { writable, get } from "svelte/store";
|
||||||
import type { Room } from "discount";
|
import type { Room } from "discount.js";
|
||||||
|
|
||||||
export function handleAccount(_: void, event: { type: string, content: any }) {
|
export function handleAccount(_: void, event: { type: string, content: any }) {
|
||||||
if (event.type === "m.fully_read") {
|
if (event.type === "m.fully_read") {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import type { Event } from "discount";
|
import type { Room, Event } from "discount.js";
|
||||||
import type { Room } from "discount";
|
|
||||||
|
|
||||||
export function reslice(room: Room, force = false) {
|
export function reslice(room: Room, force = false) {
|
||||||
if (!room.events.live) return;
|
if (!room.events.live) return;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Room } from "discount";
|
import type { Room } from "discount.js";
|
||||||
|
|
||||||
interface Notifications {
|
interface Notifications {
|
||||||
level: "default" | "all" | "mentions" | "muted",
|
level: "default" | "all" | "mentions" | "muted",
|
||||||
|
|
|
@ -17,8 +17,11 @@ defaultSettings.set("sendtyping", true);
|
||||||
defaultSettings.set("autocomplete", true);
|
defaultSettings.set("autocomplete", true);
|
||||||
defaultSettings.set("showembeds", "unencrypted");
|
defaultSettings.set("showembeds", "unencrypted");
|
||||||
|
|
||||||
|
// rooms and spaces
|
||||||
|
defaultSettings.set("autospace", false);
|
||||||
|
defaultSettings.set("autodm", false);
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
defaultSettings.set("autojoin", true);
|
|
||||||
|
|
||||||
export default class Settings extends Map {
|
export default class Settings extends Map {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
import { openDB, deleteDB } from "idb";
|
|
||||||
import { Event } from "discount";
|
|
||||||
|
|
||||||
class PersistentMap extends Map {
|
|
||||||
constructor(name, db) {
|
|
||||||
super();
|
|
||||||
this.name = name;
|
|
||||||
this._db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetch(key) {
|
|
||||||
return super.get(key) ?? await this._db.get(this.name, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
async all() {
|
|
||||||
return (await this._db.all(this.name)).map(i => i);
|
|
||||||
}
|
|
||||||
|
|
||||||
async put(key, val) {
|
|
||||||
super.set(key, val);
|
|
||||||
await this._db.put(this.name, val, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Events extends PersistentMap {
|
|
||||||
async fetch(room, eventId) {
|
|
||||||
if (this.has(eventId)) return this.get(eventId);
|
|
||||||
// const fromDb = await this._db.get(this.name, eventId);
|
|
||||||
const event = new Event(room, (await state.api.fetchEvent(room.id, eventId)));
|
|
||||||
// const event = new Event(room, (await this._db.get(this.name, eventId) ?? await state.api.fetchEvent(room.roomId, eventId)));
|
|
||||||
this.set(eventId, event);
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Store {
|
|
||||||
async init() {
|
|
||||||
const db = await openDB("discard", 1, {
|
|
||||||
upgrade(db) {
|
|
||||||
db.createObjectStore("account");
|
|
||||||
db.createObjectStore("events");
|
|
||||||
db.createObjectStore("rooms");
|
|
||||||
db.createObjectStore("users");
|
|
||||||
db.createObjectStore("sync");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this._db = db;
|
|
||||||
this.events = new Events("events", db);
|
|
||||||
this.account = new PersistentMap("account", db);
|
|
||||||
this.rooms = new PersistentMap("rooms", db);
|
|
||||||
this.sync = new PersistentMap("sync", db);
|
|
||||||
this.users = new PersistentMap("users", db);
|
|
||||||
}
|
|
||||||
|
|
||||||
async purge() {
|
|
||||||
this._db.close();
|
|
||||||
await deleteDB("discard");
|
|
||||||
}
|
|
||||||
|
|
||||||
save(state) {
|
|
||||||
for (let [key, val] of state.accountDataRef) this.account.put(key, val);
|
|
||||||
for (let [id, room] of state.rooms) this.rooms.put(id, {
|
|
||||||
state: room.state,
|
|
||||||
roomId: room.roomId,
|
|
||||||
accountData: [...room.accountData.entries()],
|
|
||||||
notifications: room.notifications,
|
|
||||||
});
|
|
||||||
// for (let [id, event] of state.events) this.events.put(id, {
|
|
||||||
// roomId: event.roomId,
|
|
||||||
// raw: event.raw,
|
|
||||||
// relations: event.relations,
|
|
||||||
// });
|
|
||||||
state.log.matrix("persisting state");
|
|
||||||
}
|
|
||||||
|
|
||||||
load(state) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { handle } from "../actions/timeline";
|
import { handle } from "../actions/timeline";
|
||||||
import { Event, StateEvent } from "discount";
|
import { Event, StateEvent } from "discount.js";
|
||||||
|
|
||||||
class Timeline extends Array {
|
class Timeline extends Array {
|
||||||
constructor(roomId, start, end) {
|
constructor(roomId, start, end) {
|
||||||
|
|
|
@ -85,7 +85,7 @@ summary {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
button, input[type=text], input[type=password] {
|
button, input[type=text], input[type=password], textarea {
|
||||||
outline: none;
|
outline: none;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
.hljs-regexp { color: #95e6cb }
|
.hljs-regexp { color: #95e6cb }
|
||||||
.hljs-regex-delimiter { color: #95d8e5 }
|
.hljs-regex-delimiter { color: #95d8e5 }
|
||||||
|
|
||||||
|
|
||||||
.hljs-type { color: #ffb454 }
|
.hljs-type { color: #ffb454 }
|
||||||
.hljs-type { color: #f29668 }
|
.hljs-type { color: #f29668 }
|
||||||
.hljs-type { color: #ff8f40 }
|
.hljs-type { color: #ff8f40 }
|
||||||
|
|
|
@ -74,7 +74,7 @@ function select(option) {
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="container">
|
<div class="container" role="listbox">
|
||||||
<!-- FIXME: close on blur -->
|
<!-- FIXME: close on blur -->
|
||||||
<div class="dropdown" class:show on:click={() => show = !show} bind:clientWidth={width}>
|
<div class="dropdown" class:show on:click={() => show = !show} bind:clientWidth={width}>
|
||||||
{options.find(i => i[1] === selected)?.[0]}
|
{options.find(i => i[1] === selected)?.[0]}
|
||||||
|
@ -83,12 +83,18 @@ function select(option) {
|
||||||
{#if show}
|
{#if show}
|
||||||
<div class="options" style:width={width + "px"}>
|
<div class="options" style:width={width + "px"}>
|
||||||
{#each options as option}
|
{#each options as option}
|
||||||
<div class="option" class:selected={selected === option[1]} on:click={() => select(option[1])}>
|
<div
|
||||||
|
class="option"
|
||||||
|
role="option"
|
||||||
|
aria-selected={selected === option[1] }
|
||||||
|
class:selected={selected === option[1]}
|
||||||
|
on:click={() => select(option[1])}
|
||||||
|
>
|
||||||
<div class="label">
|
<div class="label">
|
||||||
{option[0]}
|
{option[0]}
|
||||||
</div>
|
</div>
|
||||||
{#if option[1] === selected}
|
{#if option[1] === selected}
|
||||||
<div class="checkmark icon">check_circle</div>
|
<div class="checkmark icon" aria-hidden="true">check_circle</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -94,8 +94,20 @@ function opacity() {
|
||||||
transition: color 0.4s;
|
transition: color 0.4s;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="background" on:mousedown|stopPropagation={closePopup} transition:opacity on:click|stopPropagation>
|
<div
|
||||||
<div class="card" on:mousedown|stopPropagation class:raw transition:card role="dialog" aria-modal="true">
|
class="background"
|
||||||
|
on:mousedown|stopPropagation={closePopup}
|
||||||
|
on:click|stopPropagation
|
||||||
|
transition:opacity
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="card"
|
||||||
|
class:raw
|
||||||
|
on:mousedown|stopPropagation
|
||||||
|
transition:card
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
>
|
||||||
{#if showClose}
|
{#if showClose}
|
||||||
<button class="icon close" on:click={closePopup}>close</button>
|
<button class="icon close" on:click={closePopup}>close</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let checked = false;
|
export let checked = false;
|
||||||
export let toggled = () => {};
|
export let toggled = (_checked: boolean) => {};
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.toggle {
|
.toggle {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script type="ts">
|
<script type="ts">
|
||||||
import type { Room } from "discount";
|
import type { Room } from "discount.js";
|
||||||
export let room: Room | null = null;
|
export let room: Room | null = null;
|
||||||
export let users: Array<string>;
|
export let users: Array<string>;
|
||||||
$: [single, plural, typing] = getType(room?.name);
|
$: [single, plural, typing] = getType(room?.name);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import Avatar from "../atoms/Avatar.svelte";
|
import Avatar from "../atoms/Avatar.svelte";
|
||||||
import { memberContext, roomContext } from "../../util/context";
|
import { memberContext, roomContext } from "../../util/context";
|
||||||
import { fastclick } from "../../util/use";
|
import { fastclick } from "../../util/use";
|
||||||
import { Room, Member } from "discount";
|
import { Room, Member } from "discount.js";
|
||||||
export let member;
|
export let member;
|
||||||
let { popup, popout, context } = state;
|
let { popup, popout, context } = state;
|
||||||
$: isRoomPing = member instanceof Room;
|
$: isRoomPing = member instanceof Room;
|
||||||
|
|
|
@ -30,7 +30,6 @@ export let size = 0;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Button from "../atoms/Button.svelte";
|
||||||
import Popup from "../atoms/Popup.svelte";
|
import Popup from "../atoms/Popup.svelte";
|
||||||
import Search from "../atoms/Search.svelte";
|
import Search from "../atoms/Search.svelte";
|
||||||
import Checkbox from "../atoms/Checkbox.svelte";
|
import Checkbox from "../atoms/Checkbox.svelte";
|
||||||
import type { Room } from "discount";
|
import type { Room } from "discount.js";
|
||||||
// export const confirm = () => {};
|
// export const confirm = () => {};
|
||||||
export let current;
|
export let current;
|
||||||
let checked = {};
|
let checked = {};
|
||||||
|
|
|
@ -8,13 +8,16 @@ export const confirm = kick;
|
||||||
export let current;
|
export let current;
|
||||||
let reason;
|
let reason;
|
||||||
|
|
||||||
const rnd = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
||||||
const placeholders = [
|
const placeholders = [
|
||||||
"they disagree with my opinion",
|
"they disagree with my opinion",
|
||||||
"i dont like them",
|
"i dont like them",
|
||||||
"they were acting sussy",
|
"they were acting sussy",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function getPlaceholder() {
|
||||||
|
return placeholders[Math.floor(Math.random() * placeholders.length)];
|
||||||
|
}
|
||||||
|
|
||||||
const scopes = [];
|
const scopes = [];
|
||||||
if (current.room.type === "m.space") {
|
if (current.room.type === "m.space") {
|
||||||
scopes.push(["This space", "space"]);
|
scopes.push(["This space", "space"]);
|
||||||
|
@ -43,7 +46,7 @@ function kick() {
|
||||||
<Dropdown options={scopes} />
|
<Dropdown options={scopes} />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="title">Reason for Kick</div>
|
<div class="title">Reason for Kick</div>
|
||||||
<Textarea autofocus placeholder={rnd(placeholders)} bind:value={reason} />
|
<Textarea autofocus placeholder={getPlaceholder()} bind:value={reason} />
|
||||||
</div>
|
</div>
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
<Button type="link" label="Nevermind!" clicked={() => state.popup.set({ ...current, id: null })} />
|
<Button type="link" label="Nevermind!" clicked={() => state.popup.set({ ...current, id: null })} />
|
||||||
|
|
|
@ -3,7 +3,7 @@ import fuzzysort from "fuzzysort";
|
||||||
import Search from "../atoms/Search.svelte";
|
import Search from "../atoms/Search.svelte";
|
||||||
import Popup from "../atoms/Popup.svelte";
|
import Popup from "../atoms/Popup.svelte";
|
||||||
import { getLastMessage } from "../../util/timeline";
|
import { getLastMessage } from "../../util/timeline";
|
||||||
import type { Room } from "discount";
|
import type { Room } from "discount.js";
|
||||||
let search = "";
|
let search = "";
|
||||||
let highlighted = 0;
|
let highlighted = 0;
|
||||||
$: results = getRooms(search);
|
$: results = getRooms(search);
|
||||||
|
|
|
@ -13,6 +13,7 @@ import UnknownRoom from './rooms/Unknown.svelte';
|
||||||
// hmmm...
|
// hmmm...
|
||||||
import MediaRoom from './rooms/Media.svelte';
|
import MediaRoom from './rooms/Media.svelte';
|
||||||
import ForumRoom from './rooms/Forum.svelte';
|
import ForumRoom from './rooms/Forum.svelte';
|
||||||
|
import LongformRoom from './rooms/Longform.svelte';
|
||||||
|
|
||||||
let { focusedRoom: room, focusedSpace: space, navRooms, roomState, slice, settings } = state;
|
let { focusedRoom: room, focusedSpace: space, navRooms, roomState, slice, settings } = state;
|
||||||
let { search } = roomState;
|
let { search } = roomState;
|
||||||
|
@ -56,6 +57,8 @@ $: if ($navRooms) room = state.focusedRoom;
|
||||||
<MediaRoom room={$room} slice={$slice} />
|
<MediaRoom room={$room} slice={$slice} />
|
||||||
{:else if type === "org.eu.celery.room.forum"}
|
{:else if type === "org.eu.celery.room.forum"}
|
||||||
<ForumRoom room={$room} />
|
<ForumRoom room={$room} />
|
||||||
|
{:else if type === "org.eu.celery.room.longform"}
|
||||||
|
<LongformRoom room={$room} slice={$slice} />
|
||||||
{:else}
|
{:else}
|
||||||
<UnknownRoom room={$room} />
|
<UnknownRoom room={$room} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Typing from "../atoms/Typing.svelte";
|
import Typing from "../atoms/Typing.svelte";
|
||||||
import RoomInput from "./RoomInput.svelte";
|
import RoomInput from "./RoomInput.svelte";
|
||||||
let textarea;
|
let textareaEl;
|
||||||
let room = state.focusedRoom;
|
let room = state.focusedRoom;
|
||||||
let { reply, edit, input, typing } = state.roomState;
|
let { reply, edit, input, typing } = state.roomState;
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@ function getRoomName(room) {
|
||||||
return other?.name ?? other?.id;
|
return other?.name ?? other?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if ($input) textarea?.focus();
|
$: if ($input) textareaEl?.focus();
|
||||||
$: if ($room) textarea?.focus();
|
$: if ($room) textareaEl?.focus();
|
||||||
$: if ($reply || true) textarea?.focus();
|
$: if ($reply || true) textareaEl?.focus();
|
||||||
$: if (!$edit) textarea?.focus();
|
$: if (!$edit) textareaEl?.focus();
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
|
@ -77,12 +77,12 @@ $: if (!$edit) textarea?.focus();
|
||||||
<div class="input disabled"><div class="center">You can't send messages in a e2ee room yet</div></div>
|
<div class="input disabled"><div class="center">You can't send messages in a e2ee room yet</div></div>
|
||||||
{:else}
|
{:else}
|
||||||
<RoomInput
|
<RoomInput
|
||||||
showUpload={true}
|
showUpload
|
||||||
placeholder={`Message ${getRoomName($room)}`}
|
placeholder={`Message ${getRoomName($room)}`}
|
||||||
onsend={sendMessage}
|
onsend={sendMessage}
|
||||||
bind:input={$input}
|
bind:input={$input}
|
||||||
bind:reply={$reply}
|
bind:reply={$reply}
|
||||||
bind:textarea={textarea}
|
bind:textarea={textareaEl}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="typing">
|
<div class="typing">
|
||||||
|
|
|
@ -15,6 +15,8 @@ export let input = "";
|
||||||
export let textarea;
|
export let textarea;
|
||||||
let showEmoji = false;
|
let showEmoji = false;
|
||||||
|
|
||||||
|
export let asdfasdfasdf = false;
|
||||||
|
|
||||||
let { focusedRoom: room, focusedSpace, slice, popup } = state;
|
let { focusedRoom: room, focusedSpace, slice, popup } = state;
|
||||||
let { edit } = state.roomState;
|
let { edit } = state.roomState;
|
||||||
|
|
||||||
|
@ -144,6 +146,7 @@ async function handleUpload(file) {
|
||||||
mimetype: file.type,
|
mimetype: file.type,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
...(["m.image", "m.video"].includes(type) ? await getSize(file, type) : {}),
|
...(["m.image", "m.video"].includes(type) ? await getSize(file, type) : {}),
|
||||||
|
filename: file.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
onsend({
|
onsend({
|
||||||
|
@ -237,11 +240,16 @@ function slide() {
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input.asdfasdfasdf {
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--bg-misc);
|
||||||
|
}
|
||||||
|
|
||||||
.upload {
|
.upload {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: start;
|
||||||
padding: 0 16px;
|
padding: 8px 16px 0;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: var(--fg-light);
|
color: var(--fg-light);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -310,14 +318,14 @@ function slide() {
|
||||||
onclose={() => reply = null}
|
onclose={() => reply = null}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="input" class:withreply={reply}>
|
<div class="input" class:asdfasdfasdf class:withreply={reply}>
|
||||||
{#if showUpload}
|
{#if showUpload}
|
||||||
<label class="upload">
|
<label class="upload">
|
||||||
<div class="icon">add_circle</div>
|
<div class="icon">add_circle</div>
|
||||||
<input type="file" on:change={e => onfile(e.target.files[0])} />
|
<input type="file" on:change={e => onfile(e.target.files[0])} />
|
||||||
</label>
|
</label>
|
||||||
{/if}
|
{/if}
|
||||||
<RoomTextarea {placeholder} bind:input={input} bind:textarea={textarea} {onfile} {oninput} />
|
<RoomTextarea {placeholder} {asdfasdfasdf} bind:input={input} bind:textarea={textarea} {onfile} {oninput} />
|
||||||
<!--
|
<!--
|
||||||
<div class="icon" style="font-size: 28px; color: var(--fg-light)">gif_box</div>
|
<div class="icon" style="font-size: 28px; color: var(--fg-light)">gif_box</div>
|
||||||
<div class="icon" style="font-size: 28px; margin-left: 8px; color: var(--fg-light)">sticky_note_2</div>
|
<div class="icon" style="font-size: 28px; margin-left: 8px; color: var(--fg-light)">sticky_note_2</div>
|
||||||
|
|
|
@ -5,7 +5,8 @@ export let onfile = () => {};
|
||||||
export let textarea;
|
export let textarea;
|
||||||
export let placeholder;
|
export let placeholder;
|
||||||
export let input = "";
|
export let input = "";
|
||||||
$: rows = Math.min(input.split("\n").length, 10);
|
export let asdfasdfasdf = false; // i am very good at naming
|
||||||
|
$: rows = asdfasdfasdf ? 8 : Math.min(input.split("\n").length, 10);
|
||||||
|
|
||||||
function handleKeyDown(e) {
|
function handleKeyDown(e) {
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey) {
|
||||||
|
@ -40,7 +41,7 @@ function handleKeyDown(e) {
|
||||||
// case "u": return wrapInsert("__");
|
// case "u": return wrapInsert("__");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e.key !== "Enter" || e.shiftKey) return;
|
if (e.key !== "Enter" || (asdfasdfasdf ? !e.ctrlKey : e.shiftKey)) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
@ -57,13 +58,7 @@ function handlePaste(e) {
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
textarea {
|
textarea {
|
||||||
font: inherit;
|
|
||||||
color: inherit;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
resize: none;
|
resize: none;
|
||||||
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
}
|
}
|
||||||
|
@ -71,10 +66,14 @@ textarea {
|
||||||
textarea::placeholder {
|
textarea::placeholder {
|
||||||
color: var(--fg-dim);
|
color: var(--fg-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea.asdfasdfasdf {
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<textarea
|
<textarea
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{rows}
|
{rows}
|
||||||
|
class:asdfasdfasdf
|
||||||
bind:this={textarea}
|
bind:this={textarea}
|
||||||
bind:value={input}
|
bind:value={input}
|
||||||
on:keydown={handleKeyDown}
|
on:keydown={handleKeyDown}
|
||||||
|
|
|
@ -44,13 +44,12 @@ const showMemberContext = (member) => (e) => {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 2px 72px 4px;
|
padding: 2px 72px 4px;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
position: relative;
|
|
||||||
color: var(--fg-content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
color: var(--fg-content);
|
color: var(--fg-content);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.author {
|
.author {
|
||||||
|
|
|
@ -49,7 +49,7 @@ let eventPromise = room.events.fetch(eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
max-width: 100%;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -70,7 +70,7 @@ function checkShift(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHighlight(event, reply) {
|
function getHighlight(event, reply) {
|
||||||
if (reply?.eventId === event.id) return "var(--color-blue)";
|
if (reply?.id === event.id) return "var(--color-blue)";
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldRender(_type, _settings) {
|
function shouldRender(_type, _settings) {
|
||||||
|
@ -81,12 +81,19 @@ function shouldRender(_type, _settings) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldPing(event) {
|
function shouldPing(event) {
|
||||||
// FIXME: doesnt parse pings in muted rooms
|
|
||||||
const parsed = $pushRules.parse(event);
|
const parsed = $pushRules.parse(event);
|
||||||
if (!parsed) return false;
|
let highlight = false;
|
||||||
const highlight = parsed.actions.find(i => i.set_tweak === "highlight");
|
parsed.reverse();
|
||||||
if (!highlight) return false;
|
for (let { actions } of parsed) {
|
||||||
return highlight.value !== false;
|
const hl = actions.find(a => a.set_tweak === "highlight");
|
||||||
|
if (!hl) continue;
|
||||||
|
if (hl.value === false) {
|
||||||
|
highlight = false;
|
||||||
|
} else {
|
||||||
|
highlight = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return highlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(state.focusedRoom.subscribe(() => {
|
onDestroy(state.focusedRoom.subscribe(() => {
|
||||||
|
|
364
src/ui/room/rooms/Longform.svelte
Normal file
364
src/ui/room/rooms/Longform.svelte
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
<script>
|
||||||
|
import { onDestroy } from "svelte";
|
||||||
|
import Scroller from '../../molecules/Scroller.svelte';
|
||||||
|
import Upload from '../timeline/Upload.svelte';
|
||||||
|
import Placeholder from '../timeline/Placeholder.svelte';
|
||||||
|
import Create from "../events/Create.svelte";
|
||||||
|
import MessageReply from "../message/MessageReply.svelte";
|
||||||
|
import MessageContent from "../message/MessageContent.svelte";
|
||||||
|
import MessageEdit from "../message/MessageEdit.svelte";
|
||||||
|
import RoomInput from "../RoomInput.svelte";
|
||||||
|
import { formatDate } from "../../../util/format.ts";
|
||||||
|
import Avatar from "../../atoms/Avatar.svelte";
|
||||||
|
import Name from "../../atoms/Name.svelte";
|
||||||
|
import { memberContext } from "../../../util/context";
|
||||||
|
export let room;
|
||||||
|
export let slice;
|
||||||
|
let { focused, reply, edit, upload, input } = state.roomState;
|
||||||
|
let { pushRules, popout, context } = state;
|
||||||
|
let scrollTop, scrollMax, scrollTo, reset;
|
||||||
|
let textareaEl;
|
||||||
|
|
||||||
|
$: if (slice) refocus();
|
||||||
|
|
||||||
|
function getReply(content) {
|
||||||
|
return content["m.relates_to"]?.["m.in_reply_to"]?.event_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchBackwards() {
|
||||||
|
const success = await actions.slice.backwards();
|
||||||
|
return [!success || slice.events[0]?.type === "m.room.create", slice.atEnd()];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchForwards() {
|
||||||
|
const success = await actions.slice.forwards();
|
||||||
|
return [!success || slice.events[0]?.type === "m.room.create", slice.atEnd()];
|
||||||
|
}
|
||||||
|
|
||||||
|
function refocus() {
|
||||||
|
if (scrollTo && scrollTop > scrollMax - 16) {
|
||||||
|
queueMicrotask(() => scrollTo(-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let resizeTimeout;
|
||||||
|
function handleResize() {
|
||||||
|
clearTimeout(resizeTimeout);
|
||||||
|
resizeTimeout = setTimeout(refocus, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHighlight(event, reply) {
|
||||||
|
if (reply?.id === event.id) return "var(--color-blue)";
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPing(event) {
|
||||||
|
const parsed = $pushRules.parse(event);
|
||||||
|
let highlight = false;
|
||||||
|
parsed.reverse();
|
||||||
|
for (let { actions } of parsed) {
|
||||||
|
const hl = actions.find(a => a.set_tweak === "highlight");
|
||||||
|
if (!hl) continue;
|
||||||
|
if (hl.value === false) {
|
||||||
|
highlight = false;
|
||||||
|
} else {
|
||||||
|
highlight = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showMemberPopout = (event) => (e) => {
|
||||||
|
const rect = e.target.getBoundingClientRect();
|
||||||
|
const _owner = `${event.id}-${event.sender.id}`;
|
||||||
|
if ($popout._owner === _owner) return $popout = {};
|
||||||
|
$popout = {
|
||||||
|
id: "member",
|
||||||
|
member: event.sender,
|
||||||
|
animate: "right",
|
||||||
|
top: rect.y,
|
||||||
|
left: rect.x + rect.width + 8,
|
||||||
|
_owner,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const showMemberContext = (member) => (e) => {
|
||||||
|
$context = {
|
||||||
|
items: memberContext(member),
|
||||||
|
x: e.clientX,
|
||||||
|
y: e.clientY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(state.focusedRoom.subscribe(() => {
|
||||||
|
console.time("focus room")
|
||||||
|
queueMicrotask(() => console.timeEnd("focus room"));
|
||||||
|
}));
|
||||||
|
|
||||||
|
onDestroy(state.focusedRoom.subscribe(() => queueMicrotask(() => reset && reset())));
|
||||||
|
onDestroy(upload.subscribe(refocus));
|
||||||
|
onDestroy(reply.subscribe(refocus));
|
||||||
|
onDestroy(edit.subscribe(refocus));
|
||||||
|
$: if ($focused) {
|
||||||
|
const id = $focused;
|
||||||
|
const element = document.querySelector(`[data-event-id="${id}"]`);
|
||||||
|
if (element) {
|
||||||
|
queueMicrotask(() => element.scrollIntoView({ behavior: "smooth", block: "center" }));
|
||||||
|
setTimeout(() => id === $focused && focused.set(null), 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if ($edit) {
|
||||||
|
const element = document.querySelector(`[data-event-id="${$edit}"]`);
|
||||||
|
if (element) {
|
||||||
|
setTimeout(() => element.scrollIntoView({ behavior: "smooth", block: "center" }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (room) textareaEl?.focus();
|
||||||
|
$: if ($reply || true) textareaEl?.focus();
|
||||||
|
$: if (!$edit) textareaEl?.focus();
|
||||||
|
|
||||||
|
async function sendMessage(content) {
|
||||||
|
if ($reply) {
|
||||||
|
content["m.relates_to"] = {};
|
||||||
|
content["m.relates_to"]["m.in_reply_to"] = {};
|
||||||
|
content["m.relates_to"]["m.in_reply_to"]["event_id"] = $reply.id;
|
||||||
|
reply.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = `~${Math.random().toString(36).slice(2)}`;
|
||||||
|
room.accountData.set("m.fully_read", id);
|
||||||
|
room.sendEvent("m.room.message", content, id);
|
||||||
|
state.log.debug("send event to " + room.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRoomName(room) {
|
||||||
|
if (room.name) return room.name;
|
||||||
|
const other = state.dms.get(room.id);
|
||||||
|
return other?.name ?? other?.id;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: end;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tall {
|
||||||
|
display: flex;
|
||||||
|
min-height: 1200px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight::before, .highlight::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight::after {
|
||||||
|
width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight::before {
|
||||||
|
width: 100%;
|
||||||
|
opacity: .2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editing {
|
||||||
|
position: relative;
|
||||||
|
background: rgba(4,4,5,0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.focused {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping::before, .ping::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--event-ping);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping::before {
|
||||||
|
width: 100%;
|
||||||
|
opacity: .1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping::after {
|
||||||
|
width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focused {
|
||||||
|
background: var(--event-focus-bg);
|
||||||
|
animation: unfocus 1s 1s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes unfocus {
|
||||||
|
100% { background: none }
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 14px 16px;
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--bg-rooms-members);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
box-shadow: 0 0 0 #00000022;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: box-shadow .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar:hover, .avatar.selected {
|
||||||
|
box-shadow: 0 4px 4px #00000022;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 4px;
|
||||||
|
bottom: 2px;
|
||||||
|
|
||||||
|
color: var(--fg-notice);
|
||||||
|
background: var(--color-accent);
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
time {
|
||||||
|
color: var(--fg-muted);
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: var(--font-display);
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--fg-content);
|
||||||
|
flex: 1;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="content">
|
||||||
|
<Scroller
|
||||||
|
items={slice.events}
|
||||||
|
itemKey="id"
|
||||||
|
direction="up"
|
||||||
|
bind:scrollTop={scrollTop}
|
||||||
|
bind:scrollMax={scrollMax}
|
||||||
|
bind:scrollTo={scrollTo}
|
||||||
|
bind:reset={reset}
|
||||||
|
let:data={event}
|
||||||
|
{fetchBackwards}
|
||||||
|
{fetchForwards}
|
||||||
|
getDefault={() => [slice.events[0]?.type === "m.room.create", slice.atEnd()]}
|
||||||
|
>
|
||||||
|
<div slot="top" style="margin-top: auto"></div>
|
||||||
|
<div slot="placeholder-start" class="tall" style="align-items: end"><Placeholder /></div>
|
||||||
|
<div>
|
||||||
|
{#if event.type === "m.room.message"}
|
||||||
|
<div
|
||||||
|
class="message"
|
||||||
|
class:ping={shouldPing(event)}
|
||||||
|
class:focused={$focused === event.id}
|
||||||
|
class:editing={$edit === event.id}
|
||||||
|
data-event-id={event.id}
|
||||||
|
class:highlight={getHighlight(event, $reply)}
|
||||||
|
style:--color={getHighlight(event, $reply)}
|
||||||
|
on:contextmenu={e => { const memberId = e.target.dataset.mxPing; if (memberId && room.members.has(memberId)) { e.preventDefault(); e.stopPropagation(); showMemberContext(room.members.get(memberId)) }}}
|
||||||
|
>
|
||||||
|
<div class="header">
|
||||||
|
{#if getReply(event.content)}<MessageReply {room} eventId={getReply(event.content)} />{/if}
|
||||||
|
<div
|
||||||
|
class="avatar"
|
||||||
|
class:selected={$popout._owner === `${event.id}-${event.sender.id}`}
|
||||||
|
on:click|stopPropagation={showMemberPopout(event)}
|
||||||
|
on:contextmenu|preventDefault|stopPropagation={showMemberContext(event.sender)}
|
||||||
|
>
|
||||||
|
<Avatar user={event.sender} size={40} />
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; flex-direction: column; margin-left: 8px;">
|
||||||
|
<div class="author">
|
||||||
|
<Name member={event.sender} />
|
||||||
|
{#if event.content.msgtype === "m.notice"}
|
||||||
|
<div class="badge">bot</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<time datetime={event.date.toISOString()} style="display: inline">{formatDate(event.date)}</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="message-content">
|
||||||
|
{#if event.id === $edit}
|
||||||
|
<MessageEdit {event} />
|
||||||
|
{:else}
|
||||||
|
<MessageContent {event} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if event.type === "m.room.create"}
|
||||||
|
<Create {room} {event} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div slot="placeholder-end" class="tall"><Placeholder /></div>
|
||||||
|
<div slot="bottom" class="spacer">
|
||||||
|
{#if $upload}<Upload upload={$upload} />{/if}
|
||||||
|
<div style="padding: 0 16px; padding-bottom: 14px;">
|
||||||
|
<RoomInput
|
||||||
|
asdfasdfasdf
|
||||||
|
showUpload
|
||||||
|
placeholder={`Message ${getRoomName(room)}`}
|
||||||
|
onsend={sendMessage}
|
||||||
|
bind:input={$input}
|
||||||
|
bind:reply={$reply}
|
||||||
|
bind:textarea={textareaEl}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Scroller>
|
||||||
|
</div>
|
||||||
|
<svelte:window on:resize={handleResize} />
|
|
@ -14,5 +14,9 @@ let settings = state.settings;
|
||||||
</style>
|
</style>
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<div>Automatically join suggested rooms</div>
|
<div>Automatically join suggested rooms</div>
|
||||||
<Toggle checked={$settings.get("autojoin")} toggled={(val) => $settings.put("autojoin", val)} />
|
<Toggle checked={$settings.get("autospace")} toggled={(val) => $settings.put("autospace", val)} />
|
||||||
|
</div>
|
||||||
|
<div class="setting">
|
||||||
|
<div>Automatically accept dms</div>
|
||||||
|
<Toggle checked={$settings.get("autodm")} toggled={(val) => $settings.put("autodm", val)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Event, Room, Member } from "discount";
|
import type { Event, Room, Member } from "discount.js";
|
||||||
import { getRoomNotifRule, putRoomNotifRule } from "../client/matrix/notifications";
|
import { getRoomNotifRule, putRoomNotifRule } from "../client/matrix/notifications";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import * as notif from "../client/matrix/notifications";
|
import * as notif from "../client/matrix/notifications";
|
||||||
|
|
|
@ -102,6 +102,14 @@ function matchesRule(cond, event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// function matchBuiltinRule(ruleId, event) {
|
||||||
|
// switch (ruleId) {
|
||||||
|
// case ".m.rule.master": return true;
|
||||||
|
// case ".m.rule.suppress_notices": return event.;
|
||||||
|
// default: return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
export default class PushRules {
|
export default class PushRules {
|
||||||
constructor(rules) {
|
constructor(rules) {
|
||||||
this.setRules(rules);
|
this.setRules(rules);
|
||||||
|
@ -119,7 +127,8 @@ export default class PushRules {
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(event) {
|
parse(event) {
|
||||||
if (event.sender.userId === state.userId) return null;
|
if (event.sender.userId === state.userId) return [];
|
||||||
|
const rules = [];
|
||||||
for (let rule of this.rules) {
|
for (let rule of this.rules) {
|
||||||
let passed = true;
|
let passed = true;
|
||||||
for (let cond of rule.conditions) {
|
for (let cond of rule.conditions) {
|
||||||
|
@ -128,8 +137,8 @@ export default class PushRules {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (passed) return rule;
|
if (passed) rules.push(rule);
|
||||||
}
|
}
|
||||||
return null;
|
return rules;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// import type { Timeline } from "discount";
|
// import type { Timeline } from "discount.js";
|
||||||
// import type { Event } from "discount";
|
// import type { Event } from "discount.js";
|
||||||
|
|
||||||
// export function getLastEvent(timeline: Array<Event>): Event | null {
|
// export function getLastEvent(timeline: Array<Event>): Event | null {
|
||||||
// for (let i = timeline.length - 1; i >= 0; i--) {
|
// for (let i = timeline.length - 1; i >= 0; i--) {
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
"extends": "@tsconfig/svelte",
|
"extends": "@tsconfig/svelte",
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules/*"],
|
"exclude": ["node_modules/*"],
|
||||||
"target": "es2022",
|
"compilerOptions": {
|
||||||
|
"target": "es2022",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,4 +35,9 @@ export default defineConfig({
|
||||||
commit: execSync("git log -n 1 --oneline HEAD").toString().match(/[a-z0-9]+/)[0],
|
commit: execSync("git log -n 1 --oneline HEAD").toString().match(/[a-z0-9]+/)[0],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
server: {
|
||||||
|
hmr: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
Reference in a new issue