266 lines
7.2 KiB
JavaScript
266 lines
7.2 KiB
JavaScript
import { writable } from "svelte/store";
|
|
import gateway, { GatewayEventType } from "./gateway";
|
|
import logging from "./logging";
|
|
import request from "./request";
|
|
import { apiRoute } from "./storage";
|
|
|
|
const storeLog = logging.logger("Store");
|
|
|
|
class Store {
|
|
constructor(value=null) {
|
|
this._handlers = [];
|
|
this.value = value;
|
|
}
|
|
|
|
subscribe(handler) {
|
|
const newLength = this._handlers.push(handler);
|
|
const handlerIndex = newLength - 1;
|
|
handler(this.value);
|
|
storeLog("Subscription initialized with value", this.value);
|
|
return () => {
|
|
this._handlers.splice(handlerIndex, 1);
|
|
};
|
|
}
|
|
|
|
updated() {
|
|
storeLog(`updated(): calling ${this._handlers.length} handlers - value changed`, this.value);
|
|
this._handlers.forEach(e => {
|
|
e(this.value);
|
|
});
|
|
}
|
|
}
|
|
|
|
class ChannelsStore extends Store {
|
|
constructor() {
|
|
super(gateway.channels || []);
|
|
|
|
gateway.subscribe(GatewayEventType.Ready, ({ channels }) => {
|
|
this.value = channels;
|
|
this.updated();
|
|
});
|
|
gateway.subscribe(GatewayEventType.ChannelCreate, (channel) => {
|
|
this.value.push(channel);
|
|
this.updated();
|
|
});
|
|
gateway.subscribe(GatewayEventType.ChannelDelete, ({ id }) => {
|
|
const index = this.value.findIndex(e => e.id === id);
|
|
if (index === -1)
|
|
return;
|
|
|
|
this.value.splice(index, 1);
|
|
this.updated();
|
|
});
|
|
gateway.subscribe(GatewayEventType.ChannelUpdate, (data) => {
|
|
const index = this.value.findIndex(e => e.id === data.id);
|
|
if (index === -1)
|
|
return;
|
|
if (!this.value[index])
|
|
return;
|
|
|
|
this.value[index] = data;
|
|
this.updated();
|
|
});
|
|
}
|
|
}
|
|
|
|
class GatewayStatusStore extends Store {
|
|
constructor() {
|
|
super({ open: gateway.open, ready: gateway.authenticated });
|
|
|
|
gateway.subscribe(GatewayEventType.Open, () => {
|
|
this.value.open = true;
|
|
this.updated();
|
|
});
|
|
|
|
gateway.subscribe(GatewayEventType.Close, () => {
|
|
this.value.open = false;
|
|
this.value.ready = false;
|
|
this.updated();
|
|
});
|
|
|
|
gateway.subscribe(GatewayEventType.Ready, () => {
|
|
this.value.ready = true;
|
|
this.updated();
|
|
});
|
|
}
|
|
}
|
|
|
|
class UserInfoStore extends Store {
|
|
constructor() {
|
|
super(null);
|
|
|
|
gateway.subscribe(GatewayEventType.Ready, ({ user }) => {
|
|
this.value = user;
|
|
this.updated();
|
|
});
|
|
}
|
|
}
|
|
|
|
class MessageStore extends Store {
|
|
constructor(channelId) {
|
|
super([]);
|
|
this.channelId = channelId;
|
|
this.isCollectingOldMessages = true;
|
|
this.didDoInitialLoad = false;
|
|
}
|
|
|
|
setMessage(id, message) {
|
|
const index = this.value.findIndex(e => e.id === id);
|
|
if (index === -1)
|
|
return;
|
|
|
|
this.value[index] = message;
|
|
|
|
this.updated();
|
|
}
|
|
|
|
addMessage(message) {
|
|
this.value.push(message);
|
|
// only dispatch update if collectOldMessages didn't
|
|
if (!this.collectOldMessages()) {
|
|
this.updated();
|
|
}
|
|
}
|
|
|
|
updateId(oldId, newId) {
|
|
const index = this.value.findIndex(e => e.id === oldId);
|
|
if (index === -1)
|
|
return;
|
|
|
|
this.value[index].id = newId;
|
|
|
|
this.updated();
|
|
}
|
|
|
|
updateMessage(message) {
|
|
const index = this.value.findIndex(e => e.id === message.id);
|
|
if (index === -1)
|
|
return;
|
|
|
|
this.value[index] = message;
|
|
|
|
this.updated();
|
|
}
|
|
|
|
deleteMessage({ id }) {
|
|
const index = this.value.findIndex(e => e.id === id);
|
|
if (index === -1)
|
|
return;
|
|
|
|
this.value.splice(index, 1);
|
|
|
|
this.updated();
|
|
}
|
|
|
|
collectOldMessages() {
|
|
if (!this.isCollectingOldMessages)
|
|
return false;
|
|
|
|
const target = 50;
|
|
const delta = this.value.length - target;
|
|
if (delta >= 1) {
|
|
this.value.splice(0, delta);
|
|
this.updated();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
setIsCollectingOldMessages(isCollectingOldMessages) {
|
|
this.isCollectingOldMessages = isCollectingOldMessages;
|
|
this.collectOldMessages();
|
|
}
|
|
|
|
async loadOlderMessages(beforeCommitToStore=null) {
|
|
const oldestMessage = this.value[0];
|
|
const endpoint = oldestMessage ? `channels/${this.channelId}/messages/?before=${oldestMessage.id}` : `channels/${this.channelId}/messages`;
|
|
const res = await request("GET", apiRoute(endpoint), true, null);
|
|
if (res.success && res.ok && res.json) {
|
|
if (res.json.length < 1)
|
|
return;
|
|
if (beforeCommitToStore)
|
|
beforeCommitToStore(res.json);
|
|
res.json.reverse();
|
|
this.value = res.json.concat(this.value);
|
|
this.updated();
|
|
} else {
|
|
overlayStore.open("toast", {
|
|
message: "Messages failed to load"
|
|
});
|
|
}
|
|
}
|
|
|
|
async doInitialLoad() {
|
|
await this.loadOlderMessages();
|
|
this.didDoInitialLoad = true;
|
|
}
|
|
}
|
|
|
|
class MessagesStoreProvider {
|
|
constructor() {
|
|
this.storeByChannel = new Map();
|
|
|
|
gateway.subscribe(GatewayEventType.MessageCreate, (message) => {
|
|
// we currently don't care about our own messages
|
|
if (gateway.user && message.author_id === gateway.user.id)
|
|
return;
|
|
|
|
const store = this.getStoreOrNull(message.channel_id);
|
|
if (store)
|
|
store.addMessage(message);
|
|
});
|
|
gateway.subscribe(GatewayEventType.MessageUpdate, (message) => {
|
|
const store = this.getStoreOrNull(message.channel_id);
|
|
if (store)
|
|
store.updateMessage(message);
|
|
});
|
|
gateway.subscribe(GatewayEventType.MessageDelete, (message) => {
|
|
const store = this.getStoreOrNull(message.channel_id);
|
|
if (store)
|
|
store.deleteMessage(message);
|
|
});
|
|
}
|
|
|
|
getStoreOrNull(channelId) {
|
|
return this.storeByChannel.get(channelId);
|
|
}
|
|
|
|
getStore(channelId) {
|
|
if (!this.storeByChannel.get(channelId)) {
|
|
const store = new MessageStore(channelId);
|
|
store.doInitialLoad();
|
|
this.storeByChannel.set(channelId, store);
|
|
}
|
|
return this.storeByChannel.get(channelId);
|
|
}
|
|
}
|
|
|
|
class OverlayStore extends Store {
|
|
constructor() {
|
|
super({
|
|
createChannel: null,
|
|
editChannel: null,
|
|
toast: null,
|
|
login: null,
|
|
createAccount: null
|
|
});
|
|
}
|
|
|
|
open(name, props={}) {
|
|
this.value[name] = props;
|
|
this.updated();
|
|
}
|
|
|
|
close(name) {
|
|
this.value[name] = null;
|
|
this.updated();
|
|
}
|
|
}
|
|
|
|
export const channels = new ChannelsStore();
|
|
export const gatewayStatus = new GatewayStatusStore();
|
|
export const messagesStoreProvider = new MessagesStoreProvider();
|
|
export const userInfoStore = new UserInfoStore();
|
|
export const overlayStore = new OverlayStore();
|
|
export const selectedChannel = writable({ id: -1, name: "none", creator_id: -1 });
|