@@ -13,7 +13,7 @@ module Store =
13 Nickname: string
13 Nickname: string
14 DisplayName: string
14 DisplayName: string
15 Bio: string
15 Bio: string
16− Photo: byte[] }
16+ Photo: byte[] option }
18 type Profile =
18 type Profile =
19 { DisplayName: string
19 { DisplayName: string
@@ -111,20 +111,48 @@ module Store =
112 type private ByteArrayOptionHandler() =
112 type private ByteArrayOptionHandler() =
113 inherit SqlMapper.TypeHandler<byte[] option>()
113 inherit SqlMapper.TypeHandler<byte[] option>()
114 override _.SetValue(p, value) =
115 override _.SetValue(p, value) =
115− p.Value <- match value with Some v -> box v | None -> box DBNull.Value
119+ | None -> box DBNull.Value
116 override _.Parse(value) =
121 override _.Parse(value) =
117− if value = null || value :? DBNull then None else Some(value :?> byte[])
122+ if value = null || value :? DBNull then
125+ Some(value :?> byte[])
120 OptionTypes.register ()
128 OptionTypes.register ()
121 SqlMapper.AddTypeHandler(ByteArrayOptionHandler())
129 SqlMapper.AddTypeHandler(ByteArrayOptionHandler())
122 use conn = openConn ()
130 use conn = openConn ()
123− conn.Execute("CREATE TABLE IF NOT EXISTS profile (Id TEXT PRIMARY KEY, DisplayName TEXT NOT NULL DEFAULT '', Bio TEXT NOT NULL DEFAULT '', Photo BLOB)") |> ignore
133+ "CREATE TABLE IF NOT EXISTS profile (Id TEXT PRIMARY KEY, DisplayName TEXT NOT NULL DEFAULT '', Bio TEXT NOT NULL DEFAULT '', Photo BLOB)"
124− conn.Execute("CREATE TABLE IF NOT EXISTS contacts (Pubkey TEXT PRIMARY KEY, Nickname TEXT NOT NULL DEFAULT '', DisplayName TEXT NOT NULL DEFAULT '', Bio TEXT NOT NULL DEFAULT '', Photo BLOB)") |> ignore
138+ "CREATE TABLE IF NOT EXISTS contacts (Pubkey TEXT PRIMARY KEY, Nickname TEXT NOT NULL DEFAULT '', DisplayName TEXT NOT NULL DEFAULT '', Bio TEXT NOT NULL DEFAULT '', Photo BLOB)"
125− conn.Execute("CREATE TABLE IF NOT EXISTS messages (Id TEXT PRIMARY KEY, ConversationId TEXT NOT NULL, Body TEXT NOT NULL DEFAULT '', TimestampUnix INTEGER NOT NULL, IsOutgoing INTEGER NOT NULL DEFAULT 0, Status INTEGER NOT NULL DEFAULT 0)") |> ignore
143+ "CREATE TABLE IF NOT EXISTS messages (Id TEXT PRIMARY KEY, ConversationId TEXT NOT NULL, Body TEXT NOT NULL DEFAULT '', TimestampUnix INTEGER NOT NULL, IsOutgoing INTEGER NOT NULL DEFAULT 0, Status INTEGER NOT NULL DEFAULT 0)"
126− conn.Execute("CREATE TABLE IF NOT EXISTS settings (Id TEXT PRIMARY KEY, ServerUrl TEXT NOT NULL DEFAULT '', PollCursor INTEGER NOT NULL DEFAULT 0)") |> ignore
148+ "CREATE TABLE IF NOT EXISTS settings (Id TEXT PRIMARY KEY, ServerUrl TEXT NOT NULL DEFAULT '', PollCursor INTEGER NOT NULL DEFAULT 0)"
127− conn.Execute("CREATE TABLE IF NOT EXISTS outbox (Id INTEGER PRIMARY KEY AUTOINCREMENT, RecipientHex TEXT NOT NULL, EnvelopeJson TEXT NOT NULL, CreatedAt INTEGER NOT NULL)") |> ignore
153+ "CREATE TABLE IF NOT EXISTS outbox (Id INTEGER PRIMARY KEY AUTOINCREMENT, RecipientHex TEXT NOT NULL, EnvelopeJson TEXT NOT NULL, CreatedAt INTEGER NOT NULL)"
129 let private identityPath = Path.Combine(appDataDir, "identity.key")
157 let private identityPath = Path.Combine(appDataDir, "identity.key")
@@ -153,12 +181,18 @@ module Store =
154 use conn = openConn ()
182 use conn = openConn ()
156− select { for p in profileTable do where (p.Id = "me") }
185+ for p in profileTable do
157 |> conn.SelectAsync<ProfileRow>
188 |> conn.SelectAsync<ProfileRow>
158 |> Async.AwaitTask
189 |> Async.AwaitTask
159 |> Async.RunSynchronously
190 |> Async.RunSynchronously
160 |> Seq.tryHead
191 |> Seq.tryHead
192+ |> Option.map (fun p ->
161− |> Option.map (fun p -> { DisplayName = p.DisplayName; Bio = p.Bio; Photo = p.Photo })
193+ { DisplayName = p.DisplayName
162 with _ ->
196 with _ ->
@@ -168,8 +202,11 @@ module Store =
169 conn.Execute(
203 conn.Execute(
170 "INSERT OR REPLACE INTO profile (Id, DisplayName, Bio, Photo) VALUES (@Id, @DisplayName, @Bio, @Photo)",
204 "INSERT OR REPLACE INTO profile (Id, DisplayName, Bio, Photo) VALUES (@Id, @DisplayName, @Bio, @Photo)",
171− {| Id = "me"; DisplayName = profile.DisplayName; Bio = profile.Bio
206+ DisplayName = profile.DisplayName
172− Photo = profile.Photo |> Option.defaultValue null |})
208+ Photo = profile.Photo |> Option.defaultValue null |}
173 |> ignore
210 |> ignore
174 with _ ->
211 with _ ->
@@ -179,7 +216,10 @@ module Store =
179 use conn = openConn ()
216 use conn = openConn ()
181 let settings =
218 let settings =
182− select { for s in settingsTable do where (s.Id = "settings") }
220+ for s in settingsTable do
221+ where (s.Id = "settings")
183 |> conn.SelectAsync<SettingsRow>
223 |> conn.SelectAsync<SettingsRow>
184 |> Async.AwaitTask
224 |> Async.AwaitTask
185 |> Async.RunSynchronously
225 |> Async.RunSynchronously
@@ -188,7 +228,10 @@ module Store =
189 |> Option.map (fun s ->
229 |> Option.map (fun s ->
190 let contacts =
230 let contacts =
191− select { for c in contactTable do selectAll }
232+ for c in contactTable do
192 |> conn.SelectAsync<ContactRow>
235 |> conn.SelectAsync<ContactRow>
193 |> Async.AwaitTask
236 |> Async.AwaitTask
194 |> Async.RunSynchronously
237 |> Async.RunSynchronously
@@ -198,13 +241,16 @@ module Store =
198 Nickname = c.Nickname
241 Nickname = c.Nickname
199 DisplayName = c.DisplayName
242 DisplayName = c.DisplayName
200 Bio = c.Bio
243 Bio = c.Bio
201− Photo = c.Photo |> Option.defaultValue [||] }
244+ Photo = c.Photo }
203 c.Pubkey, contact)
246 c.Pubkey, contact)
204 |> Map.ofSeq
247 |> Map.ofSeq
206 let messages =
249 let messages =
207− select { for m in messageTable do selectAll }
251+ for m in messageTable do
208 |> conn.SelectAsync<MessageRow>
254 |> conn.SelectAsync<MessageRow>
209 |> Async.AwaitTask
255 |> Async.AwaitTask
210 |> Async.RunSynchronously
256 |> Async.RunSynchronously
@@ -217,8 +263,15 @@ module Store =
217 Body = m.Body
263 Body = m.Body
218 Timestamp = DateTimeOffset.FromUnixTimeMilliseconds m.TimestampUnix
264 Timestamp = DateTimeOffset.FromUnixTimeMilliseconds m.TimestampUnix
219 Direction =
265 Direction =
220− if not m.IsOutgoing then Incoming
266+ if not m.IsOutgoing then
270+ if m.Status = 1 then
221− else Outgoing(if m.Status = 1 then DeliveryStatus.Delivered else DeliveryStatus.Sent) })
271+ DeliveryStatus.Delivered
222 |> Seq.sortBy _.Timestamp
275 |> Seq.sortBy _.Timestamp
223 |> Seq.toList)
276 |> Seq.toList)
224 |> Map.ofSeq
277 |> Map.ofSeq
@@ -237,8 +290,12 @@ module Store =
237 for c in data.Contacts.Values do
290 for c in data.Contacts.Values do
238 conn.Execute(
291 conn.Execute(
239 "INSERT OR REPLACE INTO contacts (Pubkey, Nickname, DisplayName, Bio, Photo) VALUES (@Pubkey, @Nickname, @DisplayName, @Bio, @Photo)",
292 "INSERT OR REPLACE INTO contacts (Pubkey, Nickname, DisplayName, Bio, Photo) VALUES (@Pubkey, @Nickname, @DisplayName, @Bio, @Photo)",
293+ {| Pubkey = c.Pubkey
294+ Nickname = c.Nickname
240− {| Pubkey = c.Pubkey; Nickname = c.Nickname; DisplayName = c.DisplayName; Bio = c.Bio
295+ DisplayName = c.DisplayName
241− Photo = if c.Photo = null || c.Photo.Length = 0 then null else c.Photo |})
296+ Bio = c.Bio
242 |> ignore
299 |> ignore
244 data.Messages
301 data.Messages
@@ -252,28 +309,37 @@ module Store =
253 conn.Execute(
310 conn.Execute(
254 "INSERT OR REPLACE INTO messages (Id, ConversationId, Body, TimestampUnix, IsOutgoing, Status) VALUES (@Id, @ConversationId, @Body, @TimestampUnix, @IsOutgoing, @Status)",
311 "INSERT OR REPLACE INTO messages (Id, ConversationId, Body, TimestampUnix, IsOutgoing, Status) VALUES (@Id, @ConversationId, @Body, @TimestampUnix, @IsOutgoing, @Status)",
255− {| Id = m.Id; ConversationId = convId; Body = m.Body
313+ ConversationId = convId
256 TimestampUnix = m.Timestamp.ToUnixTimeMilliseconds()
315 TimestampUnix = m.Timestamp.ToUnixTimeMilliseconds()
257− IsOutgoing = isOutgoing; Status = status |})
316+ IsOutgoing = isOutgoing
258 |> ignore)
319 |> ignore)
260 conn.Execute(
321 conn.Execute(
261 "INSERT OR REPLACE INTO settings (Id, ServerUrl, PollCursor) VALUES (@Id, @ServerUrl, @PollCursor)",
322 "INSERT OR REPLACE INTO settings (Id, ServerUrl, PollCursor) VALUES (@Id, @ServerUrl, @PollCursor)",
324+ ServerUrl = data.ServerUrl
262− {| Id = "settings"; ServerUrl = data.ServerUrl; PollCursor = data.PollCursor |})
325+ PollCursor = data.PollCursor |}
263 |> ignore
327 |> ignore
264 with _ ->
328 with _ ->
267 let enqueueOutbox (recipientHex: string) (envelopeJson: string) =
331 let enqueueOutbox (recipientHex: string) (envelopeJson: string) =
268 use conn = openConn ()
332 use conn = openConn ()
270 for o in outboxTable do
335 for o in outboxTable do
272− { Id = 0L
337+ { Id = 0L
273− RecipientHex = recipientHex
338+ RecipientHex = recipientHex
274− EnvelopeJson = envelopeJson
339+ EnvelopeJson = envelopeJson
275− CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }
340+ CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }
276− excludeColumn o.Id
342+ excludeColumn o.Id
278 |> conn.InsertAsync
344 |> conn.InsertAsync
279 |> Async.AwaitTask
345 |> Async.AwaitTask
@@ -282,9 +348,10 @@ module Store =
283 let peekOutbox () : OutboxRow option =
349 let peekOutbox () : OutboxRow option =
284 use conn = openConn ()
350 use conn = openConn ()
286 for o in outboxTable do
353 for o in outboxTable do
287− orderBy o.Id
354+ orderBy o.Id
289 |> conn.SelectAsync<OutboxRow>
356 |> conn.SelectAsync<OutboxRow>
290 |> Async.AwaitTask
357 |> Async.AwaitTask
@@ -293,9 +360,10 @@ module Store =
294 let dequeueOutbox (id: int64) =
361 let dequeueOutbox (id: int64) =
295 use conn = openConn ()
362 use conn = openConn ()
297 for o in outboxTable do
365 for o in outboxTable do
298− where (o.Id = id)
366+ where (o.Id = id)
300 |> conn.DeleteAsync
368 |> conn.DeleteAsync
301 |> Async.AwaitTask
369 |> Async.AwaitTask