summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-04-17 15:30:26 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-04-17 15:30:26 +0900
commit68571d8f577e0a9eb8679e8dd30e11d8b1709340 (patch)
tree623ad0865b76cdb8a4551042530655ef7c22c754 /src
parentImprove client (diff)
downloadsharkey-68571d8f577e0a9eb8679e8dd30e11d8b1709340.tar.gz
sharkey-68571d8f577e0a9eb8679e8dd30e11d8b1709340.tar.bz2
sharkey-68571d8f577e0a9eb8679e8dd30e11d8b1709340.zip
Implement user online status
Resolve #7422 Fix #7424
Diffstat (limited to 'src')
-rw-r--r--src/client/pages/settings/privacy.vue7
-rw-r--r--src/const.ts2
-rw-r--r--src/models/entities/user.ts11
-rw-r--r--src/models/repositories/user.ts14
-rw-r--r--src/server/api/endpoints/get-online-users-count.ts19
-rw-r--r--src/server/api/endpoints/i/update.ts5
-rw-r--r--src/server/api/streaming.ts7
7 files changed, 56 insertions, 9 deletions
diff --git a/src/client/pages/settings/privacy.vue b/src/client/pages/settings/privacy.vue
index 0542c527f9..c8df378410 100644
--- a/src/client/pages/settings/privacy.vue
+++ b/src/client/pages/settings/privacy.vue
@@ -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,
});
}
}
diff --git a/src/const.ts b/src/const.ts
new file mode 100644
index 0000000000..43f59f1e4f
--- /dev/null
+++ b/src/const.ts
@@ -0,0 +1,2 @@
+export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
+export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
diff --git a/src/models/entities/user.ts b/src/models/entities/user.ts
index 91fbe35d94..060ec06b9a 100644
--- a/src/models/entities/user.ts
+++ b/src/models/entities/user.ts
@@ -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.'
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index a3b4c69f43..0d59ed2545 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -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
diff --git a/src/server/api/endpoints/get-online-users-count.ts b/src/server/api/endpoints/get-online-users-count.ts
index 150ac9e365..a13363055f 100644
--- a/src/server/api/endpoints/get-online-users-count.ts
+++ b/src/server/api/endpoints/get-online-users-count.ts
@@ -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
+ };
});
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index c0ffd75e23..032dccd91a 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -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;
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index 81b83edcf5..7224c23570 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -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(),
+ });
+ }
});
};