fix: handle bookmarks syncing signals properly

Bookmarks were only synced when devices were synced, but not when
bookmarks were added/removed/updated.

To account for this, there's are new messenger APIs in status-go
proposed here: https://github.com/status-im/status-go/pull/2709

Based on those APIs, desktop can now add/remove/update bookmarks and the
changes are automatically synced to other devices in real-time.
This commit also ensures that changes from other devices with regards to
bookmarks are handled and updated on the current device.

Partially addresses #5201
This commit is contained in:
Pascal Precht 2022-06-08 21:40:02 +02:00 committed by r4bbit.eth
parent 1046d6ab4d
commit 858caeca73
10 changed files with 125 additions and 17 deletions

View File

@ -153,7 +153,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
) )
result.transactionService = transaction_service.newService(statusFoundation.events, statusFoundation.threadpool, result.transactionService = transaction_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.walletAccountService, result.networkService, result.settingsService, result.tokenService) result.walletAccountService, result.networkService, result.settingsService, result.tokenService)
result.bookmarkService = bookmark_service.newService() result.bookmarkService = bookmark_service.newService(statusFoundation.events)
result.profileService = profile_service.newService(result.contactsService, result.settingsService) result.profileService = profile_service.newService(result.contactsService, result.settingsService)
result.stickersService = stickers_service.newService( result.stickersService = stickers_service.newService(
statusFoundation.events, statusFoundation.events,

View File

@ -4,12 +4,14 @@ import base
import ../../../../app_service/service/message/dto/[message, pinned_message_update, reaction, removed_message] import ../../../../app_service/service/message/dto/[message, pinned_message_update, reaction, removed_message]
import ../../../../app_service/service/chat/dto/[chat] import ../../../../app_service/service/chat/dto/[chat]
import ../../../../app_service/service/bookmarks/dto/[bookmark]
import ../../../../app_service/service/community/dto/[community] import ../../../../app_service/service/community/dto/[community]
import ../../../../app_service/service/activity_center/dto/[notification] import ../../../../app_service/service/activity_center/dto/[notification]
import ../../../../app_service/service/contacts/dto/[contacts, status_update] import ../../../../app_service/service/contacts/dto/[contacts, status_update]
import ../../../../app_service/service/devices/dto/[device] import ../../../../app_service/service/devices/dto/[device]
type MessageSignal* = ref object of Signal type MessageSignal* = ref object of Signal
bookmarks*: seq[BookmarkDto]
messages*: seq[MessageDto] messages*: seq[MessageDto]
pinnedMessages*: seq[PinnedMessageUpdateDto] pinnedMessages*: seq[PinnedMessageUpdateDto]
chats*: seq[ChatDto] chats*: seq[ChatDto]
@ -52,6 +54,11 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
var currentStatus = event["event"]["currentStatus"].toStatusUpdateDto() var currentStatus = event["event"]["currentStatus"].toStatusUpdateDto()
signal.currentStatus.add(currentStatus) signal.currentStatus.add(currentStatus)
if event["event"]{"bookmarks"} != nil:
for jsonBookmark in event["event"]["bookmarks"]:
var bookmark = jsonBookmark.toBookmarkDto()
signal.bookmarks.add(bookmark)
if event["event"]{"installations"} != nil: if event["event"]{"installations"} != nil:
for jsonDevice in event["event"]["installations"]: for jsonDevice in event["event"]["installations"]:
signal.devices.add(jsonDevice.toDeviceDto()) signal.devices.add(jsonDevice.toDeviceDto())

View File

@ -3,16 +3,20 @@ import result
import io_interface import io_interface
import ../../../../../app_service/service/bookmarks/service as bookmark_service import ../../../../../app_service/service/bookmarks/service as bookmark_service
import ../../../../core/eventemitter
type type
Controller* = ref object of RootObj Controller* = ref object of RootObj
delegate: io_interface.AccessInterface delegate: io_interface.AccessInterface
events: EventEmitter
bookmarkService: bookmark_service.Service bookmarkService: bookmark_service.Service
proc newController*(delegate: io_interface.AccessInterface, proc newController*(delegate: io_interface.AccessInterface,
events: EventEmitter,
bookmarkService: bookmark_service.Service): bookmarkService: bookmark_service.Service):
Controller = Controller =
result = Controller() result = Controller()
result.events = events
result.delegate = delegate result.delegate = delegate
result.bookmarkService = bookmarkService result.bookmarkService = bookmarkService
@ -20,7 +24,17 @@ proc delete*(self: Controller) =
discard discard
proc init*(self: Controller) = proc init*(self: Controller) =
discard self.events.on(SIGNAL_BOOKMARK_REMOVED) do(e: Args):
let args = BookmarkRemovedArgs(e)
self.delegate.onBookmarkDeleted(args.url)
self.events.on(SIGNAL_BOOKMARK_ADDED) do(e: Args):
let args = BookmarkArgs(e)
self.delegate.onBoomarkStored(args.bookmark.url, args.bookmark.name, args.bookmark.imageUrl)
self.events.on(SIGNAL_BOOKMARK_UPDATED) do(e: Args):
let args = BookmarkArgs(e)
self.delegate.onBookmarkUpdated(args.bookmark.url, args.bookmark.url, args.bookmark.name, args.bookmark.imageUrl)
proc getBookmarks*(self: Controller): seq[bookmark_service.BookmarkDto] = proc getBookmarks*(self: Controller): seq[bookmark_service.BookmarkDto] =
return self.bookmarkService.getBookmarks() return self.bookmarkService.getBookmarks()

View File

@ -6,6 +6,7 @@ import view
import controller import controller
import ../../../../global/global_singleton import ../../../../global/global_singleton
import ../../../../../app_service/service/bookmarks/service as bookmark_service import ../../../../../app_service/service/bookmarks/service as bookmark_service
import ../../../../core/eventemitter
export io_interface export io_interface
@ -17,13 +18,13 @@ type
moduleLoaded: bool moduleLoaded: bool
controller: Controller controller: Controller
proc newModule*(delegate: delegate_interface.AccessInterface, bookmarkService: bookmark_service.Service): Module = proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, bookmarkService: bookmark_service.Service): Module =
result = Module() result = Module()
result.delegate = delegate result.delegate = delegate
result.view = view.newView(result) result.view = view.newView(result)
result.viewVariant = newQVariant(result.view) result.viewVariant = newQVariant(result.view)
result.moduleLoaded = false result.moduleLoaded = false
result.controller = controller.newController(result, bookmarkService) result.controller = controller.newController(result, events, bookmarkService)
method delete*(self: Module) = method delete*(self: Module) =
self.view.delete self.view.delete
@ -32,6 +33,7 @@ method delete*(self: Module) =
method load*(self: Module) = method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("bookmarkModule", self.viewVariant) singletonInstance.engine.setRootContextProperty("bookmarkModule", self.viewVariant)
self.controller.init()
self.view.load() self.view.load()
method isLoaded*(self: Module): bool = method isLoaded*(self: Module): bool =

View File

@ -42,7 +42,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
result.viewVariant = newQVariant(result.view) result.viewVariant = newQVariant(result.view)
result.moduleLoaded = false result.moduleLoaded = false
result.providerModule = provider_module.newModule(result, events, settingsService, providerService) result.providerModule = provider_module.newModule(result, events, settingsService, providerService)
result.bookmarkModule = bookmark_module.newModule(result, bookmarkService) result.bookmarkModule = bookmark_module.newModule(result, events, bookmarkService)
result.dappsModule = dapps_module.newModule(result, dappPermissionsService, walletAccountService) result.dappsModule = dapps_module.newModule(result, dappPermissionsService, walletAccountService)
result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService) result.currentAccountModule = current_account_module.newModule(result, events, walletAccountService)

View File

@ -6,9 +6,13 @@ type BookmarkDto* = object
name*: string name*: string
url*: string url*: string
imageUrl*: string imageUrl*: string
removed*: bool
deletedAt*: int
proc toBookmarkDto*(jsonObj: JsonNode): BookmarkDto = proc toBookmarkDto*(jsonObj: JsonNode): BookmarkDto =
result = BookmarkDto() result = BookmarkDto()
discard jsonObj.getProp("name", result.name) discard jsonObj.getProp("name", result.name)
discard jsonObj.getProp("url", result.url) discard jsonObj.getProp("url", result.url)
discard jsonObj.getProp("imageUrl", result.imageUrl) discard jsonObj.getProp("imageUrl", result.imageUrl)
discard jsonObj.getProp("removed", result.removed)
discard jsonObj.getProp("deletedAt", result.deletedAt)

View File

@ -1,47 +1,95 @@
import Tables, json, sequtils, strformat, chronicles import Tables, json, sequtils, strformat, chronicles, strutils
import result import result
include ../../common/json_utils include ../../common/json_utils
import ./dto/bookmark as bookmark_dto import ./dto/bookmark as bookmark_dto
import ../../../app/core/eventemitter
import ../../../app/core/signals/types
import ../../../backend/backend import ../../../backend/backend
import ../../../backend/browser
export bookmark_dto export bookmark_dto
logScope: logScope:
topics = "bookmarks-service" topics = "bookmarks-service"
const SIGNAL_BOOKMARK_ADDED* = "bookmarkAdded"
const SIGNAL_BOOKMARK_REMOVED* = "bookmarkRemoved"
const SIGNAL_BOOKMARK_UPDATED* = "bookmarkUpdated"
type type
Service* = ref object of RootObj Service* = ref object of RootObj
bookmarks: Table[string, BookmarkDto] # [url, BookmarkDto] bookmarks: Table[string, BookmarkDto] # [url, BookmarkDto]
events: EventEmitter
type R = Result[BookmarkDto, string] type R = Result[BookmarkDto, string]
type
BookmarkArgs* = ref object of Args
bookmark*: BookmarkDto
type
BookmarkRemovedArgs* = ref object of Args
url*: string
proc delete*(self: Service) = proc delete*(self: Service) =
discard discard
proc newService*(): Service = proc newService*(events: EventEmitter): Service =
result = Service() result = Service()
result.events = events
result.bookmarks = initTable[string, BookmarkDto]() result.bookmarks = initTable[string, BookmarkDto]()
proc init*(self: Service) = proc init*(self: Service) =
try: try:
let response = backend.getBookmarks() let response = backend.getBookmarks()
for bookmark in response.result.getElems().mapIt(it.toBookmarkDto()): for bookmark in response.result.getElems().mapIt(it.toBookmarkDto()):
if not bookmark.removed:
self.bookmarks[bookmark.url] = bookmark self.bookmarks[bookmark.url] = bookmark
except Exception as e: except Exception as e:
let errDescription = e.msg let errDescription = e.msg
error "error: ", errDescription error "error: ", errDescription
self.events.on(SignalType.Message.event) do(e: Args):
var receivedData = MessageSignal(e)
if receivedData.bookmarks.len > 0:
for bookmark in receivedData.bookmarks:
let url = bookmark.url
if bookmark.removed and not self.bookmarks.hasKey(url):
return
if self.bookmarks.hasKey(url) and bookmark.removed:
self.bookmarks.del(url)
self.events.emit(SIGNAL_BOOKMARK_REMOVED, BookmarkRemovedArgs(url: url))
return
let emitUpdateEvent = self.bookmarks.hasKey(url)
self.bookmarks[url] = BookmarkDto()
self.bookmarks[url].url = bookmark.url
self.bookmarks[url].name = bookmark.name
self.bookmarks[url].imageUrl = bookmark.imageUrl
self.bookmarks[url].removed = bookmark.removed
if emitUpdateEvent:
self.events.emit(SIGNAL_BOOKMARK_UPDATED, BookmarkArgs(bookmark: self.bookmarks[url]))
return
self.events.emit(SIGNAL_BOOKMARK_ADDED, BookmarkArgs(bookmark: self.bookmarks[url]))
proc getBookmarks*(self: Service): seq[BookmarkDto] = proc getBookmarks*(self: Service): seq[BookmarkDto] =
return toSeq(self.bookmarks.values) return toSeq(self.bookmarks.values)
proc storeBookmark*(self: Service, url, name: string): R = proc storeBookmark*(self: Service, url, name: string): R =
try: try:
let response = backend.storeBookmark(backend.Bookmark(name: name, url: url)).result if not url.isEmptyOrWhitespace:
let response = browser.addBookmark(backend.Bookmark(name: name, url: url)).result
self.bookmarks[url] = BookmarkDto() self.bookmarks[url] = BookmarkDto()
self.bookmarks[url].url = url self.bookmarks[url].url = url
self.bookmarks[url].name = name self.bookmarks[url].name = name
discard response.getProp("imageUrl", self.bookmarks[url].imageUrl) discard response.getProp("imageUrl", self.bookmarks[url].imageUrl)
discard response.getProp("removed", self.bookmarks[url].removed)
discard response.getProp("deletedAt", self.bookmarks[url].deletedAt)
result.ok self.bookmarks[url] result.ok self.bookmarks[url]
except Exception as e: except Exception as e:
let errDescription = e.msg let errDescription = e.msg
@ -52,7 +100,7 @@ proc deleteBookmark*(self: Service, url: string): bool =
try: try:
if not self.bookmarks.hasKey(url): if not self.bookmarks.hasKey(url):
return return
discard backend.deleteBookmark(url) discard browser.removeBookmark(url).result
self.bookmarks.del(url) self.bookmarks.del(url)
except Exception as e: except Exception as e:
let errDescription = e.msg let errDescription = e.msg
@ -65,12 +113,14 @@ proc updateBookmark*(self: Service, oldUrl, newUrl, newName: string): R =
if not self.bookmarks.hasKey(oldUrl): if not self.bookmarks.hasKey(oldUrl):
return return
let response = backend.updateBookmark(oldUrl, backend.Bookmark(name: newName, url: newUrl)).result let response = browser.updateBookmark(oldUrl, backend.Bookmark(name: newName, url: newUrl)).result
self.bookmarks.del(oldUrl) self.bookmarks.del(oldUrl)
self.bookmarks[newUrl] = BookmarkDto() self.bookmarks[newUrl] = BookmarkDto()
self.bookmarks[newUrl].url = newUrl self.bookmarks[newUrl].url = newUrl
self.bookmarks[newUrl].name = newName self.bookmarks[newUrl].name = newName
discard response.getProp("imageUrl", self.bookmarks[newurl].imageUrl) discard response.getProp("imageUrl", self.bookmarks[newurl].imageUrl)
discard response.getProp("removed", self.bookmarks[newurl].removed)
discard response.getProp("deletedAt", self.bookmarks[newurl].deletedAt)
result.ok self.bookmarks[newUrl] result.ok self.bookmarks[newUrl]
except Exception as e: except Exception as e:
let errDescription = e.msg let errDescription = e.msg

View File

@ -16,6 +16,9 @@ type
Bookmark* = ref object of RootObj Bookmark* = ref object of RootObj
name* {.serializedFieldName("name").}: string name* {.serializedFieldName("name").}: string
url* {.serializedFieldName("url").}: string url* {.serializedFieldName("url").}: string
imageUrl* {.serializedFieldName("imageUrl").}: string
removed* {.serializedFieldName("removed").}: bool
deletedAt* {.serializedFieldName("deletedAt").}: int
Permission* = ref object of RootObj Permission* = ref object of RootObj
dapp* {.serializedFieldName("dapp").}: string dapp* {.serializedFieldName("dapp").}: string

28
src/backend/browser.nim Normal file
View File

@ -0,0 +1,28 @@
import json, strutils
import core, utils
import response_type
import ./backend
export response_type
proc addBookmark*(bookmark: backend.Bookmark): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("addBookmark".prefix, %*[{
"url": bookmark.url,
"name": bookmark.name,
"imageUrl": bookmark.imageUrl,
"removed": bookmark.removed,
"deletedAt": bookmark.deletedAt
}])
proc removeBookmark*(url: string): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("removeBookmark".prefix, %*[url])
proc updateBookmark*(oldUrl: string, bookmark: backend.Bookmark): RpcResponse[JsonNode] {.raises: [Exception].} =
result = callPrivateRPC("updateBookmark".prefix, %*[oldUrl, {
"url": bookmark.url,
"name": bookmark.name,
"imageUrl": bookmark.imageUrl,
"removed": bookmark.removed,
"deletedAt": bookmark.deletedAt
}])

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 961526556b98feec3a1584d93fbde6ac62fcb9ed Subproject commit 4f722b6fe8215fa02f54e02cb454b402e1728c8f