1
0
Fork 0

Use intent and timeout for open in mobile app

This commit is contained in:
Chocobozzz 2025-02-14 10:42:45 +01:00
parent bdcc626347
commit a93ad97aa1
No known key found for this signature in database
GPG key ID: 583A612D890159BE
8 changed files with 142 additions and 17 deletions

View file

@ -1,7 +1,7 @@
<div *ngIf="mobileMsg" class="mobile-msg">
<div class="msg ellipsis me-auto" i18n>Open in the application?</div>
<a class="peertube-button-link secondary-button me-3" [href]="mobileAppUrl">Open</a>
<a class="peertube-button-link secondary-button me-3" [href]="androidAppUrl || iosAppUrl" (click)="onOpenClientClick()">Open</a>
<button class="border-0 p-0" title="Close this message" i18n-title (click)="hideMobileMsg()">
<my-global-icon iconName="cross"></my-global-icon>

View file

@ -22,7 +22,7 @@ import { SignupLabelComponent } from '@app/shared/shared-main/users/signup-label
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
import { ServerConfig } from '@peertube/peertube-models'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { isAndroid } from '@root-helpers/web-browser'
import { isAndroid, isIOS, isIphone } from '@root-helpers/web-browser'
import { Subscription } from 'rxjs'
import { GlobalIconComponent } from '../shared/shared-icons/global-icon.component'
import { ButtonComponent } from '../shared/shared-main/buttons/button.component'
@ -66,7 +66,8 @@ export class HeaderComponent implements OnInit, OnDestroy {
currentInterfaceLanguage: string
mobileMsg = false
mobileAppUrl = ''
androidAppUrl = ''
iosAppUrl = ''
private serverConfig: ServerConfig
@ -159,12 +160,15 @@ export class HeaderComponent implements OnInit, OnDestroy {
private setupMobileMsg () {
if (!this.isInMobileView()) return
if (peertubeLocalStorage.getItem(HeaderComponent.LS_HIDE_MOBILE_MSG) === 'true') return
if (!isAndroid()) return
if (!isAndroid() && !isIphone()) return
this.mobileMsg = true
document.body.classList.add('mobile-app-msg')
const host = window.location.host
const intentConfig = this.serverConfig.client.openInApp.android.intent
const iosConfig = this.serverConfig.client.openInApp.ios
const getVideoId = (url: string) => {
const matches = url.match(/^\/w\/([^/]+)$/)
@ -183,19 +187,39 @@ export class HeaderComponent implements OnInit, OnDestroy {
const url = event.url
const baseAndroid = `intent://${intentConfig.host}`
const fallbackAndroid = `#Intent;scheme=${intentConfig.scheme};S.browser_fallback_url=${intentConfig.fallbackUrl};end`
const baseIOS = `peertube://${iosConfig.host}`
const videoId = getVideoId(url)
if (videoId) {
this.mobileAppUrl = `peertube://joinpeertube.org/video/${videoId}?host=${host}`
return
}
const channelId = getChannelId(url)
if (channelId) {
this.mobileAppUrl = `peertube://joinpeertube.org/video-channel/${channelId}?host=${host}`
if (videoId) {
if (isAndroid()) {
this.androidAppUrl = `${baseAndroid}/video/${videoId}?host=${host}${fallbackAndroid}`
} else {
this.iosAppUrl = `${baseIOS}/video/${videoId}?host=${host}`
}
return
}
this.mobileAppUrl = `peertube://joinpeertube.org/?host=${host}`
if (channelId) {
if (isAndroid()) {
this.androidAppUrl = `${baseAndroid}/video-channel/${channelId}?host=${host}${fallbackAndroid}`
} else {
this.iosAppUrl = `${baseIOS}/video/${videoId}?host=${host}`
}
return
}
if (isAndroid()) {
this.androidAppUrl = `${baseAndroid}/?host=${host}${fallbackAndroid}`
} else {
this.iosAppUrl = `${baseIOS}/?host=${host}`
}
})
}
@ -206,6 +230,14 @@ export class HeaderComponent implements OnInit, OnDestroy {
peertubeLocalStorage.setItem(HeaderComponent.LS_HIDE_MOBILE_MSG, 'true')
}
onOpenClientClick () {
if (!isIOS()) return
setTimeout(() => {
window.location.href = this.serverConfig.client.openInApp.ios.fallbackUrl
}, 2500)
}
// ---------------------------------------------------------------------------
isRegistrationAllowed () {

View file

@ -1,12 +1,11 @@
import { NgClass, NgIf, NgTemplateOutlet } from '@angular/common'
import { Component, Input, OnChanges, ViewChild } from '@angular/core'
import { AuthService, Notifier, RedirectService } from '@app/core'
import { NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'
import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'
import { FeedFormat } from '@peertube/peertube-models'
import { concat, forkJoin, merge } from 'rxjs'
import { Account } from '../shared-main/account/account.model'
import { VideoChannel } from '../shared-main/channel/video-channel.model'
import { NumberFormatterPipe } from '../shared-main/common/number-formatter.pipe'
import { VideoService } from '../shared-main/video/video.service'
import { RemoteSubscribeComponent } from './remote-subscribe.component'
import { UserSubscriptionService } from './user-subscription.service'
@ -22,9 +21,7 @@ import { UserSubscriptionService } from './user-subscription.service'
NgbDropdown,
NgbDropdownToggle,
NgbDropdownMenu,
NgbDropdownItem,
RemoteSubscribeComponent,
NumberFormatterPipe
RemoteSubscribeComponent
]
})
export class SubscribeButtonComponent implements OnChanges {

View file

@ -1077,6 +1077,29 @@ client:
# You can automatically redirect your users on this external platform when they click on the login button
redirect_on_single_external_auth: false
open_in_app:
android:
# Use an intent URL: https://developer.chrome.com/docs/android/intents
intent:
enabled: true
# Host registered by the mobile app
host: 'joinpeertube.org'
# Scheme registered by the mobile app
scheme: 'peertube'
# If not having the app on the mobile device, open this page
# F-Droid alternative: https://f-droid.org/packages/org.framasoft.peertube/
fallback_url: 'https://play.google.com/store/apps/details?id=org.framasoft.peertube'
ios:
# We use a timeout for iOS: if the app is not opened after a few seconds, open the fallback URL
enabled: true
# Host registered by the mobile app
host: 'joinpeertube.org'
# Scheme registered by the mobile app
scheme: 'peertube'
# If not having the app on the mobile device, open this page
fallback_url: 'https://apps.apple.com/fr/app/peertube/id6737834858'
storyboards:
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
enabled: true

View file

@ -1087,6 +1087,29 @@ client:
# You can automatically redirect your users on this external platform when they click on the login button
redirect_on_single_external_auth: false
open_in_app:
android:
# Use an intent URL: https://developer.chrome.com/docs/android/intents
intent:
enabled: true
# Host registered by the mobile app
host: 'joinpeertube.org'
# Scheme registered by the mobile app
scheme: 'peertube'
# If not having the app on the mobile device, open this page
# F-Droid alternative: https://f-droid.org/packages/org.framasoft.peertube/
fallback_url: 'https://play.google.com/store/apps/details?id=org.framasoft.peertube'
ios:
# We use a timeout for iOS: if the app is not opened after a few seconds, open the fallback URL
enabled: true
# Host registered by the mobile app
host: 'joinpeertube.org'
# Scheme registered by the mobile app
scheme: 'peertube'
# If not having the app on the mobile device, open this page
fallback_url: 'https://apps.apple.com/fr/app/peertube/id6737834858'
storyboards:
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
enabled: true

View file

@ -51,6 +51,24 @@ export interface ServerConfig {
redirectOnSingleExternalAuth: boolean
}
}
openInApp: {
android: {
intent: {
enabled: boolean
host: string
scheme: string
fallbackUrl: string
}
}
ios: {
enabled: boolean
host: string
scheme: string
fallbackUrl: string
}
}
}
defaults: {

View file

@ -87,6 +87,22 @@ const CONFIG = {
LOGIN: {
get REDIRECT_ON_SINGLE_EXTERNAL_AUTH () { return config.get<boolean>('client.menu.login.redirect_on_single_external_auth') }
}
},
OPEN_IN_APP: {
ANDROID: {
INTENT: {
get ENABLED () { return config.get<boolean>('client.open_in_app.android.intent.enabled') },
get HOST () { return config.get<string>('client.open_in_app.android.intent.host') },
get SCHEME () { return config.get<string>('client.open_in_app.android.intent.scheme') },
get FALLBACK_URL () { return config.get<string>('client.open_in_app.android.intent.fallback_url') }
}
},
IOS: {
get ENABLED () { return config.get<boolean>('client.open_in_app.ios.enabled') },
get HOST () { return config.get<string>('client.open_in_app.ios.host') },
get SCHEME () { return config.get<string>('client.open_in_app.ios.scheme') },
get FALLBACK_URL () { return config.get<string>('client.open_in_app.ios.fallback_url') }
}
}
},

View file

@ -66,6 +66,22 @@ class ServerConfigManager {
login: {
redirectOnSingleExternalAuth: CONFIG.CLIENT.MENU.LOGIN.REDIRECT_ON_SINGLE_EXTERNAL_AUTH
}
},
openInApp: {
android: {
intent: {
enabled: CONFIG.CLIENT.OPEN_IN_APP.ANDROID.INTENT.ENABLED,
host: CONFIG.CLIENT.OPEN_IN_APP.ANDROID.INTENT.HOST,
scheme: CONFIG.CLIENT.OPEN_IN_APP.ANDROID.INTENT.SCHEME,
fallbackUrl: CONFIG.CLIENT.OPEN_IN_APP.ANDROID.INTENT.FALLBACK_URL
}
},
ios: {
enabled: CONFIG.CLIENT.OPEN_IN_APP.IOS.ENABLED,
host: CONFIG.CLIENT.OPEN_IN_APP.IOS.HOST,
scheme: CONFIG.CLIENT.OPEN_IN_APP.IOS.SCHEME,
fallbackUrl: CONFIG.CLIENT.OPEN_IN_APP.IOS.FALLBACK_URL
}
}
},