fix(@desktop/sections): make ctrl+k display chats from communities

Closes: #4059
This commit is contained in:
Patryk Osmaczko 2022-02-04 14:07:48 +01:00 committed by osmaczko
parent 16328823a9
commit 914c7b2839
16 changed files with 400 additions and 53 deletions

View File

@ -0,0 +1,35 @@
type
Item* = ref object
chatId: string
name: string
color: string
icon: string
sectionId: string
sectionName: string
proc initItem*(chatId, name, color, icon, sectionId, sectionName: string): Item =
result = Item()
result.chatId = chatId
result.name = name
result.color = color
result.icon = icon
result.sectionId = sectionId
result.sectionName = sectionName
proc chatId*(self: Item): string =
self.chatId
proc name*(self: Item): string =
self.name
proc color*(self: Item): string =
self.color
proc icon*(self: Item): string =
self.icon
proc sectionId*(self: Item): string =
self.sectionId
proc sectionName*(self: Item): string =
self.sectionName

View File

@ -0,0 +1,66 @@
import NimQml, Tables
import chat_search_item
type
ModelRole {.pure.} = enum
ChatId = UserRole + 1
Name
Color
Icon
SectionId
SectionName
QtObject:
type Model* = ref object of QAbstractListModel
items: seq[Item]
proc setup(self: Model) =
self.QAbstractListModel.setup
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc newModel*(): Model =
new(result, delete)
result.setup
proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel()
self.items = items
self.endResetModel()
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.ChatId.int:"chatId",
ModelRole.Name.int:"name",
ModelRole.Color.int:"color",
ModelRole.Icon.int:"icon",
ModelRole.SectionId.int:"sectionId",
ModelRole.SectionName.int:"sectionName",
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.items.len:
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.ChatId:
result = newQVariant(item.chatId)
of ModelRole.Name:
result = newQVariant(item.name)
of ModelRole.Color:
result = newQVariant(item.color)
of ModelRole.Icon:
result = newQVariant(item.icon)
of ModelRole.SectionId:
result = newQVariant(item.sectionId)
of ModelRole.SectionName:
result = newQVariant(item.sectionName)

View File

@ -64,6 +64,9 @@ QtObject:
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
proc items*(self: Model): seq[Item] =
return self.items
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Id.int:"itemId",

View File

@ -1,7 +1,8 @@
import NimQml, Tables, chronicles, json, sequtils
import io_interface
import ../io_interface as delegate_interface
import view, controller, item, sub_item, model, sub_model, base_item
import view, controller, item, sub_item, sub_model, base_item
import model as chats_model
import ../../shared_models/contacts_item as contacts_item
import ../../shared_models/contacts_model as contacts_model
@ -355,6 +356,9 @@ method onActiveSectionChange*(self: Module, sectionId: string) =
self.updateNotifications(self.controller.getActiveChatId(), unviewedMessagesCount=0, unviewedMentionsCount=0)
self.delegate.onActiveChatChange(self.controller.getMySectionId(), self.controller.getActiveChatId())
method chatsModel*(self: Module): chats_model.Model =
return self.view.chatsModel()
method createPublicChat*(self: Module, chatId: string) =
if(self.controller.isCommunity()):
debug "creating public chat is not allowed for community, most likely it's an error in qml", methodName="createPublicChat"

View File

@ -8,6 +8,8 @@ import ../../../../../app_service/service/message/service as message_service
import ../../../../../app_service/service/gif/service as gif_service
import ../../../../../app_service/service/mailservers/service as mailservers_service
import ../model as chats_model
import ../../../../core/eventemitter
method delete*(self: AccessInterface) {.base.} =
@ -31,3 +33,6 @@ method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
method onActiveSectionChange*(self: AccessInterface, sectionId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method chatsModel*(self: AccessInterface): chats_model.Model {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -57,6 +57,9 @@ QtObject:
method rowCount(self: SubModel, index: QModelIndex = nil): int =
return self.items.len
proc items*(self: SubModel): seq[SubItem] =
return self.items
method roleNames(self: SubModel): Table[int, string] =
{
ModelRole.Id.int:"itemId",

View File

@ -252,3 +252,6 @@ method resolveENS*(self: Controller, ensName: string, uuid: string = "") =
method isMnemonicBackedUp*(self: Controller): bool =
result = self.privacyService.isMnemonicBackedUp()
method switchTo*(self: Controller, sectionId, chatId: string) =
self.messageService.switchTo(sectionId, chatId, "")

View File

@ -49,3 +49,6 @@ method resolveENS*(self: AccessInterface, ensName: string, uuid: string = "") {.
method isMnemonicBackedUp*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method switchTo*(self: AccessInterface, sectionId, chatId: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,12 +1,15 @@
import NimQml, tables, json, sugar, sequtils
import io_interface, view, controller
import io_interface, view, controller, chat_search_item, chat_search_model
import ./communities/models/[pending_request_item, pending_request_model]
import ../shared_models/[user_item, user_model, section_item, section_model, active_section]
import ../../global/app_sections_config as conf
import ../../global/app_signals
import ../../global/global_singleton
import chat_section/[model, sub_item, sub_model]
import chat_section/base_item as chat_section_base_item
import chat_section/item as chat_section_item
import chat_section/module as chat_section_module
import wallet_section/module as wallet_section_module
import browser_section/module as browser_section_module
@ -508,6 +511,27 @@ method getCommunitySectionModule*[T](self: Module[T], communityId: string): QVar
return self.communitySectionsModule[communityId].getModuleAsVariant()
method rebuildChatSearchModel*[T](self: Module[T]) =
let transformItem = proc(item: chat_section_base_item.BaseItem, sectionId, sectionName: string): chat_search_item.Item =
result = chat_search_item.initItem(item.id(), item.name(), item.color(), item.icon(), sectionId, sectionName)
let transform = proc(items: seq[chat_section_item.Item], sectionId, sectionName: string): seq[chat_search_item.Item] =
for item in items:
if item.type() != ChatType.Unknown.int:
result.add(transformItem(item, sectionId, sectionName))
else:
for subItem in item.subItems().items():
result.add(transformItem(subItem, sectionId, sectionName))
var items = transform(self.chatSectionModule.chatsModel().items(), conf.CHAT_SECTION_ID, conf.CHAT_SECTION_NAME)
for cId in self.communitySectionsModule.keys:
items.add(transform(self.communitySectionsModule[cId].chatsModel().items(), cId, self.view.model().getItemById(cId).name()))
self.view.chatSearchModel().setItems(items)
method switchTo*[T](self: Module[T], sectionId, chatId: string) =
self.controller.switchTo(sectionId, chatId)
method onActiveChatChange*[T](self: Module[T], sectionId: string, chatId: string) =
self.appSearchModule.onActiveChatChange(sectionId, chatId)

View File

@ -1,5 +1,6 @@
import NimQml
import ../../shared_models/section_item
import ../chat_search_item
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
@ -27,3 +28,9 @@ method getContactDetailsAsJson*(self: AccessInterface, publicKey: string): strin
method resolveENS*(self: AccessInterface, ensName: string, uuid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method rebuildChatSearchModel*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method switchTo*(self: AccessInterface, sectionId, chatId: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -3,15 +3,18 @@ import ../shared_models/section_model
import ../shared_models/section_item
import ../shared_models/active_section
import io_interface
import chat_search_model
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
model: SectionModel
model: section_model.SectionModel
modelVariant: QVariant
activeSection: ActiveSection
activeSectionVariant: QVariant
chatSearchModel: chat_search_model.Model
chatSearchModelVariant: QVariant
tmpCommunityId: string # shouldn't be used anywhere except in prepareCommunitySectionModuleForCommunityId/getCommunitySectionModule procs
proc activeSectionChanged*(self:View) {.signal.}
@ -21,16 +24,20 @@ QtObject:
self.modelVariant.delete
self.activeSection.delete
self.activeSectionVariant.delete
self.chatSearchModel.delete
self.chatSearchModelVariant.delete
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
result.model = newModel()
result.model = section_model.newModel()
result.modelVariant = newQVariant(result.model)
result.activeSection = newActiveSection()
result.activeSectionVariant = newQVariant(result.activeSection)
result.chatSearchModel = chat_search_model.newModel()
result.chatSearchModelVariant = newQVariant(result.chatSearchModel)
proc load*(self: View) =
# In some point, here, we will setup some exposed main module related things.
@ -54,6 +61,21 @@ QtObject:
read = getModel
notify = modelChanged
proc chatSearchModel*(self: View): chat_search_model.Model =
return self.chatSearchModel
proc chatSearchModelChanged*(self: View) {.signal.}
proc getChatSearchModel(self: View): QVariant {.slot.} =
return self.chatSearchModelVariant
proc rebuildChatSearchModel*(self: View) {.slot.} =
self.delegate.rebuildChatSearchModel()
QtProperty[QVariant] chatSearchModel:
read = getChatSearchModel
notify = chatSearchModelChanged
proc openStoreToKeychainPopup*(self: View) {.signal.}
proc offerToStorePassword*(self: View) =
@ -96,6 +118,9 @@ QtObject:
let item = self.model.getItemBySectionType(sectionType.SectionType)
self.delegate.setActiveSection(item)
proc switchTo*(self: View, sectionId: string, chatId: string) {.slot.} =
self.delegate.switchTo(sectionId, chatId)
proc setUserStatus*(self: View, status: bool) {.slot.} =
self.delegate.setUserStatus(status)

@ -1 +1 @@
Subproject commit d85ed4c3ed95fa1f32d8e50b2746691e5d9e5e4e
Subproject commit 5780f183c7b3cf63c3abbc584daa72078241e917

View File

@ -19,6 +19,16 @@ QtObject {
property EmojiReactions emojiReactionsModel: EmojiReactions {
}
property var chatSearchModel: mainModuleInst.chatSearchModel
function rebuildChatSearchModel() {
mainModuleInst.rebuildChatSearchModel()
}
function setActiveSectionChat(sectionId, chatId) {
mainModuleInst.switchTo(sectionId, chatId)
}
// Not Refactored Yet
// property var chatsModelInst: chatsModel
// Not Refactored Yet

View File

@ -783,52 +783,44 @@ Item {
}
}
Component {
id: statusSmartIdenticonComponent
StatusSmartIdenticon {
property string imageSource: ""
image: StatusImageSettings {
width: channelPicker.imageWidth
height: channelPicker.imageHeight
source: imageSource
isIdenticon: true
}
icon: StatusIconSettings {
width: channelPicker.imageWidth
height: channelPicker.imageHeight
letterSize: 15
color: Theme.palette.miscColor5
}
}
}
StatusInputListPopup {
StatusSearchListPopup {
id: channelPicker
//% "Where do you want to go?"
title: qsTrId("where-do-you-want-to-go-")
showSearchBox: true
width: 350
x: parent.width / 2 - width / 2
y: parent.height / 2 - height / 2
// TODO improve this to work with community Chats as well
modelList: mainModule.getChatSectionModule().model
getText: function (modelData) {
return modelData.name
}
getId: function (modelData) {
return modelData.itemId
}
getImageComponent: function (parent, modelData) {
return statusSmartIdenticonComponent.createObject(parent, {
imageSource: modelData.identicon,
name: modelData.name
});
searchBoxPlaceholder: qsTr("Where do you want to go?")
model: rootStore.chatSearchModel
delegate: StatusListItem {
property var modelData
property bool isCurrentItem: true
function filterAccepts(searchText) {
return title.includes(searchText)
}
title: modelData ? modelData.name : ""
label: modelData? modelData.sectionName : ""
highlighted: isCurrentItem
sensor.hoverEnabled: false
statusListItemIcon {
name: modelData ? modelData.name : ""
active: true
}
icon {
width: image.width
height: image.height
color: modelData ? modelData.color : ""
}
image {
source: modelData ? modelData.icon : ""
isIdenticon: true
}
}
onClicked: function (index, id) {
Global.changeAppSectionBySectionType(Constants.appSection.chat)
mainModule.getChatSectionModule().setActiveItem(id, "")
channelPicker.close()
onAboutToShow: rootStore.rebuildChatSearchModel()
onSelected: {
rootStore.setActiveSectionChat(modelData.sectionId, modelData.chatId)
close()
}
}
}

View File

@ -0,0 +1,166 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.0
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
Popup {
id: root
width: 400
height: 300
property alias model: listView.model
// delegate interface has to be fulfilled
property Component delegate: Item {
property var modelData
property bool isCurrentItem
function filterAccepts(searchText) {
return true
}
}
property string searchBoxPlaceholder: qsTr("Search...")
signal selected(int index, var modelData)
background: Rectangle {
radius: Style.current.radius
color: Style.current.background
border.color: Style.current.border
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: "#22000000"
}
}
ColumnLayout {
anchors.fill: parent
StatusInput {
id: searchBox
Layout.fillWidth: true
leftPadding: 0
rightPadding: 0
input.placeholderText: root.searchBoxPlaceholder
input.icon: StatusIconSettings {
width: 24
height: 24
name: "search"
color: Theme.palette.baseColor1
}
function goToNextAvailableIndex(up) {
var currentIndex = listView.currentIndex
for (var i = 0; i < listView.count; i++) {
currentIndex = up ? (currentIndex === 0 ? listView.count - 1 : currentIndex - 1)
: (currentIndex === listView.count - 1 ? 0 : currentIndex + 1)
listView.currentIndex = currentIndex
if (listView.currentItem.visible) {
return
}
}
listView.currentIndex = 0
}
Keys.onReleased: {
listView.selectByHover = false
if (event.key === Qt.Key_Down) {
searchBox.goToNextAvailableIndex(false)
}
if (event.key === Qt.Key_Up) {
searchBox.goToNextAvailableIndex(true)
}
if (event.key === Qt.Key_Escape) {
return root.close()
}
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
return root.selected(listView.currentIndex,
listView.currentItem.myData)
}
if (!listView.currentItem.visible) {
goToNextAvailableIndex(false)
}
}
onTextChanged: if (text === "") listView.currentIndex = 0
}
ListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
property bool selectByHover: false
clip: true
highlightMoveDuration: 200
delegate: Item {
id: delegateItem
property var myData: typeof modelData === "undefined" ? model : modelData
width: listView.width
height: visible ? delegateLoader.height : 0
Loader {
id: delegateLoader
width: parent.width
sourceComponent: root.delegate
onLoaded: {
item.modelData = delegateItem.myData
item.isCurrentItem = Qt.binding(() => delegateItem.ListView.isCurrentItem)
delegateItem.visible = Qt.binding(() => item.filterAccepts(searchBox.text))
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: (mouse) => {
listView.currentIndex = index
root.selected(index, delegateItem.myData)
mouse.accepted = false
}
onContainsMouseChanged: if (containsMouse) listView.currentIndex = index
cursorShape: Qt.PointingHandCursor
}
}
Loader {
anchors.fill: parent
active: !listView.selectByHover
sourceComponent: MouseArea {
hoverEnabled: true
onPositionChanged: listView.selectByHover = true
}
}
}
}
onAboutToShow: {
listView.currentIndex = 0
listView.selectByHover = false
searchBox.text = ""
searchBox.input.edit.forceActiveFocus()
}
}

View File

@ -21,6 +21,7 @@ StatusImageModal 1.0 StatusImageModal.qml
StatusImageRadioButton 1.0 StatusImageRadioButton.qml
StatusInputListPopup 1.0 StatusInputListPopup.qml
StatusNotification 1.0 StatusNotification.qml
StatusSearchListPopup 1.0 StatusSearchListPopup.qml
StatusSectionDescItem 1.0 StatusSectionDescItem.qml
StatusSectionHeadline 1.0 StatusSectionHeadline.qml
StatusSettingsLineButton 1.0 StatusSettingsLineButton.qml