@@ -2,6 +2,8 @@ package wust.webApp
22
33import wust .util .Memo
44import wust .ids ._
5+ import wust .graph .Graph
6+ import wust .graph .Page
57import collection .mutable
68import cats .effect .SyncIO
79import io .circe ._
@@ -15,22 +17,32 @@ import wust.api.Authentication
1517import wust .api .serialize .Circe ._
1618import wust .graph .GraphChanges
1719import wust .webUtil .outwatchHelpers ._
20+ import wust .util .time .time
1821
19- import scala .util .{Failure , Success , Try }
22+ import scala .util .{ Failure , Success , Try }
2023import wust .facades .segment .Segment
2124
2225class ClientStorage (implicit owner : Ctx .Owner ) {
23- import org .scalajs .dom .ext .{LocalStorage => internal }
26+ import org .scalajs .dom .ext .{ LocalStorage => internal }
2427
2528 object keys {
2629 val auth = " wust.auth"
2730 val sidebarOpen = " wust.sidebar.open"
2831 val sidebarWithProjects = " wust.sidebar.projects"
2932 val taglistOpen = " wust.taglist.open"
3033 val filterlistOpen = " wust.filterlist.open"
31- def pendingChanges (userId: UserId ) = s " wust.pendingchanges. ${userId.toUuid.toString}"
32- def pendingChangesInvalid (userId: UserId ) = s " wust.pendingchanges.invalid. ${userId.toUuid.toString}"
34+ def pendingChanges (userId : UserId ) = s " wust.pendingchanges. ${userId.toUuid.toString}"
35+ def pendingChangesInvalid (userId : UserId ) = s " wust.pendingchanges.invalid. ${userId.toUuid.toString}"
3336 val backendTimeDelta = " wust.backendtimedelta"
37+ def pageCache (page : Page ) = new {
38+ val baseKey = page.parentId match {
39+ case Some (parentId) => s " wust.pageCache. ${parentId.toUuid.toString}"
40+ case None => s " wust.pageCache.empty "
41+ }
42+ val graph = s " ${baseKey}.graph "
43+ val time = s " ${baseKey}.time "
44+ val keys = List (graph, time)
45+ }
3446 }
3547
3648 private def toJson [T : Encoder ](value : T ): String = value.asJson.noSpaces
@@ -51,8 +63,30 @@ class ClientStorage(implicit owner: Ctx.Owner) {
5163 }
5264 }
5365
66+ def getGraph (page : Page ): Option [Graph ] = {
67+ val storageKey = keys.pageCache(page)
68+ val encodedOpt = internal(storageKey.graph)
69+ encodedOpt flatMap { encoded =>
70+ decode[Graph ](encoded) match {
71+ case Left (_) =>
72+ // cannot decode cache -> prune this entry
73+ storageKey.keys.foreach(internal.remove)
74+ None
75+ case Right (decoded) => Some (decoded)
76+ }
77+ }
78+ }
79+
80+ def updateGraph (page : Page , graph : Graph ): Unit = {
81+ time(s " writing to cache: $page" ) {
82+ val storageKey = keys.pageCache(page)
83+ internal.update(storageKey.graph, toJson(graph))
84+ internal.update(storageKey.time, EpochMilli .now.toString)
85+ }
86+ }
87+
5488 val auth : Var [Option [Authentication ]] = {
55- if (canAccessLs) {
89+ if (canAccessLs) {
5690 LocalStorage
5791 .handlerWithoutEvents[SyncIO ](keys.auth)
5892 .unsafeRunSync()
@@ -61,48 +95,47 @@ class ClientStorage(implicit owner: Ctx.Owner) {
6195 } else Var (None )
6296 }
6397
64- def addPendingGraphChange (userId : UserId , change: GraphChanges ) = {
98+ def addPendingGraphChange (userId : UserId , change : GraphChanges ) = {
6599 pendingChanges(userId).update(_ :+ toJson(change))
66100 }
67101
68102 def clearPendingGraphChanges (userId : UserId ) = {
69103 pendingChanges(userId).update(_ => List .empty)
70104 }
71105
72- def getDecodablePendingGraphChanges (userId : UserId ): List [GraphChanges ] = {
106+ def getDecodablePendingGraphChanges (userId : UserId ): List [GraphChanges ] = {
73107 val all = pendingChanges(userId).now
74108 val validEncodedBuilder = mutable.ListBuffer .empty[String ]
75109 val validDecodedBuilder = mutable.ListBuffer .empty[GraphChanges ]
76110 val invalidBuilder = mutable.ListBuffer .empty[String ]
77111 all.foreach { encoded =>
78112 decode[GraphChanges ](encoded) match {
79- case Left (error) =>
113+ case Left (error) =>
80114 invalidBuilder += encoded
81115 Segment .trackError(" Failed to decode pending GraphChange" , s " ${error.toString}: ${encoded}" )
82- case Right (valid) =>
116+ case Right (valid) =>
83117 validEncodedBuilder += encoded
84118 validDecodedBuilder += valid
85119 }
86120 }
87121
88122 val invalid = invalidBuilder.result()
89123
90- if (invalid.nonEmpty) {
124+ if (invalid.nonEmpty) {
91125 pendingChangesInvalid(userId).update(_ ++ invalid)
92126 pendingChanges(userId)() = validEncodedBuilder.result()
93127 }
94128
95129 validDecodedBuilder.result()
96130 }
97131
98-
99132 val pendingChanges = Memo .mutableHashMapMemo[UserId , Var [List [String ]]](pendingChangesByUser)
100133 val pendingChangesInvalid = Memo .mutableHashMapMemo[UserId , Var [List [String ]]](pendingChangesInvalidByUser)
101134
102135 // TODO: howto handle with events from other tabs?
103136 private def pendingChangesByUser (userId : UserId ): Var [List [String ]] = {
104137 val storageKey = keys.pendingChanges(userId)
105- if (canAccessLs) {
138+ if (canAccessLs) {
106139 LocalStorage
107140 .handlerWithoutEvents[SyncIO ](storageKey)
108141 .unsafeRunSync()
@@ -113,7 +146,7 @@ class ClientStorage(implicit owner: Ctx.Owner) {
113146
114147 private def pendingChangesInvalidByUser (userId : UserId ): Var [List [String ]] = {
115148 val storageKey = keys.pendingChangesInvalid(userId)
116- if (canAccessLs) {
149+ if (canAccessLs) {
117150 LocalStorage
118151 .handlerWithoutEvents[SyncIO ](storageKey)
119152 .unsafeRunSync()
@@ -122,9 +155,8 @@ class ClientStorage(implicit owner: Ctx.Owner) {
122155 } else Var (List .empty)
123156 }
124157
125-
126158 val sidebarOpen : Var [Option [Boolean ]] = {
127- if (canAccessLs) {
159+ if (canAccessLs) {
128160 LocalStorage
129161 .handlerWithoutEvents[SyncIO ](keys.sidebarOpen)
130162 .unsafeRunSync()
@@ -134,7 +166,7 @@ class ClientStorage(implicit owner: Ctx.Owner) {
134166 }
135167
136168 val sidebarWithProjects : Var [Option [Boolean ]] = {
137- if (canAccessLs) {
169+ if (canAccessLs) {
138170 LocalStorage
139171 .handlerWithoutEvents[SyncIO ](keys.sidebarWithProjects)
140172 .unsafeRunSync()
@@ -144,7 +176,7 @@ class ClientStorage(implicit owner: Ctx.Owner) {
144176 }
145177
146178 val taglistOpen : Var [Option [Boolean ]] = {
147- if (canAccessLs) {
179+ if (canAccessLs) {
148180 LocalStorage
149181 .handlerWithoutEvents[SyncIO ](keys.taglistOpen)
150182 .unsafeRunSync()
@@ -154,7 +186,7 @@ class ClientStorage(implicit owner: Ctx.Owner) {
154186 }
155187
156188 val filterlistOpen : Var [Option [Boolean ]] = {
157- if (canAccessLs) {
189+ if (canAccessLs) {
158190 LocalStorage
159191 .handlerWithoutEvents[SyncIO ](keys.filterlistOpen)
160192 .unsafeRunSync()
@@ -164,7 +196,7 @@ class ClientStorage(implicit owner: Ctx.Owner) {
164196 }
165197
166198 val backendTimeDelta : Var [Long ] = {
167- if (canAccessLs) {
199+ if (canAccessLs) {
168200 LocalStorage
169201 .handlerWithoutEvents[SyncIO ](keys.backendTimeDelta)
170202 .unsafeRunSync()
0 commit comments