aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/core
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/core')
-rw-r--r--client/src/app/core/core.module.ts14
-rw-r--r--client/src/app/core/routing/custom-reuse-strategy.ts6
-rw-r--r--client/src/app/core/routing/index.ts2
-rw-r--r--client/src/app/core/routing/peertube-router.service.ts78
-rw-r--r--client/src/app/core/routing/scroll.service.ts91
5 files changed, 189 insertions, 2 deletions
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts
index 3e2056481..04be0671c 100644
--- a/client/src/app/core/core.module.ts
+++ b/client/src/app/core/core.module.ts
@@ -14,7 +14,17 @@ import { throwIfAlreadyLoaded } from './module-import-guard'
14import { Notifier } from './notification' 14import { Notifier } from './notification'
15import { HtmlRendererService, LinkifierService, MarkdownService } from './renderer' 15import { HtmlRendererService, LinkifierService, MarkdownService } from './renderer'
16import { RestExtractor, RestService } from './rest' 16import { RestExtractor, RestService } from './rest'
17import { HomepageRedirectComponent, LoginGuard, MetaGuard, MetaService, RedirectService, UnloggedGuard, UserRightGuard } from './routing' 17import {
18 HomepageRedirectComponent,
19 LoginGuard,
20 MetaGuard,
21 MetaService,
22 PeerTubeRouterService,
23 RedirectService,
24 ScrollService,
25 UnloggedGuard,
26 UserRightGuard
27} from './routing'
18import { CanDeactivateGuard } from './routing/can-deactivate-guard.service' 28import { CanDeactivateGuard } from './routing/can-deactivate-guard.service'
19import { ServerConfigResolver } from './routing/server-config-resolver.service' 29import { ServerConfigResolver } from './routing/server-config-resolver.service'
20import { ScopedTokensService } from './scoped-tokens' 30import { ScopedTokensService } from './scoped-tokens'
@@ -80,6 +90,8 @@ import { LocalStorageService, ScreenService, SessionStorageService } from './wra
80 PeerTubeSocket, 90 PeerTubeSocket,
81 ServerConfigResolver, 91 ServerConfigResolver,
82 CanDeactivateGuard, 92 CanDeactivateGuard,
93 PeerTubeRouterService,
94 ScrollService,
83 95
84 MetaService, 96 MetaService,
85 MetaGuard 97 MetaGuard
diff --git a/client/src/app/core/routing/custom-reuse-strategy.ts b/client/src/app/core/routing/custom-reuse-strategy.ts
index c2510f1df..3000093a8 100644
--- a/client/src/app/core/routing/custom-reuse-strategy.ts
+++ b/client/src/app/core/routing/custom-reuse-strategy.ts
@@ -1,5 +1,7 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router' 2import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
3import { RouterSetting } from './'
4import { PeerTubeRouterService } from './peertube-router.service'
3 5
4@Injectable() 6@Injectable()
5export class CustomReuseStrategy implements RouteReuseStrategy { 7export class CustomReuseStrategy implements RouteReuseStrategy {
@@ -78,6 +80,8 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
78 } 80 }
79 81
80 private isReuseEnabled (route: ActivatedRouteSnapshot) { 82 private isReuseEnabled (route: ActivatedRouteSnapshot) {
81 return route.data.reuse?.enabled && route.queryParams['a-state'] 83 // Cannot use peertube router here because of cyclic router dependency
84 return route.data.reuse?.enabled &&
85 !!(route.queryParams[PeerTubeRouterService.ROUTE_SETTING_NAME] & RouterSetting.REUSE_COMPONENT)
82 } 86 }
83} 87}
diff --git a/client/src/app/core/routing/index.ts b/client/src/app/core/routing/index.ts
index d0c688a2f..3b1690ecc 100644
--- a/client/src/app/core/routing/index.ts
+++ b/client/src/app/core/routing/index.ts
@@ -5,9 +5,11 @@ export * from './homepage-redirect.component'
5export * from './login-guard.service' 5export * from './login-guard.service'
6export * from './menu-guard.service' 6export * from './menu-guard.service'
7export * from './meta-guard.service' 7export * from './meta-guard.service'
8export * from './peertube-router.service'
8export * from './meta.service' 9export * from './meta.service'
9export * from './preload-selected-modules-list' 10export * from './preload-selected-modules-list'
10export * from './redirect.service' 11export * from './redirect.service'
12export * from './scroll.service'
11export * from './server-config-resolver.service' 13export * from './server-config-resolver.service'
12export * from './unlogged-guard.service' 14export * from './unlogged-guard.service'
13export * from './user-right-guard.service' 15export * from './user-right-guard.service'
diff --git a/client/src/app/core/routing/peertube-router.service.ts b/client/src/app/core/routing/peertube-router.service.ts
new file mode 100644
index 000000000..35716cc79
--- /dev/null
+++ b/client/src/app/core/routing/peertube-router.service.ts
@@ -0,0 +1,78 @@
1import { filter } from 'rxjs/operators'
2import { Injectable } from '@angular/core'
3import { ActivatedRoute, ActivatedRouteSnapshot, Event, NavigationEnd, Router, Scroll } from '@angular/router'
4import { ServerService } from '../server'
5
6export const enum RouterSetting {
7 NONE = 0,
8 REUSE_COMPONENT = 1 << 0,
9 DISABLE_SCROLL_RESTORE = 1 << 1
10}
11
12@Injectable()
13export class PeerTubeRouterService {
14 static readonly ROUTE_SETTING_NAME = 's'
15
16 constructor (
17 private route: ActivatedRoute,
18 private router: Router,
19 private server: ServerService
20 ) { }
21
22 addRouteSetting (toAdd: RouterSetting) {
23 if (this.hasRouteSetting(toAdd)) return
24
25 const current = this.getRouteSetting()
26
27 this.setRouteSetting(current | toAdd)
28 }
29
30 deleteRouteSetting (toDelete: RouterSetting) {
31 const current = this.getRouteSetting()
32
33 this.setRouteSetting(current & ~toDelete)
34 }
35
36 getRouteSetting (snapshot?: ActivatedRouteSnapshot) {
37 return (snapshot || this.route.snapshot).queryParams[PeerTubeRouterService.ROUTE_SETTING_NAME]
38 }
39
40 setRouteSetting (value: number) {
41 let path = window.location.pathname
42 if (!path || path === '/') path = this.server.getHTMLConfig().instance.defaultClientRoute
43
44 const queryParams = { [PeerTubeRouterService.ROUTE_SETTING_NAME]: value }
45
46 this.router.navigate([ path ], { queryParams, replaceUrl: true, queryParamsHandling: 'merge' })
47 }
48
49 hasRouteSetting (setting: RouterSetting, snapshot?: ActivatedRouteSnapshot) {
50 return !!(this.getRouteSetting(snapshot) & setting)
51 }
52
53 getNavigationEndEvents () {
54 return this.router.events.pipe(
55 filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd)
56 )
57 }
58
59 getScrollEvents () {
60 return this.router.events.pipe(
61 filter((e: Event): e is Scroll => e instanceof Scroll)
62 )
63 }
64
65 silentNavigate (baseRoute: string[], queryParams: { [id: string]: string }) {
66 let routeSetting = this.getRouteSetting() ?? RouterSetting.NONE
67 routeSetting |= RouterSetting.DISABLE_SCROLL_RESTORE
68
69 queryParams = {
70 ...queryParams,
71
72 [PeerTubeRouterService.ROUTE_SETTING_NAME]: routeSetting
73 }
74
75 return this.router.navigate(baseRoute, { queryParams })
76 }
77
78}
diff --git a/client/src/app/core/routing/scroll.service.ts b/client/src/app/core/routing/scroll.service.ts
new file mode 100644
index 000000000..bd5076502
--- /dev/null
+++ b/client/src/app/core/routing/scroll.service.ts
@@ -0,0 +1,91 @@
1import * as debug from 'debug'
2import { pairwise } from 'rxjs'
3import { ViewportScroller } from '@angular/common'
4import { Injectable } from '@angular/core'
5import { RouterSetting } from '../'
6import { PeerTubeRouterService } from './peertube-router.service'
7
8const logger = debug('peertube:main:ScrollService')
9
10@Injectable()
11export class ScrollService {
12
13 private resetScroll = true
14
15 constructor (
16 private viewportScroller: ViewportScroller,
17 private peertubeRouter: PeerTubeRouterService
18 ) { }
19
20 enableScrollRestoration () {
21 // We'll manage scroll restoration ourselves
22 this.viewportScroller.setHistoryScrollRestoration('manual')
23
24 this.consumeScroll()
25 this.produceScroll()
26 }
27
28 private produceScroll () {
29 // When we add the a-state parameter, we don't want to alter the scroll
30 this.peertubeRouter.getNavigationEndEvents().pipe(pairwise())
31 .subscribe(([ e1, e2 ]) => {
32 try {
33 this.resetScroll = false
34
35 const previousUrl = new URL(window.location.origin + e1.urlAfterRedirects)
36 const nextUrl = new URL(window.location.origin + e2.urlAfterRedirects)
37
38 if (previousUrl.pathname !== nextUrl.pathname) {
39 this.resetScroll = true
40 return
41 }
42
43 if (this.peertubeRouter.hasRouteSetting(RouterSetting.DISABLE_SCROLL_RESTORE)) {
44 this.resetScroll = false
45 return
46 }
47
48 // Remove route settings from the comparison
49 const nextSearchParams = nextUrl.searchParams
50 nextSearchParams.delete(PeerTubeRouterService.ROUTE_SETTING_NAME)
51
52 const previousSearchParams = previousUrl.searchParams
53
54 nextSearchParams.sort()
55 previousSearchParams.sort()
56
57 if (nextSearchParams.toString() !== previousSearchParams.toString()) {
58 this.resetScroll = true
59 }
60 } catch (e) {
61 console.error('Cannot parse URL to check next scroll.', e)
62 this.resetScroll = true
63 }
64 })
65 }
66
67 private consumeScroll () {
68 // Handle anchors/restore position
69 this.peertubeRouter.getScrollEvents().subscribe(e => {
70 logger('Will schedule scroll after router event %o.', e)
71
72 // scrollToAnchor first to preserve anchor position when using history navigation
73 if (e.anchor) {
74 setTimeout(() => this.viewportScroller.scrollToAnchor(e.anchor))
75
76 return
77 }
78
79 if (e.position) {
80 setTimeout(() => this.viewportScroller.scrollToPosition(e.position))
81
82 return
83 }
84
85 if (this.resetScroll) {
86 return this.viewportScroller.scrollToPosition([ 0, 0 ])
87 }
88 })
89 }
90
91}