Simplify and improve subscribe button

This commit is contained in:
Chocobozzz 2024-10-24 10:07:02 +02:00
parent d9a1d170f1
commit 34957c5a18
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 138 additions and 109 deletions

View File

@ -9,6 +9,11 @@ input {
.btn {
@include button-with-icon(18px);
& {
line-height: 18px;
top: 0;
}
}
.copy-text {

View File

@ -1,71 +1,63 @@
<div
class="btn-group-subscribe btn-group" role="group"
[ngClass]="{'subscribe-button': !isAllChannelsSubscribed, 'unsubscribe-button': isAllChannelsSubscribed, 'big': isBigButton }"
class="btn-group" role="group"
[ngClass]="{ 'big': isBigButton }"
ngbDropdown="dropdown" autoClose="outside" placement="bottom-right bottom-left bottom auto"
role="group" aria-label="Multiple ways to subscribe to the current channel" i18n-aria-label
>
<ng-template #userLoggedOut>
<div [ngClass]="{ 'extra-text': isAtLeastOneChannelSubscribed }">
<ng-template #subscribeContent>
<div class="d-inline-block" [ngClass]="{ 'extra-text': isAtLeastOneChannelSubscribed }">
@if (isSingleSubscribe) {
<ng-container i18n>Subscribe</ng-container>
} @else {
<div i18n>Subscribe to all channels</div>
<div class="mt-1 fs-8" *ngIf="isAtLeastOneChannelSubscribed" i18n>
<div class="fs-8 fw-normal lh-1" *ngIf="isAtLeastOneChannelSubscribed" i18n>
{{ subscribeStatus(true).length }}/{{ subscribed.size }} channels subscribed
</div>
}
</div>
<span *ngIf="!isBigButton && displayFollowers && videoChannels.length > 1 && videoChannel.followersCount !== 0" class="followers-count">
{{ videoChannels[0].followersCount | myNumberFormatter }}
</span>
</ng-template>
<ng-template #userLoggedIn>
@if (isUserLoggedIn()) {
@if (isAllChannelsSubscribed) {
<button type="button" class="btn" role="button" (click)="unsubscribe()">
<button type="button" [ngClass]="buttonClasses" class="btn" (click)="unsubscribe()">
<ng-container i18n>{account + "", select, undefined {Unsubscribe} other {Unsubscribe from all channels}}</ng-container>
</button>
} @else {
<button type="button" class="btn" (click)="subscribe()">
<ng-template [ngTemplateOutlet]="userLoggedOut"></ng-template>
<button type="button" [ngClass]="buttonClasses" class="btn" (click)="subscribe()">
<ng-template [ngTemplateOutlet]="subscribeContent"></ng-template>
</button>
<button type="button" [ngClass]="buttonClasses" class="btn dropdown-toggle-split" ngbDropdownToggle aria-label="Open subscription dropdown" i18n-ariaLabel></button>
}
</ng-template>
} @else {
<button ngbDropdownToggle [ngClass]="buttonClasses" type="button" class="btn">
<ng-template [ngTemplateOutlet]="subscribeContent"></ng-template>
</button>
}
<ng-container
*ngIf="isUserLoggedIn(); then userLoggedIn">
</ng-container>
<div class="dropdown-menu" ngbDropdownMenu>
<div
class="btn-group" ngbDropdown autoClose="outside" placement="bottom-right bottom-left bottom auto"
role="group" aria-label="Multiple ways to subscribe to the current channel" i18n-aria-label
>
<button class="btn dropdown-toggle-split last-in-group" ngbDropdownToggle aria-label="Open subscription dropdown" i18n-aria-label>
<ng-container
*ngIf="!isUserLoggedIn(); then userLoggedOut">
</ng-container>
<h6 class="dropdown-header" i18n>Using an ActivityPub account</h6>
<button type="button" class="dropdown-item" (click)="subscribe()">
@if (isUserLoggedIn()) {
<span i18n>Subscribe with your local account</span>
} @else {
<span i18n>Subscribe with an account on this instance</span>
}
</button>
<div class="dropdown-menu" ngbDropdownMenu>
<h6 class="dropdown-header" i18n>Using an ActivityPub account</h6>
<button class="dropdown-item" (click)="subscribe()">
<span *ngIf="!isUserLoggedIn()" i18n>Subscribe with an account on this instance</span>
<span *ngIf="isUserLoggedIn()" i18n>Subscribe with your local account</span>
</button>
<button *ngIf="isRemoteSubscribeAvailable()" class="dropdown-item dropdown-item-neutral">
<div class="mb-1" i18n>Subscribe with a remote account:</div>
<my-remote-subscribe [showHelp]="true" [uri]="uri"></my-remote-subscribe>
</button>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header" i18n>Using a syndication feed</h6>
<a [href]="rssUri" target="_blank" class="dropdown-item" i18n>Subscribe via RSS</a>
<div type="button" *ngIf="isRemoteSubscribeAvailable()" class="dropdown-item dropdown-item-neutral">
<div class="mb-1" i18n>Subscribe with a remote account:</div>
<my-remote-subscribe [showHelp]="true" [uri]="uri"></my-remote-subscribe>
</div>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header" i18n>Using a syndication feed</h6>
<a [href]="rssUri" target="_blank" class="dropdown-item" i18n>Subscribe via RSS</a>
</div>
</div>

View File

@ -1,14 +1,26 @@
@use '_variables' as *;
@use '_mixins' as *;
.btn-group-subscribe {
.btn-group {
padding: 0;
@include peertube-button;
.peertube-button {
// Prevent weird border radius blur on chrome
z-index: unset !important;
}
button.dropdown-toggle {
font-size: $button-font-size;
line-height: 1.2;
.dropdown-toggle::after {
position: relative;
top: 1px;
}
.dropdown-toggle:not(.dropdown-toggle-split)::after {
margin-left: 0.5rem;
}
.dropdown-toggle-split {
padding-left: 8px;
padding-right: 8px;
}
&:not(.big) {
@ -20,45 +32,6 @@
min-width: 175px;
}
// Unlogged
> .dropdown > .dropdown-toggle span {
@include padding-right(5px);
}
// Logged
> .btn {
@include padding-right(4px);
+ .dropdown > button {
@include padding-left(2px);
&::after {
position: relative;
top: 1px;
}
}
}
&.subscribe-button {
.btn {
font-weight: 600;
@include orange-button;
}
span.followers-count {
@include padding-left(5px);
}
}
&.unsubscribe-button {
.btn {
font-weight: 600;
@include grey-button;
}
}
.dropdown-menu {
cursor: default;

View File

@ -1,15 +1,15 @@
import { concat, forkJoin, merge } from 'rxjs'
import { Component, Input, OnChanges, OnInit } from '@angular/core'
import { AuthService, Notifier, RedirectService } from '@app/core'
import { FeedFormat } from '@peertube/peertube-models'
import { UserSubscriptionService } from './user-subscription.service'
import { NumberFormatterPipe } from '../shared-main/common/number-formatter.pipe'
import { RemoteSubscribeComponent } from './remote-subscribe.component'
import { NgbDropdown, NgbDropdownToggle, NgbDropdownMenu } from '@ng-bootstrap/ng-bootstrap'
import { NgClass, NgIf, NgTemplateOutlet } from '@angular/common'
import { VideoChannel } from '../shared-main/channel/video-channel.model'
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 { 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'
@Component({
selector: 'my-subscribe-button',
@ -23,11 +23,12 @@ import { VideoService } from '../shared-main/video/video.service'
NgbDropdown,
NgbDropdownToggle,
NgbDropdownMenu,
NgbDropdownItem,
RemoteSubscribeComponent,
NumberFormatterPipe
]
})
export class SubscribeButtonComponent implements OnInit, OnChanges {
export class SubscribeButtonComponent implements OnChanges {
/**
* SubscribeButtonComponent can be used with a single VideoChannel passed as [VideoChannel],
* or with an account and a full list of that account's videoChannels. The latter is intended
@ -36,11 +37,14 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
*/
@Input() account: Account
@Input() videoChannels: VideoChannel[]
@Input() displayFollowers = false
@Input() size: 'small' | 'normal' = 'normal'
@ViewChild('dropdown') dropdown: NgbDropdown
subscribed = new Map<string, boolean>()
buttonClasses: Record<string, boolean> = {}
constructor (
private authService: AuthService,
private redirectService: RedirectService,
@ -97,15 +101,16 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
return !this.account
}
ngOnInit () {
ngOnChanges () {
this.loadSubscribedStatus()
this.buildClasses()
}
ngOnChanges () {
this.ngOnInit()
}
// ---------------------------------------------------------------------------
subscribe () {
if (this.dropdown) this.dropdown.close()
if (this.isUserLoggedIn()) {
return this.localSubscribe()
}
@ -113,7 +118,7 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
return this.gotoLogin()
}
localSubscribe () {
private localSubscribe () {
const subscribedStatus = this.subscribeStatus(false)
const observableBatch = this.videoChannels
@ -124,6 +129,8 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
forkJoin(observableBatch)
.subscribe({
next: () => {
this.buildClasses()
this.notifier.success(
this.account
? $localize`Subscribed to all current channels of ${this.account.displayName}. You will be notified of all their new videos.`
@ -137,13 +144,17 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
})
}
// ---------------------------------------------------------------------------
unsubscribe () {
if (this.dropdown) this.dropdown.close()
if (this.isUserLoggedIn()) {
this.localUnsubscribe()
}
}
localUnsubscribe () {
private localUnsubscribe () {
const subscribeStatus = this.subscribeStatus(true)
const observableBatch = this.videoChannels
@ -154,6 +165,8 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
concat(...observableBatch)
.subscribe({
complete: () => {
this.buildClasses()
this.notifier.success(
this.account
? $localize`Unsubscribed from all channels of ${this.account.nameWithHost}`
@ -167,6 +180,8 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
})
}
// ---------------------------------------------------------------------------
isUserLoggedIn () {
return this.authService.isLoggedIn()
}
@ -207,10 +222,22 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
this.userSubscriptionService.listenToSubscriptionCacheChange(handle),
this.userSubscriptionService.doesSubscriptionExist(handle)
).subscribe({
next: res => this.subscribed.set(handle, res),
next: res => {
this.subscribed.set(handle, res)
this.buildClasses()
},
error: err => this.notifier.error(err.message)
})
}
}
private buildClasses () {
this.buttonClasses = {
'peertube-button': true,
'orange-button': !this.isAllChannelsSubscribed,
'grey-button': this.isAllChannelsSubscribed
}
}
}

View File

@ -56,6 +56,12 @@ body {
--bs-btn-color: #{pvar(--greyForegroundColor)};
}
.btn {
--bs-btn-active-color: inherit;
--bs-btn-active-bg: inherit;
--bs-btn-active-border-color: inherit;
}
.flex-auto {
flex: auto;
}
@ -291,7 +297,6 @@ body {
.btn:not(.btn-sm) {
font-size: $button-font-size;
line-height: 1.2;
}
.btn-outline-secondary {

View File

@ -127,6 +127,16 @@
&:focus {
color: #fff;
background-color: pvar(--mainColor);
border-color: inherit;
}
// Override bootstrap
&.btn:active,
&.btn:focus-visible,
&.btn.show {
color: #fff !important;
background-color: pvar(--mainColor) !important;
border-color: inherit !important;
}
&:hover {
@ -159,6 +169,15 @@
background-color: pvar(--mainBackgroundColor);
}
// Override bootstrap
&.btn:active,
&.btn:focus-visible,
&.btn.show {
color: pvar(--mainColor);
background-color: pvar(--mainBackgroundColor);
border-color: inherit !important;
}
&:hover {
color: pvar(--mainColor);
background-color: pvar(--mainColorLightest);
@ -204,6 +223,15 @@
background-color: pvar(--greySecondaryBackgroundColor);
}
// Override bootstrap
&.btn:active,
&.btn:focus-visible,
&.btn.show {
color: pvar(--greyForegroundColor);
background-color: pvar(--greySecondaryBackgroundColor);
border-color: inherit !important;
}
&[disabled] {
cursor: default;
}
@ -241,8 +269,7 @@
border: 0;
font-weight: $font-semibold;
// Because of primeng that redefines border-radius of all input[type="..."]
border-radius: 3px !important;
border-radius: 3px;
text-align: center;
cursor: pointer;