diff --git a/client/src/app/header/header.component.html b/client/src/app/header/header.component.html index 1137c203ece..d24a4559a5d 100644 --- a/client/src/app/header/header.component.html +++ b/client/src/app/header/header.component.html @@ -1,3 +1,13 @@ +
+
Open in the application?
+ + Open + + +
+
diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss index b2951c28711..2926648a520 100644 --- a/client/src/app/header/header.component.scss +++ b/client/src/app/header/header.component.scss @@ -3,6 +3,27 @@ @use '_mixins' as *; @use '_button-mixins' as *; +.mobile-msg { + display: flex; + align-items: center; + padding: 0 1.5rem; + height: $header-mobile-msg-height; + background: pvar(--bg-secondary-400); + + .msg { + color: pvar(--fg-300); + font-weight: $font-bold; + + @include font-size(14px); + } + + button { + my-global-icon { + color: pvar(--fg-200); + } + } +} + .root { --co-logo-size: 34px; --co-root-padding: 1.5rem; diff --git a/client/src/app/header/header.component.ts b/client/src/app/header/header.component.ts index c8760c19f28..657306da933 100644 --- a/client/src/app/header/header.component.ts +++ b/client/src/app/header/header.component.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common' import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core' -import { Router, RouterLink } from '@angular/router' +import { ActivatedRoute, NavigationEnd, Router, RouterLink } from '@angular/router' import { AuthService, AuthStatus, @@ -25,6 +25,8 @@ import { Subscription } from 'rxjs' import { GlobalIconComponent } from '../shared/shared-icons/global-icon.component' import { ButtonComponent } from '../shared/shared-main/buttons/button.component' import { SearchTypeaheadComponent } from './search-typeahead.component' +import { isAndroid, isIOS, isIphone } from '@root-helpers/web-browser' +import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' @Component({ selector: 'my-header', @@ -51,6 +53,8 @@ import { SearchTypeaheadComponent } from './search-typeahead.component' }) export class HeaderComponent implements OnInit, OnDestroy { + private static LS_HIDE_MOBILE_MSG = 'hide-mobile-msg' + @ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent @ViewChild('quickSettingsModal', { static: true }) quickSettingsModal: QuickSettingsModalComponent @ViewChild('dropdown') dropdown: NgbDropdown @@ -62,6 +66,9 @@ export class HeaderComponent implements OnInit, OnDestroy { currentInterfaceLanguage: string + mobileMsg = false + mobileAppUrl = '' + private serverConfig: ServerConfig private quickSettingsModalSub: Subscription @@ -76,6 +83,7 @@ export class HeaderComponent implements OnInit, OnDestroy { private screenService: ScreenService, private modalService: PeertubeModalService, private router: Router, + private route: ActivatedRoute, private menu: MenuService ) { } @@ -127,6 +135,8 @@ export class HeaderComponent implements OnInit, OnDestroy { this.quickSettingsModalSub = this.modalService.openQuickSettingsSubject .subscribe(() => this.openQuickSettings()) + + this.setupMobileMsg() } ngOnDestroy () { @@ -147,6 +157,58 @@ 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 + + this.mobileMsg = true + document.body.classList.add('mobile-app-msg') + + const host = window.location.host + + const getVideoId = (url: string) => { + const matches = url.match(/^\/w\/([^/]+)$/) + + if (matches) return matches[1] + } + + const getChannelId = (url: string) => { + const matches = url.match(/^\/c\/([^/]+)/) + + if (matches) return matches[1] + } + + this.router.events.subscribe(event => { + if (!(event instanceof NavigationEnd)) return + + const url = event.url + + const videoId = getVideoId(url) + if (videoId) { + this.mobileAppUrl = `peertube:///video/${videoId}?host=${host}` + return + } + + const channelId = getChannelId(url) + if (channelId) { + this.mobileAppUrl = `peertube:///video-channel/${channelId}?host=${host}` + return + } + + this.mobileAppUrl = `peertube:///?host=${host}` + }) + } + + hideMobileMsg () { + this.mobileMsg = false + document.body.classList.remove('mobile-app-msg') + + peertubeLocalStorage.setItem(HeaderComponent.LS_HIDE_MOBILE_MSG, 'true') + } + + // --------------------------------------------------------------------------- + isRegistrationAllowed () { if (!this.serverConfig) return false diff --git a/client/src/root-helpers/web-browser.ts b/client/src/root-helpers/web-browser.ts index 9dade20e8e0..b3193766117 100644 --- a/client/src/root-helpers/web-browser.ts +++ b/client/src/root-helpers/web-browser.ts @@ -1,4 +1,4 @@ -function isIOS () { +export function isIOS () { if (/iPad|iPhone|iPod/.test(navigator.platform)) { return true } @@ -9,16 +9,19 @@ function isIOS () { navigator.platform.includes('MacIntel')) } -function isSafari () { +export function isSafari () { return /^((?!chrome|android).)*safari/i.test(navigator.userAgent) } -function isMobile () { +export function isMobile () { return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) } -export { - isIOS, - isSafari, - isMobile +export function isIphone () { + return /iPhone/i.test(navigator.userAgent) } + +export function isAndroid () { + return /Android/i.test(navigator.userAgent) +} + diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 7b3d04e2984..ea352614533 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss @@ -85,15 +85,27 @@ body { --header-height: #{$header-height}; + &.mobile-app-msg { + --header-height: #{$header-height + $header-mobile-msg-height}; + } + --menu-fg: #{pvar(--fg-400)}; --menu-bg: #{pvar(--bg-secondary-400)}; @media screen and (max-width: $small-view) { --header-height: #{$header-height-small-view}; + + &.mobile-app-msg { + --header-height: #{$header-height-small-view + $header-mobile-msg-height}; + } } @media screen and (max-width: $mobile-view) { --header-height: #{$header-height-mobile-view}; + + &.mobile-app-msg { + --header-height: #{$header-height-mobile-view + $header-mobile-msg-height}; + } } // Light theme diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss index 6baf1a0c1b5..30b294ee84a 100644 --- a/client/src/sass/include/_variables.scss +++ b/client/src/sass/include/_variables.scss @@ -23,14 +23,13 @@ $button-font-size: 1rem; $header-height: 94px; $header-height-small-view: 74px; $header-height-mobile-view: 124px; +$header-mobile-msg-height: 48px; $menu-width: 248px; $menu-collapsed-width: 50px; $menu-margin-left: 2rem; $menu-overlay-view: 1200px; -$sub-menu-height: 81px; - $banner-inverted-ratio: math.div(1, 6); $max-channels-width: 1200px;