fix(@desktop/general): Fix community deep links

Cleanups in deep links - removing not needed links handlers
Improve `open community` and `open channel` deep links - spectate community if not a member

Fix #7892
This commit is contained in:
Michal Iskierko 2022-10-17 11:26:52 +02:00 committed by Michał Iskierko
parent 6c90034b6b
commit 6ac091d094
5 changed files with 56 additions and 145 deletions

View File

@ -6,29 +6,15 @@ import ../../global/app_signals
logScope: logScope:
topics = "urls-manager" topics = "urls-manager"
const UriFormatBrowserShort = "status-im://b/" const UriFormatUserProfile = "status-im://u/"
const UriFormatBrowserLong = "status-im://browser/"
const UriFormatUserProfileShort = "status-im://u/" const UriFormatCommunity = "status-im://c/"
const UriFormatUserProfileLong = "status-im://user/"
const UriFormatPrivateChatShort = "status-im://pm/" const UriFormatCommunityChannel = "status-im://cc/"
const UriFormatPrivateChatLong = "status-im://private-message/"
const UriFormatPublicChatShort = "status-im://p/" const UriFormatGroupChat = "status-im://g/"
const UriFormatPublicChatLong = "status-im://public/"
const UriFormatGroupChatShort = "status-im://g/" const UriFormatBrowser = "status-im://b/"
const UriFormatGroupChatLong = "status-im://group/"
const UriFormatCommunityRequestsShort = "status-im://cr/"
const UriFormatCommunityRequestsLong = "status-im://community-requests/"
const UriFormatCommunityShort = "status-im://c/"
const UriFormatCommunityLong = "status-im://community/"
const UriFormatCommunityChannelShort = "status-im://cc/"
const UriFormatCommunityChannelLong = "status-im://community-channel/"
QtObject: QtObject:
type UrlsManager* = ref object of QObject type UrlsManager* = ref object of QObject
@ -55,16 +41,6 @@ QtObject:
result.protocolUriOnStart = protocolUriOnStart result.protocolUriOnStart = protocolUriOnStart
result.loggedIn = false result.loggedIn = false
proc prepareGroupChatDetails(self: UrlsManager, urlQuery: string,
data: var StatusUrlArgs) =
var urlParams = rsplit(urlQuery, "/u/")
if(urlParams.len > 0):
data.groupName = urlParams[0]
urlParams.delete(0)
data.listOfUserIds = urlParams
else:
info "wrong url format for group chat"
proc onUrlActivated*(self: UrlsManager, urlRaw: string) {.slot.} = proc onUrlActivated*(self: UrlsManager, urlRaw: string) {.slot.} =
if not self.loggedIn: if not self.loggedIn:
self.protocolUriOnStart = urlRaw self.protocolUriOnStart = urlRaw
@ -75,72 +51,26 @@ QtObject:
.multiReplace(("\r\n", "")) .multiReplace(("\r\n", ""))
.multiReplace(("\n", "")) .multiReplace(("\n", ""))
# Open `url` in the app's browser
if url.startsWith(UriFormatBrowserShort):
data.action = StatusUrlAction.OpenLinkInBrowser
data.url = url[UriFormatBrowserShort.len .. url.len-1]
elif url.startsWith(UriFormatBrowserLong):
data.action = StatusUrlAction.OpenLinkInBrowser
data.url = url[UriFormatBrowserLong.len .. url.len-1]
# Display user profile popup for user with `user_pk` or `ens_name` # Display user profile popup for user with `user_pk` or `ens_name`
elif url.startsWith(UriFormatUserProfileShort): if url.startsWith(UriFormatUserProfile):
data.action = StatusUrlAction.DisplayUserProfile data.action = StatusUrlAction.DisplayUserProfile
data.userId = url[UriFormatUserProfileShort.len .. url.len-1] data.userId = url[UriFormatUserProfile.len .. url.len-1]
elif url.startsWith(UriFormatUserProfileLong):
data.action = StatusUrlAction.DisplayUserProfile
data.userId = url[UriFormatUserProfileLong.len .. url.len-1]
# Open or create 1:1 chat with user with `user_pk` or `ens_name`
elif url.startsWith(UriFormatPrivateChatShort):
data.action = StatusUrlAction.OpenOrCreatePrivateChat
data.chatId = url[UriFormatPrivateChatShort.len .. url.len-1]
elif url.startsWith(UriFormatPrivateChatLong):
data.action = StatusUrlAction.OpenOrCreatePrivateChat
data.chatId = url[UriFormatPrivateChatLong.len .. url.len-1]
# Open public chat with `chat_key`
elif url.startsWith(UriFormatPublicChatShort):
data.action = StatusUrlAction.OpenOrJoinPublicChat
data.chatId = url[UriFormatPublicChatShort.len .. url.len-1]
elif url.startsWith(UriFormatPublicChatLong):
data.action = StatusUrlAction.OpenOrJoinPublicChat
data.chatId = url[UriFormatPublicChatLong.len .. url.len-1]
# Open a group chat with named `group_name`, adding up to 19 participants with their `user_pk` or `ens_name`.
# Group chat may have up to 20 participants including the admin of a group
elif url.startsWith(UriFormatGroupChatShort):
data.action = StatusUrlAction.OpenOrCreateGroupChat
let urlQuery = url[UriFormatGroupChatShort.len .. url.len-1]
self.prepareGroupChatDetails(urlQuery, data)
elif url.startsWith(UriFormatGroupChatLong):
data.action = StatusUrlAction.OpenOrCreateGroupChat
let urlQuery = url[UriFormatGroupChatLong.len .. url.len-1]
self.prepareGroupChatDetails(urlQuery, data)
# Send a join community request to a community with `community_key`
elif url.startsWith(UriFormatCommunityRequestsShort):
data.action = StatusUrlAction.RequestToJoinCommunity
data.communityId = url[UriFormatCommunityRequestsShort.len .. url.len-1]
elif url.startsWith(UriFormatCommunityRequestsLong):
data.action = StatusUrlAction.RequestToJoinCommunity
data.communityId = url[UriFormatCommunityRequestsLong.len .. url.len-1]
# Open community with `community_key` # Open community with `community_key`
elif url.startsWith(UriFormatCommunityShort): elif url.startsWith(UriFormatCommunity):
data.action = StatusUrlAction.OpenCommunity data.action = StatusUrlAction.OpenCommunity
data.communityId = url[UriFormatCommunityShort.len .. url.len-1] data.communityId = url[UriFormatCommunity.len .. url.len-1]
elif url.startsWith(UriFormatCommunityLong):
data.action = StatusUrlAction.OpenCommunity
data.communityId = url[UriFormatCommunityLong.len .. url.len-1]
# Open community which has a channel with `channel_key` and makes that channel active # Open community which has a channel with `channel_key` and makes that channel active
elif url.startsWith(UriFormatCommunityChannelShort): elif url.startsWith(UriFormatCommunityChannel):
data.action = StatusUrlAction.OpenCommunityChannel data.action = StatusUrlAction.OpenCommunityChannel
data.chatId = url[UriFormatCommunityChannelShort.len .. url.len-1] data.chatId = url[UriFormatCommunityChannel.len .. url.len-1]
elif url.startsWith(UriFormatCommunityChannelLong):
data.action = StatusUrlAction.OpenCommunityChannel # Open `url` in the app's browser
data.chatId = url[UriFormatCommunityChannelLong.len .. url.len-1] # Enable after MVP
#elif url.startsWith(UriFormatBrowser):
# data.action = StatusUrlAction.OpenLinkInBrowser
# data.url = url[UriFormatBrowser.len .. url.len-1]
else: else:
info "Unsupported deep link structure: ", url info "Unsupported deep link structure: ", url

View File

@ -28,10 +28,6 @@ type
StatusUrlAction* {.pure.} = enum StatusUrlAction* {.pure.} = enum
OpenLinkInBrowser = 0 OpenLinkInBrowser = 0
DisplayUserProfile, DisplayUserProfile,
OpenOrCreatePrivateChat,
OpenOrJoinPublicChat,
OpenOrCreateGroupChat,
RequestToJoinCommunity,
OpenCommunity, OpenCommunity,
OpenCommunityChannel OpenCommunityChannel
@ -42,7 +38,5 @@ type
chatId*: string chatId*: string
url*: string url*: string
userId*: string # can be public key or ens name userId*: string # can be public key or ens name
groupName*: string
listOfUserIds*: seq[string] # used for creating group chat
const SIGNAL_STATUS_URL_REQUESTED* = "statusUrlRequested" const SIGNAL_STATUS_URL_REQUESTED* = "statusUrlRequested"

View File

@ -170,6 +170,10 @@ proc init*(self: Controller) =
setActive = false setActive = false
) )
self.events.on(SIGNAL_COMMUNITY_DATA_IMPORTED) do(e:Args):
let args = CommunityArgs(e)
self.delegate.communityDataImported(args.community)
self.events.on(SIGNAL_COMMUNITY_LEFT) do(e:Args): self.events.on(SIGNAL_COMMUNITY_LEFT) do(e:Args):
let args = CommunityIdArgs(e) let args = CommunityIdArgs(e)
self.delegate.communityLeft(args.communityId) self.delegate.communityLeft(args.communityId)
@ -220,8 +224,7 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_STATUS_URL_REQUESTED) do(e: Args): self.events.on(SIGNAL_STATUS_URL_REQUESTED) do(e: Args):
var args = StatusUrlArgs(e) var args = StatusUrlArgs(e)
self.delegate.onStatusUrlRequested(args.action, args.communityId, args.chatId, args.url, args.userId, self.delegate.onStatusUrlRequested(args.action, args.communityId, args.chatId, args.url, args.userId)
args.groupName, args.listOfUserIds)
self.events.on(SIGNAL_OS_NOTIFICATION_CLICKED) do(e: Args): self.events.on(SIGNAL_OS_NOTIFICATION_CLICKED) do(e: Args):
var args = ClickedNotificationArgs(e) var args = ClickedNotificationArgs(e)

View File

@ -189,6 +189,9 @@ method getAppSearchModule*(self: AccessInterface): QVariant {.base.} =
method getContactDetailsAsJson*(self: AccessInterface, publicKey: string, getVerificationRequest: bool): string {.base.} = method getContactDetailsAsJson*(self: AccessInterface, publicKey: string, getVerificationRequest: bool): string {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method communityDataImported*(self: AccessInterface, community: CommunityDto) {.base.} =
raise newException(ValueError, "No implementation available")
method resolveENS*(self: AccessInterface, ensName: string, uuid: string, reason: string = "") {.base.} = method resolveENS*(self: AccessInterface, ensName: string, uuid: string, reason: string = "") {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
@ -202,7 +205,7 @@ method isConnected*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method onStatusUrlRequested*(self: AccessInterface, action: StatusUrlAction, communityId: string, chatId: string, method onStatusUrlRequested*(self: AccessInterface, action: StatusUrlAction, communityId: string, chatId: string,
url: string, userId: string, groupName: string, listOfUserIds: seq[string]) {.base.} = url: string, userId: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getVerificationRequestFrom*(self: AccessInterface, publicKey: string): VerificationRequest {.base.} = method getVerificationRequestFrom*(self: AccessInterface, publicKey: string): VerificationRequest {.base.} =

View File

@ -92,9 +92,7 @@ type
networksModule: networks_module.AccessInterface networksModule: networks_module.AccessInterface
keycardSharedModule: keycard_shared_module.AccessInterface keycardSharedModule: keycard_shared_module.AccessInterface
moduleLoaded: bool moduleLoaded: bool
statusUrlGroupName: string statusUrlCommunityToSpectate: string
statusUrlGroupMembers: seq[string] # used only for creating group chat from the status url
statusUrlGroupMembersCount: int
# Forward declaration # Forward declaration
method calculateProfileSectionHasNotification*[T](self: Module[T]): bool method calculateProfileSectionHasNotification*[T](self: Module[T]): bool
@ -791,6 +789,11 @@ method getContactDetailsAsJson*[T](self: Module[T], publicKey: string, getVerifi
} }
return $jsonObj return $jsonObj
method communityDataImported*[T](self: Module[T], community: CommunityDto) =
if community.id == self.statusUrlCommunityToSpectate:
self.statusUrlCommunityToSpectate = ""
discard self.communitiesModule.spectateCommunity(community.id)
method resolveENS*[T](self: Module[T], ensName: string, uuid: string, reason: string = "") = method resolveENS*[T](self: Module[T], ensName: string, uuid: string, reason: string = "") =
if ensName.len == 0: if ensName.len == 0:
echo "error: cannot do a lookup for empty ens name" echo "error: cannot do a lookup for empty ens name"
@ -806,19 +809,6 @@ method resolvedENS*[T](self: Module[T], publicKey: string, address: string, uuid
let item = self.view.model().getItemById(singletonInstance.userProfile.getPubKey()) let item = self.view.model().getItemById(singletonInstance.userProfile.getPubKey())
self.setActiveSection(item) self.setActiveSection(item)
self.view.emitDisplayUserProfileSignal(publicKey) self.view.emitDisplayUserProfileSignal(publicKey)
elif(reason == STATUS_URL_ENS_RESOLVE_REASON & $StatusUrlAction.OpenOrCreatePrivateChat):
let item = self.view.model().getItemById(singletonInstance.userProfile.getPubKey())
self.setActiveSection(item)
self.getChatSectionModule().switchToOrCreateOneToOneChat(publicKey)
elif(reason == STATUS_URL_ENS_RESOLVE_REASON & $StatusUrlAction.OpenOrCreateGroupChat):
self.statusUrlGroupMembers.add(publicKey)
if(self.statusUrlGroupMembers.len == self.statusUrlGroupMembersCount):
let item = self.view.model().getItemById(singletonInstance.userProfile.getPubKey())
self.setActiveSection(item)
self.getChatSectionModule().createGroupChat(self.statusUrlGroupName, self.statusUrlGroupMembers)
self.statusUrlGroupName = ""
self.statusUrlGroupMembers = @[]
self.statusUrlGroupMembersCount = 0
else: else:
self.view.emitResolvedENSSignal(publicKey, address, uuid) self.view.emitResolvedENSSignal(publicKey, address, uuid)
@ -912,56 +902,47 @@ method ephemeralNotificationClicked*[T](self: Module[T], id: int64) =
method onMyRequestAdded*[T](self: Module[T]) = method onMyRequestAdded*[T](self: Module[T]) =
self.displayEphemeralNotification("Your Request has been submitted", "" , "checkmark-circle", false, EphemeralNotificationType.Success.int, "") self.displayEphemeralNotification("Your Request has been submitted", "" , "checkmark-circle", false, EphemeralNotificationType.Success.int, "")
proc getCommunityIdFromFullChatId(fullChatId: string): string =
const communityIdLength = 68
return fullChatId.substr(0, communityIdLength-1)
method onStatusUrlRequested*[T](self: Module[T], action: StatusUrlAction, communityId: string, chatId: string, method onStatusUrlRequested*[T](self: Module[T], action: StatusUrlAction, communityId: string, chatId: string,
url: string, userId: string, groupName: string, listOfUserIds: seq[string]) = url: string, userId: string) =
if(action == StatusUrlAction.OpenLinkInBrowser and singletonInstance.localAccountSensitiveSettings.getIsBrowserEnabled()): if(action == StatusUrlAction.DisplayUserProfile):
let item = self.view.model().getItemById(conf.BROWSER_SECTION_ICON)
self.setActiveSection(item)
self.browserSectionModule.openUrl(url)
elif(action == StatusUrlAction.DisplayUserProfile):
self.resolveENS(userId, "", STATUS_URL_ENS_RESOLVE_REASON & $StatusUrlAction.DisplayUserProfile) self.resolveENS(userId, "", STATUS_URL_ENS_RESOLVE_REASON & $StatusUrlAction.DisplayUserProfile)
elif(action == StatusUrlAction.OpenOrCreatePrivateChat):
self.resolveENS(chatId, "", STATUS_URL_ENS_RESOLVE_REASON & $StatusUrlAction.OpenOrCreatePrivateChat)
elif(action == StatusUrlAction.OpenOrJoinPublicChat):
let item = self.view.model().getItemById(singletonInstance.userProfile.getPubKey())
self.setActiveSection(item)
self.getChatSectionModule().createPublicChat(chatId)
elif(action == StatusUrlAction.OpenOrCreateGroupChat):
self.statusUrlGroupName = groupName
# if there are more than 20 members added to the url, we add only first 19
self.statusUrlGroupMembersCount = if listOfUserIds.len <= MAX_MEMBERS_IN_GROUP_CHAT_WITHOUT_ADMIN: listOfUserIds.len else: MAX_MEMBERS_IN_GROUP_CHAT_WITHOUT_ADMIN
var i = 0
for id in listOfUserIds:
if(i >= MAX_MEMBERS_IN_GROUP_CHAT_WITHOUT_ADMIN):
break
i.inc
self.resolveENS(id, "", STATUS_URL_ENS_RESOLVE_REASON & $StatusUrlAction.OpenOrCreateGroupChat)
elif(action == StatusUrlAction.RequestToJoinCommunity):
let item = self.view.model().getItemById(singletonInstance.userProfile.getPubKey())
self.setActiveSection(item)
self.communitiesModule.requestToJoinCommunity(communityId, singletonInstance.userProfile.getName())
elif(action == StatusUrlAction.OpenCommunity): elif(action == StatusUrlAction.OpenCommunity):
let item = self.view.model().getItemById(communityId) let item = self.view.model().getItemById(communityId)
if item.isEmpty():
# request community info and then spectate
self.statusUrlCommunityToSpectate = communityId
self.communitiesModule.requestCommunityInfo(communityId)
else:
self.setActiveSection(item) self.setActiveSection(item)
elif(action == StatusUrlAction.OpenCommunityChannel): elif(action == StatusUrlAction.OpenCommunityChannel):
var found = false
for cId, cModule in self.channelGroupModules.pairs: for cId, cModule in self.channelGroupModules.pairs:
if(cId == singletonInstance.userProfile.getPubKey()): if(cId == singletonInstance.userProfile.getPubKey()):
continue continue
if(cModule.doesCatOrChatExist(chatId)): if(cModule.doesCatOrChatExist(chatId)):
let item = self.view.model().getItemById(cId) let item = self.view.model().getItemById(cId)
self.setActiveSection(item) self.setActiveSection(item)
cModule.makeChatWithIdActive(chatId) cModule.makeChatWithIdActive(chatId)
found = true
break break
if not found:
let communityIdToSpectate = getCommunityIdFromFullChatId(chatId)
# request community info and then spectate
self.statusUrlCommunityToSpectate = communityIdToSpectate
self.communitiesModule.requestCommunityInfo(communityIdToSpectate)
# enable after MVP
#else(action == StatusUrlAction.OpenLinkInBrowser and singletonInstance.localAccountSensitiveSettings.getIsBrowserEnabled()):
# let item = self.view.model().getItemById(conf.BROWSER_SECTION_ICON)
# self.setActiveSection(item)
# self.browserSectionModule.openUrl(url)
method getKeycardSharedModule*[T](self: Module[T]): QVariant = method getKeycardSharedModule*[T](self: Module[T]): QVariant =
return self.keycardSharedModule.getModuleAsVariant() return self.keycardSharedModule.getModuleAsVariant()