parent
							
								
									61461b7f59
								
							
						
					
					
						commit
						68571d8f57
					
				
					 10 changed files with 92 additions and 9 deletions
				
			
		|  | @ -722,6 +722,12 @@ notSpecifiedMentionWarning: "宛先に含まれていないメンションがあ | |||
| info: "情報" | ||||
| userInfo: "ユーザー情報" | ||||
| unknown: "不明" | ||||
| onlineStatus: "オンライン状態" | ||||
| hideOnlineStatus: "オンライン状態を隠す" | ||||
| hideOnlineStatusDescription: "オンライン状態を隠すと、検索などの一部機能において利便性が低下することがあります。" | ||||
| online: "オンライン" | ||||
| active: "アクティブ" | ||||
| offline: "オフライン" | ||||
| 
 | ||||
| _email: | ||||
|   _follow: | ||||
|  |  | |||
							
								
								
									
										16
									
								
								migration/1618637372000-user-last-active-date.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								migration/1618637372000-user-last-active-date.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| import {MigrationInterface, QueryRunner} from "typeorm"; | ||||
| 
 | ||||
| export class userLastActiveDate1618637372000 implements MigrationInterface { | ||||
|     name = 'userLastActiveDate1618637372000' | ||||
| 
 | ||||
|     public async up(queryRunner: QueryRunner): Promise<void> { | ||||
|         await queryRunner.query(`ALTER TABLE "user" ADD "lastActiveDate" TIMESTAMP WITH TIME ZONE DEFAULT NULL`); | ||||
|         await queryRunner.query(`CREATE INDEX "IDX_seoignmeoprigmkpodgrjmkpormg" ON "user" ("lastActiveDate") `); | ||||
|     } | ||||
| 
 | ||||
|     public async down(queryRunner: QueryRunner): Promise<void> { | ||||
|         await queryRunner.query(`DROP INDEX "IDX_seoignmeoprigmkpodgrjmkpormg"`); | ||||
|         await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "lastActiveDate"`); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										14
									
								
								migration/1618639857000-user-hide-online-status.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								migration/1618639857000-user-hide-online-status.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| import {MigrationInterface, QueryRunner} from "typeorm"; | ||||
| 
 | ||||
| export class userHideOnlineStatus1618639857000 implements MigrationInterface { | ||||
|     name = 'userHideOnlineStatus1618639857000' | ||||
| 
 | ||||
|     public async up(queryRunner: QueryRunner): Promise<void> { | ||||
| 			await queryRunner.query(`ALTER TABLE "user" ADD "hideOnlineStatus" boolean NOT NULL DEFAULT false`); | ||||
|     } | ||||
| 
 | ||||
|     public async down(queryRunner: QueryRunner): Promise<void> { | ||||
|         await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "hideOnlineStatus"`); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -5,6 +5,10 @@ | |||
| 		<FormSwitch v-model:value="autoAcceptFollowed" :disabled="!isLocked" @update:value="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch> | ||||
| 		<template #caption>{{ $ts.lockedAccountInfo }}</template> | ||||
| 	</FormGroup> | ||||
| 	<FormSwitch v-model:value="hideOnlineStatus" @update:value="save()"> | ||||
| 		{{ $ts.hideOnlineStatus }} | ||||
| 		<template #desc>{{ $ts.hideOnlineStatusDescription }}</template> | ||||
| 	</FormSwitch> | ||||
| 	<FormSwitch v-model:value="noCrawle" @update:value="save()"> | ||||
| 		{{ $ts.noCrawle }} | ||||
| 		<template #desc>{{ $ts.noCrawleDescription }}</template> | ||||
|  | @ -58,6 +62,7 @@ export default defineComponent({ | |||
| 			autoAcceptFollowed: false, | ||||
| 			noCrawle: false, | ||||
| 			isExplorable: false, | ||||
| 			hideOnlineStatus: false, | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -72,6 +77,7 @@ export default defineComponent({ | |||
| 		this.autoAcceptFollowed = this.$i.autoAcceptFollowed; | ||||
| 		this.noCrawle = this.$i.noCrawle; | ||||
| 		this.isExplorable = this.$i.isExplorable; | ||||
| 		this.hideOnlineStatus = this.$i.hideOnlineStatus; | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
|  | @ -85,6 +91,7 @@ export default defineComponent({ | |||
| 				autoAcceptFollowed: !!this.autoAcceptFollowed, | ||||
| 				noCrawle: !!this.noCrawle, | ||||
| 				isExplorable: !!this.isExplorable, | ||||
| 				hideOnlineStatus: !!this.hideOnlineStatus, | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										2
									
								
								src/const.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/const.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
 | ||||
| export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
 | ||||
|  | @ -26,6 +26,17 @@ export class User { | |||
| 	}) | ||||
| 	public lastFetchedAt: Date | null; | ||||
| 
 | ||||
| 	@Index() | ||||
| 	@Column('timestamp with time zone', { | ||||
| 		nullable: true | ||||
| 	}) | ||||
| 	public lastActiveDate: Date | null; | ||||
| 
 | ||||
| 	@Column('boolean', { | ||||
| 		default: false, | ||||
| 	}) | ||||
| 	public hideOnlineStatus: boolean; | ||||
| 
 | ||||
| 	@Column('varchar', { | ||||
| 		length: 128, | ||||
| 		comment: 'The username of the User.' | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import { SchemaType } from '@/misc/schema'; | |||
| import { awaitAll } from '../../prelude/await-all'; | ||||
| import { populateEmojis } from '@/misc/populate-emojis'; | ||||
| import { getAntennas } from '@/misc/antenna-cache'; | ||||
| import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const'; | ||||
| 
 | ||||
| export type PackedUser = SchemaType<typeof packedUserSchema>; | ||||
| 
 | ||||
|  | @ -145,6 +146,17 @@ export class UserRepository extends Repository<User> { | |||
| 		return count > 0; | ||||
| 	} | ||||
| 
 | ||||
| 	public getOnlineStatus(user: User): string { | ||||
| 		if (user.hideOnlineStatus == null) return 'unknown'; | ||||
| 		if (user.lastActiveDate == null) return 'unknown'; | ||||
| 		const elapsed = Date.now() - user.lastActiveDate.getTime(); | ||||
| 		return ( | ||||
| 			elapsed < USER_ONLINE_THRESHOLD ? 'online' : | ||||
| 			elapsed < USER_ACTIVE_THRESHOLD ? 'active' : | ||||
| 			'offline' | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	public async pack( | ||||
| 		src: User['id'] | User, | ||||
| 		me?: { id: User['id'] } | null | undefined, | ||||
|  | @ -192,6 +204,7 @@ export class UserRepository extends Repository<User> { | |||
| 				themeColor: instance.themeColor, | ||||
| 			} : undefined) : undefined, | ||||
| 			emojis: populateEmojis(user.emojis, user.host), | ||||
| 			onlineStatus: this.getOnlineStatus(user), | ||||
| 
 | ||||
| 			...(opts.detail ? { | ||||
| 				url: profile!.url, | ||||
|  | @ -239,6 +252,7 @@ export class UserRepository extends Repository<User> { | |||
| 				autoAcceptFollowed: profile!.autoAcceptFollowed, | ||||
| 				noCrawle: profile!.noCrawle, | ||||
| 				isExplorable: user.isExplorable, | ||||
| 				hideOnlineStatus: user.hideOnlineStatus, | ||||
| 				hasUnreadSpecifiedNotes: NoteUnreads.count({ | ||||
| 					where: { userId: user.id, isSpecified: true }, | ||||
| 					take: 1 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import { USER_ONLINE_THRESHOLD } from '@/const'; | ||||
| import { Users } from '@/models'; | ||||
| import { MoreThan } from 'typeorm'; | ||||
| import define from '../define'; | ||||
| import { redisClient } from '../../../db/redis'; | ||||
| import config from '@/config'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['meta'], | ||||
|  | @ -11,12 +12,12 @@ export const meta = { | |||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default define(meta, (ps, user) => { | ||||
| 	return new Promise((res, rej) => { | ||||
| 		redisClient.pubsub('numsub', config.host, (_, x) => { | ||||
| 			res({ | ||||
| 				count: x[1] | ||||
| 			}); | ||||
| 		}); | ||||
| export default define(meta, async () => { | ||||
| 	const count = await Users.count({ | ||||
| 		lastActiveDate: MoreThan(new Date(Date.now() - USER_ONLINE_THRESHOLD)) | ||||
| 	}); | ||||
| 
 | ||||
| 	return { | ||||
| 		count | ||||
| 	}; | ||||
| }); | ||||
|  |  | |||
|  | @ -96,6 +96,10 @@ export const meta = { | |||
| 			validator: $.optional.bool, | ||||
| 		}, | ||||
| 
 | ||||
| 		hideOnlineStatus: { | ||||
| 			validator: $.optional.bool, | ||||
| 		}, | ||||
| 
 | ||||
| 		carefulBot: { | ||||
| 			validator: $.optional.bool, | ||||
| 			desc: { | ||||
|  | @ -228,6 +232,7 @@ export default define(meta, async (ps, _user, token) => { | |||
| 	if (ps.mutingNotificationTypes !== undefined) profileUpdates.mutingNotificationTypes = ps.mutingNotificationTypes as typeof notificationTypes[number][]; | ||||
| 	if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; | ||||
| 	if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; | ||||
| 	if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; | ||||
| 	if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; | ||||
| 	if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; | ||||
| 	if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import { ParsedUrlQuery } from 'querystring'; | |||
| import authenticate from './authenticate'; | ||||
| import { EventEmitter } from 'events'; | ||||
| import { subsdcriber as redisClient } from '../../db/redis'; | ||||
| import { Users } from '@/models'; | ||||
| 
 | ||||
| module.exports = (server: http.Server) => { | ||||
| 	// Init websocket server
 | ||||
|  | @ -45,5 +46,11 @@ module.exports = (server: http.Server) => { | |||
| 				connection.send('pong'); | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		if (user) { | ||||
| 			Users.update(user.id, { | ||||
| 				lastActiveDate: new Date(), | ||||
| 			}); | ||||
| 		} | ||||
| 	}); | ||||
| }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue