diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index d1bddafee4..23360c2fec 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -209,7 +209,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.mailserversService = mailservers_service.newService(statusFoundation.events, statusFoundation.threadpool, result.settingsService, result.nodeConfigurationService, statusFoundation.fleetConfiguration) result.nodeService = node_service.newService(statusFoundation.events, result.settingsService, result.nodeConfigurationService) - result.gifService = gif_service.newService(result.settingsService) + result.gifService = gif_service.newService(result.settingsService, statusFoundation.events, statusFoundation.threadpool) result.ensService = ens_service.newService(statusFoundation.events, statusFoundation.threadpool, result.settingsService, result.walletAccountService, result.transactionService, result.networkService, result.tokenService) diff --git a/src/app/modules/main/chat_section/chat_content/input_area/controller.nim b/src/app/modules/main/chat_section/chat_content/input_area/controller.nim index 8c43b65055..ec449c9bd0 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/controller.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/controller.nim @@ -4,11 +4,13 @@ import ../../../../../../app_service/service/community/service as community_serv import ../../../../../../app_service/service/chat/service as chat_service import ../../../../../../app_service/service/gif/service as gif_service import ../../../../../../app_service/service/gif/dto +import ../../../../../core/eventemitter type Controller* = ref object of RootObj delegate: io_interface.AccessInterface sectionId: string + events: EventEmitter chatId: string belongsToCommunity: bool communityService: community_service.Service @@ -17,6 +19,7 @@ type proc newController*( delegate: io_interface.AccessInterface, + events: EventEmitter, sectionId: string, chatId: string, belongsToCommunity: bool, @@ -26,6 +29,7 @@ proc newController*( ): Controller = result = Controller() result.delegate = delegate + result.events = events result.sectionId = chatId result.chatId = chatId result.belongsToCommunity = belongsToCommunity @@ -37,7 +41,13 @@ proc delete*(self: Controller) = discard proc init*(self: Controller) = - discard + self.events.on(SIGNAL_LOAD_RECENT_GIFS_DONE) do(e:Args): + let args = GifsArgs(e) + self.delegate.loadRecentGifsDone(args.gifs) + + self.events.on(SIGNAL_LOAD_FAVORITE_GIFS_DONE) do(e:Args): + let args = GifsArgs(e) + self.delegate.loadFavoriteGifsDone(args.gifs) proc getChatId*(self: Controller): string = return self.chatId @@ -84,6 +94,12 @@ proc getTrendingsGifs*(self: Controller): seq[GifDto] = proc getRecentsGifs*(self: Controller): seq[GifDto] = return self.gifService.getRecents() +proc loadRecentGifs*(self: Controller) = + self.gifService.asyncLoadRecentGifs() + +proc loadFavoriteGifs*(self: Controller) = + self.gifService.asyncLoadFavoriteGifs() + proc getFavoritesGifs*(self: Controller): seq[GifDto] = return self.gifService.getFavorites() diff --git a/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim b/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim index 5711ec79b9..0c67fee465 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim @@ -50,9 +50,21 @@ method getTrendingsGifs*(self: AccessInterface): seq[GifDto] {.base.} = method getRecentsGifs*(self: AccessInterface): seq[GifDto] {.base.} = raise newException(ValueError, "No implementation available") +method loadRecentGifs*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method loadRecentGifsDone*(self: AccessInterface, gifs: seq[GifDto]) {.base.} = + raise newException(ValueError, "No implementation available") + method getFavoritesGifs*(self: AccessInterface): seq[GifDto] {.base.} = raise newException(ValueError, "No implementation available") +method loadFavoriteGifs*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method loadFavoriteGifsDone*(self: AccessInterface, gifs: seq[GifDto]) {.base.} = + raise newException(ValueError, "No implementation available") + method toggleFavoriteGif*(self: AccessInterface, item: GifDto) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/chat_section/chat_content/input_area/module.nim b/src/app/modules/main/chat_section/chat_content/input_area/module.nim index 20107c866a..b2d1f4d749 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/module.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/module.nim @@ -3,6 +3,7 @@ import io_interface import ../io_interface as delegate_interface import view, controller import ../../../../../global/global_singleton +import ../../../../../core/eventemitter import ../../../../../../app_service/service/chat/service as chat_service import ../../../../../../app_service/service/community/service as community_service @@ -21,6 +22,7 @@ type proc newModule*( delegate: delegate_interface.AccessInterface, + events: EventEmitter, sectionId: string, chatId: string, belongsToCommunity: bool, @@ -33,7 +35,7 @@ proc newModule*( result.delegate = delegate result.view = view.newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService) + result.controller = controller.newController(result, events, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService) result.moduleLoaded = false method delete*(self: Module) = @@ -98,9 +100,21 @@ method getTrendingsGifs*(self: Module): seq[GifDto] = method getRecentsGifs*(self: Module): seq[GifDto] = return self.controller.getRecentsGifs() +method loadRecentGifs*(self: Module) = + self.controller.loadRecentGifs() + +method loadRecentGifsDone*(self: Module, gifs: seq[GifDto]) = + self.view.updateGifColumns(gifs) + method getFavoritesGifs*(self: Module): seq[GifDto] = return self.controller.getFavoritesGifs() +method loadFavoriteGifs*(self: Module) = + self.controller.loadFavoriteGifs() + +method loadFavoriteGifsDone*(self: Module, gifs: seq[GifDto]) = + self.view.updateGifColumns(gifs) + method toggleFavoriteGif*(self: Module, item: GifDto) = self.controller.toggleFavoriteGif(item) diff --git a/src/app/modules/main/chat_section/chat_content/input_area/view.nim b/src/app/modules/main/chat_section/chat_content/input_area/view.nim index 27dec24e28..65b41d46ab 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/view.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/view.nim @@ -80,7 +80,7 @@ QtObject: read = getGifColumnC notify = gifLoaded - proc updateGifColumns(self: View, data: seq[GifDto]) = + proc updateGifColumns*(self: View, data: seq[GifDto]) = var columnAData: seq[GifDto] = @[] var columnAHeight = 0 var columnBData: seq[GifDto] = @[] @@ -115,11 +115,21 @@ QtObject: proc getRecentsGifs*(self: View) {.slot.} = let data = self.delegate.getRecentsGifs() - self.updateGifColumns(data) + if data.len > 0: + self.updateGifColumns(data) + return + + # recent gifs were not loaded yet, so we do it now + self.delegate.loadRecentGifs() proc getFavoritesGifs*(self: View) {.slot.} = let data = self.delegate.getFavoritesGifs() - self.updateGifColumns(data) + if data.len > 0: + self.updateGifColumns(data) + return + + # favorite gifs were not loaded yet, so we do it now + self.delegate.loadFavoriteGifs() proc findGifDto(self: View, id: string): GifDto = for item in self.gifColumnAModel.gifs: diff --git a/src/app/modules/main/chat_section/chat_content/module.nim b/src/app/modules/main/chat_section/chat_content/module.nim index ccae3f93dd..b3c0a1b179 100644 --- a/src/app/modules/main/chat_section/chat_content/module.nim +++ b/src/app/modules/main/chat_section/chat_content/module.nim @@ -52,7 +52,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt isUsersListAvailable, settingsService, nodeConfigurationService, contactService, chatService, communityService, messageService) result.moduleLoaded = false - result.inputAreaModule = input_area_module.newModule(result, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService) + result.inputAreaModule = input_area_module.newModule(result, events, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService) result.messagesModule = messages_module.newModule(result, events, sectionId, chatId, belongsToCommunity, contactService, communityService, chatService, messageService, mailserversService) result.usersModule = diff --git a/src/app_service/service/gif/async_tasks.nim b/src/app_service/service/gif/async_tasks.nim new file mode 100644 index 0000000000..e90cdca52e --- /dev/null +++ b/src/app_service/service/gif/async_tasks.nim @@ -0,0 +1,19 @@ +include ../../common/json_utils +include ../../../app/core/tasks/common +import ../../../backend/gifs as status_go + +type + AsyncGetRecentGifsTaskArg = ref object of QObjectTaskArg + +const asyncGetRecentGifsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncGetRecentGifsTaskArg](argEncoded) + let response = status_go.getRecentGifs() + arg.finish(response) + +type + AsyncGetFavoriteGifsTaskArg = ref object of QObjectTaskArg + +const asyncGetFavoriteGifsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncGetFavoriteGifsTaskArg](argEncoded) + let response = status_go.getFavoriteGifs() + arg.finish(response) diff --git a/src/app_service/service/gif/service.nim b/src/app_service/service/gif/service.nim index 091280840a..857bbac21e 100644 --- a/src/app_service/service/gif/service.nim +++ b/src/app_service/service/gif/service.nim @@ -4,11 +4,17 @@ import os import uri import chronicles import sequtils +import NimQml import ../settings/service as settings_service +import ../../../app/core/eventemitter import ../../../backend/backend +import ../../../app/core/tasks/[qt, threadpool] +import ../../../app/core/[main] import ./dto +include ./async_tasks + logScope: topics = "gif-service" @@ -23,127 +29,192 @@ let TENOR_API_KEY_RESOLVED = else: TENOR_API_KEY +const SIGNAL_LOAD_RECENT_GIFS_STARTED* = "loadRecentGifsStarted" +const SIGNAL_LOAD_RECENT_GIFS_DONE* = "loadRecentGifsDone" + +const SIGNAL_LOAD_FAVORITE_GIFS_STARTED* = "loadFavoriteGifsStarted" +const SIGNAL_LOAD_FAVORITE_GIFS_DONE* = "loadFavoriteGifsDone" + type - Service* = ref object of RootObj - settingsService: settings_service.Service - favorites: seq[GifDto] - recents: seq[GifDto] + GifsArgs* = ref object of Args + gifs*: seq[GifDto] + error*: string -proc delete*(self: Service) = - discard +QtObject: + type + Service* = ref object of QObject + threadpool: ThreadPool + settingsService: settings_service.Service + favorites: seq[GifDto] + recents: seq[GifDto] + events: EventEmitter -proc newService*(settingsService: settings_service.Service): Service = - result = Service() - result.settingsService = settingsService - result.favorites = @[] - result.recents = @[] + proc delete*(self: Service) = + discard -proc setTenorAPIKey(self: Service) = - try: - let response = backend.setTenorAPIKey(TENOR_API_KEY_RESOLVED) - if(not response.error.isNil): - error "error setTenorAPIKey: ", errDescription = response.error.message + proc newService*(settingsService: settings_service.Service, events: EventEmitter, threadpool: ThreadPool): Service = + result = Service() + result.QObject.setup + result.settingsService = settingsService + result.events = events + result.threadpool = threadpool + result.favorites = @[] + result.recents = @[] - except Exception as e: - error "error: ", procName="setTenorAPIKey", errName = e.name, errDesription = e.msg + proc setTenorAPIKey(self: Service) = + try: + let response = backend.setTenorAPIKey(TENOR_API_KEY_RESOLVED) + if(not response.error.isNil): + error "error setTenorAPIKey: ", errDescription = response.error.message -proc getRecentGifs(self: Service) = - try: - let response = backend.getRecentGifs() + except Exception as e: + error "error: ", procName="setTenorAPIKey", errName = e.name, errDesription = e.msg - if(not response.error.isNil): - error "error getRecentGifs: ", errDescription = response.error.message + proc getRecentGifs(self: Service) = + try: + let response = backend.getRecentGifs() - self.recents = map(response.result.getElems(), settingToGifDto) + if(not response.error.isNil): + error "error getRecentGifs: ", errDescription = response.error.message - except Exception as e: - error "error: ", procName="getRecentGifs", errName = e.name, errDesription = e.msg + self.recents = map(response.result.getElems(), settingToGifDto) -proc getFavoriteGifs(self: Service) = - try: - let response = backend.getFavoriteGifs() + except Exception as e: + error "error: ", procName="getRecentGifs", errName = e.name, errDesription = e.msg - if(not response.error.isNil): - error "error getFavoriteGifs: ", errDescription = response.error.message + proc getFavoriteGifs(self: Service) = + try: + let response = backend.getFavoriteGifs() - self.favorites = map(response.result.getElems(), settingToGifDto) + if(not response.error.isNil): + error "error getFavoriteGifs: ", errDescription = response.error.message - except Exception as e: - error "error: ", procName="getFavoriteGifs", errName = e.name, errDesription = e.msg + self.favorites = map(response.result.getElems(), settingToGifDto) -proc init*(self: Service) = - # set Tenor API Key - self.setTenorAPIKey() + except Exception as e: + error "error: ", procName="getFavoriteGifs", errName = e.name, errDesription = e.msg - # get recent and favorite gifs on the database - self.getRecentGifs() - self.getFavoriteGifs() + proc asyncLoadRecentGifs*(self: Service) = + self.events.emit(SIGNAL_LOAD_RECENT_GIFS_STARTED, Args()) + try: + let arg = AsyncGetRecentGifsTaskArg( + tptr: cast[ByteAddress](asyncGetRecentGifsTask), + vptr: cast[ByteAddress](self.vptr), + slot: "onAsyncGetRecentGifsDone" + ) + self.threadpool.start(arg) + except Exception as e: + error "Error loading recent gifs", msg = e.msg -proc tenorQuery(self: Service, path: string): seq[GifDto] = - try: - let response = backend.fetchGifs(path) - let doc = response.result.str.parseJson() + proc onAsyncGetRecentGifsDone*(self: Service, response: string) {.slot.} = + try: + let rpcResponseObj = response.parseJson + if (rpcResponseObj{"error"}.kind != JNull): + let error = Json.decode($rpcResponseObj["error"], RpcError) + error "error loading recent gifs", msg = error.message + return - var items: seq[GifDto] = @[] - for json in doc["results"]: - items.add(tenorToGifDto(json)) + self.recents = map(rpcResponseObj{"result"}.getElems(), settingToGifDto) + self.events.emit(SIGNAL_LOAD_RECENT_GIFS_DONE, GifsArgs(gifs: self.recents)) + except Exception as e: + let errMsg = e.msg + error "error: ", errMsg - return items - except: - return @[] + proc asyncLoadFavoriteGifs*(self: Service) = + self.events.emit(SIGNAL_LOAD_FAVORITE_GIFS_STARTED, Args()) + try: + let arg = AsyncGetFavoriteGifsTaskArg( + tptr: cast[ByteAddress](asyncGetFavoriteGifsTask), + vptr: cast[ByteAddress](self.vptr), + slot: "onAsyncGetFavoriteGifsDone" + ) + self.threadpool.start(arg) + except Exception as e: + error "Error loading favorite gifs", msg = e.msg -proc search*(self: Service, query: string): seq[GifDto] = - return self.tenorQuery(fmt("search?q={encodeUrl(query)}")) + proc onAsyncGetFavoriteGifsDone*(self: Service, response: string) {.slot.} = + try: + let rpcResponseObj = response.parseJson + if (rpcResponseObj{"error"}.kind != JNull): + let error = Json.decode($rpcResponseObj["error"], RpcError) + error "error loading favorite gifs", msg = error.message + return -proc getTrendings*(self: Service): seq[GifDto] = - return self.tenorQuery("trending?") + self.favorites = map(rpcResponseObj{"result"}.getElems(), settingToGifDto) + self.events.emit(SIGNAL_LOAD_FAVORITE_GIFS_DONE, GifsArgs(gifs: self.favorites)) + except Exception as e: + let errMsg = e.msg + error "error: ", errMsg -proc getRecents*(self: Service): seq[GifDto] = - return self.recents + proc init*(self: Service) = + # set Tenor API Key + self.setTenorAPIKey() -proc addToRecents*(self: Service, gifDto: GifDto) = - let recents = self.getRecents() - var newRecents: seq[GifDto] = @[gifDto] - var idx = 0 + proc tenorQuery(self: Service, path: string): seq[GifDto] = + try: + let response = backend.fetchGifs(path) + let doc = response.result.str.parseJson() - while idx < MAX_RECENT - 1: - if idx >= recents.len: - break + var items: seq[GifDto] = @[] + for json in doc["results"]: + items.add(tenorToGifDto(json)) - if recents[idx].id == gifDto.id: + return items + except: + return @[] + + proc search*(self: Service, query: string): seq[GifDto] = + return self.tenorQuery(fmt("search?q={encodeUrl(query)}")) + + proc getTrendings*(self: Service): seq[GifDto] = + return self.tenorQuery("trending?") + + proc getRecents*(self: Service): seq[GifDto] = + return self.recents + + proc addToRecents*(self: Service, gifDto: GifDto) = + let recents = self.getRecents() + var newRecents: seq[GifDto] = @[gifDto] + var idx = 0 + + while idx < MAX_RECENT - 1: + if idx >= recents.len: + break + + if recents[idx].id == gifDto.id: + idx += 1 + continue + + newRecents.add(recents[idx]) idx += 1 - continue - newRecents.add(recents[idx]) - idx += 1 + self.recents = newRecents + let recent = %*{"items": map(newRecents, toJsonNode)} + discard backend.updateRecentGifs(recent) - self.recents = newRecents - let recent = %*{"items": map(newRecents, toJsonNode)} - discard backend.updateRecentGifs(recent) + proc getFavorites*(self: Service): seq[GifDto] = + return self.favorites -proc getFavorites*(self: Service): seq[GifDto] = - return self.favorites + proc isFavorite*(self: Service, gifDto: GifDto): bool = + for favorite in self.favorites: + if favorite.id == gifDto.id: + return true + return false -proc isFavorite*(self: Service, gifDto: GifDto): bool = - for favorite in self.favorites: - if favorite.id == gifDto.id: - return true - return false + proc toggleFavorite*(self: Service, gifDto: GifDto) = + var newFavorites: seq[GifDto] = @[] + var found = false -proc toggleFavorite*(self: Service, gifDto: GifDto) = - var newFavorites: seq[GifDto] = @[] - var found = false + for favoriteGif in self.getFavorites(): + if favoriteGif.id == gifDto.id: + found = true + continue - for favoriteGif in self.getFavorites(): - if favoriteGif.id == gifDto.id: - found = true - continue + newFavorites.add(favoriteGif) - newFavorites.add(favoriteGif) + if not found: + newFavorites.add(gifDto) - if not found: - newFavorites.add(gifDto) - - self.favorites = newFavorites - let favorites = %*{"items": map(newFavorites, toJsonNode)} - discard backend.updateFavoriteGifs(favorites) + self.favorites = newFavorites + let favorites = %*{"items": map(newFavorites, toJsonNode)} + discard backend.updateFavoriteGifs(favorites) diff --git a/src/backend/gifs.nim b/src/backend/gifs.nim new file mode 100644 index 0000000000..90e5345be9 --- /dev/null +++ b/src/backend/gifs.nim @@ -0,0 +1,13 @@ +import json, strutils +import core +import response_type + +export response_type + +proc getRecentGifs*(): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* [] + result = callPrivateRPC("gif_getRecentGifs", payload) + +proc getFavoriteGifs*(): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* [] + result = callPrivateRPC("gif_getFavoriteGifs", payload)