refactor(@desktop): gif section

This commit is contained in:
Anthony Laibe 2022-01-13 09:58:42 +01:00 committed by Sale Djenic
parent e717a515a4
commit 848308ed04
23 changed files with 626 additions and 86 deletions

View File

@ -29,6 +29,7 @@ import ../../app_service/service/activity_center/service as activity_center_serv
import ../../app_service/service/saved_address/service as saved_address_service
import ../../app_service/service/devices/service as devices_service
import ../../app_service/service/mailservers/service as mailservers_service
import ../../app_service/service/gif/service as gif_service
import ../modules/startup/module as startup_module
import ../modules/main/module as main_module
@ -80,6 +81,7 @@ type
devicesService: devices_service.Service
mailserversService: mailservers_service.Service
nodeService: node_service.Service
gifService: gif_service.Service
# Modules
startupModule: startup_module.AccessInterface
@ -165,6 +167,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.settingsService, result.nodeConfigurationService, statusFoundation.fleetConfiguration)
result.nodeService = node_service.newService(statusFoundation.events, statusFoundation.threadpool,
result.settingsService)
result.gifService = gif_service.newService(result.settingsService)
# Modules
result.startupModule = startup_module.newModule[AppController](
@ -201,7 +204,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.nodeConfigurationService,
result.devicesService,
result.mailserversService,
result.nodeService
result.nodeService,
result.gifService,
)
# Do connections
@ -213,6 +217,7 @@ proc delete*(self: AppController) =
self.keychainService.delete
self.contactsService.delete
self.bookmarkService.delete
self.gifService.delete
self.startupModule.delete
self.mainModule.delete
self.ethService.delete
@ -306,7 +311,8 @@ proc load(self: AppController) =
self.contactsService,
self.chatService,
self.communityService,
self.messageService
self.messageService,
self.gifService,
)
proc userLoggedIn*(self: AppController) =

View File

@ -3,6 +3,8 @@ import io_interface
import ../../../../../../app_service/service/community/service as community_service
import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/gif/service as gif_service
import ../../../../../../app_service/service/gif/dto
export controller_interface
@ -14,6 +16,7 @@ type
belongsToCommunity: bool
communityService: community_service.Service
chatService: chat_service.Service
gifService: gif_service.Service
proc newController*(
delegate: io_interface.AccessInterface,
@ -21,7 +24,8 @@ proc newController*(
chatId: string,
belongsToCommunity: bool,
chatService: chat_service.Service,
communityService: community_service.Service
communityService: community_service.Service,
gifService: gif_service.Service
): Controller =
result = Controller()
result.delegate = delegate
@ -30,6 +34,7 @@ proc newController*(
result.belongsToCommunity = belongsToCommunity
result.chatService = chatService
result.communityService = communityService
result.gifService = gifService
method delete*(self: Controller) =
discard
@ -71,4 +76,25 @@ method acceptRequestAddressForTransaction*(self: Controller, messageId: string,
self.chatService.acceptRequestAddressForTransaction(messageId, address)
method acceptRequestTransaction*(self: Controller, transactionHash: string, messageId: string, signature: string) =
self.chatService.acceptRequestTransaction(transactionHash, messageId, signature)
self.chatService.acceptRequestTransaction(transactionHash, messageId, signature)
method searchGifs*(self: Controller, query: string): seq[GifDto] =
return self.gifService.search(query)
method getTrendingsGifs*(self: Controller): seq[GifDto] =
return self.gifService.getTrendings()
method getRecentsGifs*(self: Controller): seq[GifDto] =
return self.gifService.getRecents()
method getFavoritesGifs*(self: Controller): seq[GifDto] =
return self.gifService.getFavorites()
method toggleFavoriteGif*(self: Controller, item: GifDto) =
self.gifService.toggleFavorite(item)
method addToRecentsGif*(self: Controller, item: GifDto) =
self.gifService.addToRecents(item)
method isFavorite*(self: Controller, item: GifDto): bool =
return self.gifService.isFavorite(item)

View File

@ -1,3 +1,4 @@
import ../../../../../../app_service/service/gif/dto
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -38,4 +39,24 @@ method acceptRequestAddressForTransaction*(self: AccessInterface, messageId: str
method acceptRequestTransaction*(self: AccessInterface, transactionHash: string, messageId: string, signature: string) {.base.} =
raise newException(ValueError, "No implementation available")
method searchGifs*(self: AccessInterface, query: string): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method getTrendingsGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method getRecentsGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method getFavoritesGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method toggleFavoriteGif*(self: AccessInterface, item: GifDto) {.base.} =
raise newException(ValueError, "No implementation available")
method addToRecentsGif*(self: AccessInterface, item: GifDto) {.base.} =
raise newException(ValueError, "No implementation available")
method isFavorite*(self: AccessInterface, item: GifDto): bool {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,54 @@
import NimQml, Tables, sequtils
import ../../../../../../app_service/service/gif/dto
type
GifRoles {.pure.} = enum
Url = UserRole + 1
Id = UserRole + 2
Title = UserRole + 3
TinyUrl = UserRole + 4
QtObject:
type
GifColumnModel* = ref object of QAbstractListModel
gifs*: seq[GifDto]
proc setup(self: GifColumnModel) = self.QAbstractListModel.setup
proc delete(self: GifColumnModel) = self.QAbstractListModel.delete
proc newGifColumnModel*(): GifColumnModel =
new(result, delete)
result.gifs = @[]
result.setup()
proc setNewData*(self: GifColumnModel, gifs: seq[GifDto]) =
self.beginResetModel()
self.gifs = gifs
self.endResetModel()
method rowCount(self: GifColumnModel, index: QModelIndex = nil): int =
self.gifs.len
method data(self: GifColumnModel, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.gifs.len:
return
let gif = self.gifs[index.row]
case role.GifRoles:
of GifRoles.Url: result = newQVariant(gif.url)
of GifRoles.Id: result = newQVariant(gif.id)
of GifRoles.Title: result = newQVariant(gif.title)
of GifRoles.TinyUrl: result = newQVariant(gif.tinyUrl)
method roleNames(self: GifColumnModel): Table[int, string] =
{
GifRoles.Url.int:"url",
GifRoles.Id.int:"id",
GifRoles.Title.int:"title",
GifRoles.TinyUrl.int:"tinyUrl",
}.toTable

View File

@ -6,6 +6,8 @@ import ../../../../../global/global_singleton
import ../../../../../../app_service/service/chat/service as chat_service
import ../../../../../../app_service/service/community/service as community_service
import ../../../../../../app_service/service/gif/service as gif_service
import ../../../../../../app_service/service/gif/dto
export io_interface
@ -23,14 +25,15 @@ proc newModule*(
chatId: string,
belongsToCommunity: bool,
chatService: chat_service.Service,
communityService: community_service.Service
communityService: community_service.Service,
gifService: gif_service.Service,
):
Module =
result = Module()
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, sectionId, chatId, belongsToCommunity, chatService, communityService)
result.controller = controller.newController(result, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService)
result.moduleLoaded = false
method delete*(self: Module) =
@ -39,6 +42,8 @@ method delete*(self: Module) =
self.controller.delete
method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("chatSectionChatContentInputArea", self.viewVariant)
self.controller.init()
self.view.load()
@ -82,4 +87,25 @@ method acceptRequestAddressForTransaction*(self: Module, messageId: string, addr
self.controller.acceptRequestAddressForTransaction(messageId, address)
method acceptRequestTransaction*(self: Module, transactionHash: string, messageId: string, signature: string) =
self.controller.acceptRequestTransaction(transactionHash, messageId, signature)
self.controller.acceptRequestTransaction(transactionHash, messageId, signature)
method searchGifs*(self: Module, query: string): seq[GifDto] =
return self.controller.searchGifs(query)
method getTrendingsGifs*(self: Module): seq[GifDto] =
return self.controller.getTrendingsGifs()
method getRecentsGifs*(self: Module): seq[GifDto] =
return self.controller.getRecentsGifs()
method getFavoritesGifs*(self: Module): seq[GifDto] =
return self.controller.getFavoritesGifs()
method toggleFavoriteGif*(self: Module, item: GifDto) =
self.controller.toggleFavoriteGif(item)
method addToRecentsGif*(self: Module, item: GifDto) =
self.controller.addToRecentsGif(item)
method isFavorite*(self: Module, item: GifDto): bool =
return self.controller.isFavorite(item)

View File

@ -1,5 +1,8 @@
import NimQml
import ../../../../../../../app_service/service/gif/dto
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
@ -34,4 +37,25 @@ method acceptRequestAddressForTransaction*(self: AccessInterface, messageId: str
raise newException(ValueError, "No implementation available")
method acceptRequestTransaction*(self: AccessInterface, transactionHash: string, messageId: string, signature: string) {.base.} =
raise newException(ValueError, "No implementation available")
method searchGifs*(self: AccessInterface, query: string): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method getTrendingsGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method getRecentsGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method getFavoritesGifs*(self: AccessInterface): seq[GifDto] {.base.} =
raise newException(ValueError, "No implementation available")
method toggleFavoriteGif*(self: AccessInterface, item: GifDto) {.base.} =
raise newException(ValueError, "No implementation available")
method addToRecentsGif*(self: AccessInterface, item: GifDto) {.base.} =
raise newException(ValueError, "No implementation available")
method isFavorite*(self: AccessInterface, item: GifDto): bool {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,12 +1,17 @@
import NimQml
import model
import io_interface
import ./model
import ./io_interface
import ./gif_column_model
import ../../../../../../app_service/service/gif/dto
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
model: Model
gifColumnAModel: GifColumnModel
gifColumnBModel: GifColumnModel
gifColumnCModel: GifColumnModel
proc delete*(self: View) =
self.model.delete
@ -17,6 +22,9 @@ QtObject:
result.QObject.setup
result.delegate = delegate
result.model = newModel()
result.gifColumnAModel = newGifColumnModel()
result.gifColumnBModel = newGifColumnModel()
result.gifColumnCModel = newGifColumnModel()
proc load*(self: View) =
self.delegate.viewDidLoad()
@ -48,4 +56,97 @@ QtObject:
proc acceptRequestTransaction*(self: View, transactionHash: string, messageId: string, signature: string) {.slot.} =
self.delegate.acceptRequestTransaction(transactionHash, messageId, signature)
proc gifLoaded*(self: View) {.signal.}
proc getGifColumnA*(self: View): QVariant {.slot.} =
result = newQVariant(self.gifColumnAModel)
QtProperty[QVariant] gifColumnA:
read = getGifColumnA
notify = gifLoaded
proc getGifColumnB*(self: View): QVariant {.slot.} =
result = newQVariant(self.gifColumnBModel)
QtProperty[QVariant] gifColumnB:
read = getGifColumnB
notify = gifLoaded
proc getGifColumnC*(self: View): QVariant {.slot.} =
result = newQVariant(self.gifColumnCModel)
QtProperty[QVariant] gifColumnC:
read = getGifColumnC
notify = gifLoaded
proc updateGifColumns(self: View, data: seq[GifDto]) =
var columnAData: seq[GifDto] = @[]
var columnAHeight = 0
var columnBData: seq[GifDto] = @[]
var columnBHeight = 0
var columnCData: seq[GifDto] = @[]
var columnCHeight = 0
for item in data:
if columnAHeight <= columnBHeight:
columnAData.add(item)
columnAHeight += item.height
elif columnBHeight <= columnCHeight:
columnBData.add(item)
columnBHeight += item.height
else:
columnCData.add(item)
columnCHeight += item.height
self.gifColumnAModel.setNewData(columnAData)
self.gifColumnBModel.setNewData(columnBData)
self.gifColumnCModel.setNewData(columnCData)
self.gifLoaded()
proc searchGifs*(self: View, query: string) {.slot.} =
let data = self.delegate.searchGifs(query)
self.updateGifColumns(data)
proc getTrendingsGifs*(self: View) {.slot.} =
let data = self.delegate.getTrendingsGifs()
self.updateGifColumns(data)
proc getRecentsGifs*(self: View) {.slot.} =
let data = self.delegate.getRecentsGifs()
self.updateGifColumns(data)
proc getFavoritesGifs*(self: View) {.slot.} =
let data = self.delegate.getFavoritesGifs()
self.updateGifColumns(data)
proc findGifDto(self: View, id: string): GifDto =
for item in self.gifColumnAModel.gifs:
if item.id == id:
return item
for item in self.gifColumnBModel.gifs:
if item.id == id:
return item
for item in self.gifColumnCModel.gifs:
if item.id == id:
return item
raise newException(ValueError, "Invalid id " & $id)
proc toggleFavoriteGif*(self: View, id: string, reload: bool = false) {.slot.} =
let gifItem = self.findGifDto(id)
self.delegate.toggleFavoriteGif(gifItem)
if reload:
self.getFavoritesGifs()
proc addToRecentsGif*(self: View, id: string) {.slot.} =
let gifItem = self.findGifDto(id)
self.delegate.addToRecentsGif(gifItem)
proc isFavorite*(self: View, id: string): bool {.slot.} =
let gifItem = self.findGifDto(id)
return self.delegate.isFavorite(gifItem)

View File

@ -16,6 +16,7 @@ import ../../../../../app_service/service/settings/service_interface as settings
import ../../../../../app_service/service/contacts/service as contact_service
import ../../../../../app_service/service/chat/service as chat_service
import ../../../../../app_service/service/community/service as community_service
import ../../../../../app_service/service/gif/service as gif_service
import ../../../../../app_service/service/message/service as message_service
export io_interface
@ -37,7 +38,7 @@ type
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
belongsToCommunity: bool, isUsersListAvailable: bool, settingsService: settings_service.ServiceInterface,
contactService: contact_service.Service, chatService: chat_service.Service,
communityService: community_service.Service, messageService: message_service.Service):
communityService: community_service.Service, messageService: message_service.Service, gifService: gif_service.Service):
Module =
result = Module()
result.delegate = delegate
@ -47,7 +48,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
isUsersListAvailable, settingsService, contactService, chatService, communityService, messageService)
result.moduleLoaded = false
result.inputAreaModule = input_area_module.newModule(result, sectionId, chatId, belongsToCommunity, chatService, communityService)
result.inputAreaModule = input_area_module.newModule(result, sectionId, chatId, belongsToCommunity, chatService, communityService, gifService)
result.messagesModule = messages_module.newModule(result, events, sectionId, chatId, belongsToCommunity,
contactService, communityService, chatService, messageService)
result.usersModule = users_module.newModule(result, events, sectionId, chatId, belongsToCommunity, isUsersListAvailable,

View File

@ -14,6 +14,7 @@ import ../../../../app_service/service/contacts/service as contact_service
import ../../../../app_service/service/chat/service as chat_service
import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/message/service as message_service
import ../../../../app_service/service/gif/service as gif_service
export io_interface
@ -39,7 +40,7 @@ proc newModule*(
contactService: contact_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service
messageService: message_service.Service,
): Module =
result = Module()
result.delegate = delegate
@ -73,10 +74,11 @@ proc addSubmodule(self: Module, chatId: string, belongToCommunity: bool, isUsers
contactService: contact_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service) =
messageService: message_service.Service,
gifService: gif_service.Service) =
self.chatContentModules[chatId] = chat_content_module.newModule(self, events, self.controller.getMySectionId(), chatId,
belongToCommunity, isUsersListAvailable, settingsService, contactService, chatService, communityService,
messageService)
messageService, gifService)
proc removeSubmodule(self: Module, chatId: string) =
if(not self.chatContentModules.contains(chatId)):
@ -88,7 +90,8 @@ proc buildChatUI(self: Module, events: EventEmitter,
contactService: contact_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service) =
messageService: message_service.Service,
gifService: gif_service.Service) =
let types = @[ChatType.OneToOne, ChatType.Public, ChatType.PrivateGroupChat]
let chats = self.controller.getChatDetailsForChatTypes(types)
@ -110,7 +113,7 @@ proc buildChatUI(self: Module, events: EventEmitter,
hasNotification, notificationsCount, c.muted, false, 0)
self.view.chatsModel().appendItem(item)
self.addSubmodule(c.id, false, isUsersListAvailable, events, settingsService, contactService, chatService,
communityService, messageService)
communityService, messageService, gifService)
# make the first Public chat active when load the app
if(selectedItemId.len == 0 and c.chatType == ChatType.Public):
@ -123,7 +126,8 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
contactService: contact_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service) =
messageService: message_service.Service,
gifService: gif_service.Service) =
var selectedItemId = ""
var selectedSubItemId = ""
let communities = self.controller.getJoinedCommunities()
@ -143,7 +147,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
chatDto.chatType.int, amIChatAdmin, hasNotification, notificationsCount, chatDto.muted, false, c.position)
self.view.chatsModel().appendItem(channelItem)
self.addSubmodule(chatDto.id, true, true, events, settingsService, contactService, chatService, communityService,
messageService)
messageService, gifService)
# make the first channel which doesn't belong to any category active when load the app
if(selectedItemId.len == 0):
@ -173,7 +177,7 @@ proc buildCommunityUI(self: Module, events: EventEmitter,
false, c.position)
categoryChannels.add(channelItem)
self.addSubmodule(chatDto.id, true, true, events, settingsService, contactService, chatService, communityService,
messageService)
messageService, gifService)
# in case there is no channels beyond categories,
# make the first channel of the first category active when load the app
@ -216,14 +220,15 @@ method load*(self: Module, events: EventEmitter,
contactService: contact_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service) =
messageService: message_service.Service,
gifService: gif_service.Service) =
self.controller.init()
self.view.load()
if(self.controller.isCommunity()):
self.buildCommunityUI(events, settingsService, contactService, chatService, communityService, messageService)
self.buildCommunityUI(events, settingsService, contactService, chatService, communityService, messageService, gifService)
else:
self.buildChatUI(events, settingsService, contactService, chatService, communityService, messageService)
self.buildChatUI(events, settingsService, contactService, chatService, communityService, messageService, gifService)
self.initContactRequestsModel() # we do this only in case of chat section (not in case of communities)
for cModule in self.chatContentModules.values:
@ -309,7 +314,8 @@ method addNewChat*(
contactService: contact_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service) =
messageService: message_service.Service,
gifService: gif_service.Service) =
let hasNotification = chatDto.unviewedMessagesCount > 0 or chatDto.unviewedMentionsCount > 0
let notificationsCount = chatDto.unviewedMentionsCount
var chatName = chatDto.name
@ -323,7 +329,7 @@ method addNewChat*(
let item = initItem(chatDto.id, chatName, chatImage, isIdenticon, chatDto.color, chatDto.description,
chatDto.chatType.int, amIChatAdmin, hasNotification, notificationsCount, chatDto.muted, false, 0)
self.addSubmodule(chatDto.id, false, isUsersListAvailable, events, settingsService, contactService, chatService,
communityService, messageService)
communityService, messageService, gifService)
self.chatContentModules[chatDto.id].load()
self.view.chatsModel().appendItem(item)

View File

@ -5,6 +5,7 @@ import ../../../../../app_service/service/contacts/service as contact_service
import ../../../../../app_service/service/chat/service as chat_service
import ../../../../../app_service/service/community/service as community_service
import ../../../../../app_service/service/message/service as message_service
import ../../../../../app_service/service/gif/service as gif_service
import ../../../../core/eventemitter
@ -16,7 +17,8 @@ method load*(self: AccessInterface, events: EventEmitter,
contactService: contact_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service) {.base.} =
messageService: message_service.Service,
gifService: gif_service.Service) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =

View File

@ -10,6 +10,7 @@ import ../../../app_service/service/chat/service as chat_service
import ../../../app_service/service/community/service as community_service
import ../../../app_service/service/contacts/service as contacts_service
import ../../../app_service/service/message/service as message_service
import ../../../app_service/service/gif/service as gif_service
export controller_interface
@ -27,6 +28,7 @@ type
communityService: community_service.Service
messageService: message_service.Service
contactsService: contacts_service.Service
gifService: gif_service.Service
activeSectionId: string
proc newController*(delegate: io_interface.AccessInterface,
@ -37,7 +39,9 @@ proc newController*(delegate: io_interface.AccessInterface,
chatService: chat_service.Service,
communityService: community_service.Service,
contactsService: contacts_service.Service,
messageService: message_service.Service):
messageService: message_service.Service,
gifService: gif_service.Service,
):
Controller =
result = Controller()
result.delegate = delegate
@ -49,6 +53,7 @@ proc newController*(delegate: io_interface.AccessInterface,
result.communityService = communityService
result.contactsService = contactsService
result.messageService = messageService
result.gifService = gifService
method delete*(self: Controller) =
discard
@ -83,8 +88,9 @@ method init*(self: Controller) =
self.contactsService,
self.chatService,
self.communityService,
self.messageService
)
self.messageService,
self.gifService,
)
self.events.on(TOGGLE_SECTION) do(e:Args):
let args = ToggleSectionArgs(e)
@ -99,8 +105,9 @@ method init*(self: Controller) =
self.contactsService,
self.chatService,
self.communityService,
self.messageService
)
self.messageService,
self.gifService,
)
self.events.on(SIGNAL_COMMUNITY_LEFT) do(e:Args):
let args = CommunityIdArgs(e)

View File

@ -41,6 +41,8 @@ import ../../../app_service/service/node/service as node_service
import ../../../app_service/service/node_configuration/service_interface as node_configuration_service
import ../../../app_service/service/devices/service as devices_service
import ../../../app_service/service/mailservers/service as mailservers_service
import ../../../app_service/service/gif/service as gif_service
import ../../core/eventemitter
@ -91,8 +93,9 @@ proc newModule*[T](
nodeConfigurationService: node_configuration_service.ServiceInterface,
devicesService: devices_service.Service,
mailserversService: mailservers_service.Service,
nodeService: node_service.Service
): Module[T] =
nodeService: node_service.Service,
gifService: gif_service.Service,
): Module[T] =
result = Module[T]()
result.delegate = delegate
result.view = view.newView(result)
@ -106,7 +109,8 @@ proc newModule*[T](
chatService,
communityService,
contactsService,
messageService
messageService,
gifService,
)
result.moduleLoaded = false
@ -176,14 +180,15 @@ proc createCommunityItem[T](self: Module[T], c: CommunityDto): SectionItem =
c.members.map(x => member_item.initItem(x.id, x.roles)))
method load*[T](
self: Module[T],
events: EventEmitter,
settingsService: settings_service.ServiceInterface,
contactsService: contacts_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service
) =
self: Module[T],
events: EventEmitter,
settingsService: settings_service.ServiceInterface,
contactsService: contacts_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service,
gifService: gif_service.Service,
) =
singletonInstance.engine.setRootContextProperty("mainModule", self.viewVariant)
self.controller.init()
self.view.load()
@ -201,7 +206,7 @@ method load*[T](
contactsService,
chatService,
communityService,
messageService
messageService,
)
var activeSection: SectionItem
@ -310,9 +315,9 @@ method load*[T](
activeSection = profileSettingsSectionItem
# Load all sections
self.chatSectionModule.load(events, settingsService, contactsService, chatService, communityService, messageService)
self.chatSectionModule.load(events, settingsService, contactsService, chatService, communityService, messageService, gifService)
for cModule in self.communitySectionsModule.values:
cModule.load(events, settingsService, contactsService, chatService, communityService, messageService)
cModule.load(events, settingsService, contactsService, chatService, communityService, messageService, gifService)
self.walletSectionModule.load()
# self.walletV2SectionModule.load()
self.browserSectionModule.load()
@ -489,15 +494,16 @@ method getAppSearchModule*[T](self: Module[T]): QVariant =
self.appSearchModule.getModuleAsVariant()
method communityJoined*[T](
self: Module[T],
community: CommunityDto,
events: EventEmitter,
settingsService: settings_service.ServiceInterface,
contactsService: contacts_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service
) =
self: Module[T],
community: CommunityDto,
events: EventEmitter,
settingsService: settings_service.ServiceInterface,
contactsService: contacts_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service,
gifService: gif_service.Service,
) =
self.communitySectionsModule[community.id] = chat_section_module.newModule(
self,
events,
@ -509,7 +515,7 @@ method communityJoined*[T](
communityService,
messageService
)
self.communitySectionsModule[community.id].load(events, settingsService, contactsService, chatService, communityService, messageService)
self.communitySectionsModule[community.id].load(events, settingsService, contactsService, chatService, communityService, messageService, gifService)
let communitySectionItem = self.createCommunityItem(community)
self.view.addItem(communitySectionItem)

View File

@ -3,6 +3,7 @@ import ../../../../app_service/service/contacts/service as contacts_service
import ../../../../app_service/service/chat/service as chat_service
import ../../../../app_service/service/community/service as community_service
import ../../../../app_service/service/message/service as message_service
import ../../../../app_service/service/gif/service as gif_service
import ../../../core/eventemitter
@ -16,7 +17,8 @@ method load*(
contactsService: contacts_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service
messageService: message_service.Service,
gifService: gif_service.Service,
)
{.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -20,7 +20,8 @@ method communityJoined*(self: AccessInterface, community: CommunityDto, events:
contactsService: contacts_service.Service,
chatService: chat_service.Service,
communityService: community_service.Service,
messageService: message_service.Service) {.base.} =
messageService: message_service.Service,
gifService: gif_service.Service) {.base.} =
raise newException(ValueError, "No implementation available")
method communityEdited*(self: AccessInterface, community: CommunityDto) {.base.} =

View File

@ -0,0 +1,40 @@
import json, strformat
type
GifDto* = object
id*: string
title*: string
url*: string
tinyUrl*: string
height*: int
isFavorite*: bool
proc tenorToGifDto*(jsonMsg: JsonNode): GifDto =
return GifDto(
id: jsonMsg{"id"}.getStr,
title: jsonMsg{"title"}.getStr,
url: jsonMsg{"media"}[0]["gif"]["url"].getStr,
tinyUrl: jsonMsg{"media"}[0]["tinygif"]["url"].getStr,
height: jsonMsg{"media"}[0]["gif"]["dims"][1].getInt
)
proc settingToGifDto*(jsonMsg: JsonNode): GifDto =
return GifDto(
id: jsonMsg{"id"}.getStr,
title: jsonMsg{"title"}.getStr,
url: jsonMsg{"url"}.getStr,
tinyUrl: jsonMsg{"tinyUrl"}.getStr,
height: jsonMsg{"height"}.getInt
)
proc toJsonNode*(self: GifDto): JsonNode =
result = %* {
"id": self.id,
"title": self.title,
"url": self.url,
"tinyUrl": self.tinyUrl,
"height": self.height
}
proc `$`*(self: GifDto): string =
return fmt"GifDto(id:{self.id}, title:{self.title}, url:{self.url}, tinyUrl:{self.tinyUrl}, height:{self.height})"

View File

@ -0,0 +1,148 @@
import httpclient
import json
import strformat
import os
import uri
import chronicles
import sequtils
import ../settings/service_interface as settings_service
import ./dto
import ./service_interface
logScope:
topics = "gif-service"
const MAX_RECENT = 50
# set via `nim c` param `-d:TENOR_API_KEY:[api_key]`; should be set in CI/release builds
const TENOR_API_KEY {.strdefine.} = ""
let TENOR_API_KEY_ENV = $getEnv("TENOR_API_KEY")
let TENOR_API_KEY_RESOLVED =
if TENOR_API_KEY_ENV != "":
TENOR_API_KEY_ENV
else:
TENOR_API_KEY
const baseUrl = "https://g.tenor.com/v1/"
let defaultParams = fmt("&media_filter=minimal&limit=50&key={TENOR_API_KEY_RESOLVED}")
type
Service* = ref object of service_interface.ServiceInterface
settingsService: settings_service.ServiceInterface
client: HttpClient
favorites: seq[GifDto]
recents: seq[GifDto]
favoritesLoaded: bool
recentsLoaded: bool
method delete*(self: Service) =
discard
proc newService*(settingsService: settings_service.ServiceInterface): Service =
result = Service()
result.settingsService = settingsService
result.client = newHttpClient()
result.favorites = @[]
result.recents = @[]
proc setFavoriteGifs(self: Service, gifDtos: seq[GifDto]) =
let node = %*{"items": map(gifDtos, toJsonNode)}
discard self.settingsService.saveGifFavorites(node)
proc setRecentGifs(self: Service, gifDtos: seq[GifDto]) =
let node = %*{"items": map(gifDtos, toJsonNode)}
discard self.settingsService.saveGifRecents(node)
proc getContentWithRetry(self: Service, path: string, maxRetry: int = 3): string =
var currentRetry = 0
while true:
try:
let content = self.client.getContent(fmt("{baseUrl}{path}{defaultParams}"))
return content
except Exception as e:
currentRetry += 1
error "could not query tenor API", msg=e.msg
if currentRetry >= maxRetry:
raise
sleep(100 * currentRetry)
proc tenorQuery(self: Service, path: string): seq[GifDto] =
try:
let content = self.getContentWithRetry(path)
let doc = content.parseJson()
var items: seq[GifDto] = @[]
for json in doc["results"]:
items.add(tenorToGifDto(json))
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 getFavorites*(self: Service): seq[GifDto] =
if not self.favoritesLoaded:
self.favoritesLoaded = true
let node = self.settingsService.getGifFavorites()
self.favorites = map(node{"items"}.getElems(), settingToGifDto)
return self.favorites
proc getRecents*(self: Service): seq[GifDto] =
if not self.recentsLoaded:
self.recentsLoaded = true
let node = self.settingsService.getGifRecents()
self.recents = map(node{"items"}.getElems(), settingToGifDto)
return self.recents
proc isFavorite*(self: Service, gifDto: GifDto): bool =
for favorite in self.getFavorites():
if favorite.id == gifDto.id:
return true
return 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
newFavorites.add(favoriteGif)
if not found:
newFavorites.add(gifDto)
self.favorites = newFavorites
self.setFavoriteGifs(newFavorites)
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
self.recents = newRecents
self.setRecentGifs(newRecents)

View File

@ -0,0 +1,6 @@
type
ServiceInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for this service access.
method delete*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -41,6 +41,8 @@ const KEY_FLEET* = "fleet"
const KEY_NODE_CONFIG* = "node-config"
const KEY_WAKU_BLOOM_FILTER_MODE* = "waku-bloom-filter-mode"
const KEY_AUTO_MESSAGE_ENABLED* = "auto-message-enabled?"
const KEY_GIF_FAVORITES* = "gifs/favorite-gifs"
const KEY_GIF_RECENTS* = "gifs/recent-gifs"
type UpstreamConfig* = object
Enabled*: bool
@ -112,6 +114,8 @@ type
recentStickerHashes*: seq[string]
installedStickerPacks*: Table[int, StickerPackDto]
autoMessageEnabled*: bool
gifRecents*: JsonNode
gifFavorites*: JsonNode
proc toUpstreamConfig*(jsonObj: JsonNode): UpstreamConfig =
discard jsonObj.getProp("Enabled", result.Enabled)
@ -208,6 +212,8 @@ proc toSettingsDto*(jsonObj: JsonNode): SettingsDto =
discard jsonObj.getProp(KEY_TELEMETRY_SERVER_URL, result.telemetryServerUrl)
discard jsonObj.getProp(KEY_FLEET, result.fleet)
discard jsonObj.getProp(KEY_AUTO_MESSAGE_ENABLED, result.autoMessageEnabled)
discard jsonObj.getProp(KEY_GIF_RECENTS, result.gifRecents)
discard jsonObj.getProp(KEY_GIF_FAVORITES, result.gifFavorites)
var pinnedMailserverObj: JsonNode
if(jsonObj.getProp(KEY_PINNED_MAILSERVERS, pinnedMailserverObj)):

View File

@ -449,3 +449,21 @@ method autoMessageEnabled*(self: Service): bool =
method getWakuBloomFilterMode*(self: Service): bool =
return self.settings.wakuBloomFilterMode
method getGifRecents*(self: Service): JsonNode =
return self.settings.gifRecents
method getGifFavorites*(self: Service): JsonNode =
return self.settings.gifFavorites
method saveGifRecents*(self: Service, value: JsonNode): bool =
if(self.saveSetting(KEY_GIF_RECENTS, value)):
self.settings.gifRecents = value
return true
return false
method saveGifFavorites*(self: Service, value: JsonNode): bool =
if(self.saveSetting(KEY_GIF_FAVORITES, value)):
self.settings.gifFavorites = value
return true
return false

View File

@ -258,4 +258,16 @@ method autoMessageEnabled*(self: ServiceInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method getWakuBloomFilterMode*(self: ServiceInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method getGifRecents*(self: ServiceInterface): JsonNode {.base.} =
raise newException(ValueError, "No implementation available")
method getGifFavorites*(self: ServiceInterface): JsonNode {.base.} =
raise newException(ValueError, "No implementation available")
method saveGifRecents*(self: ServiceInterface, value: JsonNode): bool {.base.} =
raise newException(ValueError, "No implementation available")
method saveGifFavorites*(self: ServiceInterface, value: JsonNode): bool {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -15,6 +15,7 @@ Column {
spacing: 8
property alias gifList: repeater
property var gifWidth: 0
property var store
property var gifSelected: function () {}
property var toggleFavorite: function () {}
property string lastHoveredId
@ -51,7 +52,7 @@ Column {
StatusBaseButton {
id: starButton
property bool favorite: model.isFavorite
property bool favorite: RootStore.isFavorite(model.id)
type: StatusFlatRoundButton.Type.Secondary
textColor: hovered || favorite ? Style.current.yellow : Style.current.secondaryText
@ -110,8 +111,7 @@ Column {
hoverEnabled: true
onClicked: function (event) {
root.gifSelected(event, model.url)
// Not Refactored Yet
// RootStore.chatsModelInst.gif.addToRecents(model.id)
root.store.addToRecentsGif(model.id)
}
}
}

View File

@ -22,24 +22,22 @@ Popup {
id: popup
property var gifSelected: function () {}
property var searchGif: Backpressure.debounce(searchBox, 500, function (query) {
// Not Refactored Yet
// RootStore.chatsModelInst.gif.search(query)
RootStore.searchGifs(query)
});
property var toggleCategory: function(newCategory) {
previousCategory = currentCategory
currentCategory = newCategory
searchBox.text = ""
// Not Refactored Yet
// if (currentCategory === StatusGifPopup.Category.Trending) {
// RootStore.chatsModelInst.gif.getTrendings()
// } else if(currentCategory === StatusGifPopup.Category.Favorite) {
// RootStore.chatsModelInst.gif.getFavorites()
// } else if(currentCategory === StatusGifPopup.Category.Recent) {
// RootStore.chatsModelInst.gif.getRecents()
// }
if (currentCategory === StatusGifPopup.Category.Trending) {
RootStore.getTrendingsGifs()
} else if(currentCategory === StatusGifPopup.Category.Favorite) {
RootStore.getFavoritesGifs()
} else if(currentCategory === StatusGifPopup.Category.Recent) {
RootStore.getRecentsGifs()
}
}
property var toggleFavorite: function(item) {
// RootStore.chatsModelInst.gif.toggleFavorite(item.id, currentCategory === StatusGifPopup.Category.Favorite)
RootStore.toggleFavoriteGif(item.id, currentCategory === StatusGifPopup.Category.Favorite)
}
property alias searchString: searchBox.text
property int currentCategory: StatusGifPopup.Category.Trending
@ -68,7 +66,7 @@ Popup {
searchBox.text = ""
searchBox.forceActiveFocus(Qt.MouseFocusReason)
if (RootStore.isTenorWarningAccepted) {
// RootStore.chatsModelInst.gif.getTrendings()
RootStore.getTrendingsGifs()
} else {
confirmationPopup.open()
}
@ -162,9 +160,7 @@ Popup {
const headerTextHeight = searchBox.text === "" ? headerText.height : 0
return 400 - gifHeader.height - headerTextHeight
}
// Not Refactored Yet
sourceComponent: empty
// sourceComponent: RootStore.chatsModelInst.gif.columnA.rowCount() == 0 ? empty : gifItems
sourceComponent: RootStore.gifColumnA.rowCount() == 0 ? empty : gifItems
}
Row {
@ -262,8 +258,7 @@ Popup {
text: qsTr("Enable")
onClicked: {
RootStore.setIsTenorWarningAccepted(true);
// Not Refactored Yet
// RootStore.chatsModelInst.gif.getTrendings()
RootStore.getTrendingsGifs()
confirmationPopup.close()
}
}
@ -303,8 +298,8 @@ Popup {
visible: currentCategory === StatusGifPopup.Category.Trending || currentCategory === StatusGifPopup.Category.Search
onClicked: {
if (searchBox.text === "") {
// Not Refactored Yet
// RootStore.chatsModelInst.gif.getTrendings()
RootStore.getTrendingsGifs()
return
}
@ -335,36 +330,36 @@ Popup {
property string lastHoveredId
StatusGifColumn {
// Not Refactored Yet
// gifList.model: RootStore.chatsModelInst.gif.columnA
gifList.model: RootStore.gifColumnA
gifWidth: (popup.width / 3) - Style.current.padding
gifSelected: popup.gifSelected
toggleFavorite: popup.toggleFavorite
lastHoveredId: gifs.lastHoveredId
store: RootStore
onGifHovered: {
gifs.lastHoveredId = id
}
}
StatusGifColumn {
// Not Refactored Yet
// gifList.model: RootStore.chatsModelInst.gif.columnB
gifList.model: RootStore.gifColumnB
gifWidth: (popup.width / 3) - Style.current.padding
gifSelected: popup.gifSelected
toggleFavorite: popup.toggleFavorite
lastHoveredId: gifs.lastHoveredId
store: RootStore
onGifHovered: {
gifs.lastHoveredId = id
}
}
StatusGifColumn {
// Not Refactored Yet
// gifList.model: RootStore.chatsModelInst.gif.columnC
gifList.model: RootStore.gifColumnC
gifWidth: (popup.width / 3) - Style.current.padding
gifSelected: popup.gifSelected
toggleFavorite: popup.toggleFavorite
lastHoveredId: gifs.lastHoveredId
store: RootStore
onGifHovered: {
gifs.lastHoveredId = id
}

View File

@ -72,4 +72,36 @@ QtObject {
function copyToClipboard(textToCopy) {
// chatsModelInst.copyToClipboard(textToCopy)
}
property var gifColumnA: chatSectionChatContentInputArea.gifColumnA
property var gifColumnB: chatSectionChatContentInputArea.gifColumnB
property var gifColumnC: chatSectionChatContentInputArea.gifColumnC
function searchGifs(query) {
chatSectionChatContentInputArea.searchGifs(query)
}
function getTrendingsGifs() {
chatSectionChatContentInputArea.getTrendingsGifs()
}
function getRecentsGifs() {
chatSectionChatContentInputArea.getRecentsGifs()
}
function getFavoritesGifs() {
return chatSectionChatContentInputArea.getFavoritesGifs()
}
function isFavorite(id) {
return chatSectionChatContentInputArea.isFavorite(id)
}
function toggleFavoriteGif(id, reload) {
chatSectionChatContentInputArea.toggleFavoriteGif(id, reload)
}
function addToRecentsGif(id) {
chatSectionChatContentInputArea.addToRecentsGif(id)
}
}