fix(@desktop/general): Fix clicking deep links in chat

Clicking any deep-link in chat: /u, /c, /cc does not open browser but executes instantly

Fix: #6302
This commit is contained in:
Michal Iskierko 2022-10-20 11:05:10 +02:00 committed by Iuri Matias
parent 3b3b737956
commit 6d2a2e6e03
11 changed files with 67 additions and 71 deletions

View File

@ -202,6 +202,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.mainModule = main_module.newModule[AppController](
result,
statusFoundation.events,
statusFoundation.urlsManager,
result.keychainService,
result.accountsService,
result.chatService,

View File

@ -6,15 +6,18 @@ import ../../global/app_signals
logScope:
topics = "urls-manager"
const UriFormatUserProfile = "status-im://u/"
const StatusInternalLink = "status-im"
const StatusExternalLink = "join.status.im"
const UriFormatCommunity = "status-im://c/"
const UriFormatUserProfile = StatusInternalLink & "://u/"
const UriFormatCommunityChannel = "status-im://cc/"
const UriFormatCommunity = StatusInternalLink & "://c/"
const UriFormatGroupChat = "status-im://g/"
const UriFormatCommunityChannel = StatusInternalLink & "://cc/"
const UriFormatBrowser = "status-im://b/"
# enable after MVP
#const UriFormatGroupChat = StatusInternalLink & "://g/"
#const UriFormatBrowser = StatusInternalLink & "://b/"
QtObject:
type UrlsManager* = ref object of QObject
@ -71,7 +74,6 @@ QtObject:
#elif url.startsWith(UriFormatBrowser):
# data.action = StatusUrlAction.OpenLinkInBrowser
# data.url = url[UriFormatBrowser.len .. url.len-1]
else:
info "Unsupported deep link structure: ", url
return
@ -83,3 +85,10 @@ QtObject:
if self.protocolUriOnStart != "":
self.onUrlActivated(self.protocolUriOnStart)
self.protocolUriOnStart = ""
proc convertExternalLinkToInternal*(self: UrlsManager, statusDeepLink: string): string =
let idx = find(statusDeepLink, StatusExternalLink)
result = statusDeepLink
if idx != -1:
result = statusDeepLink[idx + StatusExternalLink.len .. ^1]
result = StatusInternalLink & ":/" & result

View File

@ -15,7 +15,7 @@ type StatusFoundation* = ref object
fleetConfiguration*: FleetConfiguration
threadpool*: ThreadPool
signalsManager*: SignalsManager
urlsManager: UrlsManager
urlsManager*: UrlsManager
proc newStatusFoundation*(fleetConfig: string): StatusFoundation =
result = StatusFoundation()

View File

@ -226,6 +226,9 @@ method runAuthenticationPopup*(self: AccessInterface, keyUid: string, bip44Path:
method onMyRequestAdded*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method activateStatusDeepLink*(self: AccessInterface, statusDeepLink: string) {.base.} =
raise newException(ValueError, "No implementation available")
# This way (using concepts) is used only for the modules managed by AppController
type
DelegateInterface* = concept c

View File

@ -59,6 +59,7 @@ import ../../../app_service/common/social_links
import ../../core/notifications/details
import ../../core/eventemitter
import ../../core/custom_urls/urls_manager
export io_interface
@ -75,6 +76,7 @@ type
controller: Controller
channelGroupModules: OrderedTable[string, chat_section_module.AccessInterface]
events: EventEmitter
urlsManager: UrlsManager
keycardService: keycard_service.Service
settingsService: settings_service.Service
privacyService: privacy_service.Service
@ -100,6 +102,7 @@ method calculateProfileSectionHasNotification*[T](self: Module[T]): bool
proc newModule*[T](
delegate: T,
events: EventEmitter,
urlsManager: UrlsManager,
keychainService: keychain_service.Service,
accountsService: accounts_service.Service,
chatService: chat_service.Service,
@ -153,6 +156,7 @@ proc newModule*[T](
result.moduleLoaded = false
result.events = events
result.urlsManager = urlsManager
result.keycardService = keycardService
result.settingsService = settingsService
result.privacyService = privacyService
@ -970,3 +974,7 @@ method runAuthenticationPopup*[T](self: Module[T], keyUid: string, bip44Path: st
method onDisplayKeycardSharedModuleFlow*[T](self: Module[T]) =
self.view.emitDisplayKeycardSharedModuleFlow()
method activateStatusDeepLink*[T](self: Module[T], statusDeepLink: string) =
let linkToActivate = self.urlsManager.convertExternalLinkToInternal(statusDeepLink)
self.urlsManager.onUrlActivated(linkToActivate)

View File

@ -227,6 +227,9 @@ QtObject:
QtProperty[QVariant] keycardSharedModule:
read = getKeycardSharedModule
proc activateStatusDeepLink*(self: View, statusDeepLink: string) {.slot.} =
self.delegate.activateStatusDeepLink(statusDeepLink)
proc displayKeycardSharedModuleFlow*(self: View) {.signal.}
proc emitDisplayKeycardSharedModuleFlow*(self: View) =
self.displayKeycardSharedModuleFlow()

View File

@ -104,8 +104,13 @@ proc mainProc() =
# We increase js stack size to prevent "Maximum call stack size exceeded" on UI loading.
os.putEnv("QV4_JS_MAX_STACK_SIZE", "10485760")
os.putEnv("QT_QUICK_CONTROLS_HOVER_ENABLED", "1")
let appController = newAppController(statusFoundation)
let singleInstance = newSingleInstance($toMD5(DATADIR), openUri)
let urlSchemeEvent = newStatusUrlSchemeEventObject()
# init url manager before app controller
statusFoundation.initUrlSchemeManager(urlSchemeEvent, singleInstance, openUri)
let appController = newAppController(statusFoundation)
let networkAccessFactory = newQNetworkAccessManagerFactory(TMPDIR & "netcache")
let isProductionQVariant = newQVariant(if defined(production): true else: false)
@ -116,9 +121,6 @@ proc mainProc() =
# Register events objects
let dockShowAppEvent = newStatusDockShowAppEventObject(singletonInstance.engine)
let osThemeEvent = newStatusOSThemeEventObject(singletonInstance.engine)
let urlSchemeEvent = newStatusUrlSchemeEventObject()
statusFoundation.initUrlSchemeManager(urlSchemeEvent, singleInstance, openUri)
if not defined(macosx):
app.icon(app.applicationDirPath & statusAppIconPath)

View File

@ -50,6 +50,10 @@ QtObject {
mainModule.setActiveSectionById(communityId);
}
function activateStatusDeepLink(link) {
mainModuleInst.activateStatusDeepLink(link)
}
function setObservedCommunity(communityId) {
communitiesModuleInst.setObservedCommunity(communityId);
}
@ -394,29 +398,20 @@ QtObject {
callback: null
}
// Link to send a direct message
let index = link.indexOf("/u/")
if (index === -1) {
// Try /p/ as well
index = link.indexOf("/p/")
}
// User profile
// There is invitation bubble only for /c/ link for now
/*let index = link.indexOf("/u/")
if (index > -1) {
const pk = link.substring(index + 3)
result.title = qsTr("Start a 1-on-1 chat with %1")
.arg(Utils.isChatKey(pk) ? globalUtils.generateAlias(pk) : ("@" + Utils.removeStatusEns(pk)))
//const pk = link.substring(index + 3)
result.title = qsTr("Display user profile")
result.callback = function () {
if (Utils.isChatKey(pk)) {
chatCommunitySectionModule.createOneToOneChat("", pk, "")
} else {
// Not Refactored Yet
// chatsModel.channelView.joinWithENS(pk);
}
mainModuleInst.activateStatusDeepLink(link)
}
return result
}
}*/
// Community
index = link.lastIndexOf("/c/")
let index = link.lastIndexOf("/c/")
if (index > -1) {
const communityId = link.substring(index + 3)
@ -448,42 +443,16 @@ QtObject {
return result
}
// Group chat
index = link.lastIndexOf("/g/")
if (index > -1) {
let indexAdminPk = link.lastIndexOf("a=")
let indexChatName = link.lastIndexOf("a1=")
let indexChatId = link.lastIndexOf("a2=")
const pubKey = link.substring(indexAdminPk + 2, indexChatName - 1)
const chatName = link.substring(indexChatName + 3, indexChatId - 1)
const chatId = link.substring(indexChatId + 3, link.length)
result.title = qsTr("Join the %1 group chat").arg(chatName)
result.callback = function () {
// Not Refactored Yet
// chatsModel.groups.joinGroupChatFromInvitation(chatName, chatId, pubKey);
}
return result
}
// Not Refactored Yet (when we get to this we will most likely remove it, since other approach will be used)
// // Public chat
// // This needs to be the last check because it is as VERY loose check
// index = link.lastIndexOf("/")
// if (index > -1) {
// const chatId = link.substring(index + 1)
// result.title = qsTr("Join the %1 public channel").arg(chatId)
// result.callback = function () {
// chatsModel.channelView.joinPublicChat(chatId);
// }
// return result
// }
return result
function isStatusDeepLink(link) {
return link.includes(Constants.deepLinkPrefix) || link.includes(Constants.joinStatusLink)
}
function getLinkDataForStatusLinks(link) {
if (!link.includes(Constants.deepLinkPrefix) && !link.includes(Constants.joinStatusLink)) {
if (!isStatusDeepLink(link)) {
return
}

View File

@ -120,8 +120,8 @@ Column {
if (!data.fetching && data.communityId) {
return linkMessageLoader.sourceComponent = invitationBubble
}
return linkMessageLoader.sourceComponent = unfurledLinkComponent
// do not show unfurledLinkComponent
return
}
}
}
@ -144,13 +144,13 @@ Column {
// Reset the height in case we set it to 0 below. See note below
// for more information
this.height = undefined
const linkHostname = Utils.getHostname(link)
if (!localAccountSensitiveSettings.whitelistedUnfurlingSites) {
localAccountSensitiveSettings.whitelistedUnfurlingSites = {}
}
const whitelistHosts = Object.keys(localAccountSensitiveSettings.whitelistedUnfurlingSites)
const linkExists = whitelistHosts.some(hostname => linkHostname.endsWith(hostname))
const linkWhiteListed = linkExists && whitelistHosts.some(hostname =>
@ -181,7 +181,8 @@ Column {
return invitationBubble
}
return unfurledLinkComponent
// do not show unfurledLinkComponent
return
}
linkFetchConnections.enabled = true
@ -316,7 +317,6 @@ Column {
if (!!linkData.callback) {
return linkData.callback()
}
Global.openLink(linkData.address)
}
}

View File

@ -518,12 +518,13 @@ Loader {
if (link.startsWith('//')) {
const pubkey = link.replace("//", "");
Global.openProfilePopup(pubkey)
return;
}
if (link.startsWith('#')) {
return
} else if (link.startsWith('#')) {
rootStore.chatCommunitySectionModule.switchToChannel(link.replace("#", ""))
return;
return
} else if (rootStore.isStatusDeepLink(link)) {
rootStore.activateStatusDeepLink(link)
return
}
Global.openLink(link)

View File

@ -618,7 +618,7 @@ QtObject {
readonly property int repeatHeaderInterval: 2
readonly property string deepLinkPrefix: 'statusim://'
readonly property string deepLinkPrefix: 'status-im://'
readonly property string joinStatusLink: 'join.status.im'
readonly property string communityLinkPrefix: 'https://join.status.im/c/'
readonly property string userLinkPrefix: 'https://join.status.im/u/'