+
+
+
-
+
You don't have any video history yet.
You don't have any video in your watch history yet.
diff --git a/client/src/app/+my-library/my-history/my-history.component.scss b/client/src/app/+my-library/my-history/my-history.component.scss
index 9eeeaf310..928a8a3da 100644
--- a/client/src/app/+my-library/my-history/my-history.component.scss
+++ b/client/src/app/+my-library/my-history/my-history.component.scss
@@ -10,17 +10,23 @@
}
.top-buttons {
- margin-bottom: 20px;
+ margin-bottom: 30px;
display: flex;
align-items: center;
flex-wrap: wrap;
+ #history-search {
+ @include peertube-input-text(250px);
+ }
+
.history-switch {
display: flex;
- flex-grow: 1;
label {
margin: 0 0 0 5px;
+ color: var(--greyForegroundColor);
+ font-size: 15px;
+ font-weight: $font-semibold;
}
}
diff --git a/client/src/app/+my-library/my-history/my-history.component.ts b/client/src/app/+my-library/my-history/my-history.component.ts
index 4ba95124d..0c8e4b83f 100644
--- a/client/src/app/+my-library/my-history/my-history.component.ts
+++ b/client/src/app/+my-library/my-history/my-history.component.ts
@@ -13,6 +13,8 @@ import {
import { immutableAssign } from '@app/helpers'
import { UserHistoryService } from '@app/shared/shared-main'
import { AbstractVideoList } from '@app/shared/shared-video-miniature'
+import { Subject } from 'rxjs'
+import { debounceTime, tap, distinctUntilChanged } from 'rxjs/operators'
@Component({
templateUrl: './my-history.component.html',
@@ -26,6 +28,9 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD
totalItems: null
}
videosHistoryEnabled: boolean
+ search: string
+
+ protected searchStream: Subject
constructor (
protected router: Router,
@@ -41,7 +46,7 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD
) {
super()
- this.titlePage = $localize`My videos history`
+ this.titlePage = $localize`My watch history`
}
ngOnInit () {
@@ -52,6 +57,28 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD
this.videosHistoryEnabled = this.authService.getUser().videosHistoryEnabled
})
+ this.searchStream = new Subject()
+
+ this.searchStream
+ .pipe(
+ debounceTime(400),
+ distinctUntilChanged()
+ )
+ .subscribe(search => {
+ this.search = search
+ this.reloadVideos()
+ })
+ }
+
+ onSearch (event: Event) {
+ const target = event.target as HTMLInputElement
+ this.searchStream.next(target.value)
+ }
+
+ resetSearch () {
+ const searchInput = document.getElementById('history-search') as HTMLInputElement
+ searchInput.value = ''
+ this.searchStream.next('')
}
ngOnDestroy () {
@@ -61,7 +88,10 @@ export class MyHistoryComponent extends AbstractVideoList implements OnInit, OnD
getVideosObservable (page: number) {
const newPagination = immutableAssign(this.pagination, { currentPage: page })
- return this.userHistoryService.getUserVideosHistory(newPagination)
+ return this.userHistoryService.getUserVideosHistory(newPagination, this.search)
+ .pipe(
+ tap(res => this.pagination.totalItems = res.total)
+ )
}
generateSyndicationList () {
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
index 6ab3826ba..510b400c0 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
+++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
@@ -15,7 +15,7 @@
You don't have any subscriptions yet.
You don't have any subscription yet.
diff --git a/client/src/app/shared/shared-main/users/user-history.service.ts b/client/src/app/shared/shared-main/users/user-history.service.ts
index 43970dc5b..bb87dcba8 100644
--- a/client/src/app/shared/shared-main/users/user-history.service.ts
+++ b/client/src/app/shared/shared-main/users/user-history.service.ts
@@ -18,11 +18,12 @@ export class UserHistoryService {
private videoService: VideoService
) {}
- getUserVideosHistory (historyPagination: ComponentPaginationLight) {
+ getUserVideosHistory (historyPagination: ComponentPaginationLight, search?: string) {
const pagination = this.restService.componentPaginationToRestPagination(historyPagination)
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination)
+ params = params.append('search', search)
return this.authHttp
.get>(UserHistoryService.BASE_USER_VIDEOS_HISTORY_URL, { params })
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts
index 80d4dc748..72c7da373 100644
--- a/server/controllers/api/users/my-history.ts
+++ b/server/controllers/api/users/my-history.ts
@@ -5,6 +5,7 @@ import {
authenticate,
paginationValidator,
setDefaultPagination,
+ userHistoryListValidator,
userHistoryRemoveValidator
} from '../../../middlewares'
import { getFormattedObjects } from '../../../helpers/utils'
@@ -18,6 +19,7 @@ myVideosHistoryRouter.get('/me/history/videos',
authenticate,
paginationValidator,
setDefaultPagination,
+ userHistoryListValidator,
asyncMiddleware(listMyVideosHistory)
)
@@ -38,7 +40,7 @@ export {
async function listMyVideosHistory (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.User
- const resultList = await UserVideoHistoryModel.listForApi(user, req.query.start, req.query.count)
+ const resultList = await UserVideoHistoryModel.listForApi(user, req.query.start, req.query.count, req.query.search)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
diff --git a/server/middlewares/validators/user-history.ts b/server/middlewares/validators/user-history.ts
index 2f1d3cc41..058bf7758 100644
--- a/server/middlewares/validators/user-history.ts
+++ b/server/middlewares/validators/user-history.ts
@@ -1,8 +1,22 @@
import * as express from 'express'
-import { body } from 'express-validator'
+import { body, query } from 'express-validator'
import { logger } from '../../helpers/logger'
import { areValidationErrors } from './utils'
-import { isDateValid } from '../../helpers/custom-validators/misc'
+import { exists, isDateValid } from '../../helpers/custom-validators/misc'
+
+const userHistoryListValidator = [
+ query('search')
+ .optional()
+ .custom(exists).withMessage('Should have a valid search'),
+
+ (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking userHistoryListValidator parameters', { parameters: req.query })
+
+ if (areValidationErrors(req, res)) return
+
+ return next()
+ }
+]
const userHistoryRemoveValidator = [
body('beforeDate')
@@ -21,5 +35,6 @@ const userHistoryRemoveValidator = [
// ---------------------------------------------------------------------------
export {
+ userHistoryListValidator,
userHistoryRemoveValidator
}
diff --git a/server/models/account/user-video-history.ts b/server/models/account/user-video-history.ts
index 45171fc60..6be1d65ea 100644
--- a/server/models/account/user-video-history.ts
+++ b/server/models/account/user-video-history.ts
@@ -55,10 +55,11 @@ export class UserVideoHistoryModel extends Model {
})
User: UserModel
- static listForApi (user: MUserAccountId, start: number, count: number) {
+ static listForApi (user: MUserAccountId, start: number, count: number, search?: string) {
return VideoModel.listForApi({
start,
count,
+ search,
sort: '-"userVideoHistory"."updatedAt"',
nsfw: null, // All
includeLocalVideos: true,
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index abf823d4b..5027e980d 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1087,6 +1087,7 @@ export class VideoModel extends Model {
user?: MUserAccountId
historyOfUser?: MUserId
countVideos?: boolean
+ search?: string
}) {
if ((options.filter === 'all-local' || options.filter === 'all') && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
throw new Error('Try to filter all-local but no user has not the see all videos right')
@@ -1123,7 +1124,8 @@ export class VideoModel extends Model {
includeLocalVideos: options.includeLocalVideos,
user: options.user,
historyOfUser: options.historyOfUser,
- trendingDays
+ trendingDays,
+ search: options.search
}
return VideoModel.getAvailableForApi(queryOptions, options.countVideos)
diff --git a/server/tests/api/videos/videos-history.ts b/server/tests/api/videos/videos-history.ts
index 661d603cb..b25cff879 100644
--- a/server/tests/api/videos/videos-history.ts
+++ b/server/tests/api/videos/videos-history.ts
@@ -152,6 +152,15 @@ describe('Test videos history', function () {
expect(res.body.data).to.have.lengthOf(0)
})
+ it('Should be able to search through videos in my history', async function () {
+ const res = await listMyVideosHistory(server.url, server.accessToken, '2')
+
+ expect(res.body.total).to.equal(1)
+
+ const videos: Video[] = res.body.data
+ expect(videos[0].name).to.equal('video 2')
+ })
+
it('Should clear my history', async function () {
await removeMyVideosHistory(server.url, server.accessToken, video3WatchedDate.toISOString())
})
diff --git a/shared/extra-utils/videos/video-history.ts b/shared/extra-utils/videos/video-history.ts
index 0dd3afb24..b989e14dc 100644
--- a/shared/extra-utils/videos/video-history.ts
+++ b/shared/extra-utils/videos/video-history.ts
@@ -14,13 +14,16 @@ function userWatchVideo (
return makePutBodyRequest({ url, path, token, fields, statusCodeExpected })
}
-function listMyVideosHistory (url: string, token: string) {
+function listMyVideosHistory (url: string, token: string, search?: string) {
const path = '/api/v1/users/me/history/videos'
return makeGetRequest({
url,
path,
token,
+ query: {
+ search
+ },
statusCodeExpected: HttpStatusCode.OK_200
})
}
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index fe4552ff7..8ad98a9a9 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -933,6 +933,7 @@ paths:
parameters:
- $ref: '#/components/parameters/start'
- $ref: '#/components/parameters/count'
+ - $ref: '#/components/parameters/search'
responses:
'200':
description: successful operation