diff --git a/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.scss b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.scss
index 76fb2cde0..4ecb4f408 100644
--- a/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.scss
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.scss
@@ -5,6 +5,10 @@
@include create-button;
}
+input[type=text] {
+ @include peertube-input-text(300px);
+}
+
::ng-deep .action-button {
&.action-button-edit {
margin-right: 10px;
@@ -55,11 +59,6 @@
}
}
-.video-channels-header {
- text-align: right;
- margin: 20px 0 50px;
-}
-
::ng-deep .chartjs-render-monitor {
position: relative;
top: 1px;
diff --git a/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.ts b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.ts
index 70510d7c9..da8c7298f 100644
--- a/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.ts
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.ts
@@ -1,10 +1,11 @@
import { ChartData } from 'chart.js'
import { max, maxBy, min, minBy } from 'lodash-es'
-import { flatMap } from 'rxjs/operators'
+import { flatMap, debounceTime } from 'rxjs/operators'
import { Component, OnInit } from '@angular/core'
import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core'
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { Subject } from 'rxjs'
@Component({
selector: 'my-account-video-channels',
@@ -12,11 +13,16 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
styleUrls: [ './my-account-video-channels.component.scss' ]
})
export class MyAccountVideoChannelsComponent implements OnInit {
+ totalItems: number
+
videoChannels: VideoChannel[] = []
videoChannelsChartData: ChartData[]
videoChannelsMinimumDailyViews = 0
videoChannelsMaximumDailyViews: number
+ channelsSearch: string
+ channelsSearchChanged = new Subject
()
+
private user: User
constructor (
@@ -32,6 +38,12 @@ export class MyAccountVideoChannelsComponent implements OnInit {
this.user = this.authService.getUser()
this.loadVideoChannels()
+
+ this.channelsSearchChanged
+ .pipe(debounceTime(500))
+ .subscribe(() => {
+ this.loadVideoChannels()
+ })
}
get isInSmallView () {
@@ -87,6 +99,15 @@ export class MyAccountVideoChannelsComponent implements OnInit {
}
}
+ resetSearch() {
+ this.channelsSearch = ''
+ this.onChannelsSearchChanged()
+ }
+
+ onChannelsSearchChanged () {
+ this.channelsSearchChanged.next()
+ }
+
async deleteVideoChannel (videoChannel: VideoChannel) {
const res = await this.confirmService.confirmWithInput(
this.i18n(
@@ -118,9 +139,10 @@ export class MyAccountVideoChannelsComponent implements OnInit {
private loadVideoChannels () {
this.authService.userInformationLoaded
- .pipe(flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true)))
+ .pipe(flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true, this.channelsSearch)))
.subscribe(res => {
this.videoChannels = res.data
+ this.totalItems = res.total
// chart data
this.videoChannelsChartData = this.videoChannels.map(v => ({
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
index 9b5f2dd2f..8de152b5e 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
@@ -6,10 +6,10 @@
-
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts
index 8a51319fe..0ec67d401 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.ts
@@ -1,6 +1,8 @@
import { Component, ViewChild } from '@angular/core'
import { UserNotificationsComponent } from '@app/shared/shared-main'
+type NotificationSortType = 'createdAt' | 'read'
+
@Component({
templateUrl: './my-account-notifications.component.html',
styleUrls: [ './my-account-notifications.component.scss' ]
@@ -8,7 +10,17 @@ import { UserNotificationsComponent } from '@app/shared/shared-main'
export class MyAccountNotificationsComponent {
@ViewChild('userNotification', { static: true }) userNotification: UserNotificationsComponent
- notificationSortType = 'created'
+ _notificationSortType: NotificationSortType = 'createdAt'
+
+ get notificationSortType () {
+ return !this.hasUnreadNotifications()
+ ? 'createdAt'
+ : this._notificationSortType
+ }
+
+ set notificationSortType (type: NotificationSortType) {
+ this._notificationSortType = type
+ }
markAllAsRead () {
this.userNotification.markAllAsRead()
@@ -17,4 +29,8 @@ export class MyAccountNotificationsComponent {
hasUnreadNotifications () {
return this.userNotification.notifications.filter(n => n.read === false).length !== 0
}
+
+ onChangeSortColumn () {
+ this.userNotification.changeSortColumn(this.notificationSortType)
+ }
}
diff --git a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html
index be5d41f3b..4475178c7 100644
--- a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html
+++ b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html
@@ -62,7 +62,11 @@
{{ videoChangeOwnership.createdAt | date: 'short' }} |
- {{ videoChangeOwnership.status }} |
+
+
+ {{ videoChangeOwnership.status }}
+ |
+
diff --git a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.scss b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.scss
index c04e26374..7cac9c9f3 100644
--- a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.scss
+++ b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.scss
@@ -5,6 +5,10 @@
@include chip;
}
+.badge {
+ @include table-badge;
+}
+
.video-table-video {
display: inline-flex;
diff --git a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.ts b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.ts
index 98360dfb3..7473470aa 100644
--- a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.ts
+++ b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.ts
@@ -2,7 +2,7 @@ import { SortMeta } from 'primeng/api'
import { Component, OnInit, ViewChild } from '@angular/core'
import { Notifier, RestPagination, RestTable } from '@app/core'
import { VideoOwnershipService, Actor, Video, Account } from '@app/shared/shared-main'
-import { VideoChangeOwnership } from '@shared/models'
+import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '@shared/models'
import { MyAccountAcceptOwnershipComponent } from './my-account-accept-ownership/my-account-accept-ownership.component'
import { getAbsoluteAPIUrl } from '@app/helpers'
@@ -34,6 +34,17 @@ export class MyAccountOwnershipComponent extends RestTable implements OnInit {
return 'MyAccountOwnershipComponent'
}
+ getStatusClass (status: VideoChangeOwnershipStatus) {
+ switch (status) {
+ case VideoChangeOwnershipStatus.ACCEPTED:
+ return 'badge-green'
+ case VideoChangeOwnershipStatus.REFUSED:
+ return 'badge-red'
+ default:
+ return 'badge-yellow'
+ }
+ }
+
switchToDefaultAvatar ($event: Event) {
($event.target as HTMLImageElement).src = Actor.GET_DEFAULT_AVATAR_URL()
}
diff --git a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html
index 3b4c3022e..6cec7c0d5 100644
--- a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html
+++ b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html
@@ -1,6 +1,15 @@
-
-
- My subscriptions
+
+
+
+ My subscriptions
+ {{ pagination.totalItems }}
+
+
+
You don't have any subscriptions yet.
diff --git a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.scss b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.scss
index dd990c42b..884959070 100644
--- a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.scss
+++ b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.scss
@@ -1,6 +1,10 @@
@import '_variables';
@import '_mixins';
+input[type=text] {
+ @include peertube-input-text(300px);
+}
+
.video-channel {
@include row-blocks;
diff --git a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts
index 390293a28..994fe5142 100644
--- a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts
+++ b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts
@@ -3,6 +3,7 @@ import { Component, OnInit } from '@angular/core'
import { ComponentPagination, Notifier } from '@app/core'
import { VideoChannel } from '@app/shared/shared-main'
import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
+import { debounceTime } from 'rxjs/operators'
@Component({
selector: 'my-account-subscriptions',
@@ -20,6 +21,9 @@ export class MyAccountSubscriptionsComponent implements OnInit {
onDataSubject = new Subject()
+ subscriptionsSearch: string
+ subscriptionsSearchChanged = new Subject()
+
constructor (
private userSubscriptionService: UserSubscriptionService,
private notifier: Notifier
@@ -27,20 +31,22 @@ export class MyAccountSubscriptionsComponent implements OnInit {
ngOnInit () {
this.loadSubscriptions()
+
+ this.subscriptionsSearchChanged
+ .pipe(debounceTime(500))
+ .subscribe(() => {
+ this.pagination.currentPage = 1
+ this.loadSubscriptions(false)
+ })
}
- loadSubscriptions () {
- this.userSubscriptionService.listSubscriptions(this.pagination)
- .subscribe(
- res => {
- this.videoChannels = this.videoChannels.concat(res.data)
- this.pagination.totalItems = res.total
+ resetSearch () {
+ this.subscriptionsSearch = ''
+ this.onSubscriptionsSearchChanged()
+ }
- this.onDataSubject.next(res.data)
- },
-
- error => this.notifier.error(error.message)
- )
+ onSubscriptionsSearchChanged () {
+ this.subscriptionsSearchChanged.next()
}
onNearOfBottom () {
@@ -51,4 +57,19 @@ export class MyAccountSubscriptionsComponent implements OnInit {
this.loadSubscriptions()
}
+ private loadSubscriptions (more = true) {
+ this.userSubscriptionService.listSubscriptions({ pagination: this.pagination, search: this.subscriptionsSearch })
+ .subscribe(
+ res => {
+ this.videoChannels = more
+ ? this.videoChannels.concat(res.data)
+ : res.data
+ this.pagination.totalItems = res.total
+
+ this.onDataSubject.next(res.data)
+ },
+
+ error => this.notifier.error(error.message)
+ )
+ }
}
diff --git a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html
index 98a2039cc..854126443 100644
--- a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html
+++ b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.html
@@ -45,7 +45,12 @@
|
- {{ videoImport.state.label }} |
+
+
+ {{ videoImport.state.label }}
+
+ |
+
{{ videoImport.createdAt | date: 'short' }} |
diff --git a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.scss b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.scss
index bdd2f8270..a93c28028 100644
--- a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.scss
+++ b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.scss
@@ -7,4 +7,8 @@ pre {
.video-import-error {
color: red;
-}
\ No newline at end of file
+}
+
+.badge {
+ @include table-badge;
+}
diff --git a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.ts b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.ts
index 42ddb0ee2..9dd5ef142 100644
--- a/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.ts
+++ b/client/src/app/+my-account/my-account-video-imports/my-account-video-imports.component.ts
@@ -30,6 +30,19 @@ export class MyAccountVideoImportsComponent extends RestTable implements OnInit
return 'MyAccountVideoImportsComponent'
}
+ getVideoImportStateClass (state: VideoImportState) {
+ switch (state) {
+ case VideoImportState.FAILED:
+ return 'badge-red'
+ case VideoImportState.REJECTED:
+ return 'badge-banned'
+ case VideoImportState.PENDING:
+ return 'badge-yellow'
+ default:
+ return 'badge-green'
+ }
+ }
+
isVideoImportSuccess (videoImport: VideoImport) {
return videoImport.state.id === VideoImportState.SUCCESS
}
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html
index 8d69c3a5a..d8e3fb2fa 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html
@@ -1,17 +1,20 @@
-
-
- My playlists {{ pagination.totalItems }}
-
+
+
+
+ My playlists {{ pagination.totalItems }}
+
-
-
+
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss
index 4381d74b0..ade28c70b 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss
@@ -5,6 +5,10 @@
@include create-button;
}
+input[type=text] {
+ @include peertube-input-text(300px);
+}
+
::ng-deep .action-button {
&.action-button-delete {
margin-right: 10px;
@@ -33,16 +37,6 @@
}
}
-.video-playlists-header {
- display: flex;
- justify-content: space-between;
- margin: 20px 0 50px;
-
- input[type=text] {
- @include peertube-input-text(300px);
- }
-}
-
@media screen and (max-width: $small-view) {
.video-playlists-header {
text-align: center;
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
index ea3bcde4f..668a23d8f 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
@@ -84,6 +84,11 @@ export class MyAccountVideoPlaylistsComponent implements OnInit {
this.loadVideoPlaylists()
}
+ resetSearch () {
+ this.videoPlaylistsSearch = ''
+ this.onVideoPlaylistSearchChanged()
+ }
+
onVideoPlaylistSearchChanged () {
this.videoPlaylistSearchChanged.next()
}
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
index 6d098b507..faeb3b56c 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
@@ -1,11 +1,16 @@
-
-
- My videos {{ pagination.totalItems }}
-
+
+
+
+ My videos
+ {{ pagination.totalItems }}
+
-
+
+
{
this.videosSelection.reloadVideos()
})
}
+ resetSearch () {
+ this.videosSearch = ''
+ this.onVideosSearchChanged()
+ }
+
onVideosSearchChanged () {
this.videosSearchChanged.next()
}
diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts
index 5f9300bec..c98b3844c 100644
--- a/client/src/app/core/users/user.service.ts
+++ b/client/src/app/core/users/user.service.ts
@@ -381,14 +381,14 @@ export class UserService {
const videoQuotaUsed = this.bytesPipe.transform(user.videoQuotaUsed, 0)
- let videoQuotaDaily
- let videoQuotaUsedDaily
+ let videoQuotaDaily: string
+ let videoQuotaUsedDaily: string
if (user.videoQuotaDaily === -1) {
videoQuotaDaily = '∞'
- videoQuotaUsedDaily = this.bytesPipe.transform(0, 0)
+ videoQuotaUsedDaily = this.bytesPipe.transform(0, 0) + ''
} else {
- videoQuotaDaily = this.bytesPipe.transform(user.videoQuotaDaily, 0)
- videoQuotaUsedDaily = this.bytesPipe.transform(user.videoQuotaUsedDaily || 0, 0)
+ videoQuotaDaily = this.bytesPipe.transform(user.videoQuotaDaily, 0) + ''
+ videoQuotaUsedDaily = this.bytesPipe.transform(user.videoQuotaUsedDaily || 0, 0) + ''
}
const roleLabels: { [ id in UserRole ]: string } = {
diff --git a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
index 732ed6bcb..eb1fdf91c 100644
--- a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
+++ b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts
@@ -105,13 +105,18 @@ export class UserSubscriptionService {
)
}
- listSubscriptions (componentPagination: ComponentPaginationLight): Observable> {
+ listSubscriptions (parameters: {
+ pagination: ComponentPaginationLight
+ search: string
+ }): Observable> {
+ const { pagination, search } = parameters
const url = UserSubscriptionService.BASE_USER_SUBSCRIPTIONS_URL
- const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
+ const restPagination = this.restService.componentPaginationToRestPagination(pagination)
let params = new HttpParams()
- params = this.restService.addRestGetParams(params, pagination)
+ params = this.restService.addRestGetParams(params, restPagination)
+ if (search) params = params.append('search', search)
return this.authHttp.get>(url, { params })
.pipe(
diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss
index 897182e53..8b7eab366 100644
--- a/client/src/sass/bootstrap.scss
+++ b/client/src/sass/bootstrap.scss
@@ -310,6 +310,7 @@ ngb-tooltip-window {
position: absolute;
right: .5rem;
height: 95%;
+ font-size: 14px;
&:hover {
color: rgba(0, 0, 0, 0.7);
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 75fe2ab11..0fb54f121 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -690,12 +690,11 @@
overflow: hidden;
font-size: 0.75rem;
border-radius: 0.25rem;
- isolation: isolate;
position: relative;
span {
position: absolute;
- color: rgb(92, 92, 92);
+ color: $grey-foreground-color;
top: -1px;
&:nth-of-type(1) {
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index ccdc610a2..b1c05c6c0 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -120,7 +120,8 @@ async function listAccountChannels (req: express.Request, res: express.Response)
start: req.query.start,
count: req.query.count,
sort: req.query.sort,
- withStats: req.query.withStats
+ withStats: req.query.withStats,
+ search: req.query.search
}
const resultList = await VideoChannelModel.listByAccount(options)
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts
index efe1b9bc3..d207a19ae 100644
--- a/server/controllers/api/users/my-subscriptions.ts
+++ b/server/controllers/api/users/my-subscriptions.ts
@@ -13,7 +13,7 @@ import {
userSubscriptionAddValidator,
userSubscriptionGetValidator
} from '../../../middlewares'
-import { areSubscriptionsExistValidator, userSubscriptionsSortValidator, videosSortValidator } from '../../../middlewares/validators'
+import { areSubscriptionsExistValidator, userSubscriptionsSortValidator, videosSortValidator, userSubscriptionListValidator } from '../../../middlewares/validators'
import { VideoModel } from '../../../models/video/video'
import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
@@ -45,6 +45,7 @@ mySubscriptionsRouter.get('/me/subscriptions',
userSubscriptionsSortValidator,
setDefaultSort,
setDefaultPagination,
+ userSubscriptionListValidator,
asyncMiddleware(getUserSubscriptions)
)
@@ -141,7 +142,13 @@ async function getUserSubscriptions (req: express.Request, res: express.Response
const user = res.locals.oauth.token.User
const actorId = user.Account.Actor.id
- const resultList = await ActorFollowModel.listSubscriptionsForApi(actorId, req.query.start, req.query.count, req.query.sort)
+ const resultList = await ActorFollowModel.listSubscriptionsForApi({
+ actorId,
+ start: req.query.start,
+ count: req.query.count,
+ sort: req.query.sort,
+ search: req.query.search
+ })
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index d96998209..f705034fd 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -119,8 +119,7 @@ async function listVideoChannels (req: express.Request, res: express.Response) {
actorId: serverActor.id,
start: req.query.start,
count: req.query.count,
- sort: req.query.sort,
- search: req.query.search
+ sort: req.query.sort
})
return res.json(getFormattedObjects(resultList.data, resultList.total))
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts
index 5d4cc94c5..a54ecb704 100644
--- a/server/middlewares/validators/user-subscriptions.ts
+++ b/server/middlewares/validators/user-subscriptions.ts
@@ -7,6 +7,18 @@ import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-v
import { toArray } from '../../helpers/custom-validators/misc'
import { WEBSERVER } from '../../initializers/constants'
+const userSubscriptionListValidator = [
+ query('search').optional().not().isEmpty().withMessage('Should have a valid search'),
+
+ (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking userSubscriptionListValidator parameters', { parameters: req.query })
+
+ if (areValidationErrors(req, res)) return
+
+ return next()
+ }
+]
+
const userSubscriptionAddValidator = [
body('uri').custom(isValidActorHandle).withMessage('Should have a valid URI to follow (username@domain)'),
@@ -64,6 +76,7 @@ const userSubscriptionGetValidator = [
export {
areSubscriptionsExistValidator,
+ userSubscriptionListValidator,
userSubscriptionAddValidator,
userSubscriptionGetValidator
}
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts
index 3e85cc329..529cb35cc 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/activitypub/actor-follow.ts
@@ -15,14 +15,15 @@ import {
Max,
Model,
Table,
- UpdatedAt
+ UpdatedAt,
+ Sequelize
} from 'sequelize-typescript'
import { FollowState } from '../../../shared/models/actors'
import { ActorFollow } from '../../../shared/models/actors/follow.model'
import { logger } from '../../helpers/logger'
import { ACTOR_FOLLOW_SCORE, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
import { ServerModel } from '../server/server'
-import { createSafeIn, getFollowsSort, getSort } from '../utils'
+import { createSafeIn, getFollowsSort, getSort, searchAttribute } from '../utils'
import { ActorModel, unusedActorAttributesForAPI } from './actor'
import { VideoChannelModel } from '../video/video-channel'
import { AccountModel } from '../account/account'
@@ -440,16 +441,34 @@ export class ActorFollowModel extends Model {
})
}
- static listSubscriptionsForApi (actorId: number, start: number, count: number, sort: string) {
+ static listSubscriptionsForApi (options: {
+ actorId: number
+ start: number
+ count: number
+ sort: string
+ search?: string
+ }) {
+ const { actorId, start, count, sort } = options
+ const where = {
+ actorId: actorId
+ }
+
+ if (options.search) {
+ Object.assign(where, {
+ [Op.or]: [
+ searchAttribute(options.search, '$ActorFollowing.preferredUsername$'),
+ searchAttribute(options.search, '$ActorFollowing.VideoChannel.name$')
+ ]
+ })
+ }
+
const query = {
attributes: [],
distinct: true,
offset: start,
limit: count,
order: getSort(sort),
- where: {
- actorId: actorId
- },
+ where,
include: [
{
attributes: [ 'id' ],
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index f3401fb9c..9da965bbc 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -315,9 +315,8 @@ export class VideoChannelModel extends Model {
start: number
count: number
sort: string
- search?: string
}) {
- const { actorId, search } = parameters
+ const { actorId } = parameters
const query = {
offset: parameters.start,
@@ -326,7 +325,7 @@ export class VideoChannelModel extends Model {
}
const scopes = {
- method: [ ScopeNames.FOR_API, { actorId, search } as AvailableForListOptions ]
+ method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
}
return VideoChannelModel
.scope(scopes)
@@ -405,7 +404,23 @@ export class VideoChannelModel extends Model {
count: number
sort: string
withStats?: boolean
+ search?: string
}) {
+ const escapedSearch = VideoModel.sequelize.escape(options.search)
+ const escapedLikeSearch = VideoModel.sequelize.escape('%' + options.search + '%')
+ const where = options.search
+ ? {
+ [Op.or]: [
+ Sequelize.literal(
+ 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))'
+ ),
+ Sequelize.literal(
+ 'lower(immutable_unaccent("VideoChannelModel"."name")) LIKE lower(immutable_unaccent(' + escapedLikeSearch + '))'
+ )
+ ]
+ }
+ : null
+
const query = {
offset: options.start,
limit: options.count,
@@ -418,7 +433,8 @@ export class VideoChannelModel extends Model {
},
required: true
}
- ]
+ ],
+ where
}
const scopes: string | ScopeOptions | (string | ScopeOptions)[] = [ ScopeNames.WITH_ACTOR ]
|