From 858caeca7381d8f200ac4cc2e5f64fd0f738d47a Mon Sep 17 00:00:00 2001 From: Pascal Precht <445106+PascalPrecht@users.noreply.github.com> Date: Wed, 8 Jun 2022 21:40:02 +0200 Subject: [PATCH] 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 --- src/app/boot/app_controller.nim | 2 +- .../core/signals/remote_signals/messages.nim | 7 ++ .../browser_section/bookmark/controller.nim | 16 ++++- .../main/browser_section/bookmark/module.nim | 6 +- .../modules/main/browser_section/module.nim | 4 +- .../service/bookmarks/dto/bookmark.nim | 4 ++ src/app_service/service/bookmarks/service.nim | 70 ++++++++++++++++--- src/backend/backend.nim | 3 + src/backend/browser.nim | 28 ++++++++ vendor/status-go | 2 +- 10 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 src/backend/browser.nim diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index b75142c3d6..e72990009c 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -153,7 +153,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = ) result.transactionService = transaction_service.newService(statusFoundation.events, statusFoundation.threadpool, 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.stickersService = stickers_service.newService( statusFoundation.events, diff --git a/src/app/core/signals/remote_signals/messages.nim b/src/app/core/signals/remote_signals/messages.nim index 513b7b1c70..f8e4f01d97 100644 --- a/src/app/core/signals/remote_signals/messages.nim +++ b/src/app/core/signals/remote_signals/messages.nim @@ -4,12 +4,14 @@ import base import ../../../../app_service/service/message/dto/[message, pinned_message_update, reaction, removed_message] 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/activity_center/dto/[notification] import ../../../../app_service/service/contacts/dto/[contacts, status_update] import ../../../../app_service/service/devices/dto/[device] type MessageSignal* = ref object of Signal + bookmarks*: seq[BookmarkDto] messages*: seq[MessageDto] pinnedMessages*: seq[PinnedMessageUpdateDto] chats*: seq[ChatDto] @@ -52,6 +54,11 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal = var currentStatus = event["event"]["currentStatus"].toStatusUpdateDto() 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: for jsonDevice in event["event"]["installations"]: signal.devices.add(jsonDevice.toDeviceDto()) diff --git a/src/app/modules/main/browser_section/bookmark/controller.nim b/src/app/modules/main/browser_section/bookmark/controller.nim index cfe923aabc..8af48115f0 100644 --- a/src/app/modules/main/browser_section/bookmark/controller.nim +++ b/src/app/modules/main/browser_section/bookmark/controller.nim @@ -3,16 +3,20 @@ import result import io_interface import ../../../../../app_service/service/bookmarks/service as bookmark_service +import ../../../../core/eventemitter type Controller* = ref object of RootObj delegate: io_interface.AccessInterface + events: EventEmitter bookmarkService: bookmark_service.Service proc newController*(delegate: io_interface.AccessInterface, + events: EventEmitter, bookmarkService: bookmark_service.Service): Controller = result = Controller() + result.events = events result.delegate = delegate result.bookmarkService = bookmarkService @@ -20,7 +24,17 @@ proc delete*(self: Controller) = discard 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] = return self.bookmarkService.getBookmarks() diff --git a/src/app/modules/main/browser_section/bookmark/module.nim b/src/app/modules/main/browser_section/bookmark/module.nim index 3dc68d9b25..ef9b706c23 100644 --- a/src/app/modules/main/browser_section/bookmark/module.nim +++ b/src/app/modules/main/browser_section/bookmark/module.nim @@ -6,6 +6,7 @@ import view import controller import ../../../../global/global_singleton import ../../../../../app_service/service/bookmarks/service as bookmark_service +import ../../../../core/eventemitter export io_interface @@ -17,13 +18,13 @@ type moduleLoaded: bool 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.delegate = delegate result.view = view.newView(result) result.viewVariant = newQVariant(result.view) result.moduleLoaded = false - result.controller = controller.newController(result, bookmarkService) + result.controller = controller.newController(result, events, bookmarkService) method delete*(self: Module) = self.view.delete @@ -32,6 +33,7 @@ method delete*(self: Module) = method load*(self: Module) = singletonInstance.engine.setRootContextProperty("bookmarkModule", self.viewVariant) + self.controller.init() self.view.load() method isLoaded*(self: Module): bool = diff --git a/src/app/modules/main/browser_section/module.nim b/src/app/modules/main/browser_section/module.nim index ca06f0d7d7..6e9c9dc313 100644 --- a/src/app/modules/main/browser_section/module.nim +++ b/src/app/modules/main/browser_section/module.nim @@ -42,7 +42,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, result.viewVariant = newQVariant(result.view) result.moduleLoaded = false 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.currentAccountModule = current_account_module.newModule(result, events, walletAccountService) @@ -94,4 +94,4 @@ method viewDidLoad*(self: Module) = self.checkIfModuleDidLoad() method openUrl*(self: Module, url: string) = - self.view.sendOpenUrlSignal(url) \ No newline at end of file + self.view.sendOpenUrlSignal(url) diff --git a/src/app_service/service/bookmarks/dto/bookmark.nim b/src/app_service/service/bookmarks/dto/bookmark.nim index 8d7c962f7d..fcc0ee385f 100644 --- a/src/app_service/service/bookmarks/dto/bookmark.nim +++ b/src/app_service/service/bookmarks/dto/bookmark.nim @@ -6,9 +6,13 @@ type BookmarkDto* = object name*: string url*: string imageUrl*: string + removed*: bool + deletedAt*: int proc toBookmarkDto*(jsonObj: JsonNode): BookmarkDto = result = BookmarkDto() discard jsonObj.getProp("name", result.name) discard jsonObj.getProp("url", result.url) discard jsonObj.getProp("imageUrl", result.imageUrl) + discard jsonObj.getProp("removed", result.removed) + discard jsonObj.getProp("deletedAt", result.deletedAt) diff --git a/src/app_service/service/bookmarks/service.nim b/src/app_service/service/bookmarks/service.nim index add9a14d31..4f04e524b0 100644 --- a/src/app_service/service/bookmarks/service.nim +++ b/src/app_service/service/bookmarks/service.nim @@ -1,48 +1,96 @@ -import Tables, json, sequtils, strformat, chronicles +import Tables, json, sequtils, strformat, chronicles, strutils import result include ../../common/json_utils import ./dto/bookmark as bookmark_dto +import ../../../app/core/eventemitter +import ../../../app/core/signals/types import ../../../backend/backend +import ../../../backend/browser export bookmark_dto logScope: topics = "bookmarks-service" +const SIGNAL_BOOKMARK_ADDED* = "bookmarkAdded" +const SIGNAL_BOOKMARK_REMOVED* = "bookmarkRemoved" +const SIGNAL_BOOKMARK_UPDATED* = "bookmarkUpdated" + type Service* = ref object of RootObj bookmarks: Table[string, BookmarkDto] # [url, BookmarkDto] + events: EventEmitter 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) = discard -proc newService*(): Service = +proc newService*(events: EventEmitter): Service = result = Service() + result.events = events result.bookmarks = initTable[string, BookmarkDto]() proc init*(self: Service) = try: let response = backend.getBookmarks() for bookmark in response.result.getElems().mapIt(it.toBookmarkDto()): + if not bookmark.removed: self.bookmarks[bookmark.url] = bookmark except Exception as e: let errDescription = e.msg 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] = return toSeq(self.bookmarks.values) proc storeBookmark*(self: Service, url, name: string): R = try: - let response = backend.storeBookmark(backend.Bookmark(name: name, url: url)).result - self.bookmarks[url] = BookmarkDto() - self.bookmarks[url].url = url - self.bookmarks[url].name = name - discard response.getProp("imageUrl", self.bookmarks[url].imageUrl) - result.ok self.bookmarks[url] + if not url.isEmptyOrWhitespace: + let response = browser.addBookmark(backend.Bookmark(name: name, url: url)).result + self.bookmarks[url] = BookmarkDto() + self.bookmarks[url].url = url + self.bookmarks[url].name = name + 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] except Exception as e: let errDescription = e.msg error "error: ", errDescription @@ -52,7 +100,7 @@ proc deleteBookmark*(self: Service, url: string): bool = try: if not self.bookmarks.hasKey(url): return - discard backend.deleteBookmark(url) + discard browser.removeBookmark(url).result self.bookmarks.del(url) except Exception as e: let errDescription = e.msg @@ -65,12 +113,14 @@ proc updateBookmark*(self: Service, oldUrl, newUrl, newName: string): R = if not self.bookmarks.hasKey(oldUrl): 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[newUrl] = BookmarkDto() self.bookmarks[newUrl].url = newUrl self.bookmarks[newUrl].name = newName 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] except Exception as e: let errDescription = e.msg diff --git a/src/backend/backend.nim b/src/backend/backend.nim index f08f56ee75..a8025aa01a 100644 --- a/src/backend/backend.nim +++ b/src/backend/backend.nim @@ -16,6 +16,9 @@ type Bookmark* = ref object of RootObj name* {.serializedFieldName("name").}: string url* {.serializedFieldName("url").}: string + imageUrl* {.serializedFieldName("imageUrl").}: string + removed* {.serializedFieldName("removed").}: bool + deletedAt* {.serializedFieldName("deletedAt").}: int Permission* = ref object of RootObj dapp* {.serializedFieldName("dapp").}: string diff --git a/src/backend/browser.nim b/src/backend/browser.nim new file mode 100644 index 0000000000..a7e38d8fd3 --- /dev/null +++ b/src/backend/browser.nim @@ -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 + }]) diff --git a/vendor/status-go b/vendor/status-go index 961526556b..4f722b6fe8 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit 961526556b98feec3a1584d93fbde6ac62fcb9ed +Subproject commit 4f722b6fe8215fa02f54e02cb454b402e1728c8f