history.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import {
  2. addDoc,
  3. collection,
  4. deleteDoc,
  5. doc,
  6. getDocs,
  7. getFirestore,
  8. limit,
  9. onSnapshot,
  10. orderBy,
  11. query,
  12. updateDoc,
  13. } from "firebase/firestore"
  14. import { FormDataKeyValue } from "../types/HoppRESTRequest"
  15. import { currentUser$ } from "./auth"
  16. import { settingsStore } from "~/newstore/settings"
  17. import {
  18. GQLHistoryEntry,
  19. graphqlHistoryStore,
  20. HISTORY_LIMIT,
  21. RESTHistoryEntry,
  22. restHistoryStore,
  23. setGraphqlHistoryEntries,
  24. setRESTHistoryEntries,
  25. translateToNewGQLHistory,
  26. translateToNewRESTHistory,
  27. } from "~/newstore/history"
  28. type HistoryFBCollections = "history" | "graphqlHistory"
  29. /**
  30. * Whether the history are loaded. If this is set to true
  31. * Updates to the history store are written into firebase.
  32. *
  33. * If you have want to update the store and not fire the store update
  34. * subscription, set this variable to false, do the update and then
  35. * set it to true
  36. */
  37. let loadedRESTHistory = false
  38. /**
  39. * Whether the history are loaded. If this is set to true
  40. * Updates to the history store are written into firebase.
  41. *
  42. * If you have want to update the store and not fire the store update
  43. * subscription, set this variable to false, do the update and then
  44. * set it to true
  45. */
  46. let loadedGraphqlHistory = false
  47. const purgeFormDataFromRequest = (req: RESTHistoryEntry): RESTHistoryEntry => {
  48. if (req.request.body.contentType !== "multipart/form-data") return req
  49. req.request.body.body = req.request.body.body.map<FormDataKeyValue>(
  50. (formData) => {
  51. if (!formData.isFile) return formData
  52. return {
  53. active: formData.active,
  54. isFile: false, // Something we can do to keep the status ?
  55. key: formData.key,
  56. value: "",
  57. }
  58. }
  59. )
  60. return req
  61. }
  62. async function writeHistory(
  63. entry: RESTHistoryEntry | GQLHistoryEntry,
  64. col: HistoryFBCollections
  65. ) {
  66. const processedEntry =
  67. col === "history"
  68. ? purgeFormDataFromRequest(entry as RESTHistoryEntry)
  69. : entry
  70. if (currentUser$.value == null)
  71. throw new Error("User not logged in to sync history")
  72. const hs = {
  73. ...processedEntry,
  74. updatedOn: new Date(),
  75. }
  76. try {
  77. await addDoc(
  78. collection(getFirestore(), "users", currentUser$.value.uid, col),
  79. hs
  80. )
  81. } catch (e) {
  82. console.error("error writing to history", hs, e)
  83. throw e
  84. }
  85. }
  86. async function deleteHistory(
  87. entry: (RESTHistoryEntry | GQLHistoryEntry) & { id: string },
  88. col: HistoryFBCollections
  89. ) {
  90. if (currentUser$.value == null)
  91. throw new Error("User not logged in to delete history")
  92. try {
  93. await deleteDoc(
  94. doc(getFirestore(), "users", currentUser$.value.uid, col, entry.id)
  95. )
  96. } catch (e) {
  97. console.error("error deleting history", entry, e)
  98. throw e
  99. }
  100. }
  101. async function clearHistory(col: HistoryFBCollections) {
  102. if (currentUser$.value == null)
  103. throw new Error("User not logged in to clear history")
  104. const { docs } = await getDocs(
  105. collection(getFirestore(), "users", currentUser$.value.uid, col)
  106. )
  107. await Promise.all(docs.map((e) => deleteHistory(e as any, col)))
  108. }
  109. async function toggleStar(
  110. entry: (RESTHistoryEntry | GQLHistoryEntry) & { id: string },
  111. col: HistoryFBCollections
  112. ) {
  113. if (currentUser$.value == null)
  114. throw new Error("User not logged in to toggle star")
  115. try {
  116. await updateDoc(
  117. doc(getFirestore(), "users", currentUser$.value.uid, col, entry.id),
  118. { star: !entry.star }
  119. )
  120. } catch (e) {
  121. console.error("error toggling star", entry, e)
  122. throw e
  123. }
  124. }
  125. export function initHistory() {
  126. restHistoryStore.dispatches$.subscribe((dispatch) => {
  127. if (
  128. loadedRESTHistory &&
  129. currentUser$.value &&
  130. settingsStore.value.syncHistory
  131. ) {
  132. if (dispatch.dispatcher === "addEntry") {
  133. writeHistory(dispatch.payload.entry, "history")
  134. } else if (dispatch.dispatcher === "deleteEntry") {
  135. deleteHistory(dispatch.payload.entry, "history")
  136. } else if (dispatch.dispatcher === "clearHistory") {
  137. clearHistory("history")
  138. } else if (dispatch.dispatcher === "toggleStar") {
  139. toggleStar(dispatch.payload.entry, "history")
  140. }
  141. }
  142. })
  143. graphqlHistoryStore.dispatches$.subscribe((dispatch) => {
  144. if (
  145. loadedGraphqlHistory &&
  146. currentUser$.value &&
  147. settingsStore.value.syncHistory
  148. ) {
  149. if (dispatch.dispatcher === "addEntry") {
  150. writeHistory(dispatch.payload.entry, "graphqlHistory")
  151. } else if (dispatch.dispatcher === "deleteEntry") {
  152. deleteHistory(dispatch.payload.entry, "graphqlHistory")
  153. } else if (dispatch.dispatcher === "clearHistory") {
  154. clearHistory("graphqlHistory")
  155. } else if (dispatch.dispatcher === "toggleStar") {
  156. toggleStar(dispatch.payload.entry, "graphqlHistory")
  157. }
  158. }
  159. })
  160. let restSnapshotStop: (() => void) | null = null
  161. let graphqlSnapshotStop: (() => void) | null = null
  162. currentUser$.subscribe((user) => {
  163. if (!user) {
  164. // Clear the snapshot listeners when the user logs out
  165. if (restSnapshotStop) {
  166. restSnapshotStop()
  167. restSnapshotStop = null
  168. }
  169. if (graphqlSnapshotStop) {
  170. graphqlSnapshotStop()
  171. graphqlSnapshotStop = null
  172. }
  173. } else {
  174. restSnapshotStop = onSnapshot(
  175. query(
  176. collection(getFirestore(), "users", user.uid, "history"),
  177. orderBy("updatedOn", "desc"),
  178. limit(HISTORY_LIMIT)
  179. ),
  180. (historyRef) => {
  181. const history: RESTHistoryEntry[] = []
  182. historyRef.forEach((doc) => {
  183. const entry = doc.data()
  184. entry.id = doc.id
  185. history.push(translateToNewRESTHistory(entry))
  186. })
  187. loadedRESTHistory = false
  188. setRESTHistoryEntries(history)
  189. loadedRESTHistory = true
  190. }
  191. )
  192. graphqlSnapshotStop = onSnapshot(
  193. query(
  194. collection(getFirestore(), "users", user.uid, "graphqlHistory"),
  195. orderBy("updatedOn", "desc"),
  196. limit(HISTORY_LIMIT)
  197. ),
  198. (historyRef) => {
  199. const history: GQLHistoryEntry[] = []
  200. historyRef.forEach((doc) => {
  201. const entry = doc.data()
  202. entry.id = doc.id
  203. history.push(translateToNewGQLHistory(entry))
  204. })
  205. loadedGraphqlHistory = false
  206. setGraphqlHistoryEntries(history)
  207. loadedGraphqlHistory = true
  208. }
  209. )
  210. }
  211. })
  212. }