aboutsummaryrefslogtreecommitdiff
path: root/src/modules/serverSideStorage.js
blob: 5ebe17fd5e889732c823c9e8aaeba0a0b07f9c6e (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { toRaw } from 'vue'

const VERSION = 1
const NEW_USER_DATE = new Date('04-08-2022') // date of writing this, basically

const COMMAND_TRIM_FLAGS = 1000
const COMMAND_TRIM_FLAGS_AND_RESET = 1001

const defaultState = {
  // last timestamp
  timestamp: 0,
  // need to update server
  dirty: false,
  // storage of flags - stuff that can only be set and incremented
  flagStorage: {
    updateCounter: 0, // Counter for most recent update notification seen
    // TODO move to prefsStorage when that becomes a thing since only way
    // this can be reset is by complete reset of all flags
    dontShowUpdateNotifs: 0, // if user chose to not show update notifications ever again
    reset: 0 // special flag that can be used to force-reset all flags, debug purposes only
    // special reset codes:
    // 1000: trim keys to those known by currently running FE
    // 1001: same as above + reset everything to 0
  },
  // raw data
  raw: null,
  // local cache
  cache: null
}

const newUserFlags = {
  ...defaultState.flagStorage,
  updateCounter: 1 // new users don't need to see update notification
}

const serverSideStorage = {
  state: {
    ...defaultState
  },
  mutations: {
    setServerSideStorage (state, userData) {
      const live = userData.storage
      const userNew = userData.created_at > NEW_USER_DATE
      const flagsTemplate = userNew ? newUserFlags : defaultState.defaultState
      state.raw = live
      console.log(1111, live._timestamp)
      let recent = null
      const cache = state.cache || {}
      const cacheValid = cache._timestamp > 0 && cache._version > 0
      const liveValid = live._timestamp > 0 && live._version > 0
      if (!liveValid) {
        state.dirty = true
        console.debug('Nothing valid stored on server, assuming cache to be source of truth')
        if (cacheValid) {
          recent = cache
        } else {
          console.debug(`Local cache is empty, initializing for ${userNew ? 'new' : 'existing'} user`)

          recent = {
            _timestamp: Date.now(),
            _version: VERSION,
            flagStorage: { ...flagsTemplate }
          }
        }
      } else if (!cacheValid) {
        console.debug('Valid storage on server found, no local cache found, using live as source of truth')
        recent = live
      } else {
        console.debug('Both sources have valid data, figuring things out...')
        console.log(live._timestamp, cache._timestamp)
        if (live._timestamp === cache._timestamp && live._version === cache._version) {
          console.debug('Same version/timestamp on both source, source of truth irrelevant')
          recent = cache
        } else {
          state.dirty = true
          console.debug('Different timestamp, figuring out which one is more recent')
          let stale
          if (live._timestamp < cache._timestamp) {
            recent = cache
            stale = live
          } else {
            recent = live
            stale = cache
          }

          // Merge the flags
          console.debug('Merging the flags...')
          recent.flagStorage = recent.flagStorage || { ...flagsTemplate }
          stale.flagStorage = stale.flagStorage || { ...flagsTemplate }
          const allFlags = Array.from(new Set([
            ...Object.keys(toRaw(recent.flagStorage)),
            ...Object.keys(toRaw(stale.flagStorage))
          ]))

          const totalFlags = Object.fromEntries(allFlags.map(flag => {
            const recentFlag = recent.flagStorage[flag]
            const staleFlag = stale.flagStorage[flag]
            // use flag that is of higher value
            return [flag, recentFlag > staleFlag ? recentFlag : staleFlag]
          }))

          console.debug('AAA', totalFlags)
          // flag reset functionality
          if (totalFlags.reset >= COMMAND_TRIM_FLAGS && totalFlags.reset <= COMMAND_TRIM_FLAGS_AND_RESET) {
            console.debug('Received command to trim the flags')
            const knownKeys = new Set(Object.keys(defaultState.flagStorage))
            allFlags.forEach(flag => {
              if (!knownKeys.has(flag)) {
                delete totalFlags[flag]
              }
            })
            if (totalFlags.reset === COMMAND_TRIM_FLAGS_AND_RESET) {
              // 1001 - and reset everything to 0
              console.debug('Received command to reset the flags')
              allFlags.forEach(flag => { totalFlags[flag] = 0 })
            } else {
              // reset the reset 0
              totalFlags.reset = 0
            }
          } else if (totalFlags.reset > 0 && totalFlags.reset < 9000) {
            console.debug('Received command to reset the flags')
            allFlags.forEach(flag => { totalFlags[flag] = 0 })
            // for good luck
            totalFlags.reset = 0
          }
          console.log('AAAA', totalFlags)
          state.cache.flagStorage = totalFlags
        }
      }
      state.cache = recent
      state.flagStorage = state.cache.flagStorage
    },
    setFlag (state, { flag, value }) {
      state.flagStorage[flag] = value
      state.dirty = true
    }
  },
  actions: {
    pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) {
      console.log('PUSH')
      const needPush = state.dirty || force
      if (!needPush) return
      state.cache = {
        _timestamp: Date.now(),
        _version: VERSION,
        flagStorage: toRaw(state.flagStorage)
      }
      console.log('YES')
      const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } }
      rootState.api.backendInteractor
        .updateProfile({ params })
        .then((user) => commit('setServerSideStorage', user))
      state.dirty = false
    }
  }
}

export default serverSideStorage