diff --git a/client/src/components/UserSettingsModal/PreferencesPane.jsx b/client/src/components/UserSettingsModal/PreferencesPane.jsx new file mode 100644 index 00000000..1a4d5495 --- /dev/null +++ b/client/src/components/UserSettingsModal/PreferencesPane.jsx @@ -0,0 +1,34 @@ +import React, { useCallback } from 'react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import { Radio, Tab } from 'semantic-ui-react'; + +import styles from './PreferencesPane.module.css'; + +const PreferencesPane = React.memo(({ subscribeToOwnCards, onUpdate }) => { + const [t] = useTranslation(); + + const handleSubscribeToOwnCardsChange = useCallback(() => { + onUpdate({ + subscribeToOwnCards: !subscribeToOwnCards, + }); + }, [subscribeToOwnCards, onUpdate]); + + return ( + + + + ); +}); + +PreferencesPane.propTypes = { + subscribeToOwnCards: PropTypes.bool.isRequired, + onUpdate: PropTypes.func.isRequired, +}; + +export default PreferencesPane; diff --git a/client/src/components/UserSettingsModal/PreferencesPane.module.css b/client/src/components/UserSettingsModal/PreferencesPane.module.css new file mode 100644 index 00000000..cbc2be9f --- /dev/null +++ b/client/src/components/UserSettingsModal/PreferencesPane.module.css @@ -0,0 +1,4 @@ +.wrapper { + border: none !important; + box-shadow: none !important; +} diff --git a/client/src/components/UserSettingsModal/UserSettingsModal.jsx b/client/src/components/UserSettingsModal/UserSettingsModal.jsx index eb1d5ec0..feb44383 100644 --- a/client/src/components/UserSettingsModal/UserSettingsModal.jsx +++ b/client/src/components/UserSettingsModal/UserSettingsModal.jsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { Modal, Tab } from 'semantic-ui-react'; import AccountPane from './AccountPane'; +import PreferencesPane from './PreferencesPane'; const UserSettingsModal = React.memo( ({ @@ -13,6 +14,7 @@ const UserSettingsModal = React.memo( avatar, phone, organization, + subscribeToOwnCards, isAvatarUploading, usernameUpdateForm, emailUpdateForm, @@ -57,12 +59,26 @@ const UserSettingsModal = React.memo( /> ), }, + { + menuItem: t('common.preferences', { + context: 'title', + }), + render: () => ( + + ), + }, ]; return ( - + ); @@ -76,6 +92,7 @@ UserSettingsModal.propTypes = { avatar: PropTypes.string, phone: PropTypes.string, organization: PropTypes.string, + subscribeToOwnCards: PropTypes.bool.isRequired, isAvatarUploading: PropTypes.bool.isRequired, /* eslint-disable react/forbid-prop-types */ usernameUpdateForm: PropTypes.object.isRequired, diff --git a/client/src/containers/UserSettingsModalContainer.js b/client/src/containers/UserSettingsModalContainer.js index 38ebae7b..9480e8a9 100644 --- a/client/src/containers/UserSettingsModalContainer.js +++ b/client/src/containers/UserSettingsModalContainer.js @@ -23,6 +23,7 @@ const mapStateToProps = (state) => { avatar, phone, organization, + subscribeToOwnCards, isAvatarUploading, emailUpdateForm, passwordUpdateForm, @@ -36,6 +37,7 @@ const mapStateToProps = (state) => { avatar, phone, organization, + subscribeToOwnCards, isAvatarUploading, emailUpdateForm, passwordUpdateForm, diff --git a/client/src/locales/en-US/app.js b/client/src/locales/en-US/app.js index 14d39522..b0d8ac62 100644 --- a/client/src/locales/en-US/app.js +++ b/client/src/locales/en-US/app.js @@ -86,12 +86,14 @@ export default { optional_inline: 'optional', organization: 'Organization', phone: 'Phone', + preferences: 'Preferences', projectNotFound_title: 'Project Not Found', refreshPageToLoadLastDataAndReceiveUpdates: '<0>Refresh the page to load last data
and receive updates', removeMember_title: 'Remove Member', seconds: 'Seconds', settings: 'Settings', + subscribeToMyOwnCardsByDefault: 'Subscribe to my own cards by default', taskActions_title: 'Task Actions', tasks: 'Tasks', time: 'Time', diff --git a/client/src/locales/ru-RU/app.js b/client/src/locales/ru-RU/app.js index a2c1258a..3fd9dd19 100644 --- a/client/src/locales/ru-RU/app.js +++ b/client/src/locales/ru-RU/app.js @@ -90,12 +90,14 @@ export default { optional_inline: 'необязательно', organization: 'Организация', phone: 'Телефон', + preferences: 'Предпочтения', projectNotFound: 'Доска не найдена', refreshPageToLoadLastDataAndReceiveUpdates: '<0>Обновите страницу, чтобы загрузить
актуальные данные и получать обновления', removeMember: 'Удаление участника', seconds: 'Секунды', settings: 'Настройки', + subscribeToMyOwnCardsByDefault: 'По умолчанию подписаться на мои собственные карточки', taskActions: 'Действия с задачей', tasks: 'Задачи', time: 'Время', diff --git a/client/src/models/User.js b/client/src/models/User.js index 8a73a289..9ebfe636 100755 --- a/client/src/models/User.js +++ b/client/src/models/User.js @@ -39,6 +39,7 @@ export default class extends Model { avatar: attr(), phone: attr(), organization: attr(), + subscribeToOwnCards: attr(), deletedAt: attr(), isAdmin: attr({ getDefault: () => false, diff --git a/server/api/controllers/users/create.js b/server/api/controllers/users/create.js index f7cd9876..8cdf3ce2 100755 --- a/server/api/controllers/users/create.js +++ b/server/api/controllers/users/create.js @@ -40,6 +40,9 @@ module.exports = { isNotEmptyString: true, allowNull: true, }, + subscribeToOwnCards: { + type: 'boolean', + }, }, exits: { @@ -59,6 +62,7 @@ module.exports = { 'username', 'phone', 'organization', + 'subscribeToOwnCards', ]); const user = await sails.helpers diff --git a/server/api/controllers/users/update.js b/server/api/controllers/users/update.js index d6099f24..3447b709 100755 --- a/server/api/controllers/users/update.js +++ b/server/api/controllers/users/update.js @@ -32,6 +32,9 @@ module.exports = { isNotEmptyString: true, allowNull: true, }, + subscribeToOwnCards: { + type: 'boolean', + }, }, exits: { @@ -57,7 +60,14 @@ module.exports = { throw Errors.USER_NOT_FOUND; } - const values = _.pick(inputs, ['isAdmin', 'name', 'avatar', 'phone', 'organization']); + const values = _.pick(inputs, [ + 'isAdmin', + 'name', + 'avatar', + 'phone', + 'organization', + 'subscribeToOwnCards', + ]); user = await sails.helpers.updateUser(user, values, this.req); diff --git a/server/api/helpers/create-card.js b/server/api/helpers/create-card.js index 435c81f1..800c8c40 100644 --- a/server/api/helpers/create-card.js +++ b/server/api/helpers/create-card.js @@ -49,6 +49,17 @@ module.exports = { boardId: inputs.list.boardId, }).fetch(); + if (inputs.user.subscribeToOwnCards) { + await CardSubscription.create({ + cardId: card.id, + userId: inputs.user.id, + }).tolerate('E_UNIQUE'); + + card.isSubscribed = true; + } else { + card.isSubscribed = false; + } + sails.sockets.broadcast( `board:${card.boardId}`, 'cardCreate', diff --git a/server/api/models/User.js b/server/api/models/User.js index ae16ba29..d73dad16 100755 --- a/server/api/models/User.js +++ b/server/api/models/User.js @@ -52,6 +52,11 @@ module.exports = { isNotEmptyString: true, allowNull: true, }, + subscribeToOwnCards: { + type: 'boolean', + defaultsTo: false, + columnName: 'subscribe_to_own_cards', + }, deletedAt: { type: 'ref', columnName: 'deleted_at', diff --git a/server/db/migrations/20180721220409_create_user_account_table.js b/server/db/migrations/20180721220409_create_user_account_table.js index 57ee757e..edec086b 100755 --- a/server/db/migrations/20180721220409_create_user_account_table.js +++ b/server/db/migrations/20180721220409_create_user_account_table.js @@ -13,6 +13,7 @@ module.exports.up = (knex) => table.text('avatar'); table.text('phone'); table.text('organization'); + table.boolean('subscribe_to_own_cards').notNullable(); table.timestamp('created_at', true); table.timestamp('updated_at', true); diff --git a/server/db/seeds/default.js b/server/db/seeds/default.js index 14d89750..1e52fb23 100644 --- a/server/db/seeds/default.js +++ b/server/db/seeds/default.js @@ -9,6 +9,7 @@ exports.seed = (knex) => { isAdmin: true, name: 'Demo Demo', username: 'demo', + subscribeToOwnCards: false, createdAt: date, updatedAt: date, });