random stuff

This commit is contained in:
tezlm 2022-11-08 00:01:16 -08:00
parent 0000149de1
commit 00001504db
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
34 changed files with 483 additions and 137 deletions

View file

@ -29,6 +29,7 @@
"@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",
"discount": "link:../discount",
"discount.js": "link:../discount",
"emojibase": "^6.1.0",
"emojibase-data": "^7.0.1",
"fuzzysort": "^2.0.3",

View file

@ -13,6 +13,7 @@ specifiers:
'@types/better-sqlite3': ^7.6.2
'@types/marked': ^4.0.7
discount: link:../discount
discount.js: link:../discount
emojibase: ^6.1.0
emojibase-data: ^7.0.1
fuzzysort: ^2.0.3
@ -38,6 +39,7 @@ dependencies:
'@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'
discount: link:../discount
discount.js: link:../discount
emojibase: 6.1.0
emojibase-data: 7.0.1_emojibase@6.1.0
fuzzysort: 2.0.3

View file

@ -1,7 +1,7 @@
import Api from "../matrix/api.js";
import Settings from "../matrix/settings.js";
import PushRules from "../../util/push.js";
import { Client, persist } from "discount";
import { Client, persist } from "discount.js";
const defaultFilter = {
room: {

View file

@ -1,5 +1,5 @@
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 }) {
if (event.type === "m.fully_read") {

View file

@ -1,5 +1,4 @@
import type { Event } from "discount";
import type { Room } from "discount";
import type { Room, Event } from "discount.js";
export function reslice(room: Room, force = false) {
if (!room.events.live) return;

View file

@ -1,4 +1,4 @@
import type { Room } from "discount";
import type { Room } from "discount.js";
interface Notifications {
level: "default" | "all" | "mentions" | "muted",

View file

@ -17,8 +17,11 @@ defaultSettings.set("sendtyping", true);
defaultSettings.set("autocomplete", true);
defaultSettings.set("showembeds", "unencrypted");
// rooms and spaces
defaultSettings.set("autospace", false);
defaultSettings.set("autodm", false);
// misc
defaultSettings.set("autojoin", true);
export default class Settings extends Map {
constructor(data) {

View file

@ -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) {
}
}

View file

@ -1,5 +1,5 @@
import { handle } from "../actions/timeline";
import { Event, StateEvent } from "discount";
import { Event, StateEvent } from "discount.js";
class Timeline extends Array {
constructor(roomId, start, end) {

View file

@ -85,7 +85,7 @@ summary {
cursor: pointer;
}
button, input[type=text], input[type=password] {
button, input[type=text], input[type=password], textarea {
outline: none;
border: none;
background: none;

View file

@ -20,6 +20,7 @@
.hljs-regexp { color: #95e6cb }
.hljs-regex-delimiter { color: #95d8e5 }
.hljs-type { color: #ffb454 }
.hljs-type { color: #f29668 }
.hljs-type { color: #ff8f40 }

View file

@ -74,7 +74,7 @@ function select(option) {
transform: scale(1.2);
}
</style>
<div class="container">
<div class="container" role="listbox">
<!-- FIXME: close on blur -->
<div class="dropdown" class:show on:click={() => show = !show} bind:clientWidth={width}>
{options.find(i => i[1] === selected)?.[0]}
@ -83,12 +83,18 @@ function select(option) {
{#if show}
<div class="options" style:width={width + "px"}>
{#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">
{option[0]}
</div>
{#if option[1] === selected}
<div class="checkmark icon">check_circle</div>
<div class="checkmark icon" aria-hidden="true">check_circle</div>
{/if}
</div>
{/each}

View file

@ -94,8 +94,20 @@ function opacity() {
transition: color 0.4s;
}
</style>
<div class="background" on:mousedown|stopPropagation={closePopup} transition:opacity on:click|stopPropagation>
<div class="card" on:mousedown|stopPropagation class:raw transition:card role="dialog" aria-modal="true">
<div
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}
<button class="icon close" on:click={closePopup}>close</button>
{/if}

View file

@ -1,6 +1,6 @@
<script lang="ts">
export let checked = false;
export let toggled = () => {};
export let toggled = (_checked: boolean) => {};
</script>
<style>
.toggle {

View file

@ -1,5 +1,5 @@
<script type="ts">
import type { Room } from "discount";
import type { Room } from "discount.js";
export let room: Room | null = null;
export let users: Array<string>;
$: [single, plural, typing] = getType(room?.name);

View file

@ -2,7 +2,7 @@
import Avatar from "../atoms/Avatar.svelte";
import { memberContext, roomContext } from "../../util/context";
import { fastclick } from "../../util/use";
import { Room, Member } from "discount";
import { Room, Member } from "discount.js";
export let member;
let { popup, popout, context } = state;
$: isRoomPing = member instanceof Room;

View file

@ -30,7 +30,6 @@ export let size = 0;
padding: 0 1em;
display: flex;
flex-direction: column;
gap: 8px;
overflow: hidden;
}

View file

@ -4,7 +4,7 @@ import Button from "../atoms/Button.svelte";
import Popup from "../atoms/Popup.svelte";
import Search from "../atoms/Search.svelte";
import Checkbox from "../atoms/Checkbox.svelte";
import type { Room } from "discount";
import type { Room } from "discount.js";
// export const confirm = () => {};
export let current;
let checked = {};

View file

@ -8,13 +8,16 @@ export const confirm = kick;
export let current;
let reason;
const rnd = (arr) => arr[Math.floor(Math.random() * arr.length)];
const placeholders = [
"they disagree with my opinion",
"i dont like them",
"they were acting sussy",
];
function getPlaceholder() {
return placeholders[Math.floor(Math.random() * placeholders.length)];
}
const scopes = [];
if (current.room.type === "m.space") {
scopes.push(["This space", "space"]);
@ -43,7 +46,7 @@ function kick() {
<Dropdown options={scopes} />
{/if}
<div class="title">Reason for Kick</div>
<Textarea autofocus placeholder={rnd(placeholders)} bind:value={reason} />
<Textarea autofocus placeholder={getPlaceholder()} bind:value={reason} />
</div>
<div slot="footer">
<Button type="link" label="Nevermind!" clicked={() => state.popup.set({ ...current, id: null })} />

View file

@ -3,7 +3,7 @@ import fuzzysort from "fuzzysort";
import Search from "../atoms/Search.svelte";
import Popup from "../atoms/Popup.svelte";
import { getLastMessage } from "../../util/timeline";
import type { Room } from "discount";
import type { Room } from "discount.js";
let search = "";
let highlighted = 0;
$: results = getRooms(search);

View file

@ -13,6 +13,7 @@ import UnknownRoom from './rooms/Unknown.svelte';
// hmmm...
import MediaRoom from './rooms/Media.svelte';
import ForumRoom from './rooms/Forum.svelte';
import LongformRoom from './rooms/Longform.svelte';
let { focusedRoom: room, focusedSpace: space, navRooms, roomState, slice, settings } = state;
let { search } = roomState;
@ -56,6 +57,8 @@ $: if ($navRooms) room = state.focusedRoom;
<MediaRoom room={$room} slice={$slice} />
{:else if type === "org.eu.celery.room.forum"}
<ForumRoom room={$room} />
{:else if type === "org.eu.celery.room.longform"}
<LongformRoom room={$room} slice={$slice} />
{:else}
<UnknownRoom room={$room} />
{/if}

View file

@ -1,7 +1,7 @@
<script>
import Typing from "../atoms/Typing.svelte";
import RoomInput from "./RoomInput.svelte";
let textarea;
let textareaEl;
let room = state.focusedRoom;
let { reply, edit, input, typing } = state.roomState;
@ -27,10 +27,10 @@ function getRoomName(room) {
return other?.name ?? other?.id;
}
$: if ($input) textarea?.focus();
$: if ($room) textarea?.focus();
$: if ($reply || true) textarea?.focus();
$: if (!$edit) textarea?.focus();
$: if ($input) textareaEl?.focus();
$: if ($room) textareaEl?.focus();
$: if ($reply || true) textareaEl?.focus();
$: if (!$edit) textareaEl?.focus();
</script>
<style>
.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>
{:else}
<RoomInput
showUpload={true}
showUpload
placeholder={`Message ${getRoomName($room)}`}
onsend={sendMessage}
bind:input={$input}
bind:reply={$reply}
bind:textarea={textarea}
bind:textarea={textareaEl}
/>
{/if}
<div class="typing">

View file

@ -15,6 +15,8 @@ export let input = "";
export let textarea;
let showEmoji = false;
export let asdfasdfasdf = false;
let { focusedRoom: room, focusedSpace, slice, popup } = state;
let { edit } = state.roomState;
@ -144,6 +146,7 @@ async function handleUpload(file) {
mimetype: file.type,
size: file.size,
...(["m.image", "m.video"].includes(type) ? await getSize(file, type) : {}),
filename: file.name,
};
onsend({
@ -237,11 +240,16 @@ function slide() {
border-top-right-radius: 0;
}
.input.asdfasdfasdf {
border-radius: 4px;
background: var(--bg-misc);
}
.upload {
display: flex;
justify-content: center;
align-items: center;
padding: 0 16px;
align-items: start;
padding: 8px 16px 0;
font-size: 24px;
color: var(--fg-light);
cursor: pointer;
@ -310,14 +318,14 @@ function slide() {
onclose={() => reply = null}
/>
{/if}
<div class="input" class:withreply={reply}>
<div class="input" class:asdfasdfasdf class:withreply={reply}>
{#if showUpload}
<label class="upload">
<div class="icon">add_circle</div>
<input type="file" on:change={e => onfile(e.target.files[0])} />
</label>
{/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; margin-left: 8px; color: var(--fg-light)">sticky_note_2</div>

View file

@ -5,7 +5,8 @@ export let onfile = () => {};
export let textarea;
export let placeholder;
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) {
if (e.ctrlKey) {
@ -40,7 +41,7 @@ function handleKeyDown(e) {
// case "u": return wrapInsert("__");
}
}
if (e.key !== "Enter" || e.shiftKey) return;
if (e.key !== "Enter" || (asdfasdfasdf ? !e.ctrlKey : e.shiftKey)) return;
e.preventDefault();
e.stopImmediatePropagation();
@ -57,13 +58,7 @@ function handlePaste(e) {
</script>
<style>
textarea {
font: inherit;
color: inherit;
background: none;
border: none;
outline: none;
resize: none;
flex: 1;
padding: 12px 0;
}
@ -71,10 +66,14 @@ textarea {
textarea::placeholder {
color: var(--fg-dim);
}
textarea.asdfasdfasdf {
}
</style>
<textarea
{placeholder}
{rows}
class:asdfasdfasdf
bind:this={textarea}
bind:value={input}
on:keydown={handleKeyDown}

View file

@ -44,13 +44,12 @@ const showMemberContext = (member) => (e) => {
display: flex;
padding: 2px 72px 4px;
padding-left: 0;
position: relative;
color: var(--fg-content);
}
.content {
color: var(--fg-content);
flex: 1;
max-width: 100%;
}
.author {

View file

@ -49,7 +49,7 @@ let eventPromise = room.events.fetch(eventId);
}
.content {
max-width: 100%;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;

View file

@ -70,7 +70,7 @@ function checkShift(e) {
}
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) {
@ -81,12 +81,19 @@ function shouldRender(_type, _settings) {
}
function shouldPing(event) {
// FIXME: doesnt parse pings in muted rooms
const parsed = $pushRules.parse(event);
if (!parsed) return false;
const highlight = parsed.actions.find(i => i.set_tweak === "highlight");
if (!highlight) return false;
return highlight.value !== false;
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;
}
onDestroy(state.focusedRoom.subscribe(() => {

View 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} />

View file

@ -14,5 +14,9 @@ let settings = state.settings;
</style>
<div class="setting">
<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>

View file

@ -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 { get } from "svelte/store";
import * as notif from "../client/matrix/notifications";

View file

@ -102,6 +102,14 @@ function matchesRule(cond, event) {
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 {
constructor(rules) {
this.setRules(rules);
@ -119,7 +127,8 @@ export default class PushRules {
}
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) {
let passed = true;
for (let cond of rule.conditions) {
@ -128,8 +137,8 @@ export default class PushRules {
break;
}
}
if (passed) return rule;
if (passed) rules.push(rule);
}
return null;
return rules;
}
}

View file

@ -1,5 +1,5 @@
// import type { Timeline } from "discount";
// import type { Event } from "discount";
// import type { Timeline } from "discount.js";
// import type { Event } from "discount.js";
// export function getLastEvent(timeline: Array<Event>): Event | null {
// for (let i = timeline.length - 1; i >= 0; i--) {

View file

@ -2,5 +2,7 @@
"extends": "@tsconfig/svelte",
"include": ["src/**/*"],
"exclude": ["node_modules/*"],
"target": "es2022",
"compilerOptions": {
"target": "es2022",
}
}

View file

@ -35,4 +35,9 @@ export default defineConfig({
commit: execSync("git log -n 1 --oneline HEAD").toString().match(/[a-z0-9]+/)[0],
}
},
server: {
hmr: {
}
}
});