mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 09:49:20 +02:00
Use intent and timeout for open in mobile app
This commit is contained in:
parent
bdcc626347
commit
a93ad97aa1
8 changed files with 142 additions and 17 deletions
|
@ -1,7 +1,7 @@
|
||||||
<div *ngIf="mobileMsg" class="mobile-msg">
|
<div *ngIf="mobileMsg" class="mobile-msg">
|
||||||
<div class="msg ellipsis me-auto" i18n>Open in the application?</div>
|
<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()">
|
<button class="border-0 p-0" title="Close this message" i18n-title (click)="hideMobileMsg()">
|
||||||
<my-global-icon iconName="cross"></my-global-icon>
|
<my-global-icon iconName="cross"></my-global-icon>
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { SignupLabelComponent } from '@app/shared/shared-main/users/signup-label
|
||||||
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ServerConfig } from '@peertube/peertube-models'
|
import { ServerConfig } from '@peertube/peertube-models'
|
||||||
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
|
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 { Subscription } from 'rxjs'
|
||||||
import { GlobalIconComponent } from '../shared/shared-icons/global-icon.component'
|
import { GlobalIconComponent } from '../shared/shared-icons/global-icon.component'
|
||||||
import { ButtonComponent } from '../shared/shared-main/buttons/button.component'
|
import { ButtonComponent } from '../shared/shared-main/buttons/button.component'
|
||||||
|
@ -66,7 +66,8 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||||
currentInterfaceLanguage: string
|
currentInterfaceLanguage: string
|
||||||
|
|
||||||
mobileMsg = false
|
mobileMsg = false
|
||||||
mobileAppUrl = ''
|
androidAppUrl = ''
|
||||||
|
iosAppUrl = ''
|
||||||
|
|
||||||
private serverConfig: ServerConfig
|
private serverConfig: ServerConfig
|
||||||
|
|
||||||
|
@ -159,12 +160,15 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||||
private setupMobileMsg () {
|
private setupMobileMsg () {
|
||||||
if (!this.isInMobileView()) return
|
if (!this.isInMobileView()) return
|
||||||
if (peertubeLocalStorage.getItem(HeaderComponent.LS_HIDE_MOBILE_MSG) === 'true') return
|
if (peertubeLocalStorage.getItem(HeaderComponent.LS_HIDE_MOBILE_MSG) === 'true') return
|
||||||
if (!isAndroid()) return
|
|
||||||
|
if (!isAndroid() && !isIphone()) return
|
||||||
|
|
||||||
this.mobileMsg = true
|
this.mobileMsg = true
|
||||||
document.body.classList.add('mobile-app-msg')
|
document.body.classList.add('mobile-app-msg')
|
||||||
|
|
||||||
const host = window.location.host
|
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 getVideoId = (url: string) => {
|
||||||
const matches = url.match(/^\/w\/([^/]+)$/)
|
const matches = url.match(/^\/w\/([^/]+)$/)
|
||||||
|
@ -183,19 +187,39 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
const url = event.url
|
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)
|
const videoId = getVideoId(url)
|
||||||
if (videoId) {
|
|
||||||
this.mobileAppUrl = `peertube://joinpeertube.org/video/${videoId}?host=${host}`
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const channelId = getChannelId(url)
|
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
|
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')
|
peertubeLocalStorage.setItem(HeaderComponent.LS_HIDE_MOBILE_MSG, 'true')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onOpenClientClick () {
|
||||||
|
if (!isIOS()) return
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = this.serverConfig.client.openInApp.ios.fallbackUrl
|
||||||
|
}, 2500)
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
isRegistrationAllowed () {
|
isRegistrationAllowed () {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { NgClass, NgIf, NgTemplateOutlet } from '@angular/common'
|
import { NgClass, NgIf, NgTemplateOutlet } from '@angular/common'
|
||||||
import { Component, Input, OnChanges, ViewChild } from '@angular/core'
|
import { Component, Input, OnChanges, ViewChild } from '@angular/core'
|
||||||
import { AuthService, Notifier, RedirectService } from '@app/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 { FeedFormat } from '@peertube/peertube-models'
|
||||||
import { concat, forkJoin, merge } from 'rxjs'
|
import { concat, forkJoin, merge } from 'rxjs'
|
||||||
import { Account } from '../shared-main/account/account.model'
|
import { Account } from '../shared-main/account/account.model'
|
||||||
import { VideoChannel } from '../shared-main/channel/video-channel.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 { VideoService } from '../shared-main/video/video.service'
|
||||||
import { RemoteSubscribeComponent } from './remote-subscribe.component'
|
import { RemoteSubscribeComponent } from './remote-subscribe.component'
|
||||||
import { UserSubscriptionService } from './user-subscription.service'
|
import { UserSubscriptionService } from './user-subscription.service'
|
||||||
|
@ -22,9 +21,7 @@ import { UserSubscriptionService } from './user-subscription.service'
|
||||||
NgbDropdown,
|
NgbDropdown,
|
||||||
NgbDropdownToggle,
|
NgbDropdownToggle,
|
||||||
NgbDropdownMenu,
|
NgbDropdownMenu,
|
||||||
NgbDropdownItem,
|
RemoteSubscribeComponent
|
||||||
RemoteSubscribeComponent,
|
|
||||||
NumberFormatterPipe
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SubscribeButtonComponent implements OnChanges {
|
export class SubscribeButtonComponent implements OnChanges {
|
||||||
|
|
|
@ -1077,6 +1077,29 @@ client:
|
||||||
# You can automatically redirect your users on this external platform when they click on the login button
|
# You can automatically redirect your users on this external platform when they click on the login button
|
||||||
redirect_on_single_external_auth: false
|
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:
|
storyboards:
|
||||||
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
|
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
|
@ -1087,6 +1087,29 @@ client:
|
||||||
# You can automatically redirect your users on this external platform when they click on the login button
|
# You can automatically redirect your users on this external platform when they click on the login button
|
||||||
redirect_on_single_external_auth: false
|
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:
|
storyboards:
|
||||||
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
|
# Generate storyboards of local videos using ffmpeg so users can see the video preview in the player while scrubbing the video
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
|
@ -51,6 +51,24 @@ export interface ServerConfig {
|
||||||
redirectOnSingleExternalAuth: boolean
|
redirectOnSingleExternalAuth: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openInApp: {
|
||||||
|
android: {
|
||||||
|
intent: {
|
||||||
|
enabled: boolean
|
||||||
|
host: string
|
||||||
|
scheme: string
|
||||||
|
fallbackUrl: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ios: {
|
||||||
|
enabled: boolean
|
||||||
|
host: string
|
||||||
|
scheme: string
|
||||||
|
fallbackUrl: string
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaults: {
|
defaults: {
|
||||||
|
|
|
@ -87,6 +87,22 @@ const CONFIG = {
|
||||||
LOGIN: {
|
LOGIN: {
|
||||||
get REDIRECT_ON_SINGLE_EXTERNAL_AUTH () { return config.get<boolean>('client.menu.login.redirect_on_single_external_auth') }
|
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') }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,22 @@ class ServerConfigManager {
|
||||||
login: {
|
login: {
|
||||||
redirectOnSingleExternalAuth: CONFIG.CLIENT.MENU.LOGIN.REDIRECT_ON_SINGLE_EXTERNAL_AUTH
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue