mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-12-27 15:53:08 +02:00
Resolve #5939
This commit is contained in:
parent
84958af4ce
commit
65c0b6c7da
10 changed files with 123 additions and 9 deletions
|
@ -4,6 +4,7 @@ ChangeLog
|
|||
unreleased
|
||||
-------------------
|
||||
### ✨Improvements
|
||||
* アンテナの受信ソースにグループを指定できるように
|
||||
* 時計ウィジェットを追加
|
||||
|
||||
### 🐛Fixes
|
||||
|
|
|
@ -487,6 +487,7 @@ _antennaSources:
|
|||
homeTimeline: "フォローしているユーザーのノート"
|
||||
users: "指定した一人または複数のユーザーのノート"
|
||||
userList: "指定したリストのユーザーのノート"
|
||||
userGroup: "指定したグループのユーザーのノート"
|
||||
|
||||
_weekday:
|
||||
sunday: "日曜日"
|
||||
|
|
28
migration/1581695816408-user-group-antenna.ts
Normal file
28
migration/1581695816408-user-group-antenna.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class userGroupAntenna1581695816408 implements MigrationInterface {
|
||||
name = 'userGroupAntenna1581695816408'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`, undefined);
|
||||
await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`, undefined);
|
||||
await queryRunner.query(`CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'users', 'list', 'group')`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum" USING "src"::"text"::"antenna_src_enum"`, undefined);
|
||||
await queryRunner.query(`DROP TYPE "antenna_src_enum_old"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[]`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying array NOT NULL DEFAULT '{}'`, undefined);
|
||||
await queryRunner.query(`CREATE TYPE "antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list')`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum_old" USING "src"::"text"::"antenna_src_enum_old"`, undefined);
|
||||
await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined);
|
||||
await queryRunner.query(`ALTER TYPE "antenna_src_enum_old" RENAME TO "antenna_src_enum"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`, undefined);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,12 +11,17 @@
|
|||
<option value="home">{{ $t('_antennaSources.homeTimeline') }}</option>
|
||||
<option value="users">{{ $t('_antennaSources.users') }}</option>
|
||||
<option value="list">{{ $t('_antennaSources.userList') }}</option>
|
||||
<option value="group">{{ $t('_antennaSources.userGroup') }}</option>
|
||||
</mk-select>
|
||||
<mk-select v-model="userListId" v-if="src === 'list'">
|
||||
<template #label>{{ $t('userList') }}</template>
|
||||
<option v-for="list in userLists" :value="list.id" :key="list.id">{{ list.name }}</option>
|
||||
</mk-select>
|
||||
<mk-textarea v-model="users" v-if="src === 'users'">
|
||||
<mk-select v-model="userGroupId" v-else-if="src === 'group'">
|
||||
<template #label>{{ $t('userGroup') }}</template>
|
||||
<option v-for="group in userGroups" :value="group.id" :key="group.id">{{ group.name }}</option>
|
||||
</mk-select>
|
||||
<mk-textarea v-model="users" v-else-if="src === 'users'">
|
||||
<span>{{ $t('users') }}</span>
|
||||
<template #desc>{{ $t('antennaUsersDescription') }} <button class="_textButton" @click="addUser">{{ $t('addUser') }}</button></template>
|
||||
</mk-textarea>
|
||||
|
@ -67,6 +72,7 @@ export default Vue.extend({
|
|||
name: '',
|
||||
src: '',
|
||||
userListId: null,
|
||||
userGroupId: null,
|
||||
users: '',
|
||||
keywords: '',
|
||||
caseSensitive: false,
|
||||
|
@ -74,6 +80,7 @@ export default Vue.extend({
|
|||
withFile: false,
|
||||
notify: false,
|
||||
userLists: null,
|
||||
userGroups: null,
|
||||
faSave, faTrash
|
||||
};
|
||||
},
|
||||
|
@ -83,6 +90,13 @@ export default Vue.extend({
|
|||
if (this.src === 'list' && this.userLists === null) {
|
||||
this.userLists = await this.$root.api('users/lists/list');
|
||||
}
|
||||
|
||||
if (this.src === 'group' && this.userGroups === null) {
|
||||
const groups1 = await this.$root.api('users/groups/owned');
|
||||
const groups2 = await this.$root.api('users/groups/joined');
|
||||
|
||||
this.userGroups = [...groups1, ...groups2];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -90,6 +104,7 @@ export default Vue.extend({
|
|||
this.name = this.antenna.name;
|
||||
this.src = this.antenna.src;
|
||||
this.userListId = this.antenna.userListId;
|
||||
this.userGroupId = this.antenna.userGroupId;
|
||||
this.users = this.antenna.users.join('\n');
|
||||
this.keywords = this.antenna.keywords.map(x => x.join(' ')).join('\n');
|
||||
this.caseSensitive = this.antenna.caseSensitive;
|
||||
|
@ -105,6 +120,7 @@ export default Vue.extend({
|
|||
name: this.name,
|
||||
src: this.src,
|
||||
userListId: this.userListId,
|
||||
userGroupId: this.userGroupId,
|
||||
withReplies: this.withReplies,
|
||||
withFile: this.withFile,
|
||||
notify: this.notify,
|
||||
|
@ -119,6 +135,7 @@ export default Vue.extend({
|
|||
name: this.name,
|
||||
src: this.src,
|
||||
userListId: this.userListId,
|
||||
userGroupId: this.userGroupId,
|
||||
withReplies: this.withReplies,
|
||||
withFile: this.withFile,
|
||||
notify: this.notify,
|
||||
|
|
|
@ -50,6 +50,7 @@ export default Vue.extend({
|
|||
name: '',
|
||||
src: 'all',
|
||||
userListId: null,
|
||||
userGroupId: null,
|
||||
users: [],
|
||||
keywords: [],
|
||||
withReplies: false,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Antenna } from '../models/entities/antenna';
|
||||
import { Note } from '../models/entities/note';
|
||||
import { User } from '../models/entities/user';
|
||||
import { UserListJoinings } from '../models';
|
||||
import { UserListJoinings, UserGroupJoinings } from '../models';
|
||||
import parseAcct from './acct/parse';
|
||||
import { getFullApAccount } from './convert-host';
|
||||
import { ensure } from '../prelude/ensure';
|
||||
|
||||
export async function checkHitAntenna(antenna: Antenna, note: Note, noteUser: User, followers: User['id'][]): Promise<boolean> {
|
||||
if (note.visibility === 'specified') return false;
|
||||
|
@ -22,6 +23,14 @@ export async function checkHitAntenna(antenna: Antenna, note: Note, noteUser: Us
|
|||
})).map(x => x.userId);
|
||||
|
||||
if (!listUsers.includes(note.userId)) return false;
|
||||
} else if (antenna.src === 'group') {
|
||||
const joining = await UserGroupJoinings.findOne(antenna.userGroupJoiningId!).then(ensure);
|
||||
|
||||
const groupUsers = (await UserGroupJoinings.find({
|
||||
userGroupId: joining.userGroupId
|
||||
})).map(x => x.userId);
|
||||
|
||||
if (!groupUsers.includes(note.userId)) return false;
|
||||
} else if (antenna.src === 'users') {
|
||||
const accts = antenna.users.map(x => {
|
||||
const { username, host } = parseAcct(x);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ
|
|||
import { User } from './user';
|
||||
import { id } from '../id';
|
||||
import { UserList } from './user-list';
|
||||
import { UserGroupJoining } from './user-group-joining';
|
||||
|
||||
@Entity()
|
||||
export class Antenna {
|
||||
|
@ -32,8 +33,8 @@ export class Antenna {
|
|||
})
|
||||
public name: string;
|
||||
|
||||
@Column('enum', { enum: ['home', 'all', 'users', 'list'] })
|
||||
public src: 'home' | 'all' | 'users' | 'list';
|
||||
@Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] })
|
||||
public src: 'home' | 'all' | 'users' | 'list' | 'group';
|
||||
|
||||
@Column({
|
||||
...id(),
|
||||
|
@ -47,6 +48,18 @@ export class Antenna {
|
|||
@JoinColumn()
|
||||
public userList: UserList | null;
|
||||
|
||||
@Column({
|
||||
...id(),
|
||||
nullable: true
|
||||
})
|
||||
public userGroupJoiningId: UserGroupJoining['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroupJoining, {
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
@JoinColumn()
|
||||
public userGroupJoining: UserGroupJoining | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 1024, array: true,
|
||||
default: '{}'
|
||||
|
|
|
@ -2,7 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
|
|||
import { Antenna } from '../entities/antenna';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
import { SchemaType } from '../../misc/schema';
|
||||
import { AntennaNotes } from '..';
|
||||
import { AntennaNotes, UserGroupJoinings } from '..';
|
||||
|
||||
export type PackedAntenna = SchemaType<typeof packedAntennaSchema>;
|
||||
|
||||
|
@ -14,6 +14,7 @@ export class AntennaRepository extends Repository<Antenna> {
|
|||
const antenna = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
|
||||
|
||||
const hasUnreadNote = (await AntennaNotes.findOne({ antennaId: antenna.id, read: false })) != null;
|
||||
const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOne(antenna.userGroupJoiningId) : null;
|
||||
|
||||
return {
|
||||
id: antenna.id,
|
||||
|
@ -22,6 +23,7 @@ export class AntennaRepository extends Repository<Antenna> {
|
|||
keywords: antenna.keywords,
|
||||
src: antenna.src,
|
||||
userListId: antenna.userListId,
|
||||
userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null,
|
||||
users: antenna.users,
|
||||
caseSensitive: antenna.caseSensitive,
|
||||
notify: antenna.notify,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import { genId } from '../../../../misc/gen-id';
|
||||
import { Antennas, UserLists } from '../../../../models';
|
||||
import { Antennas, UserLists, UserGroupJoinings } from '../../../../models';
|
||||
import { ID } from '../../../../misc/cafy-id';
|
||||
import { ApiError } from '../../error';
|
||||
|
||||
|
@ -18,13 +18,17 @@ export const meta = {
|
|||
},
|
||||
|
||||
src: {
|
||||
validator: $.str.or(['home', 'all', 'users', 'list'])
|
||||
validator: $.str.or(['home', 'all', 'users', 'list', 'group'])
|
||||
},
|
||||
|
||||
userListId: {
|
||||
validator: $.nullable.optional.type(ID),
|
||||
},
|
||||
|
||||
userGroupId: {
|
||||
validator: $.nullable.optional.type(ID),
|
||||
},
|
||||
|
||||
keywords: {
|
||||
validator: $.arr($.arr($.str))
|
||||
},
|
||||
|
@ -55,12 +59,19 @@ export const meta = {
|
|||
message: 'No such user list.',
|
||||
code: 'NO_SUCH_USER_LIST',
|
||||
id: '95063e93-a283-4b8b-9aa5-bcdb8df69a7f'
|
||||
},
|
||||
|
||||
noSuchUserGroup: {
|
||||
message: 'No such user group.',
|
||||
code: 'NO_SUCH_USER_GROUP',
|
||||
id: 'aa3c0b9a-8cae-47c0-92ac-202ce5906682'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, async (ps, user) => {
|
||||
let userList;
|
||||
let userGroupJoining;
|
||||
|
||||
if (ps.src === 'list') {
|
||||
userList = await UserLists.findOne({
|
||||
|
@ -71,6 +82,15 @@ export default define(meta, async (ps, user) => {
|
|||
if (userList == null) {
|
||||
throw new ApiError(meta.errors.noSuchUserList);
|
||||
}
|
||||
} else if (ps.src === 'group') {
|
||||
userGroupJoining = await UserGroupJoinings.findOne({
|
||||
userGroupId: ps.userGroupId,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
if (userGroupJoining == null) {
|
||||
throw new ApiError(meta.errors.noSuchUserGroup);
|
||||
}
|
||||
}
|
||||
|
||||
const antenna = await Antennas.save({
|
||||
|
@ -80,6 +100,7 @@ export default define(meta, async (ps, user) => {
|
|||
name: ps.name,
|
||||
src: ps.src,
|
||||
userListId: userList ? userList.id : null,
|
||||
userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null,
|
||||
keywords: ps.keywords,
|
||||
users: ps.users,
|
||||
caseSensitive: ps.caseSensitive,
|
||||
|
|
|
@ -2,7 +2,7 @@ import $ from 'cafy';
|
|||
import { ID } from '../../../../misc/cafy-id';
|
||||
import define from '../../define';
|
||||
import { ApiError } from '../../error';
|
||||
import { Antennas, UserLists } from '../../../../models';
|
||||
import { Antennas, UserLists, UserGroupJoinings } from '../../../../models';
|
||||
|
||||
export const meta = {
|
||||
tags: ['antennas'],
|
||||
|
@ -21,13 +21,17 @@ export const meta = {
|
|||
},
|
||||
|
||||
src: {
|
||||
validator: $.str.or(['home', 'all', 'users', 'list'])
|
||||
validator: $.str.or(['home', 'all', 'users', 'list', 'group'])
|
||||
},
|
||||
|
||||
userListId: {
|
||||
validator: $.nullable.optional.type(ID),
|
||||
},
|
||||
|
||||
userGroupId: {
|
||||
validator: $.nullable.optional.type(ID),
|
||||
},
|
||||
|
||||
keywords: {
|
||||
validator: $.arr($.arr($.str))
|
||||
},
|
||||
|
@ -64,6 +68,12 @@ export const meta = {
|
|||
message: 'No such user list.',
|
||||
code: 'NO_SUCH_USER_LIST',
|
||||
id: '1c6b35c9-943e-48c2-81e4-2844989407f7'
|
||||
},
|
||||
|
||||
noSuchUserGroup: {
|
||||
message: 'No such user group.',
|
||||
code: 'NO_SUCH_USER_GROUP',
|
||||
id: '109ed789-b6eb-456e-b8a9-6059d567d385'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -80,6 +90,7 @@ export default define(meta, async (ps, user) => {
|
|||
}
|
||||
|
||||
let userList;
|
||||
let userGroupJoining;
|
||||
|
||||
if (ps.src === 'list') {
|
||||
userList = await UserLists.findOne({
|
||||
|
@ -90,12 +101,22 @@ export default define(meta, async (ps, user) => {
|
|||
if (userList == null) {
|
||||
throw new ApiError(meta.errors.noSuchUserList);
|
||||
}
|
||||
} else if (ps.src === 'group') {
|
||||
userGroupJoining = await UserGroupJoinings.findOne({
|
||||
userGroupId: ps.userGroupId,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
if (userGroupJoining == null) {
|
||||
throw new ApiError(meta.errors.noSuchUserGroup);
|
||||
}
|
||||
}
|
||||
|
||||
await Antennas.update(antenna.id, {
|
||||
name: ps.name,
|
||||
src: ps.src,
|
||||
userListId: userList ? userList.id : null,
|
||||
userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null,
|
||||
keywords: ps.keywords,
|
||||
users: ps.users,
|
||||
caseSensitive: ps.caseSensitive,
|
||||
|
|
Loading…
Reference in a new issue