Compare commits

...

2 commits

Author SHA1 Message Date
hippoz
4d336e4a26
add store "pipe" method to modify data 2022-09-28 16:54:37 +03:00
hippoz
d14b0c4282
add action system and move message sending to it 2022-09-28 16:45:16 +03:00
3 changed files with 73 additions and 38 deletions

View file

@ -13,7 +13,7 @@
replyString += `@${message.author_username}: `; replyString += `@${message.author_username}: `;
setMessageInputEvent.update(replyString); setMessageInputEvent.emit(replyString);
}; };
</script> </script>

View file

@ -2,7 +2,7 @@
import { onDestroy, onMount } from "svelte"; import { onDestroy, onMount } from "svelte";
import request from "../request"; import request from "../request";
import { apiRoute, getItem } from "../storage"; import { apiRoute, getItem } from "../storage";
import { messagesStoreProvider, overlayStore, selectedChannel, setMessageInputEvent, smallViewport, typingStore, userInfoStore } from "../stores"; import { messagesStoreProvider, overlayStore, selectedChannel, sendMessageAction, setMessageInputEvent, smallViewport, typingStore, userInfoStore } from "../stores";
export let channel; export let channel;
let messageInput = ""; let messageInput = "";
@ -41,37 +41,11 @@
const sendMessage = async () => { const sendMessage = async () => {
messageTextarea.focus(); messageTextarea.focus();
sendMessageAction.emit({
if (messageInput.trim() === "" || !$userInfoStore) channelId: channel.id,
return; content: messageInput
// optimistically add message to store
const optimisticMessageId = Math.floor(Math.random() * 999999);
const optimisticMessage = {
id: optimisticMessageId,
content: messageInput,
channel_id: channel.id,
author_id: $userInfoStore.id,
author_username: $userInfoStore.username,
created_at: Date.now().toString(),
_isPending: true
};
messages.addMessage(optimisticMessage);
messageInput = "";
const res = await request("POST", apiRoute(`channels/${channel.id}/messages`), true, {
content: optimisticMessage.content,
optimistic_id: optimisticMessageId
}); });
messageInput = "";
if (res.success && res.ok) {
messages.setMessage(optimisticMessageId, res.json);
} else {
messages.deleteMessage({
id: optimisticMessageId
});
overlayStore.toast("Couldn't send message");
}
}; };
const onKeydown = async (e) => { const onKeydown = async (e) => {
@ -100,10 +74,10 @@
} }
}; };
onMount(focusTextarea); onMount(focusTextarea);
unsubscribers.push(selectedChannel.watch(focusTextarea)); unsubscribers.push(selectedChannel.on(focusTextarea));
// Handle the setMessageInput event // Handle the setMessageInput event
unsubscribers.push(setMessageInputEvent.watch((value) => { unsubscribers.push(setMessageInputEvent.on((value) => {
messageInput = value; messageInput = value;
if (messageTextarea) { if (messageTextarea) {
messageTextarea.focus(); messageTextarea.focus();

View file

@ -10,6 +10,7 @@ let storeCallbackQueue = [];
class Store { class Store {
constructor(value=null, name="[no name]") { constructor(value=null, name="[no name]") {
this._handlers = new Set(); this._handlers = new Set();
this._pipes = new Set();
this.value = value; this.value = value;
this.name = name; this.name = name;
this._isInBatch = false; this._isInBatch = false;
@ -20,7 +21,7 @@ class Store {
} }
// like subscribe, but without initially calling the handler // like subscribe, but without initially calling the handler
watch(handler) { on(handler) {
storeLog(`[Watch] (${this.name})`, "handler:", handler); storeLog(`[Watch] (${this.name})`, "handler:", handler);
this._handlers.add(handler); this._handlers.add(handler);
@ -42,6 +43,16 @@ class Store {
}; };
} }
pipe(handler) {
storeLog(`[Pipe] (${this.name})`, "handler:", handler);
this._pipes.add(handler);
return () => {
storeLog(`[Remove Pipe] (${this.name})`);
this._pipes.delete(handler);
};
}
set(value) { set(value) {
if (value === this.value) if (value === this.value)
return; return;
@ -51,7 +62,7 @@ class Store {
} }
// like set(), but without checking if the value is the same // like set(), but without checking if the value is the same
update(value) { emit(value) {
this.value = value; this.value = value;
this.updated(); this.updated();
} }
@ -87,6 +98,8 @@ class Store {
return; return;
} }
this.value = this._applyPipes(this.value);
storeLog(`[Update] (${this.name}) Will queue ${this._handlers.size} handlers`, "value:", this.value, "handlers:", this._handlers); storeLog(`[Update] (${this.name}) Will queue ${this._handlers.size} handlers`, "value:", this.value, "handlers:", this._handlers);
const isRootNode = storeCallbackQueue.length === 0; const isRootNode = storeCallbackQueue.length === 0;
@ -98,12 +111,25 @@ class Store {
this.flushCallbackQueue(); this.flushCallbackQueue();
} }
} }
_applyPipes(value) {
this._pipes.forEach(p => {
value = p(value);
});
return value;
}
}
function createAction(name, handler) {
const store = new Store(null, name);
store.on(handler);
return store;
} }
class StorageItemStore extends Store { class StorageItemStore extends Store {
constructor(key) { constructor(key) {
super(getItem(key), `StorageItemStore[key=${key}]`); super(getItem(key), `StorageItemStore[key=${key}]`);
this.watch(e => setItem(key, e)); this.on(e => setItem(key, e));
} }
} }
@ -637,7 +663,40 @@ export const typingStore = new TypingStore();
export const presenceStore = new PresenceStore(); export const presenceStore = new PresenceStore();
export const unreadStore = new UnreadStore(); export const unreadStore = new UnreadStore();
export const pluginStore = new PluginStore(); export const pluginStore = new PluginStore();
export const setMessageInputEvent = new Store(null, "event:setMessageInput"); export const setMessageInputEvent = new Store(null, "event:setMessageInput");
export const sendMessageAction = createAction("sendMessageAction", async ({channelId, content}) => {
if (content.trim() === "" || !userInfoStore.value)
return;
// optimistically add message to store
const optimisticMessageId = Math.floor(Math.random() * 999999);
const optimisticMessage = {
id: optimisticMessageId,
content: content,
channel_id: channelId,
author_id: userInfoStore.value.id,
author_username: userInfoStore.value.username,
created_at: Date.now().toString(),
_isPending: true
};
const messagesStoreForChannel = messagesStoreProvider.getStore(channelId);
messagesStoreForChannel.addMessage(optimisticMessage);
const res = await request("POST", apiRoute(`channels/${channelId}/messages`), true, {
content: optimisticMessage.content,
optimistic_id: optimisticMessageId
});
if (res.success && res.ok) {
messagesStoreForChannel.setMessage(optimisticMessageId, res.json);
} else {
messagesStoreForChannel.deleteMessage({
id: optimisticMessageId
});
overlayStore.toast("Couldn't send message");
}
});
export const allStores = { export const allStores = {
selectedChannel, selectedChannel,
@ -655,10 +714,12 @@ export const allStores = {
typingStore, typingStore,
presenceStore, presenceStore,
unreadStore, unreadStore,
pluginStore,
setMessageInputEvent, setMessageInputEvent,
sendMessageAction,
}; };
selectedChannel.watch((newSelectedChannel) => { selectedChannel.on((newSelectedChannel) => {
if (getItem("ui:stateful:presistSelectedChannel")) { if (getItem("ui:stateful:presistSelectedChannel")) {
setItem("state:openChannelId", newSelectedChannel.id); setItem("state:openChannelId", newSelectedChannel.id);
} }