mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
feat(writeapi): user settings API
This commit is contained in:
143
public/openapi/components/schemas/SettingsObj.yaml
Normal file
143
public/openapi/components/schemas/SettingsObj.yaml
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
Settings:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
showemail:
|
||||||
|
type: boolean
|
||||||
|
description: Show user email in profile page
|
||||||
|
usePagination:
|
||||||
|
type: boolean
|
||||||
|
description: Toggles between pagination (when enabled), or infinite scrolling (when disabled)
|
||||||
|
topicsPerPage:
|
||||||
|
type: number
|
||||||
|
description: Number of topics displayed on a category page
|
||||||
|
postsPerPage:
|
||||||
|
type: number
|
||||||
|
description: Number of posts displayed on a topic page
|
||||||
|
topicPostSort:
|
||||||
|
type: string
|
||||||
|
description: Default sorting strategy of the posts in of a topic
|
||||||
|
openOutgoingLinksInNewTab:
|
||||||
|
type: boolean
|
||||||
|
description: Whether to automatically open all external links in a new tab
|
||||||
|
dailyDigestFreq:
|
||||||
|
type: string
|
||||||
|
description: How often to receive the scheduled digest from this forum
|
||||||
|
showfullname:
|
||||||
|
type: boolean
|
||||||
|
description: Show user full name in profile page
|
||||||
|
followTopicsOnCreate:
|
||||||
|
type: boolean
|
||||||
|
description: Automatically be notified of new posts in a topic, when you create a topic
|
||||||
|
followTopicsOnReply:
|
||||||
|
type: boolean
|
||||||
|
description:
|
||||||
|
restrictChat:
|
||||||
|
type: boolean
|
||||||
|
description:
|
||||||
|
topicSearchEnabled:
|
||||||
|
type: boolean
|
||||||
|
description:
|
||||||
|
categoryTopicSort:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
userLang:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
bootswatchSkin:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
homePageRoute:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
scrollToMyPost:
|
||||||
|
type: boolean
|
||||||
|
description:
|
||||||
|
notificationType_new-chat:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_new-reply:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_post-edit:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
sendChatNotifications:
|
||||||
|
nullable: true
|
||||||
|
sendPostNotifications:
|
||||||
|
nullable: true
|
||||||
|
notificationType_upvote:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_new-topic:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_follow:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_group-invite:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
upvoteNotifFreq:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_mention:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
acpLang:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_new-register:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_post-queue:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_new-post-flag:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_new-user-flag:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
categoryWatchState:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
notificationType_group-request-membership:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
uid:
|
||||||
|
type: number
|
||||||
|
description:
|
||||||
|
description: A user identifier
|
||||||
|
required:
|
||||||
|
- showemail
|
||||||
|
- usePagination
|
||||||
|
- topicsPerPage
|
||||||
|
- postsPerPage
|
||||||
|
- topicPostSort
|
||||||
|
- openOutgoingLinksInNewTab
|
||||||
|
- dailyDigestFreq
|
||||||
|
- showfullname
|
||||||
|
- followTopicsOnCreate
|
||||||
|
- followTopicsOnReply
|
||||||
|
- restrictChat
|
||||||
|
- topicSearchEnabled
|
||||||
|
- categoryTopicSort
|
||||||
|
- userLang
|
||||||
|
- bootswatchSkin
|
||||||
|
- homePageRoute
|
||||||
|
- scrollToMyPost
|
||||||
|
- notificationType_new-chat
|
||||||
|
- notificationType_new-reply
|
||||||
|
- notificationType_upvote
|
||||||
|
- notificationType_new-topic
|
||||||
|
- notificationType_follow
|
||||||
|
- notificationType_group-invite
|
||||||
|
- upvoteNotifFreq
|
||||||
|
- acpLang
|
||||||
|
- notificationType_new-register
|
||||||
|
- notificationType_post-queue
|
||||||
|
- notificationType_new-post-flag
|
||||||
|
- notificationType_new-user-flag
|
||||||
|
- categoryWatchState
|
||||||
|
- notificationType_group-request-membership
|
||||||
|
- uid
|
||||||
@@ -20,114 +20,7 @@ get:
|
|||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
settings:
|
settings:
|
||||||
type: object
|
$ref: ../../../components/schemas/SettingsObj.yaml#/Settings
|
||||||
properties:
|
|
||||||
showemail:
|
|
||||||
type: boolean
|
|
||||||
usePagination:
|
|
||||||
type: boolean
|
|
||||||
topicsPerPage:
|
|
||||||
type: number
|
|
||||||
postsPerPage:
|
|
||||||
type: number
|
|
||||||
topicPostSort:
|
|
||||||
type: string
|
|
||||||
openOutgoingLinksInNewTab:
|
|
||||||
type: boolean
|
|
||||||
dailyDigestFreq:
|
|
||||||
type: string
|
|
||||||
showfullname:
|
|
||||||
type: boolean
|
|
||||||
followTopicsOnCreate:
|
|
||||||
type: boolean
|
|
||||||
followTopicsOnReply:
|
|
||||||
type: boolean
|
|
||||||
restrictChat:
|
|
||||||
type: boolean
|
|
||||||
topicSearchEnabled:
|
|
||||||
type: boolean
|
|
||||||
categoryTopicSort:
|
|
||||||
type: string
|
|
||||||
userLang:
|
|
||||||
type: string
|
|
||||||
bootswatchSkin:
|
|
||||||
type: string
|
|
||||||
homePageRoute:
|
|
||||||
type: string
|
|
||||||
scrollToMyPost:
|
|
||||||
type: boolean
|
|
||||||
notificationType_new-chat:
|
|
||||||
type: string
|
|
||||||
notificationType_new-reply:
|
|
||||||
type: string
|
|
||||||
notificationType_post-edit:
|
|
||||||
type: string
|
|
||||||
sendChatNotifications:
|
|
||||||
nullable: true
|
|
||||||
sendPostNotifications:
|
|
||||||
nullable: true
|
|
||||||
notificationType_upvote:
|
|
||||||
type: string
|
|
||||||
notificationType_new-topic:
|
|
||||||
type: string
|
|
||||||
notificationType_follow:
|
|
||||||
type: string
|
|
||||||
notificationType_group-invite:
|
|
||||||
type: string
|
|
||||||
upvoteNotifFreq:
|
|
||||||
type: string
|
|
||||||
notificationType_mention:
|
|
||||||
type: string
|
|
||||||
acpLang:
|
|
||||||
type: string
|
|
||||||
notificationType_new-register:
|
|
||||||
type: string
|
|
||||||
notificationType_post-queue:
|
|
||||||
type: string
|
|
||||||
notificationType_new-post-flag:
|
|
||||||
type: string
|
|
||||||
notificationType_new-user-flag:
|
|
||||||
type: string
|
|
||||||
categoryWatchState:
|
|
||||||
type: string
|
|
||||||
notificationType_group-request-membership:
|
|
||||||
type: string
|
|
||||||
uid:
|
|
||||||
type: number
|
|
||||||
description: A user identifier
|
|
||||||
required:
|
|
||||||
- showemail
|
|
||||||
- usePagination
|
|
||||||
- topicsPerPage
|
|
||||||
- postsPerPage
|
|
||||||
- topicPostSort
|
|
||||||
- openOutgoingLinksInNewTab
|
|
||||||
- dailyDigestFreq
|
|
||||||
- showfullname
|
|
||||||
- followTopicsOnCreate
|
|
||||||
- followTopicsOnReply
|
|
||||||
- restrictChat
|
|
||||||
- topicSearchEnabled
|
|
||||||
- categoryTopicSort
|
|
||||||
- userLang
|
|
||||||
- bootswatchSkin
|
|
||||||
- homePageRoute
|
|
||||||
- scrollToMyPost
|
|
||||||
- notificationType_new-chat
|
|
||||||
- notificationType_new-reply
|
|
||||||
- notificationType_upvote
|
|
||||||
- notificationType_new-topic
|
|
||||||
- notificationType_follow
|
|
||||||
- notificationType_group-invite
|
|
||||||
- upvoteNotifFreq
|
|
||||||
- acpLang
|
|
||||||
- notificationType_new-register
|
|
||||||
- notificationType_post-queue
|
|
||||||
- notificationType_new-post-flag
|
|
||||||
- notificationType_new-user-flag
|
|
||||||
- categoryWatchState
|
|
||||||
- notificationType_group-request-membership
|
|
||||||
- uid
|
|
||||||
languages:
|
languages:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ tags:
|
|||||||
paths:
|
paths:
|
||||||
/users/{uid}:
|
/users/{uid}:
|
||||||
$ref: 'write/users/uid.yaml'
|
$ref: 'write/users/uid.yaml'
|
||||||
|
/users/{uid}/settings:
|
||||||
|
$ref: 'write/users/uid/settings.yaml'
|
||||||
|
/users/{uid}/settings/{setting}:
|
||||||
|
$ref: 'write/users/uid/settings/setting.yaml'
|
||||||
/users/{uid}/password:
|
/users/{uid}/password:
|
||||||
$ref: 'write/users/uid/password.yaml'
|
$ref: 'write/users/uid/password.yaml'
|
||||||
/users/{uid}/follow:
|
/users/{uid}/follow:
|
||||||
|
|||||||
35
public/openapi/write/users/uid/settings.yaml
Normal file
35
public/openapi/write/users/uid/settings.yaml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- users
|
||||||
|
summary: update user settings
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: uid
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
description: uid of the user
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
settings:
|
||||||
|
type: object
|
||||||
|
description: An object containing key-value pairs of user settings to update
|
||||||
|
example:
|
||||||
|
showemail: '0'
|
||||||
|
showfullname: '1'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successfully updated user settings
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
$ref: ../../../components/schemas/SettingsObj.yaml#/Settings
|
||||||
40
public/openapi/write/users/uid/settings/setting.yaml
Normal file
40
public/openapi/write/users/uid/settings/setting.yaml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- users
|
||||||
|
summary: update one user setting
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: uid
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
description: uid of the user
|
||||||
|
example: '1'
|
||||||
|
- in: path
|
||||||
|
name: setting
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: name of the setting you wish to update
|
||||||
|
example: 'showemail'
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
example: '1'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successfully updated user settings
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
type: object
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
define('forum/account/settings', ['forum/account/header', 'components', 'translator'], function (header, components, translator) {
|
define('forum/account/settings', ['forum/account/header', 'components', 'translator', 'api'], function (header, components, translator, api) {
|
||||||
var AccountSettings = {};
|
var AccountSettings = {};
|
||||||
|
|
||||||
// If page skin is changed but not saved, switch the skin back
|
// If page skin is changed but not saved, switch the skin back
|
||||||
@@ -67,11 +67,7 @@ define('forum/account/settings', ['forum/account/header', 'components', 'transla
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveSettings(settings) {
|
function saveSettings(settings) {
|
||||||
socket.emit('user.saveSettings', { uid: ajaxify.data.theirid, settings: settings }, function (err, newSettings) {
|
api.put(`/users/${ajaxify.data.uid}/settings`, { settings }).then((newSettings) => {
|
||||||
if (err) {
|
|
||||||
return app.alertError(err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.alertSuccess('[[success:settings-saved]]');
|
app.alertSuccess('[[success:settings-saved]]');
|
||||||
var languageChanged = false;
|
var languageChanged = false;
|
||||||
for (var key in newSettings) {
|
for (var key in newSettings) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ define('forum/category', [
|
|||||||
|
|
||||||
topicList.init('category', loadTopicsAfter);
|
topicList.init('category', loadTopicsAfter);
|
||||||
|
|
||||||
sort.handleSort('categoryTopicSort', 'user.setCategorySort', 'category/' + ajaxify.data.slug);
|
sort.handleSort('categoryTopicSort', 'setCategorySort', 'category/' + ajaxify.data.slug);
|
||||||
|
|
||||||
if (!config.usePagination) {
|
if (!config.usePagination) {
|
||||||
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback);
|
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ define('forum/topic', [
|
|||||||
threadTools.init(tid, $('.topic'));
|
threadTools.init(tid, $('.topic'));
|
||||||
events.init();
|
events.init();
|
||||||
|
|
||||||
sort.handleSort('topicPostSort', 'user.setTopicSort', 'topic/' + ajaxify.data.slug);
|
sort.handleSort('topicPostSort', 'setTopicSort', 'topic/' + ajaxify.data.slug);
|
||||||
|
|
||||||
if (!config.usePagination) {
|
if (!config.usePagination) {
|
||||||
infinitescroll.init($('[component="topic"]'), posts.loadMorePosts);
|
infinitescroll.init($('[component="topic"]'), posts.loadMorePosts);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
define('sort', ['components'], function (components) {
|
define('sort', ['components', 'api'], function (components, api) {
|
||||||
var module = {};
|
var module = {};
|
||||||
|
|
||||||
module.handleSort = function (field, method, gotoOnSave) {
|
module.handleSort = function (field, key, gotoOnSave) {
|
||||||
var threadSort = components.get('thread/sort');
|
var threadSort = components.get('thread/sort');
|
||||||
threadSort.find('i').removeClass('fa-check');
|
threadSort.find('i').removeClass('fa-check');
|
||||||
var currentSetting = threadSort.find('a[data-sort="' + config[field] + '"]');
|
var currentSetting = threadSort.find('a[data-sort="' + config[field] + '"]');
|
||||||
@@ -20,10 +20,9 @@ define('sort', ['components'], function (components) {
|
|||||||
}
|
}
|
||||||
var newSetting = $(this).attr('data-sort');
|
var newSetting = $(this).attr('data-sort');
|
||||||
if (app.user.uid) {
|
if (app.user.uid) {
|
||||||
socket.emit(method, newSetting, function (err) {
|
api.put(`/users/${app.user.uid}/settings/${key}`, {
|
||||||
if (err) {
|
value: newSetting,
|
||||||
return app.alertError(err.message);
|
}).then(() => {
|
||||||
}
|
|
||||||
refresh(newSetting, utils.params());
|
refresh(newSetting, utils.params());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -89,6 +89,23 @@ usersAPI.deleteMany = async function (caller, data) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
usersAPI.updateSettings = async function (caller, data) {
|
||||||
|
if (!caller.uid || !data || !data.settings) {
|
||||||
|
throw new Error('[[error:invalid-data]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
const canEdit = await privileges.users.canEdit(caller.uid, data.uid);
|
||||||
|
if (!canEdit) {
|
||||||
|
throw new Error('[[error:no-privileges]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await user.saveSettings(data.uid, data.settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
usersAPI.updateSetting = async function (caller, data) {
|
||||||
|
await user.setSetting(data.uid, data.setting, data.value);
|
||||||
|
};
|
||||||
|
|
||||||
usersAPI.changePassword = async function (caller, data) {
|
usersAPI.changePassword = async function (caller, data) {
|
||||||
await user.changePassword(caller.uid, Object.assign(data, { ip: caller.ip }));
|
await user.changePassword(caller.uid, Object.assign(data, { ip: caller.ip }));
|
||||||
await events.log({
|
await events.log({
|
||||||
|
|||||||
@@ -28,6 +28,16 @@ Users.deleteMany = async (req, res) => {
|
|||||||
helpers.formatApiResponse(200, res);
|
helpers.formatApiResponse(200, res);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Users.updateSettings = async (req, res) => {
|
||||||
|
const settings = await api.users.updateSettings(req, { ...req.body, uid: req.params.uid });
|
||||||
|
helpers.formatApiResponse(200, res, settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
Users.updateSetting = async (req, res) => {
|
||||||
|
await api.users.updateSetting(req, { ...req.params, value: req.body.value });
|
||||||
|
helpers.formatApiResponse(200, res);
|
||||||
|
};
|
||||||
|
|
||||||
Users.changePassword = async (req, res) => {
|
Users.changePassword = async (req, res) => {
|
||||||
await api.users.changePassword(req, { ...req.body, uid: req.params.uid });
|
await api.users.changePassword(req, { ...req.body, uid: req.params.uid });
|
||||||
helpers.formatApiResponse(200, res);
|
helpers.formatApiResponse(200, res);
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ function authenticatedRoutes() {
|
|||||||
setupApiRoute(router, 'put', '/:uid', [...middlewares, middleware.assert.user], controllers.write.users.update);
|
setupApiRoute(router, 'put', '/:uid', [...middlewares, middleware.assert.user], controllers.write.users.update);
|
||||||
setupApiRoute(router, 'delete', '/:uid', [...middlewares, middleware.assert.user, middleware.exposePrivileges], controllers.write.users.delete);
|
setupApiRoute(router, 'delete', '/:uid', [...middlewares, middleware.assert.user, middleware.exposePrivileges], controllers.write.users.delete);
|
||||||
|
|
||||||
|
setupApiRoute(router, 'put', '/:uid/settings', [...middlewares, middleware.checkRequired.bind(null, ['settings'])], controllers.write.users.updateSettings);
|
||||||
|
setupApiRoute(router, 'put', '/:uid/settings/:setting', [...middlewares, middleware.checkRequired.bind(null, ['value'])], controllers.write.users.updateSetting);
|
||||||
|
|
||||||
setupApiRoute(router, 'put', '/:uid/password', [...middlewares, middleware.checkRequired.bind(null, ['newPassword']), middleware.assert.user], controllers.write.users.changePassword);
|
setupApiRoute(router, 'put', '/:uid/password', [...middlewares, middleware.checkRequired.bind(null, ['newPassword']), middleware.assert.user], controllers.write.users.changePassword);
|
||||||
|
|
||||||
setupApiRoute(router, 'put', '/:uid/follow', [...middlewares, middleware.assert.user], controllers.write.users.follow);
|
setupApiRoute(router, 'put', '/:uid/follow', [...middlewares, middleware.assert.user], controllers.write.users.follow);
|
||||||
|
|||||||
@@ -168,22 +168,27 @@ SocketUser.unfollow = async function (socket, data) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketUser.saveSettings = async function (socket, data) {
|
SocketUser.saveSettings = async function (socket, data) {
|
||||||
if (!socket.uid || !data || !data.settings) {
|
sockets.warnDeprecated(socket, 'PUT /api/v3/users/:uid/settings');
|
||||||
throw new Error('[[error:invalid-data]]');
|
const settings = await api.users.updateSettings(socket, data);
|
||||||
}
|
return settings;
|
||||||
const canEdit = await privileges.users.canEdit(socket.uid, data.uid);
|
|
||||||
if (!canEdit) {
|
|
||||||
throw new Error('[[error:no-privileges]]');
|
|
||||||
}
|
|
||||||
return await user.saveSettings(data.uid, data.settings);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketUser.setTopicSort = async function (socket, sort) {
|
SocketUser.setTopicSort = async function (socket, sort) {
|
||||||
await user.setSetting(socket.uid, 'topicPostSort', sort);
|
sockets.warnDeprecated(socket, 'PUT /api/v3/users/:uid/setting/topicPostSort');
|
||||||
|
await api.users.updateSetting(socket, {
|
||||||
|
uid: socket.uid,
|
||||||
|
setting: 'topicPostSort',
|
||||||
|
value: sort,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketUser.setCategorySort = async function (socket, sort) {
|
SocketUser.setCategorySort = async function (socket, sort) {
|
||||||
await user.setSetting(socket.uid, 'categoryTopicSort', sort);
|
sockets.warnDeprecated(socket, 'PUT /api/v3/users/:uid/setting/categoryTopicSort');
|
||||||
|
await api.users.updateSetting(socket, {
|
||||||
|
uid: socket.uid,
|
||||||
|
setting: 'categoryTopicSort',
|
||||||
|
value: sort,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketUser.getUnreadCount = async function (socket) {
|
SocketUser.getUnreadCount = async function (socket) {
|
||||||
|
|||||||
Reference in New Issue
Block a user