Merge branch 'release/5.1.0' into develop
This commit is contained in:
commit
d9dbf27a7d
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
### IMPORTANT NOTES
|
### IMPORTANT NOTES
|
||||||
|
|
||||||
|
* If your instance has signup enabled, user registration approval is automatically enabled by the default configuration of this release. You can change this setting in your `production.yaml` or in the configuration page in the web admin
|
||||||
* Update [web browsers support list](https://joinpeertube.org/faq#what-web-browsers-are-supported-by-peertube):
|
* Update [web browsers support list](https://joinpeertube.org/faq#what-web-browsers-are-supported-by-peertube):
|
||||||
* Drop support of Safari 11 on iOS
|
* Drop support of Safari 11 on iOS
|
||||||
* Drop support of Safari 11 on desktop
|
* Drop support of Safari 11 on desktop
|
||||||
|
|
|
@ -224,7 +224,7 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n>Manually set the user password</label>
|
<label i18n>Manually set the user password</label>
|
||||||
<my-user-password [userId]="user.id"></my-user-password>
|
<my-user-password [userId]="user.id" [username]="user.username"></my-user-password>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="user.twoFactorEnabled" class="form-group">
|
<div *ngIf="user.twoFactorEnabled" class="form-group">
|
||||||
|
|
|
@ -11,11 +11,11 @@ import { UserUpdate } from '@shared/models'
|
||||||
styleUrls: [ './user-password.component.scss' ]
|
styleUrls: [ './user-password.component.scss' ]
|
||||||
})
|
})
|
||||||
export class UserPasswordComponent extends FormReactive implements OnInit {
|
export class UserPasswordComponent extends FormReactive implements OnInit {
|
||||||
error: string
|
|
||||||
username: string
|
|
||||||
showPassword = false
|
|
||||||
|
|
||||||
@Input() userId: number
|
@Input() userId: number
|
||||||
|
@Input() username: string
|
||||||
|
|
||||||
|
error: string
|
||||||
|
showPassword = false
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
protected formReactiveService: FormReactiveService,
|
protected formReactiveService: FormReactiveService,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { FormReactiveService } from '@app/shared/shared-forms'
|
||||||
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
|
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
|
||||||
import { HTMLServerConfig, VideoChannelUpdate } from '@shared/models'
|
import { HTMLServerConfig, VideoChannelUpdate } from '@shared/models'
|
||||||
import { VideoChannelEdit } from './video-channel-edit'
|
import { VideoChannelEdit } from './video-channel-edit'
|
||||||
|
import { shallowCopy } from '@shared/core-utils'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-channel-update',
|
selector: 'my-video-channel-update',
|
||||||
|
@ -118,6 +119,9 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
|
||||||
this.notifier.success($localize`Avatar changed.`)
|
this.notifier.success($localize`Avatar changed.`)
|
||||||
|
|
||||||
this.videoChannel.updateAvatar(data.avatars)
|
this.videoChannel.updateAvatar(data.avatars)
|
||||||
|
|
||||||
|
// So my-actor-avatar component detects changes
|
||||||
|
this.videoChannel = shallowCopy(this.videoChannel)
|
||||||
},
|
},
|
||||||
|
|
||||||
error: (err: HttpErrorResponse) => genericUploadErrorHandler({
|
error: (err: HttpErrorResponse) => genericUploadErrorHandler({
|
||||||
|
@ -135,6 +139,9 @@ export class VideoChannelUpdateComponent extends VideoChannelEdit implements OnI
|
||||||
this.notifier.success($localize`Avatar deleted.`)
|
this.notifier.success($localize`Avatar deleted.`)
|
||||||
|
|
||||||
this.videoChannel.resetAvatar()
|
this.videoChannel.resetAvatar()
|
||||||
|
|
||||||
|
// So my-actor-avatar component detects changes
|
||||||
|
this.videoChannel = shallowCopy(this.videoChannel)
|
||||||
},
|
},
|
||||||
|
|
||||||
error: err => this.notifier.error(err.message)
|
error: err => this.notifier.error(err.message)
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http'
|
||||||
import { AfterViewChecked, Component, OnInit } from '@angular/core'
|
import { AfterViewChecked, Component, OnInit } from '@angular/core'
|
||||||
import { AuthService, Notifier, User, UserService } from '@app/core'
|
import { AuthService, Notifier, User, UserService } from '@app/core'
|
||||||
import { genericUploadErrorHandler } from '@app/helpers'
|
import { genericUploadErrorHandler } from '@app/helpers'
|
||||||
|
import { shallowCopy } from '@shared/core-utils'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-account-settings',
|
selector: 'my-account-settings',
|
||||||
|
@ -44,6 +45,9 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
|
||||||
this.notifier.success($localize`Avatar changed.`)
|
this.notifier.success($localize`Avatar changed.`)
|
||||||
|
|
||||||
this.user.updateAccountAvatar(data.avatars)
|
this.user.updateAccountAvatar(data.avatars)
|
||||||
|
|
||||||
|
// So my-actor-avatar component detects changes
|
||||||
|
this.user.account = shallowCopy(this.user.account)
|
||||||
},
|
},
|
||||||
|
|
||||||
error: (err: HttpErrorResponse) => genericUploadErrorHandler({
|
error: (err: HttpErrorResponse) => genericUploadErrorHandler({
|
||||||
|
@ -57,10 +61,13 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
|
||||||
onAvatarDelete () {
|
onAvatarDelete () {
|
||||||
this.userService.deleteAvatar()
|
this.userService.deleteAvatar()
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: data => {
|
next: () => {
|
||||||
this.notifier.success($localize`Avatar deleted.`)
|
this.notifier.success($localize`Avatar deleted.`)
|
||||||
|
|
||||||
this.user.updateAccountAvatar()
|
this.user.updateAccountAvatar()
|
||||||
|
|
||||||
|
// So my-actor-avatar component detects changes
|
||||||
|
this.user.account = shallowCopy(this.user.account)
|
||||||
},
|
},
|
||||||
|
|
||||||
error: (err: HttpErrorResponse) => this.notifier.error(err.message)
|
error: (err: HttpErrorResponse) => this.notifier.error(err.message)
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 24px;
|
@include font-size(1.5rem);
|
||||||
padding-top: 20px;
|
@include padding-top(1.25rem);
|
||||||
margin-bottom: 30px;
|
@include margin-bottom(2rem);
|
||||||
|
|
||||||
&:not(h2) {
|
&:not(h2) {
|
||||||
border-top: 1px solid $separator-border-color;
|
border-top: 1px solid $separator-border-color;
|
||||||
|
@ -38,8 +38,8 @@
|
||||||
my-actor-avatar {
|
my-actor-avatar {
|
||||||
@include margin-right(8px);
|
@include margin-right(8px);
|
||||||
|
|
||||||
position: relative;
|
display: inline-block;
|
||||||
top: -2px;
|
vertical-align: text-top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,8 +49,6 @@
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
@include margin-left(10px);
|
@include margin-left(10px);
|
||||||
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,32 @@
|
||||||
<div class="actor" *ngIf="actor">
|
<div class="actor" *ngIf="actor">
|
||||||
<div class="d-flex">
|
<div class="position-relative me-3">
|
||||||
<my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar>
|
<my-actor-avatar [actor]="actor" [actorType]="getActorType()" [previewImage]="preview" size="100"></my-actor-avatar>
|
||||||
|
|
||||||
<div class="actor-img-edit-container">
|
|
||||||
|
|
||||||
<div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body">
|
<div *ngIf="editable && !hasAvatar()" class="actor-img-edit-button" [ngbTooltip]="avatarFormat" placement="right" container="body">
|
||||||
<my-global-icon iconName="upload"></my-global-icon>
|
<my-global-icon iconName="upload"></my-global-icon>
|
||||||
<label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label>
|
<label class="visually-hidden" for="avatarfile" i18n>Upload a new avatar</label>
|
||||||
<input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
|
<input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div *ngIf="editable && hasAvatar()" ngbDropdown placement="right">
|
||||||
*ngIf="editable && hasAvatar()" class="actor-img-edit-button"
|
<div class="actor-img-edit-button" ngbDropdownToggle>
|
||||||
#avatarPopover="ngbPopover" [ngbPopover]="avatarEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right"
|
|
||||||
>
|
|
||||||
<my-global-icon iconName="edit"></my-global-icon>
|
<my-global-icon iconName="edit"></my-global-icon>
|
||||||
<label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label>
|
<label class="visually-hidden" for="avatarMenu" i18n>Change your avatar</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div ngbDropdownMenu>
|
||||||
|
<div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="avatarFormat">
|
||||||
|
<my-global-icon iconName="upload"></my-global-icon>
|
||||||
|
<span for="avatarfile" i18n>Upload a new avatar</span>
|
||||||
|
<input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
|
||||||
|
<my-global-icon iconName="delete"></my-global-icon>
|
||||||
|
<span i18n>Remove avatar</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -27,16 +36,3 @@
|
||||||
<div *ngIf="displaySubscribers" i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div>
|
<div *ngIf="displaySubscribers" i18n class="actor-info-followers">{{ actor.followersCount }} subscribers</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #avatarEditContent>
|
|
||||||
<div class="dropdown-item c-hand" [ngbTooltip]="avatarFormat" placement="right" container="body">
|
|
||||||
<my-global-icon iconName="upload"></my-global-icon>
|
|
||||||
<span for="avatarfile" i18n>Upload a new avatar</span>
|
|
||||||
<input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange(avatarfileInput)"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dropdown-item c-hand" (click)="deleteAvatar()" (key.enter)="deleteAvatar()">
|
|
||||||
<my-global-icon iconName="delete"></my-global-icon>
|
|
||||||
<span i18n>Remove avatar</span>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
|
|
|
@ -5,10 +5,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
my-actor-avatar {
|
|
||||||
@include margin-right(15px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actor-info {
|
.actor-info {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -16,12 +12,12 @@ my-actor-avatar {
|
||||||
|
|
||||||
.actor-info-display-name {
|
.actor-info-display-name {
|
||||||
@include peertube-word-wrap;
|
@include peertube-word-wrap;
|
||||||
|
@include font-size(1.25rem);
|
||||||
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: $font-bold;
|
font-weight: $font-bold;
|
||||||
|
|
||||||
@media screen and (max-width: $small-view) {
|
@media screen and (max-width: $small-view) {
|
||||||
font-size: 16px;
|
@include font-size(18px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,17 +31,18 @@ my-actor-avatar {
|
||||||
padding-bottom: .5rem;
|
padding-bottom: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-img-edit-container {
|
|
||||||
position: relative;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actor-img-edit-button {
|
.actor-img-edit-button {
|
||||||
top: 55px;
|
|
||||||
right: 45px;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
@include dropdown-with-icon-item;
|
@include dropdown-with-icon-item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
|
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
|
||||||
import { Notifier, ServerService } from '@app/core'
|
import { Notifier, ServerService } from '@app/core'
|
||||||
import { Account, VideoChannel } from '@app/shared/shared-main'
|
import { Account, VideoChannel } from '@app/shared/shared-main'
|
||||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { getBytes } from '@root-helpers/bytes'
|
import { getBytes } from '@root-helpers/bytes'
|
||||||
import { imageToDataURL } from '@root-helpers/images'
|
import { imageToDataURL } from '@root-helpers/images'
|
||||||
|
|
||||||
|
@ -15,7 +14,6 @@ import { imageToDataURL } from '@root-helpers/images'
|
||||||
})
|
})
|
||||||
export class ActorAvatarEditComponent implements OnInit {
|
export class ActorAvatarEditComponent implements OnInit {
|
||||||
@ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement>
|
@ViewChild('avatarfileInput') avatarfileInput: ElementRef<HTMLInputElement>
|
||||||
@ViewChild('avatarPopover') avatarPopover: NgbPopover
|
|
||||||
|
|
||||||
@Input() actor: VideoChannel | Account
|
@Input() actor: VideoChannel | Account
|
||||||
@Input() editable = true
|
@Input() editable = true
|
||||||
|
@ -58,7 +56,6 @@ export class ActorAvatarEditComponent implements OnInit {
|
||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('avatarfile', avatarfile)
|
formData.append('avatarfile', avatarfile)
|
||||||
this.avatarPopover?.close()
|
|
||||||
this.avatarChange.emit(formData)
|
this.avatarChange.emit(formData)
|
||||||
|
|
||||||
if (this.previewImage) {
|
if (this.previewImage) {
|
||||||
|
|
|
@ -8,18 +8,14 @@
|
||||||
<ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
|
<ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div *ngIf="hasBanner()" ngbDropdown placement="right">
|
||||||
*ngIf="hasBanner()" class="actor-img-edit-button"
|
<div class="actor-img-edit-button" ngbDropdownToggle>
|
||||||
#bannerPopover="ngbPopover" [ngbPopover]="bannerEditContent" popoverClass="popover-image-info" autoClose="outside" placement="right"
|
|
||||||
>
|
|
||||||
<my-global-icon iconName="edit"></my-global-icon>
|
<my-global-icon iconName="edit"></my-global-icon>
|
||||||
<label for="bannerMenu" i18n>Change your banner</label>
|
<label for="bannerMenu" i18n>Change your banner</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-template #bannerEditContent>
|
<div ngbDropdownMenu>
|
||||||
<div class="dropdown-item c-hand" [ngbTooltip]="bannerFormat" placement="right" container="body">
|
<div class="dropdown-item c-hand dropdown-file" [ngbTooltip]="bannerFormat">
|
||||||
<ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
|
<ng-container *ngTemplateOutlet="uploadNewBanner"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -27,7 +23,10 @@
|
||||||
<my-global-icon iconName="delete"></my-global-icon>
|
<my-global-icon iconName="delete"></my-global-icon>
|
||||||
<span i18n>Remove banner</span>
|
<span i18n>Remove banner</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-template #uploadNewBanner>
|
<ng-template #uploadNewBanner>
|
||||||
<my-global-icon iconName="upload"></my-global-icon>
|
<my-global-icon iconName="upload"></my-global-icon>
|
||||||
|
|
|
@ -16,12 +16,28 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-img-edit-button {
|
.dropdown {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
|
> .actor-img-edit-button {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-img-edit-button {
|
||||||
width: auto;
|
width: auto;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
@include dropdown-with-icon-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -1,18 +1,8 @@
|
||||||
@use '_variables' as *;
|
@use '_variables' as *;
|
||||||
@use '_mixins' as *;
|
@use '_mixins' as *;
|
||||||
|
|
||||||
.actor ::ng-deep .popover-image-info .popover-body {
|
.dropdown-file {
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
.dropdown-item {
|
|
||||||
padding: 6px 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
@include peertube-file;
|
@include peertube-file;
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.actor-img-edit-button {
|
.actor-img-edit-button {
|
||||||
|
@ -22,8 +12,6 @@
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<ng-template #img>
|
<ng-template #img>
|
||||||
<img *ngIf="displayImage()" [class]="classes" [src]="previewImage || avatarUrl || defaultAvatarUrl" [alt]="alt" />
|
<img *ngIf="displayImage()" [class]="classes" [src]="previewImage || avatarUrl || defaultAvatarUrl" alt="" />
|
||||||
|
|
||||||
<div *ngIf="displayActorInitial()" [ngClass]="classes">
|
<div *ngIf="displayActorInitial()" [ngClass]="classes">
|
||||||
<span>{{ getActorInitial() }}</span>
|
<span>{{ getActorInitial() }}</span>
|
||||||
|
|
|
@ -43,22 +43,19 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
classes: string[] = []
|
classes: string[] = []
|
||||||
alt: string
|
|
||||||
defaultAvatarUrl: string
|
defaultAvatarUrl: string
|
||||||
avatarUrl: string
|
avatarUrl: string
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.buildDefaultAvatarUrl()
|
this.buildDefaultAvatarUrl()
|
||||||
|
|
||||||
this.buildClasses()
|
|
||||||
this.buildAlt()
|
|
||||||
this.buildAvatarUrl()
|
this.buildAvatarUrl()
|
||||||
|
this.buildClasses()
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges () {
|
ngOnChanges () {
|
||||||
this.buildClasses()
|
|
||||||
this.buildAlt()
|
|
||||||
this.buildAvatarUrl()
|
this.buildAvatarUrl()
|
||||||
|
this.buildClasses()
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildClasses () {
|
private buildClasses () {
|
||||||
|
@ -81,12 +78,6 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildAlt () {
|
|
||||||
if (this.isAccount()) this.alt = $localize`Account avatar`
|
|
||||||
else if (this.isChannel()) this.alt = $localize`Channel avatar`
|
|
||||||
else this.alt = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildDefaultAvatarUrl () {
|
private buildDefaultAvatarUrl () {
|
||||||
this.defaultAvatarUrl = this.isChannel()
|
this.defaultAvatarUrl = this.isChannel()
|
||||||
? VideoChannel.GET_DEFAULT_AVATAR_URL(this.getSizeNumber())
|
? VideoChannel.GET_DEFAULT_AVATAR_URL(this.getSizeNumber())
|
||||||
|
@ -114,12 +105,13 @@ export class ActorAvatarComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
displayImage () {
|
displayImage () {
|
||||||
if (this.actorType === 'unlogged') return true
|
if (this.actorType === 'unlogged') return true
|
||||||
|
if (this.previewImage) return true
|
||||||
|
|
||||||
return !!(this.actor && this.avatarUrl)
|
return !!(this.actor && this.avatarUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
displayActorInitial () {
|
displayActorInitial () {
|
||||||
return this.actor && !this.avatarUrl
|
return !this.displayImage() && this.actor && !this.avatarUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
displayPlaceholder () {
|
displayPlaceholder () {
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
@import 'bootstrap/scss/helpers';
|
@import 'bootstrap/scss/helpers';
|
||||||
@import 'bootstrap/scss/utilities/api';
|
@import 'bootstrap/scss/utilities/api';
|
||||||
|
|
||||||
:root {
|
body {
|
||||||
--bs-border-color-translucent: #{pvar(--inputBorderColor)};
|
--bs-border-color-translucent: #{pvar(--inputBorderColor)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@
|
||||||
@mixin orange-button-inverted {
|
@mixin orange-button-inverted {
|
||||||
@include button-focus(pvar(--mainColorLightest));
|
@include button-focus(pvar(--mainColorLightest));
|
||||||
|
|
||||||
|
padding: 2px 13px;
|
||||||
border: 2px solid pvar(--mainColor);
|
border: 2px solid pvar(--mainColor);
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
|
|
||||||
|
@ -263,6 +264,7 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
font-size: $button-font-size;
|
font-size: $button-font-size;
|
||||||
|
line-height: $button-font-size + math.round(math.div($button-font-size, 2));
|
||||||
|
|
||||||
my-global-icon + * {
|
my-global-icon + * {
|
||||||
@include margin-right(4px);
|
@include margin-right(4px);
|
||||||
|
@ -312,6 +314,10 @@
|
||||||
width: $width;
|
width: $width;
|
||||||
top: $top;
|
top: $top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin peertube-file {
|
@mixin peertube-file {
|
||||||
|
|
|
@ -781,12 +781,12 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
`WHERE "account"."userId" = ${options.whereUserId} ${andWhere}`
|
`WHERE "account"."userId" = ${options.whereUserId} ${andWhere}`
|
||||||
|
|
||||||
const webtorrentFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' +
|
const webtorrentFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' +
|
||||||
'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' +
|
'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" AND "video"."isLive" IS FALSE ' +
|
||||||
videoChannelJoin
|
videoChannelJoin
|
||||||
|
|
||||||
const hlsFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' +
|
const hlsFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' +
|
||||||
'INNER JOIN "videoStreamingPlaylist" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist".id ' +
|
'INNER JOIN "videoStreamingPlaylist" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist".id ' +
|
||||||
'INNER JOIN "video" ON "videoStreamingPlaylist"."videoId" = "video"."id" ' +
|
'INNER JOIN "video" ON "videoStreamingPlaylist"."videoId" = "video"."id" AND "video"."isLive" IS FALSE ' +
|
||||||
videoChannelJoin
|
videoChannelJoin
|
||||||
|
|
||||||
return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' +
|
return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' +
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||||
|
|
||||||
|
import { join } from 'path'
|
||||||
import { areMockObjectStorageTestsDisabled } from '@shared/core-utils'
|
import { areMockObjectStorageTestsDisabled } from '@shared/core-utils'
|
||||||
import { HttpStatusCode, VideoDetails } from '@shared/models'
|
import { HttpStatusCode, VideoDetails } from '@shared/models'
|
||||||
import {
|
import {
|
||||||
|
@ -12,7 +13,7 @@ import {
|
||||||
setAccessTokensToServers,
|
setAccessTokensToServers,
|
||||||
waitJobs
|
waitJobs
|
||||||
} from '@shared/server-commands'
|
} from '@shared/server-commands'
|
||||||
import { expectStartWith } from '../shared'
|
import { checkDirectoryIsEmpty, expectStartWith } from '../shared'
|
||||||
|
|
||||||
async function checkFiles (origin: PeerTubeServer, video: VideoDetails, inObjectStorage: boolean) {
|
async function checkFiles (origin: PeerTubeServer, video: VideoDetails, inObjectStorage: boolean) {
|
||||||
for (const file of video.files) {
|
for (const file of video.files) {
|
||||||
|
@ -106,6 +107,14 @@ describe('Test create move video storage job', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should not have files on disk anymore', async function () {
|
||||||
|
await checkDirectoryIsEmpty(servers[0], 'videos', [ 'private' ])
|
||||||
|
await checkDirectoryIsEmpty(servers[0], join('videos', 'private'))
|
||||||
|
|
||||||
|
await checkDirectoryIsEmpty(servers[0], join('streaming-playlists', 'hls'), [ 'private' ])
|
||||||
|
await checkDirectoryIsEmpty(servers[0], join('streaming-playlists', 'hls', 'private'))
|
||||||
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
await cleanupTests(servers)
|
await cleanupTests(servers)
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,9 +41,14 @@ function sortObjectComparator (key: string, order: 'asc' | 'desc') {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shallowCopy <T> (o: T): T {
|
||||||
|
return Object.assign(Object.create(Object.getPrototypeOf(o)), o)
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
pick,
|
pick,
|
||||||
omit,
|
omit,
|
||||||
getKeys,
|
getKeys,
|
||||||
|
shallowCopy,
|
||||||
sortObjectComparator
|
sortObjectComparator
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,15 @@ object_storage:
|
||||||
|
|
||||||
region: "PEERTUBE_OBJECT_STORAGE_REGION"
|
region: "PEERTUBE_OBJECT_STORAGE_REGION"
|
||||||
|
|
||||||
|
upload_acl:
|
||||||
|
public: "PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC"
|
||||||
|
private: "PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PRIVATE"
|
||||||
|
|
||||||
|
proxy:
|
||||||
|
proxify_private_files:
|
||||||
|
__name: "PEERTUBE_OBJECT_STORAGE_PROXY_PROXIFY_PRIVATE_FILES"
|
||||||
|
__format: "json"
|
||||||
|
|
||||||
credentials:
|
credentials:
|
||||||
access_key_id: "PEERTUBE_OBJECT_STORAGE_CREDENTIALS_ACCESS_KEY_ID"
|
access_key_id: "PEERTUBE_OBJECT_STORAGE_CREDENTIALS_ACCESS_KEY_ID"
|
||||||
secret_access_key: 'PEERTUBE_OBJECT_STORAGE_CREDENTIALS_SECRET_ACCESS_KEY'
|
secret_access_key: 'PEERTUBE_OBJECT_STORAGE_CREDENTIALS_SECRET_ACCESS_KEY'
|
||||||
|
|
Loading…
Reference in New Issue