Add ability to unfollow a server
This commit is contained in:
parent
892211e849
commit
7e9334c34d
|
@ -7,9 +7,9 @@
|
||||||
sortField="createdAt" (onLazyLoad)="loadLazy($event)"
|
sortField="createdAt" (onLazyLoad)="loadLazy($event)"
|
||||||
>
|
>
|
||||||
<p-column field="id" header="ID"></p-column>
|
<p-column field="id" header="ID"></p-column>
|
||||||
<p-column field="host" header="Host"></p-column>
|
<p-column field="follower.host" header="Host"></p-column>
|
||||||
<p-column field="email" header="Email"></p-column>
|
<p-column field="email" header="Email"></p-column>
|
||||||
<p-column field="score" header="Score"></p-column>
|
<p-column field="follower.score" header="Score"></p-column>
|
||||||
<p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
|
<p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
|
||||||
</p-dataTable>
|
</p-dataTable>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,10 +7,15 @@
|
||||||
sortField="createdAt" (onLazyLoad)="loadLazy($event)"
|
sortField="createdAt" (onLazyLoad)="loadLazy($event)"
|
||||||
>
|
>
|
||||||
<p-column field="id" header="ID"></p-column>
|
<p-column field="id" header="ID"></p-column>
|
||||||
<p-column field="host" header="Host"></p-column>
|
<p-column field="following.host" header="Host"></p-column>
|
||||||
<p-column field="email" header="Email"></p-column>
|
<p-column field="email" header="Email"></p-column>
|
||||||
<p-column field="score" header="Score"></p-column>
|
<p-column field="following.score" header="Score"></p-column>
|
||||||
<p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
|
<p-column field="createdAt" header="Created date" [sortable]="true"></p-column>
|
||||||
|
<p-column header="Unfollow" styleClass="action-cell">
|
||||||
|
<ng-template pTemplate="body" let-following="rowData">
|
||||||
|
<span (click)="removeFollowing(following)" class="glyphicon glyphicon-remove glyphicon-black" title="Unfollow"></span>
|
||||||
|
</ng-template>
|
||||||
|
</p-column>
|
||||||
</p-dataTable>
|
</p-dataTable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from '@angular/core'
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
import { SortMeta } from 'primeng/primeng'
|
import { SortMeta } from 'primeng/primeng'
|
||||||
import { AccountFollow } from '../../../../../../shared/models/accounts/follow.model'
|
import { AccountFollow } from '../../../../../../shared/models/accounts/follow.model'
|
||||||
|
import { ConfirmService } from '../../../core/confirm/confirm.service'
|
||||||
import { RestPagination, RestTable } from '../../../shared'
|
import { RestPagination, RestTable } from '../../../shared'
|
||||||
import { FollowService } from '../shared'
|
import { FollowService } from '../shared'
|
||||||
|
|
||||||
|
@ -18,11 +19,29 @@ export class FollowingListComponent extends RestTable {
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
|
private confirmService: ConfirmService,
|
||||||
private followService: FollowService
|
private followService: FollowService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeFollowing (follow: AccountFollow) {
|
||||||
|
this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow').subscribe(
|
||||||
|
res => {
|
||||||
|
if (res === false) return
|
||||||
|
|
||||||
|
this.followService.unfollow(follow).subscribe(
|
||||||
|
() => {
|
||||||
|
this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`)
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notificationsService.error('Error', err.message)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
protected loadData () {
|
protected loadData () {
|
||||||
this.followService.getFollowing(this.pagination, this.sort)
|
this.followService.getFollowing(this.pagination, this.sort)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class FollowService {
|
||||||
.catch(res => this.restExtractor.handleError(res))
|
.catch(res => this.restExtractor.handleError(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
follow (notEmptyHosts: String[]) {
|
follow (notEmptyHosts: string[]) {
|
||||||
const body = {
|
const body = {
|
||||||
hosts: notEmptyHosts
|
hosts: notEmptyHosts
|
||||||
}
|
}
|
||||||
|
@ -46,4 +46,10 @@ export class FollowService {
|
||||||
.map(this.restExtractor.extractDataBool)
|
.map(this.restExtractor.extractDataBool)
|
||||||
.catch(res => this.restExtractor.handleError(res))
|
.catch(res => this.restExtractor.handleError(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unfollow (follow: AccountFollow) {
|
||||||
|
return this.authHttp.delete(FollowService.BASE_APPLICATION_URL + '/following/' + follow.following.id)
|
||||||
|
.map(this.restExtractor.extractDataBool)
|
||||||
|
.catch(res => this.restExtractor.handleError(res))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||||
|
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
|
@ -12,7 +12,7 @@ import { UserService } from '../shared'
|
||||||
templateUrl: './user-list.component.html',
|
templateUrl: './user-list.component.html',
|
||||||
styleUrls: [ './user-list.component.scss' ]
|
styleUrls: [ './user-list.component.scss' ]
|
||||||
})
|
})
|
||||||
export class UserListComponent extends RestTable implements OnInit {
|
export class UserListComponent extends RestTable {
|
||||||
users: User[] = []
|
users: User[] = []
|
||||||
totalRecords = 0
|
totalRecords = 0
|
||||||
rowsPerPage = 10
|
rowsPerPage = 10
|
||||||
|
@ -27,10 +27,6 @@ export class UserListComponent extends RestTable implements OnInit {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
|
||||||
this.loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
removeUser (user: User) {
|
removeUser (user: User) {
|
||||||
if (user.username === 'root') {
|
if (user.username === 'root') {
|
||||||
this.notificationsService.error('Error', 'You cannot delete root.')
|
this.notificationsService.error('Error', 'You cannot delete root.')
|
||||||
|
|
|
@ -134,7 +134,7 @@ const CONSTRAINTS_FIELDS = {
|
||||||
VIEWS: { min: 0 },
|
VIEWS: { min: 0 },
|
||||||
LIKES: { min: 0 },
|
LIKES: { min: 0 },
|
||||||
DISLIKES: { min: 0 },
|
DISLIKES: { min: 0 },
|
||||||
FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 3 /* 3Go */ },
|
FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 10 /* 10Go */ },
|
||||||
URL: { min: 3, max: 2000 } // Length
|
URL: { min: 3, max: 2000 } // Length
|
||||||
},
|
},
|
||||||
ACCOUNTS: {
|
ACCOUNTS: {
|
||||||
|
|
|
@ -10,7 +10,7 @@ async function processUndoActivity (activity: ActivityUndo) {
|
||||||
const following = await db.Account.loadByUrl(activityToUndo.object)
|
const following = await db.Account.loadByUrl(activityToUndo.object)
|
||||||
const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id)
|
const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id)
|
||||||
|
|
||||||
if (!accountFollow) throw new Error(`'Unknown account follow (${follower.id} -> ${following.id}.`)
|
if (!accountFollow) throw new Error(`'Unknown account follow ${follower.id} -> ${following.id}.`)
|
||||||
|
|
||||||
await accountFollow.destroy()
|
await accountFollow.destroy()
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { body } from 'express-validator/check'
|
import { body, param } from 'express-validator/check'
|
||||||
import { isTestInstance } from '../../helpers/core-utils'
|
import { isTestInstance } from '../../helpers/core-utils'
|
||||||
import { isAccountIdValid } from '../../helpers/custom-validators/activitypub/account'
|
|
||||||
import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
|
import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { CONFIG, database as db } from '../../initializers'
|
import { CONFIG, database as db } from '../../initializers'
|
||||||
import { checkErrors } from './utils'
|
import { checkErrors } from './utils'
|
||||||
import { getServerAccount } from '../../helpers/utils'
|
import { getServerAccount } from '../../helpers/utils'
|
||||||
|
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
|
||||||
|
|
||||||
const followValidator = [
|
const followValidator = [
|
||||||
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
|
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
|
||||||
|
@ -28,7 +28,7 @@ const followValidator = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const removeFollowingValidator = [
|
const removeFollowingValidator = [
|
||||||
body('accountId').custom(isAccountIdValid).withMessage('Should have a valid account id'),
|
param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
|
||||||
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking follow parameters', { parameters: req.body })
|
logger.debug('Checking follow parameters', { parameters: req.body })
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
import * as Sequelize from 'sequelize'
|
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { FollowState } from '../../../shared/models/accounts/follow.model'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import { AccountFollow, FollowState } from '../../../shared/models/accounts/follow.model'
|
||||||
import { ResultList } from '../../../shared/models/result-list.model'
|
import { ResultList } from '../../../shared/models/result-list.model'
|
||||||
import { AccountInstance } from './account-interface'
|
import { AccountInstance } from './account-interface'
|
||||||
|
|
||||||
export namespace AccountFollowMethods {
|
export namespace AccountFollowMethods {
|
||||||
export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird<AccountFollowInstance>
|
export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird<AccountFollowInstance>
|
||||||
|
|
||||||
export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
|
export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
|
||||||
export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> >
|
export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
|
||||||
|
|
||||||
export type ListAcceptedFollowerUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
|
export type ListAcceptedFollowerUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
|
||||||
export type ListAcceptedFollowingUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
|
export type ListAcceptedFollowingUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> >
|
||||||
export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[]) => Promise< ResultList<string> >
|
export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[]) => Promise< ResultList<string> >
|
||||||
|
export type ToFormattedJSON = (this: AccountFollowInstance) => AccountFollow
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountFollowClass {
|
export interface AccountFollowClass {
|
||||||
|
@ -38,6 +39,8 @@ export interface AccountFollowInstance extends AccountFollowClass, AccountFollow
|
||||||
|
|
||||||
AccountFollower?: AccountInstance
|
AccountFollower?: AccountInstance
|
||||||
AccountFollowing?: AccountInstance
|
AccountFollowing?: AccountInstance
|
||||||
|
|
||||||
|
toFormattedJSON: AccountFollowMethods.ToFormattedJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {}
|
export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {}
|
||||||
|
|
|
@ -12,6 +12,7 @@ let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
|
||||||
let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
|
let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
|
||||||
let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
|
let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
|
||||||
let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
|
let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
|
||||||
|
let toFormattedJSON: AccountFollowMethods.ToFormattedJSON
|
||||||
|
|
||||||
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
|
||||||
AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
|
AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
|
||||||
|
@ -46,7 +47,10 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
||||||
listAcceptedFollowingUrlsForApi,
|
listAcceptedFollowingUrlsForApi,
|
||||||
listAcceptedFollowerSharedInboxUrls
|
listAcceptedFollowerSharedInboxUrls
|
||||||
]
|
]
|
||||||
addMethodsToModel(AccountFollow, classMethods)
|
const instanceMethods = [
|
||||||
|
toFormattedJSON
|
||||||
|
]
|
||||||
|
addMethodsToModel(AccountFollow, classMethods, instanceMethods)
|
||||||
|
|
||||||
return AccountFollow
|
return AccountFollow
|
||||||
}
|
}
|
||||||
|
@ -73,6 +77,22 @@ function associate (models) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toFormattedJSON = function (this: AccountFollowInstance) {
|
||||||
|
const follower = this.AccountFollower.toFormattedJSON()
|
||||||
|
const following = this.AccountFollowing.toFormattedJSON()
|
||||||
|
|
||||||
|
const json = {
|
||||||
|
id: this.id,
|
||||||
|
follower,
|
||||||
|
following,
|
||||||
|
state: this.state,
|
||||||
|
createdAt: this.createdAt,
|
||||||
|
updatedAt: this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
loadByAccountAndTarget = function (accountId: number, targetAccountId: number) {
|
loadByAccountAndTarget = function (accountId: number, targetAccountId: number) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
|
@ -122,7 +142,7 @@ listFollowingForApi = function (id: number, start: number, count: number, sort:
|
||||||
|
|
||||||
return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
|
return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
return {
|
return {
|
||||||
data: rows.map(r => r.AccountFollowing),
|
data: rows,
|
||||||
total: count
|
total: count
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -154,7 +174,7 @@ listFollowersForApi = function (id: number, start: number, count: number, sort:
|
||||||
|
|
||||||
return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
|
return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
return {
|
return {
|
||||||
data: rows.map(r => r.AccountFollower),
|
data: rows,
|
||||||
total: count
|
total: count
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
import { Account } from './account.model'
|
||||||
|
|
||||||
export type FollowState = 'pending' | 'accepted'
|
export type FollowState = 'pending' | 'accepted'
|
||||||
|
|
||||||
export interface AccountFollow {
|
export interface AccountFollow {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
follower: Account
|
||||||
score?: number // Used for followers
|
following: Account
|
||||||
host: string
|
state: FollowState
|
||||||
|
createdAt: Date
|
||||||
|
updatedAt: Date
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ server {
|
||||||
|
|
||||||
# For the video upload
|
# For the video upload
|
||||||
client_max_body_size 2G;
|
client_max_body_size 2G;
|
||||||
|
proxy_connect_timeout 600;
|
||||||
|
proxy_send_timeout 600;
|
||||||
|
proxy_read_timeout 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Bypass PeerTube webseed route for better performances
|
# Bypass PeerTube webseed route for better performances
|
||||||
|
|
|
@ -23,6 +23,9 @@ server {
|
||||||
|
|
||||||
# For the video upload
|
# For the video upload
|
||||||
client_max_body_size 2G;
|
client_max_body_size 2G;
|
||||||
|
proxy_connect_timeout 600;
|
||||||
|
proxy_send_timeout 600;
|
||||||
|
proxy_read_timeout 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Bypass PeerTube webseed route for better performances
|
# Bypass PeerTube webseed route for better performances
|
||||||
|
|
Loading…
Reference in New Issue