1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
import { DateTime } from 'luxon';
import { get } from 'svelte/store';
import { STORE_KEY_CHANNELS_DATA, EPOCH_STRING } from '$lib/constants';
export class Channels {
channels = $state([]);
constructor({ channelsMetaList }) {
// This is the state wrapper around the channels meta object. Dammit.
this.channelsMetaList = channelsMetaList;
}
getChannel(channelId) {
return this.channels.filter((ch) => ch.id === channelId)[0] || null;
}
setChannels(channels) {
// Because this is called at initialization, we need to initialize the matching meta:
get(this.channelsMetaList).ensureChannels(channels);
this.channels = channels;
this.sort();
return this;
}
addChannel(id, name) {
const newChannel = { id, name };
this.channels = [...this.channels, newChannel];
get(this.channelsMetaList).initializeChannel(newChannel);
this.sort();
return this;
}
deleteChannel(id) {
const channelIndex = this.channels.map((e) => e.id).indexOf(id);
if (channelIndex !== -1) {
this.channels.splice(channelIndex, 1);
}
return this;
}
sort() {
this.channels.sort((a, b) => {
if (a.name < b.name) {
return -1;
} else if (a.name > b.name) {
return 1;
}
return 0;
});
}
}
export class ChannelsMeta {
// Store channelId -> { draft = '', lastReadAt = null, scrollPosition = null }
channelsMeta = $state({});
constructor({ channelsMetaData }) {
const channelsMeta = objectMap(channelsMetaData, (ch) => {
let lastReadAt = ch.lastReadAt;
if (typeof lastReadAt === 'string') {
lastReadAt = DateTime.fromISO(lastReadAt);
}
if (!Boolean(lastReadAt)) {
lastReadAt = DateTime.fromISO(EPOCH_STRING);
}
return {
...ch,
lastReadAt
};
});
this.channelsMeta = channelsMeta;
}
writeOutToLocalStorage() {
localStorage.setItem(STORE_KEY_CHANNELS_DATA, JSON.stringify(this.channelsMeta));
}
updateLastReadAt(channelId, at) {
const channelObject = this.getChannel(channelId);
// Do it this way, rather than with Math.max tricks, to avoid assignment
// when we don't need it, to minimize reactive changes:
if (at > channelObject?.lastReadAt) {
channelObject.lastReadAt = at;
this.writeOutToLocalStorage();
}
}
ensureChannels(channelsList) {
channelsList.forEach(({ id }) => {
this.initializeChannel(id);
});
}
initializeChannel(channelId) {
if (!this.channelsMeta[channelId]) {
const channelData = {
lastReadAt: null,
draft: '',
scrollPosition: null
};
this.channelsMeta[channelId] = channelData;
}
}
getChannel(channelId) {
return this.channelsMeta[channelId] || null;
}
}
function objectMap(object, mapFn) {
return Object.keys(object).reduce((result, key) => {
result[key] = mapFn(object[key]);
return result;
}, {});
}
|