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…
	
	Add table
		Add a link
		
	
		Reference in a new issue