Add notification settings to control what statuses allow push notifications (#3829)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c93ce7af3f
Коммит
065d01c121
25
api/post.go
25
api/post.go
@@ -565,7 +565,7 @@ func sendNotifications(teamId string, post *model.Post, team *model.Team, channe
|
|||||||
}
|
}
|
||||||
|
|
||||||
mentionedUserIds := make(map[string]bool)
|
mentionedUserIds := make(map[string]bool)
|
||||||
alwaysNotifyUserIds := []string{}
|
allActivityPushUserIds := []string{}
|
||||||
hereNotification := false
|
hereNotification := false
|
||||||
updateMentionChans := []store.StoreChannel{}
|
updateMentionChans := []store.StoreChannel{}
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ func sendNotifications(teamId string, post *model.Post, team *model.Team, channe
|
|||||||
if profile.NotifyProps["push"] == model.USER_NOTIFY_ALL &&
|
if profile.NotifyProps["push"] == model.USER_NOTIFY_ALL &&
|
||||||
(post.UserId != profile.Id || post.Props["from_webhook"] == "true") &&
|
(post.UserId != profile.Id || post.Props["from_webhook"] == "true") &&
|
||||||
!post.IsSystemMessage() {
|
!post.IsSystemMessage() {
|
||||||
alwaysNotifyUserIds = append(alwaysNotifyUserIds, profile.Id)
|
allActivityPushUserIds = append(allActivityPushUserIds, profile.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -705,13 +705,28 @@ func sendNotifications(teamId string, post *model.Post, team *model.Team, channe
|
|||||||
|
|
||||||
if sendPushNotifications {
|
if sendPushNotifications {
|
||||||
for _, id := range mentionedUsersList {
|
for _, id := range mentionedUsersList {
|
||||||
if profileMap[id].NotifyProps["push"] != "none" {
|
var status *model.Status
|
||||||
|
var err *model.AppError
|
||||||
|
if status, err = GetStatus(id); err != nil {
|
||||||
|
status = &model.Status{id, model.STATUS_OFFLINE, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
if profileMap[id].StatusAllowsPushNotification(status) {
|
||||||
sendPushNotification(post, profileMap[id], channel, senderName, true)
|
sendPushNotification(post, profileMap[id], channel, senderName, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, id := range alwaysNotifyUserIds {
|
|
||||||
|
for _, id := range allActivityPushUserIds {
|
||||||
if _, ok := mentionedUserIds[id]; !ok {
|
if _, ok := mentionedUserIds[id]; !ok {
|
||||||
sendPushNotification(post, profileMap[id], channel, senderName, false)
|
var status *model.Status
|
||||||
|
var err *model.AppError
|
||||||
|
if status, err = GetStatus(id); err != nil {
|
||||||
|
status = &model.Status{id, model.STATUS_OFFLINE, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
if profileMap[id].StatusAllowsPushNotification(status) {
|
||||||
|
sendPushNotification(post, profileMap[id], channel, senderName, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,6 +373,24 @@ func (u *User) IsLDAPUser() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) StatusAllowsPushNotification(status *Status) bool {
|
||||||
|
props := u.NotifyProps
|
||||||
|
|
||||||
|
if props["push"] == "none" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if pushStatus, ok := props["push_status"]; pushStatus == STATUS_ONLINE || !ok {
|
||||||
|
return true
|
||||||
|
} else if pushStatus == STATUS_AWAY && (status.Status == STATUS_AWAY || status.Status == STATUS_OFFLINE) {
|
||||||
|
return true
|
||||||
|
} else if pushStatus == STATUS_OFFLINE && status.Status == STATUS_OFFLINE {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// UserFromJson will decode the input and return a User
|
// UserFromJson will decode the input and return a User
|
||||||
func UserFromJson(data io.Reader) *User {
|
func UserFromJson(data io.Reader) *User {
|
||||||
decoder := json.NewDecoder(data)
|
decoder := json.NewDecoder(data)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default class SettingItemMin extends React.Component {
|
|||||||
>
|
>
|
||||||
<li className='col-sm-9 section-title'>{this.props.title}</li>
|
<li className='col-sm-9 section-title'>{this.props.title}</li>
|
||||||
{editButton}
|
{editButton}
|
||||||
<li className='col-sm-9 section-describe'>{this.props.describe}</li>
|
<li className='col-sm-10 section-describe'>{this.props.describe}</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,40 +11,48 @@ import Client from 'client/web_client.jsx';
|
|||||||
import * as AsyncClient from 'utils/async_client.jsx';
|
import * as AsyncClient from 'utils/async_client.jsx';
|
||||||
import * as UserAgent from 'utils/user_agent.jsx';
|
import * as UserAgent from 'utils/user_agent.jsx';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
import Constants from 'utils/constants.jsx';
|
||||||
|
|
||||||
import EmailNotificationSetting from './email_notification_setting.jsx';
|
import EmailNotificationSetting from './email_notification_setting.jsx';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
function getNotificationsStateFromStores() {
|
function getNotificationsStateFromStores() {
|
||||||
var user = UserStore.getCurrentUser();
|
const user = UserStore.getCurrentUser();
|
||||||
var soundNeeded = !UserAgent.isFirefox();
|
const soundNeeded = !UserAgent.isFirefox();
|
||||||
|
|
||||||
var sound = 'true';
|
let sound = 'true';
|
||||||
if (user.notify_props && user.notify_props.desktop_sound) {
|
let desktop = 'default';
|
||||||
sound = user.notify_props.desktop_sound;
|
let comments = 'never';
|
||||||
}
|
let enableEmail = 'true';
|
||||||
var desktop = 'default';
|
let pushActivity = 'mention';
|
||||||
if (user.notify_props && user.notify_props.desktop) {
|
let pushStatus = Constants.UserStatuses.ONLINE;
|
||||||
desktop = user.notify_props.desktop;
|
|
||||||
}
|
if (user.notify_props) {
|
||||||
var comments = 'never';
|
if (user.notify_props.desktop_sound) {
|
||||||
if (user.notify_props && user.notify_props.comments) {
|
sound = user.notify_props.desktop_sound;
|
||||||
comments = user.notify_props.comments;
|
}
|
||||||
}
|
if (user.notify_props.desktop) {
|
||||||
var enableEmail = 'true';
|
desktop = user.notify_props.desktop;
|
||||||
if (user.notify_props && user.notify_props.email) {
|
}
|
||||||
enableEmail = user.notify_props.email;
|
if (user.notify_props.comments) {
|
||||||
}
|
comments = user.notify_props.comments;
|
||||||
var push = 'mention';
|
}
|
||||||
if (user.notify_props && user.notify_props.push) {
|
if (user.notify_props.email) {
|
||||||
push = user.notify_props.push;
|
enableEmail = user.notify_props.email;
|
||||||
|
}
|
||||||
|
if (user.notify_props.push) {
|
||||||
|
pushActivity = user.notify_props.push;
|
||||||
|
}
|
||||||
|
if (user.notify_props.push_status) {
|
||||||
|
pushStatus = user.notify_props.push_status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var usernameKey = false;
|
let usernameKey = false;
|
||||||
var mentionKey = false;
|
let mentionKey = false;
|
||||||
var customKeys = '';
|
let customKeys = '';
|
||||||
var firstNameKey = false;
|
let firstNameKey = false;
|
||||||
var channelKey = false;
|
let channelKey = false;
|
||||||
|
|
||||||
if (user.notify_props) {
|
if (user.notify_props) {
|
||||||
if (user.notify_props.mention_keys) {
|
if (user.notify_props.mention_keys) {
|
||||||
@@ -78,8 +86,9 @@ function getNotificationsStateFromStores() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
notifyLevel: desktop,
|
notifyLevel: desktop,
|
||||||
notifyPushLevel: push,
|
|
||||||
enableEmail,
|
enableEmail,
|
||||||
|
pushActivity,
|
||||||
|
pushStatus,
|
||||||
soundNeeded,
|
soundNeeded,
|
||||||
enableSound: sound,
|
enableSound: sound,
|
||||||
usernameKey,
|
usernameKey,
|
||||||
@@ -123,7 +132,8 @@ export default class NotificationsTab extends React.Component {
|
|||||||
data.email = this.state.enableEmail;
|
data.email = this.state.enableEmail;
|
||||||
data.desktop_sound = this.state.enableSound;
|
data.desktop_sound = this.state.enableSound;
|
||||||
data.desktop = this.state.notifyLevel;
|
data.desktop = this.state.notifyLevel;
|
||||||
data.push = this.state.notifyPushLevel;
|
data.push = this.state.pushActivity;
|
||||||
|
data.push_status = this.state.pushStatus;
|
||||||
data.comments = this.state.notifyCommentsLevel;
|
data.comments = this.state.notifyCommentsLevel;
|
||||||
|
|
||||||
var mentionKeys = [];
|
var mentionKeys = [];
|
||||||
@@ -147,6 +157,7 @@ export default class NotificationsTab extends React.Component {
|
|||||||
() => {
|
() => {
|
||||||
this.props.updateSection('');
|
this.props.updateSection('');
|
||||||
AsyncClient.getMe();
|
AsyncClient.getMe();
|
||||||
|
$('.settings-modal .modal-body').scrollTop(0).perfectScrollbar('update');
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
this.setState({serverError: err.message});
|
this.setState({serverError: err.message});
|
||||||
@@ -195,8 +206,13 @@ export default class NotificationsTab extends React.Component {
|
|||||||
this.refs.wrapper.focus();
|
this.refs.wrapper.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePushRadio(notifyPushLevel) {
|
handlePushRadio(pushActivity) {
|
||||||
this.setState({notifyPushLevel});
|
this.setState({pushActivity});
|
||||||
|
this.refs.wrapper.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePushStatusRadio(pushStatus) {
|
||||||
|
this.setState({pushStatus});
|
||||||
this.refs.wrapper.focus();
|
this.refs.wrapper.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,22 +261,93 @@ export default class NotificationsTab extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createPushNotificationSection() {
|
createPushNotificationSection() {
|
||||||
var handleUpdateDesktopSection;
|
let handleUpdateDesktopSection;
|
||||||
if (this.props.activeSection === 'push') {
|
if (this.props.activeSection === 'push') {
|
||||||
var notifyActive = [false, false, false];
|
|
||||||
if (this.state.notifyPushLevel === 'all') {
|
|
||||||
notifyActive[0] = true;
|
|
||||||
} else if (this.state.notifyPushLevel === 'none') {
|
|
||||||
notifyActive[2] = true;
|
|
||||||
} else {
|
|
||||||
notifyActive[1] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputs = [];
|
let inputs = [];
|
||||||
let extraInfo = null;
|
let extraInfo = null;
|
||||||
let submit = null;
|
let submit = null;
|
||||||
|
|
||||||
if (global.window.mm_config.SendPushNotifications === 'true') {
|
if (global.window.mm_config.SendPushNotifications === 'true') {
|
||||||
|
const pushActivityRadio = [false, false, false];
|
||||||
|
if (this.state.pushActivity === 'all') {
|
||||||
|
pushActivityRadio[0] = true;
|
||||||
|
} else if (this.state.pushActivity === 'none') {
|
||||||
|
pushActivityRadio[2] = true;
|
||||||
|
} else {
|
||||||
|
pushActivityRadio[1] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushStatusRadio = [false, false, false];
|
||||||
|
if (this.state.pushStatus === Constants.UserStatuses.ONLINE) {
|
||||||
|
pushStatusRadio[0] = true;
|
||||||
|
} else if (this.state.pushStatus === Constants.UserStatuses.AWAY) {
|
||||||
|
pushStatusRadio[1] = true;
|
||||||
|
} else {
|
||||||
|
pushStatusRadio[2] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pushStatusSettings;
|
||||||
|
if (this.state.pushActivity !== 'none') {
|
||||||
|
pushStatusSettings = (
|
||||||
|
<div>
|
||||||
|
<hr/>
|
||||||
|
<div className='radio'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='pushNotificationStatus'
|
||||||
|
checked={pushStatusRadio[0]}
|
||||||
|
onChange={this.handlePushStatusRadio.bind(this, Constants.UserStatuses.ONLINE)}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.online'
|
||||||
|
defaultMessage='Online, away or offline'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div className='radio'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='pushNotificationStatus'
|
||||||
|
checked={pushStatusRadio[1]}
|
||||||
|
onChange={this.handlePushStatusRadio.bind(this, Constants.UserStatuses.AWAY)}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.away'
|
||||||
|
defaultMessage='Away or offline'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div className='radio'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='pushNotificationStatus'
|
||||||
|
checked={pushStatusRadio[2]}
|
||||||
|
onChange={this.handlePushStatusRadio.bind(this, Constants.UserStatuses.OFFLINE)}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.offline'
|
||||||
|
defaultMessage='Offline'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
extraInfo = (
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.status_info'
|
||||||
|
defaultMessage='Notification alerts are pushed to your mobile device depending on your online status.'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
inputs.push(
|
inputs.push(
|
||||||
<div key='userNotificationLevelOption'>
|
<div key='userNotificationLevelOption'>
|
||||||
<div className='radio'>
|
<div className='radio'>
|
||||||
@@ -268,7 +355,7 @@ export default class NotificationsTab extends React.Component {
|
|||||||
<input
|
<input
|
||||||
type='radio'
|
type='radio'
|
||||||
name='pushNotificationLevel'
|
name='pushNotificationLevel'
|
||||||
checked={notifyActive[0]}
|
checked={pushActivityRadio[0]}
|
||||||
onChange={this.handlePushRadio.bind(this, 'all')}
|
onChange={this.handlePushRadio.bind(this, 'all')}
|
||||||
/>
|
/>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -283,7 +370,7 @@ export default class NotificationsTab extends React.Component {
|
|||||||
<input
|
<input
|
||||||
type='radio'
|
type='radio'
|
||||||
name='pushNotificationLevel'
|
name='pushNotificationLevel'
|
||||||
checked={notifyActive[1]}
|
checked={pushActivityRadio[1]}
|
||||||
onChange={this.handlePushRadio.bind(this, 'mention')}
|
onChange={this.handlePushRadio.bind(this, 'mention')}
|
||||||
/>
|
/>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -298,7 +385,7 @@ export default class NotificationsTab extends React.Component {
|
|||||||
<input
|
<input
|
||||||
type='radio'
|
type='radio'
|
||||||
name='pushNotificationLevel'
|
name='pushNotificationLevel'
|
||||||
checked={notifyActive[2]}
|
checked={pushActivityRadio[2]}
|
||||||
onChange={this.handlePushRadio.bind(this, 'none')}
|
onChange={this.handlePushRadio.bind(this, 'none')}
|
||||||
/>
|
/>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -307,18 +394,17 @@ export default class NotificationsTab extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<br/>
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.info'
|
||||||
|
defaultMessage='Notification alerts are pushed to your mobile device when there is activity in Mattermost.'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
{pushStatusSettings}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
extraInfo = (
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.push_notification.info'
|
|
||||||
defaultMessage='Notification alerts are pushed to your mobile device when there is activity in Mattermost.'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
submit = this.handleSubmit;
|
submit = this.handleSubmit;
|
||||||
} else {
|
} else {
|
||||||
inputs.push(
|
inputs.push(
|
||||||
@@ -347,14 +433,30 @@ export default class NotificationsTab extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let describe = '';
|
let describe = '';
|
||||||
if (this.state.notifyPushLevel === 'all') {
|
if (this.state.pushActivity === 'all') {
|
||||||
describe = (
|
if (this.state.pushStatus === Constants.UserStatuses.AWAY) {
|
||||||
<FormattedMessage
|
describe = (
|
||||||
id='user.settings.push_notification.allActivity'
|
<FormattedMessage
|
||||||
defaultMessage='For all activity'
|
id='user.settings.push_notification.allActivityAway'
|
||||||
/>
|
defaultMessage='For all activity when away or offline'
|
||||||
);
|
/>
|
||||||
} else if (this.state.notifyPushLevel === 'none') {
|
);
|
||||||
|
} else if (this.state.pushStatus === Constants.UserStatuses.OFFLINE) {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.allActivityOffline'
|
||||||
|
defaultMessage='For all activity when offline'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.allActivityOnline'
|
||||||
|
defaultMessage='For all activity when online, away or offline'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (this.state.pushActivity === 'none') {
|
||||||
describe = (
|
describe = (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='user.settings.notifications.never'
|
id='user.settings.notifications.never'
|
||||||
@@ -369,12 +471,28 @@ export default class NotificationsTab extends React.Component {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
describe = (
|
if (this.state.pushStatus === Constants.UserStatuses.AWAY) { //eslint-disable-line no-lonely-if
|
||||||
<FormattedMessage
|
describe = (
|
||||||
id='user.settings.push_notification.onlyMentions'
|
<FormattedMessage
|
||||||
defaultMessage='For mentions and direct messages'
|
id='user.settings.push_notification.onlyMentionsAway'
|
||||||
/>
|
defaultMessage='For mentions and direct messages when away or offline'
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
} else if (this.state.pushStatus === Constants.UserStatuses.OFFLINE) {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.onlyMentionsOffline'
|
||||||
|
defaultMessage='For mentions and direct messages when offline'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.push_notification.onlyMentionsOnline'
|
||||||
|
defaultMessage='For mentions and direct messages when online, away or offline'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdateDesktopSection = function updateDesktopSection() {
|
handleUpdateDesktopSection = function updateDesktopSection() {
|
||||||
|
|||||||
@@ -1890,12 +1890,20 @@
|
|||||||
"user.settings.notifications.title": "Notification Settings",
|
"user.settings.notifications.title": "Notification Settings",
|
||||||
"user.settings.notifications.usernameMention": "Your username mentioned \"@{username}\"",
|
"user.settings.notifications.usernameMention": "Your username mentioned \"@{username}\"",
|
||||||
"user.settings.notifications.wordsTrigger": "Words that trigger mentions",
|
"user.settings.notifications.wordsTrigger": "Words that trigger mentions",
|
||||||
"user.settings.push_notification.allActivity": "For all activity",
|
"user.settings.push_notification.online": "Online, away or offline",
|
||||||
|
"user.settings.push_notification.away": "Away or offline",
|
||||||
|
"user.settings.push_notification.offline": "Offline",
|
||||||
|
"user.settings.push_notification.status_info": "Notification alerts are pushed to your mobile device depending on your online status.",
|
||||||
|
"user.settings.push_notification.allActivityOnline": "For all activity when online, away or offline",
|
||||||
|
"user.settings.push_notification.allActivityAway": "For all activity when away or offline",
|
||||||
|
"user.settings.push_notification.allActivityOffline": "For all activity when offline",
|
||||||
"user.settings.push_notification.disabled": "Disabled by System Administrator",
|
"user.settings.push_notification.disabled": "Disabled by System Administrator",
|
||||||
"user.settings.push_notification.disabled_long": "Push notifications for mobile devices have been disabled by your System Administrator.",
|
"user.settings.push_notification.disabled_long": "Push notifications for mobile devices have been disabled by your System Administrator.",
|
||||||
"user.settings.push_notification.info": "Notification alerts are pushed to your mobile device when there is activity in Mattermost.",
|
"user.settings.push_notification.info": "Notification alerts are pushed to your mobile device when there is activity in Mattermost.",
|
||||||
"user.settings.push_notification.off": "Off",
|
"user.settings.push_notification.off": "Off",
|
||||||
"user.settings.push_notification.onlyMentions": "For mentions and direct messages",
|
"user.settings.push_notification.onlyMentionsOnline": "For mentions and direct messages when online, away or offline",
|
||||||
|
"user.settings.push_notification.onlyMentionsAway": "For mentions and direct messages when away or offline",
|
||||||
|
"user.settings.push_notification.onlyMentionsOffline": "For mentions and direct messages when offline",
|
||||||
"user.settings.security.close": "Close",
|
"user.settings.security.close": "Close",
|
||||||
"user.settings.security.currentPassword": "Current Password",
|
"user.settings.security.currentPassword": "Current Password",
|
||||||
"user.settings.security.currentPasswordError": "Please enter your current password.",
|
"user.settings.security.currentPasswordError": "Please enter your current password.",
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user