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:
parent
3b3b737956
commit
6d2a2e6e03
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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/'
|
||||
|
|
Loading…
Reference in New Issue