diff --git a/client/src/app/+accounts/account-videos/account-videos.component.html b/client/src/app/+accounts/account-videos/account-videos.component.html index 86cfca3e1..887667ddf 100644 --- a/client/src/app/+accounts/account-videos/account-videos.component.html +++ b/client/src/app/+accounts/account-videos/account-videos.component.html @@ -1,11 +1,10 @@ +

Videos

+ Videos + { - if (url.length === 1 && [ 'recently-added', 'trending', 'local' ].includes(url[0].path)) { - return { - consumed: url, - posParams: { - page: new UrlSegment(url[0].path, {}) - } - } - } + path: 'trending', + redirectTo: () => { + const redirectService = inject(RedirectService) - return null - }, + return 'browse?scope=federated&sort=-' + redirectService.getDefaultTrendingAlgorithm() + } + }, + { + path: 'recently-added', + redirectTo: 'browse?scope=federated&sort=-publishedAt' + }, + { + path: 'local', + redirectTo: 'browse?scope=local&sort=-publishedAt' + }, - component: VideosListCommonPageComponent, + // --------------------------------------------------------------------------- + + { + path: 'browse', + + component: VideosListAllComponent, data: { reuse: { enabled: true, @@ -75,4 +86,4 @@ export default [ } ] } -] +] satisfies Routes diff --git a/client/src/app/+videos/video-list/index.ts b/client/src/app/+videos/video-list/index.ts index 3492f43f4..1ca0d61c5 100644 --- a/client/src/app/+videos/video-list/index.ts +++ b/client/src/app/+videos/video-list/index.ts @@ -1,2 +1,2 @@ export * from './overview' -export * from './videos-list-common-page.component' +export * from './videos-list-all.component' diff --git a/client/src/app/+videos/video-list/video-user-subscriptions.component.html b/client/src/app/+videos/video-list/video-user-subscriptions.component.html index 90eceaf9b..dcd3a0890 100644 --- a/client/src/app/+videos/video-list/video-user-subscriptions.component.html +++ b/client/src/app/+videos/video-list/video-user-subscriptions.component.html @@ -1,9 +1,9 @@ +

Videos from your subscriptions

+ Browse videos + { - this.update(params['page']) - }) + this.routeSub = this.route.params.subscribe(() => this.update()) } ngOnDestroy () { @@ -80,8 +67,8 @@ export class VideosListCommonPageComponent implements OnInit, OnDestroy, Disable this.videoService.getVideos.bind(this.videoService), params, 'common', - this.hookParams, - this.hookResult + 'filter:api.browse-videos.videos.list.params', + 'filter:api.browse-videos.videos.list.result' ) } @@ -96,18 +83,6 @@ export class VideosListCommonPageComponent implements OnInit, OnDestroy, Disable this.updateGroupByDate(filters.sort) } - baseRouteBuilder (filters: VideoFilters) { - const sanitizedSort = this.getSanitizedSort(filters.sort) - - let suffix: string - - if (filters.scope === 'local') suffix = 'local' - else if (sanitizedSort === 'publishedAt') suffix = 'recently-added' - else suffix = 'trending' - - return [ '/videos', suffix ] - } - disableForReuse () { this.disabled = true } @@ -116,80 +91,19 @@ export class VideosListCommonPageComponent implements OnInit, OnDestroy, Disable this.disabled = false } - update (page: string) { - const data = this.getData(page) - - this.hookParams = data.hookParams - this.hookResult = data.hookResult - - this.defaultSort = data.sort - this.defaultScope = data.scope - + update () { this.buildTitle() this.updateGroupByDate(this.defaultSort) this.meta.setTitle(this.title) } - private getData (page: string) { - if (page === 'trending') return this.generateTrendingData(this.route.snapshot) - - if (page === 'local') return this.generateLocalData() - - return this.generateRecentlyAddedData() - } - - private generateRecentlyAddedData (): VideosListCommonPageRouteData { - return { - sort: '-publishedAt', - scope: 'federated', - hookParams: 'filter:api.recently-added-videos.videos.list.params', - hookResult: 'filter:api.recently-added-videos.videos.list.result' - } - } - - private generateLocalData (): VideosListCommonPageRouteData { - return { - sort: '-publishedAt' as VideoSortField, - scope: 'local', - hookParams: 'filter:api.local-videos.videos.list.params', - hookResult: 'filter:api.local-videos.videos.list.result' - } - } - - private generateTrendingData (route: ActivatedRouteSnapshot): VideosListCommonPageRouteData { - const sort = route.queryParams['sort'] ?? this.parseTrendingAlgorithm(this.redirectService.getDefaultTrendingAlgorithm()) - - return { - sort, - scope: 'federated', - hookParams: 'filter:api.trending-videos.videos.list.params', - hookResult: 'filter:api.trending-videos.videos.list.result' - } - } - - private parseTrendingAlgorithm (algorithm: string): VideoSortField { - switch (algorithm) { - case 'most-viewed': - return '-trending' - - case 'most-liked': - return '-likes' - - // We'll automatically apply "best" sort if using "hot" sort with a logged user - case 'best': - return '-hot' - - default: - return '-' + algorithm as VideoSortField - } - } - private updateGroupByDate (sort: VideoSortField) { this.groupByDate = sort === '-publishedAt' || sort === 'publishedAt' } private buildTitle (scope: VideoFilterScope = this.defaultScope, sort: VideoSortField = this.defaultSort) { + const trendingDays = this.server.getHTMLConfig().trending.videos.intervalDays const sanitizedSort = this.getSanitizedSort(sort) if (scope === 'local') { @@ -223,12 +137,12 @@ export class VideosListCommonPageComponent implements OnInit, OnDestroy, Disable } if (sanitizedSort === 'trending') { - if (this.trendingDays === 1) { + if (trendingDays === 1) { this.titleTooltip = $localize`Videos with the most views during the last 24 hours` return } - this.titleTooltip = $localize`Videos with the most views during the last ${this.trendingDays} days` + this.titleTooltip = $localize`Videos with the most views during the last ${trendingDays} days` } return diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts index b82fac828..f04aa2ef8 100644 --- a/client/src/app/app.routes.ts +++ b/client/src/app/app.routes.ts @@ -5,6 +5,7 @@ import { EmptyComponent } from './empty.component' import { HomepageRedirectComponent } from './homepage-redirect.component' import { USER_USERNAME_REGEX_CHARACTERS } from './shared/form-validators/user-validators' import { ActorRedirectGuard } from './shared/shared-main/router/actor-redirect-guard.service' +import { VideosParentComponent } from './videos-parent.component' const routes: Routes = [ { @@ -12,11 +13,6 @@ const routes: Routes = [ loadChildren: () => import('./+admin/routes'), canActivateChild: [ MetaGuard ] }, - { - path: 'home', - loadChildren: () => import('./+home/routes'), - canActivateChild: [ MetaGuard ] - }, { path: 'my-account', loadChildren: () => import('./+my-account/routes'), @@ -128,11 +124,33 @@ const routes: Routes = [ preload: 5000 } }, + + // /home and other /videos routes { - path: 'videos', - loadChildren: () => import('./+videos/routes'), - canActivateChild: [ MetaGuard ] + matcher: (url): UrlMatchResult => { + if (url.length < 1) return null + + const matchResult = url[0].path === 'home' || url[0].path === 'videos' + if (!matchResult) return null + + // So the children can detect the appropriate route + return { consumed: [] } + }, + component: VideosParentComponent, + children: [ + { + path: 'home', + loadChildren: () => import('./+home/routes'), + canActivateChild: [ MetaGuard ] + }, + { + path: 'videos', + loadChildren: () => import('./+videos/routes'), + canActivateChild: [ MetaGuard ] + } + ] }, + { path: 'video-playlists/watch', redirectTo: 'videos/watch/playlist' diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts index a278d147f..0cb0b099c 100644 --- a/client/src/app/core/routing/redirect.service.ts +++ b/client/src/app/core/routing/redirect.service.ts @@ -1,11 +1,12 @@ -import debug from 'debug' import { Injectable } from '@angular/core' import { NavigationCancel, NavigationEnd, Router } from '@angular/router' +import { VideoSortField } from '@peertube/peertube-models' import { logger } from '@root-helpers/logger' +import { PluginsManager } from '@root-helpers/plugins-manager' +import debug from 'debug' +import { environment } from 'src/environments/environment' import { ServerService } from '../server' import { SessionStorageService } from '../wrappers/storage.service' -import { PluginsManager } from '@root-helpers/plugins-manager' -import { environment } from 'src/environments/environment' const debugLogger = debug('peertube:router:RedirectService') @@ -70,7 +71,22 @@ export class RedirectService { } getDefaultTrendingAlgorithm () { - return this.defaultTrendingAlgorithm + const algorithm = this.defaultTrendingAlgorithm + + switch (algorithm) { + case 'most-viewed': + return '-trending' + + case 'most-liked': + return '-likes' + + // We'll automatically apply "best" sort if using "hot" sort with a logged user + case 'best': + return '-hot' + + default: + return '-' + algorithm as VideoSortField + } } redirectToLatestSessionRoute () { diff --git a/client/src/app/header/header.component.html b/client/src/app/header/header.component.html index a0437fa8f..2b2329e34 100644 --- a/client/src/app/header/header.component.html +++ b/client/src/app/header/header.component.html @@ -4,11 +4,11 @@ {{ instanceName }} -
+
- @if (!loaded) { - + @if (!loggedIn) { + +
    +
  • + +
  • +
-
diff --git a/client/src/app/shared/shared-main/menu/list-overflow.component.scss b/client/src/app/shared/shared-main/menu/list-overflow.component.scss index 5ff376757..5d6054943 100644 --- a/client/src/app/shared/shared-main/menu/list-overflow.component.scss +++ b/client/src/app/shared/shared-main/menu/list-overflow.component.scss @@ -6,21 +6,20 @@ } .list-overflow-parent { - overflow: hidden; display: flex; + align-items: center; + // For the menu icon + position: relative; max-width: calc(100vw - 30px); } .list-overflow-menu { position: absolute; - right: 25px; + right: 0; } button { - width: 30px; - border: 0; - &::after { display: none; } @@ -36,13 +35,6 @@ button { } } -::ng-deep .dropdown-menu { - margin-top: 0 !important; - position: static; - right: auto; - bottom: auto; -} - .modal-body { a { color: currentColor; diff --git a/client/src/app/shared/shared-main/menu/list-overflow.component.ts b/client/src/app/shared/shared-main/menu/list-overflow.component.ts index 0d75189aa..d782cc1e2 100644 --- a/client/src/app/shared/shared-main/menu/list-overflow.component.ts +++ b/client/src/app/shared/shared-main/menu/list-overflow.component.ts @@ -1,5 +1,4 @@ -import { lowerFirst, uniqueId } from 'lodash-es' -import { take } from 'rxjs/operators' +import { NgClass, NgFor, NgIf, NgTemplateOutlet, SlicePipe } from '@angular/common' import { AfterViewInit, ChangeDetectionStrategy, @@ -13,11 +12,11 @@ import { ViewChild, ViewChildren } from '@angular/core' +import { RouterLink, RouterLinkActive } from '@angular/router' import { ScreenService } from '@app/core' -import { NgbDropdown, NgbModal, NgbDropdownAnchor, NgbDropdownMenu } from '@ng-bootstrap/ng-bootstrap' +import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle, NgbModal } from '@ng-bootstrap/ng-bootstrap' import debug from 'debug' -import { RouterLinkActive, RouterLink } from '@angular/router' -import { NgFor, NgTemplateOutlet, NgIf, NgClass, SlicePipe } from '@angular/common' +import { lowerFirst, uniqueId } from 'lodash-es' const debugLogger = debug('peertube:main:ListOverflowItem') @@ -37,7 +36,7 @@ export interface ListOverflowItem { NgTemplateOutlet, NgIf, NgbDropdown, - NgbDropdownAnchor, + NgbDropdownToggle, NgClass, NgbDropdownMenu, RouterLinkActive, @@ -54,10 +53,8 @@ export class ListOverflowComponent implements AfterV @ViewChildren('itemsRendered') itemsRendered: QueryList showItemsUntilIndexExcluded: number - active = false isInMobileView = false - - private openedOnHover = false + initialized = false constructor ( private cdr: ChangeDetectorRef, @@ -66,7 +63,10 @@ export class ListOverflowComponent implements AfterV ) {} ngAfterViewInit () { - setTimeout(() => this.onWindowResize(), 0) + setTimeout(() => { + this.onWindowResize() + this.initialized = true + }, 0) } isMenuDisplayed () { @@ -100,32 +100,6 @@ export class ListOverflowComponent implements AfterV this.cdr.markForCheck() } - openDropdownOnHover (dropdown: NgbDropdown) { - this.openedOnHover = true - dropdown.open() - - // Menu was closed - dropdown.openChange - .pipe(take(1)) - .subscribe(() => this.openedOnHover = false) - } - - dropdownAnchorClicked (dropdown: NgbDropdown) { - if (this.openedOnHover) { - this.openedOnHover = false - return - } - - return dropdown.toggle() - } - - closeDropdownIfHovered (dropdown: NgbDropdown) { - if (this.openedOnHover === false) return - - dropdown.close() - this.openedOnHover = false - } - toggleModal () { this.modalService.open(this.modal, { centered: true }) } diff --git a/client/src/app/shared/shared-main/menu/top-menu-dropdown.component.html b/client/src/app/shared/shared-main/menu/top-menu-dropdown.component.html index 02fc155bf..a3fdbc74e 100644 --- a/client/src/app/shared/shared-main/menu/top-menu-dropdown.component.html +++ b/client/src/app/shared/shared-main/menu/top-menu-dropdown.component.html @@ -1,4 +1,4 @@ -