Add ability to disable and clear history
authorChocobozzz <me@florianbigard.com>
Tue, 18 Dec 2018 10:32:37 +0000 (11:32 +0100)
committerChocobozzz <me@florianbigard.com>
Tue, 18 Dec 2018 10:35:51 +0000 (11:35 +0100)
client/src/app/+my-account/my-account-history/my-account-history.component.html
client/src/app/+my-account/my-account-history/my-account-history.component.scss
client/src/app/+my-account/my-account-history/my-account-history.component.ts
client/src/app/+my-account/my-account.module.ts
client/src/app/core/auth/auth-user.model.ts
client/src/app/core/routing/login-guard.service.ts
client/src/app/shared/users/user.model.ts
client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss
client/src/sass/primeng-custom.scss
server/models/account/user.ts
shared/models/users/user.model.ts

index 653b33f89deb2fde35a8cf962ec63bef90693755..d42af37d4a475f36dd6a05fce6f8ab93835c1d5d 100644 (file)
@@ -1,4 +1,16 @@
-<div i18n *ngIf="pagination.totalItems === 0">You don't have history yet.</div>
+<div class="top-buttons">
+  <div class="history-switch">
+    <p-inputSwitch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></p-inputSwitch>
+    <label i18n>History enabled</label>
+  </div>
+
+  <div class="delete-history">
+    <button (click)="deleteHistory()" i18n>Delete history</button>
+  </div>
+</div>
+
+
+<div class="no-history" i18n *ngIf="pagination.totalItems === 0">You don't have videos history yet.</div>
 
 <div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" class="videos" #videosElement>
   <div *ngFor="let videos of videoPages;" class="videos-page">
index 115bb0e5c2f02f31db3eafc7c0c2b47b116def5c..82150cbe3e9927a4f1c6765b0bbe2c4c8ec8ee0d 100644 (file)
@@ -1,6 +1,37 @@
 @import '_variables';
 @import '_mixins';
 
+.no-history {
+  display: flex;
+  justify-content: center;
+  margin-top: 50px;
+  font-weight: $font-semibold;
+  font-size: 16px;
+}
+
+.top-buttons {
+  margin-bottom: 20px;
+  display: flex;
+
+  .history-switch {
+    display: flex;
+    flex-grow: 1;
+
+    label {
+      margin: 0 0 0 5px;
+    }
+  }
+
+  .delete-history {
+    font-size: 15px;
+
+    button {
+      @include peertube-button;
+      @include grey-button;
+    }
+  }
+}
+
 .video {
   @include row-blocks;
 
index 5085521674000e97a1539d0cebc6de62b67718e4..6ec4fefe8fe42f720cf8804b6c5609610d4852fc 100644 (file)
@@ -11,6 +11,7 @@ import { VideoService } from '../../shared/video/video.service'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { ScreenService } from '@app/shared/misc/screen.service'
 import { UserHistoryService } from '@app/shared/users/user-history.service'
+import { UserService } from '@app/shared'
 
 @Component({
   selector: 'my-account-history',
@@ -25,6 +26,7 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
     itemsPerPage: 5,
     totalItems: null
   }
+  videosHistoryEnabled: boolean
 
   protected baseVideoWidth = -1
   protected baseVideoHeight = 155
@@ -33,6 +35,7 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
     protected router: Router,
     protected route: ActivatedRoute,
     protected authService: AuthService,
+    protected userService: UserService,
     protected notificationsService: NotificationsService,
     protected location: Location,
     protected screenService: ScreenService,
@@ -48,6 +51,8 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
 
   ngOnInit () {
     super.ngOnInit()
+
+    this.videosHistoryEnabled = this.authService.getUser().videosHistoryEnabled
   }
 
   ngOnDestroy () {
@@ -63,4 +68,40 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
   generateSyndicationList () {
     throw new Error('Method not implemented.')
   }
+
+  onVideosHistoryChange () {
+    this.userService.updateMyProfile({ videosHistoryEnabled: this.videosHistoryEnabled })
+      .subscribe(
+        () => {
+          const message = this.videosHistoryEnabled === true ?
+            this.i18n('Videos history is enabled') :
+            this.i18n('Videos history is disabled')
+
+          this.notificationsService.success(this.i18n('Success'), message)
+
+          this.authService.refreshUserInformation()
+        },
+
+        err => this.notificationsService.error(this.i18n('Error'), err.message)
+      )
+  }
+
+  async deleteHistory () {
+    const title = this.i18n('Delete videos history')
+    const message = this.i18n('Are you sure you want to delete all your videos history?')
+
+    const res = await this.confirmService.confirm(message, title)
+    if (res !== true) return
+
+    this.userHistoryService.deleteUserVideosHistory()
+        .subscribe(
+          () => {
+            this.notificationsService.success(this.i18n('Success'), this.i18n('Videos history deleted'))
+
+            this.reloadVideos()
+          },
+
+          err => this.notificationsService.error(this.i18n('Error'), err.message)
+        )
+  }
 }
index c05406438fcbf302c89554a1b687334bbbaa1857..80d9f0cf7312d82171b4a4204e45707b91170f3a 100644 (file)
@@ -1,6 +1,7 @@
 import { TableModule } from 'primeng/table'
 import { NgModule } from '@angular/core'
 import { AutoCompleteModule } from 'primeng/autocomplete'
+import { InputSwitchModule } from 'primeng/inputswitch'
 import { SharedModule } from '../shared'
 import { MyAccountRoutingModule } from './my-account-routing.module'
 import { MyAccountChangePasswordComponent } from './my-account-settings/my-account-change-password/my-account-change-password.component'
@@ -29,7 +30,8 @@ import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/m
     MyAccountRoutingModule,
     AutoCompleteModule,
     SharedModule,
-    TableModule
+    TableModule,
+    InputSwitchModule
   ],
 
   declarations: [
index acd13d9c560ab435b78bad1de1020ec5f4c46909..abb11fdc271aa1c38709f9a704544fd5abad9b44 100644 (file)
@@ -1,8 +1,9 @@
 import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
 import { UserRight } from '../../../../../shared/models/users/user-right.enum'
+import { User as ServerUserModel } from '../../../../../shared/models/users/user.model'
 // Do not use the barrel (dependency loop)
 import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role'
-import { User, UserConstructorHash } from '../../shared/users/user.model'
+import { User } from '../../shared/users/user.model'
 import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type'
 
 export type TokenOptions = {
@@ -70,6 +71,7 @@ export class AuthUser extends User {
     ID: 'id',
     ROLE: 'role',
     EMAIL: 'email',
+    VIDEOS_HISTORY_ENABLED: 'videos-history-enabled',
     USERNAME: 'username',
     NSFW_POLICY: 'nsfw_policy',
     WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
@@ -89,7 +91,8 @@ export class AuthUser extends User {
           role: parseInt(peertubeLocalStorage.getItem(this.KEYS.ROLE), 10) as UserRole,
           nsfwPolicy: peertubeLocalStorage.getItem(this.KEYS.NSFW_POLICY) as NSFWPolicyType,
           webTorrentEnabled: peertubeLocalStorage.getItem(this.KEYS.WEBTORRENT_ENABLED) === 'true',
-          autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true'
+          autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true',
+          videosHistoryEnabled: peertubeLocalStorage.getItem(this.KEYS.VIDEOS_HISTORY_ENABLED) === 'true'
         },
         Tokens.load()
       )
@@ -104,12 +107,13 @@ export class AuthUser extends User {
     peertubeLocalStorage.removeItem(this.KEYS.ROLE)
     peertubeLocalStorage.removeItem(this.KEYS.NSFW_POLICY)
     peertubeLocalStorage.removeItem(this.KEYS.WEBTORRENT_ENABLED)
+    peertubeLocalStorage.removeItem(this.KEYS.VIDEOS_HISTORY_ENABLED)
     peertubeLocalStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO)
     peertubeLocalStorage.removeItem(this.KEYS.EMAIL)
     Tokens.flush()
   }
 
-  constructor (userHash: UserConstructorHash, hashTokens: TokenOptions) {
+  constructor (userHash: Partial<ServerUserModel>, hashTokens: TokenOptions) {
     super(userHash)
     this.tokens = new Tokens(hashTokens)
   }
index 18bc41ca6301e9e8811f851dbd3c7cf256b33356..7b1c37ee8929976022d60ff1592d4ef9fcf28e58 100644 (file)
@@ -1,11 +1,5 @@
 import { Injectable } from '@angular/core'
-import {
-  ActivatedRouteSnapshot,
-  CanActivateChild,
-  RouterStateSnapshot,
-  CanActivate,
-  Router
-} from '@angular/router'
+import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router'
 
 import { AuthService } from '../auth/auth.service'
 
index 9819829fd1c6e91827726c5d087d1c123ab0c0ba..3663a7b610428c327d6864ae710ba1d75f5b8779 100644 (file)
@@ -1,33 +1,8 @@
-import {
-  Account as AccountServerModel,
-  hasUserRight,
-  User as UserServerModel,
-  UserRight,
-  UserRole,
-  VideoChannel
-} from '../../../../../shared'
+import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared'
 import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type'
 import { Account } from '@app/shared/account/account.model'
 import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
 
-export type UserConstructorHash = {
-  id: number,
-  username: string,
-  email: string,
-  role: UserRole,
-  emailVerified?: boolean,
-  videoQuota?: number,
-  videoQuotaDaily?: number,
-  nsfwPolicy?: NSFWPolicyType,
-  webTorrentEnabled?: boolean,
-  autoPlayVideo?: boolean,
-  createdAt?: Date,
-  account?: AccountServerModel,
-  videoChannels?: VideoChannel[]
-
-  blocked?: boolean
-  blockedReason?: string
-}
 export class User implements UserServerModel {
   id: number
   username: string
@@ -35,8 +10,11 @@ export class User implements UserServerModel {
   emailVerified: boolean
   role: UserRole
   nsfwPolicy: NSFWPolicyType
+
   webTorrentEnabled: boolean
   autoPlayVideo: boolean
+  videosHistoryEnabled: boolean
+
   videoQuota: number
   videoQuotaDaily: number
   account: Account
@@ -46,7 +24,7 @@ export class User implements UserServerModel {
   blocked: boolean
   blockedReason?: string
 
-  constructor (hash: UserConstructorHash) {
+  constructor (hash: Partial<UserServerModel>) {
     this.id = hash.id
     this.username = hash.username
     this.email = hash.email
@@ -57,6 +35,7 @@ export class User implements UserServerModel {
     this.videoQuotaDaily = hash.videoQuotaDaily
     this.nsfwPolicy = hash.nsfwPolicy
     this.webTorrentEnabled = hash.webTorrentEnabled
+    this.videosHistoryEnabled = hash.videosHistoryEnabled
     this.autoPlayVideo = hash.autoPlayVideo
     this.createdAt = hash.createdAt
     this.blocked = hash.blocked
index cf1725ef9ee22a37affb1311a8a8f9984bfee8b2..4b2c86ae992f423f2641a6a73e2e8f3af3b67eff 100644 (file)
@@ -54,9 +54,7 @@
 
     /deep/ .ui-progressbar {
       font-size: 15px !important;
-      color: #fff !important;
       height: 30px !important;
-      line-height: 30px !important;
       border-radius: 3px !important;
       background-color: rgba(11, 204, 41, 0.16) !important;
 
@@ -68,6 +66,8 @@
         text-align: left;
         padding-left: 18px;
         margin-top: 0 !important;
+        color: #fff !important;
+        line-height: 30px !important;
       }
     }
 
index 0568de4e2b04a6cbd68a69e0c577fc98ee867bfd..1d6eebcfa8abbf66022cad827cb7bd702daec1f6 100644 (file)
@@ -2,7 +2,7 @@
 @import '_mixins';
 
 @import '~primeng/resources/primeng.css';
-@import '~primeng/resources/themes/bootstrap/theme.css';
+@import '~primeng/resources/themes/nova-light/theme.css';
 
 @mixin glyphicon-light {
   font-family: 'Glyphicons Halflings';
 
 // data table customizations
 p-table {
-  font-size: 15px !important;
-
   .ui-table-caption {
-    border: none;
+    border: none !important;
+    background-color: #fff !important;
 
     .caption {
       height: 40px;
@@ -24,6 +23,17 @@ p-table {
     }
   }
 
+  th {
+    background-color: #fff !important;
+    outline: 0;
+  }
+
+  td, th {
+    font-family: $main-fonts;
+    font-size: 15px !important;
+    color: var(--mainForegroundColor) !important;
+  }
+
   td {
     padding-left: 15px !important;
 
@@ -35,12 +45,16 @@ p-table {
   }
 
   tr {
+    outline: 0;
     background-color: var(--mainBackgroundColor) !important;
     height: 46px;
 
     &.ui-state-highlight {
-      background-color:var(--submenuColor) !important;
-      color:var(--mainForegroundColor) !important;
+      background-color: var(--submenuColor) !important;
+
+      td, td > a {
+        color: var(--mainForegroundColor) !important;
+      }
     }
   }
 
@@ -56,6 +70,10 @@ p-table {
         }
       }
 
+      td {
+        border: none !important;
+      }
+
       &:first-child td {
         border-top: none !important;
       }
@@ -93,14 +111,14 @@ p-table {
     }
 
     &.ui-state-highlight {
-      background-color:var(--submenuColor) !important;
+      background-color: var(--submenuColor) !important;
 
       .pi {
         @extend .glyphicon;
 
-        color: #000;
-        font-size: 11px;
-        top: 0;
+        color: #000 !important;
+        font-size: 11px !important;
+        top: 0 !important;
 
         &.pi-sort-up {
           @extend .glyphicon-triangle-top;
@@ -177,11 +195,12 @@ p-table {
         a {
           color: #000 !important;
           font-weight: $font-semibold !important;
-          margin: 0 10px !important;
+          margin: 0 5px !important;
           outline: 0 !important;
           border-radius: 3px !important;
           padding: 5px 2px !important;
           height: auto !important;
+          line-height: initial !important;
 
           &.ui-state-active {
             &, &:hover, &:active, &:focus {
@@ -210,11 +229,23 @@ p-calendar .ui-datepicker {
     .ui-datepicker-next {
       @extend .glyphicon-chevron-right;
       @include glyphicon-light;
+
+      text-align: right;
+
+      .pi.pi-chevron-right {
+        display: none !important;
+      }
     }
 
     .ui-datepicker-prev {
       @extend .glyphicon-chevron-left;
       @include glyphicon-light;
+
+      text-align: left;
+
+      .pi.pi-chevron-left {
+        display: none !important;
+      }
     }
   }
 
@@ -232,6 +263,7 @@ p-calendar .ui-datepicker {
   }
 }
 
+
 .ui-chkbox-box {
   &.ui-state-active {
     border-color: var(--mainColor) !important;
@@ -240,13 +272,15 @@ p-calendar .ui-datepicker {
 
   .ui-chkbox-icon {
     position: relative;
+    overflow: visible !important;
 
     &:after {
       content: '';
       position: absolute;
-      left: 5px;
+      top: 1px;
+      left: 7px;
       width: 5px;
-      height: 12px;
+      height: 13px;
       opacity: 0;
       transform: rotate(45deg) scale(0);
       border-right: 2px solid var(--mainBackgroundColor);
@@ -258,4 +292,10 @@ p-calendar .ui-datepicker {
       transform: rotate(45deg) scale(1);
     }
   }
-}
\ No newline at end of file
+}
+
+p-inputswitch {
+  .ui-inputswitch-checked .ui-inputswitch-slider {
+    background-color: var(--mainColor) !important;
+  }
+}
index ea017c338fbdd4f6d73b0bac60677028d0f62490..180ced81072cbd354ed73547543dd3a2ad8f1b72 100644 (file)
@@ -370,6 +370,7 @@ export class UserModel extends Model<UserModel> {
       emailVerified: this.emailVerified,
       nsfwPolicy: this.nsfwPolicy,
       webTorrentEnabled: this.webTorrentEnabled,
+      videosHistoryEnabled: this.videosHistoryEnabled,
       autoPlayVideo: this.autoPlayVideo,
       role: this.role,
       roleLabel: USER_ROLE_LABELS[ this.role ],
index 82af175160a787cf28df67059924b5f3ba057c35..2aabff49488f8f057f30f7ac3874a30ebead81c2 100644 (file)
@@ -9,7 +9,11 @@ export interface User {
   email: string
   emailVerified: boolean
   nsfwPolicy: NSFWPolicyType
+
   autoPlayVideo: boolean
+  webTorrentEnabled: boolean
+  videosHistoryEnabled: boolean
+
   role: UserRole
   videoQuota: number
   videoQuotaDaily: number