summaryrefslogtreecommitdiff
path: root/ui/lib/store/channels.svelte.js
blob: 49cc31c6469e3c5274b61211a79be99ebed444d1 (plain)
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 { STORE_KEY_CHANNELS_DATA } from '$lib/constants';
const EPOCH_STRING = '1970-01-01T00:00:00Z';

// For reasons unclear to me, a straight up class definition with a constructor
// doesn't seem to work, reactively. So we resort to this.
// Owen suggests that this sentence in the Svelte docs should make the reason
// clear:
// > If $state is used with an array or a simple object, the result is a deeply
// > reactive state proxy.
// Emphasis on "simple object".
// --Kit
function makeChannelObject({ id, name, draft = '', lastReadAt = null, scrollPosition = null }) {
  let lastReadAtParsed;
  if (Boolean(lastReadAt)) {
    if (typeof lastReadAt === "string") {
      lastReadAtParsed = DateTime.fromISO(lastReadAt);
    } else {
      lastReadAtParsed = lastReadAt;
    }
  } else {
    lastReadAtParsed = DateTime.fromISO(EPOCH_STRING);
  }
  return {
    id,
    name,
    lastReadAt: lastReadAtParsed,
    draft,
    scrollPosition
  };
}

function mergeLocalData(remoteData, currentData) {
  let currentDataObj = currentData.reduce(
    (acc, cur) => {
      acc[cur.id] = cur;
      return acc;
    },
    {}
  );
  const ret = remoteData.map(
    (ch) => {
      const newCh = makeChannelObject(ch);
      if (Boolean(currentDataObj[ch.id])) {
        newCh.lastReadAt = currentDataObj[ch.id].lastReadAt;
      }
      return newCh;
    }
  );
  return ret;
}

export class Channels {
  channels = $state([]);

  constructor({ channelsData }) {
    this.channels = channelsData.map(makeChannelObject);
    // On channel edits (inc 'last read' ones), write out to localstorage?
  }

  writeOutToLocalStorage() {
    localStorage.setItem(
      STORE_KEY_CHANNELS_DATA,
      JSON.stringify(this.channels)
    );
  }

  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();
    }
  }

  getChannel(channelId) {
    return this.channels.filter((ch) => ch.id === channelId)[0] || null;
  }

  setChannels(channels) {
    // This gets called, among other times, when the page is first loaded, with
    // server-sent data from the `boot` endpoint. That needs to be merged with
    // locally stored data!
    this.channels = mergeLocalData(channels, this.channels);
    this.sort();
    return this;
  }

  addChannel(id, name) {
    this.channels = [...this.channels, makeChannelObject({ id, name })];
    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;
    });
  }
}