1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-19 13:19:44 +02:00

Add preferences tab to user settings, add subscribe to own cards option

This commit is contained in:
Maksim Eltyshev 2020-04-10 00:11:34 +05:00
parent 9b4e3931a9
commit 88314e826d
13 changed files with 96 additions and 2 deletions

View file

@ -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 (
<Tab.Pane attached={false} className={styles.wrapper}>
<Radio
toggle
checked={subscribeToOwnCards}
label={t('common.subscribeToMyOwnCardsByDefault')}
onChange={handleSubscribeToOwnCardsChange}
/>
</Tab.Pane>
);
});
PreferencesPane.propTypes = {
subscribeToOwnCards: PropTypes.bool.isRequired,
onUpdate: PropTypes.func.isRequired,
};
export default PreferencesPane;

View file

@ -0,0 +1,4 @@
.wrapper {
border: none !important;
box-shadow: none !important;
}

View file

@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { Modal, Tab } from 'semantic-ui-react'; import { Modal, Tab } from 'semantic-ui-react';
import AccountPane from './AccountPane'; import AccountPane from './AccountPane';
import PreferencesPane from './PreferencesPane';
const UserSettingsModal = React.memo( const UserSettingsModal = React.memo(
({ ({
@ -13,6 +14,7 @@ const UserSettingsModal = React.memo(
avatar, avatar,
phone, phone,
organization, organization,
subscribeToOwnCards,
isAvatarUploading, isAvatarUploading,
usernameUpdateForm, usernameUpdateForm,
emailUpdateForm, emailUpdateForm,
@ -57,12 +59,26 @@ const UserSettingsModal = React.memo(
/> />
), ),
}, },
{
menuItem: t('common.preferences', {
context: 'title',
}),
render: () => (
<PreferencesPane subscribeToOwnCards={subscribeToOwnCards} onUpdate={onUpdate} />
),
},
]; ];
return ( return (
<Modal open closeIcon size="small" centered={false} onClose={onClose}> <Modal open closeIcon size="small" centered={false} onClose={onClose}>
<Modal.Content> <Modal.Content>
<Tab menu={{ secondary: true, pointing: true }} panes={panes} /> <Tab
menu={{
secondary: true,
pointing: true,
}}
panes={panes}
/>
</Modal.Content> </Modal.Content>
</Modal> </Modal>
); );
@ -76,6 +92,7 @@ UserSettingsModal.propTypes = {
avatar: PropTypes.string, avatar: PropTypes.string,
phone: PropTypes.string, phone: PropTypes.string,
organization: PropTypes.string, organization: PropTypes.string,
subscribeToOwnCards: PropTypes.bool.isRequired,
isAvatarUploading: PropTypes.bool.isRequired, isAvatarUploading: PropTypes.bool.isRequired,
/* eslint-disable react/forbid-prop-types */ /* eslint-disable react/forbid-prop-types */
usernameUpdateForm: PropTypes.object.isRequired, usernameUpdateForm: PropTypes.object.isRequired,

View file

@ -23,6 +23,7 @@ const mapStateToProps = (state) => {
avatar, avatar,
phone, phone,
organization, organization,
subscribeToOwnCards,
isAvatarUploading, isAvatarUploading,
emailUpdateForm, emailUpdateForm,
passwordUpdateForm, passwordUpdateForm,
@ -36,6 +37,7 @@ const mapStateToProps = (state) => {
avatar, avatar,
phone, phone,
organization, organization,
subscribeToOwnCards,
isAvatarUploading, isAvatarUploading,
emailUpdateForm, emailUpdateForm,
passwordUpdateForm, passwordUpdateForm,

View file

@ -86,12 +86,14 @@ export default {
optional_inline: 'optional', optional_inline: 'optional',
organization: 'Organization', organization: 'Organization',
phone: 'Phone', phone: 'Phone',
preferences: 'Preferences',
projectNotFound_title: 'Project Not Found', projectNotFound_title: 'Project Not Found',
refreshPageToLoadLastDataAndReceiveUpdates: refreshPageToLoadLastDataAndReceiveUpdates:
'<0>Refresh the page</0> to load last data<br />and receive updates', '<0>Refresh the page</0> to load last data<br />and receive updates',
removeMember_title: 'Remove Member', removeMember_title: 'Remove Member',
seconds: 'Seconds', seconds: 'Seconds',
settings: 'Settings', settings: 'Settings',
subscribeToMyOwnCardsByDefault: 'Subscribe to my own cards by default',
taskActions_title: 'Task Actions', taskActions_title: 'Task Actions',
tasks: 'Tasks', tasks: 'Tasks',
time: 'Time', time: 'Time',

View file

@ -90,12 +90,14 @@ export default {
optional_inline: 'необязательно', optional_inline: 'необязательно',
organization: 'Организация', organization: 'Организация',
phone: 'Телефон', phone: 'Телефон',
preferences: 'Предпочтения',
projectNotFound: 'Доска не найдена', projectNotFound: 'Доска не найдена',
refreshPageToLoadLastDataAndReceiveUpdates: refreshPageToLoadLastDataAndReceiveUpdates:
'<0>Обновите страницу</0>, чтобы загрузить<br />актуальные данные и получать обновления', '<0>Обновите страницу</0>, чтобы загрузить<br />актуальные данные и получать обновления',
removeMember: 'Удаление участника', removeMember: 'Удаление участника',
seconds: 'Секунды', seconds: 'Секунды',
settings: 'Настройки', settings: 'Настройки',
subscribeToMyOwnCardsByDefault: 'По умолчанию подписаться на мои собственные карточки',
taskActions: 'Действия с задачей', taskActions: 'Действия с задачей',
tasks: 'Задачи', tasks: 'Задачи',
time: 'Время', time: 'Время',

View file

@ -39,6 +39,7 @@ export default class extends Model {
avatar: attr(), avatar: attr(),
phone: attr(), phone: attr(),
organization: attr(), organization: attr(),
subscribeToOwnCards: attr(),
deletedAt: attr(), deletedAt: attr(),
isAdmin: attr({ isAdmin: attr({
getDefault: () => false, getDefault: () => false,

View file

@ -40,6 +40,9 @@ module.exports = {
isNotEmptyString: true, isNotEmptyString: true,
allowNull: true, allowNull: true,
}, },
subscribeToOwnCards: {
type: 'boolean',
},
}, },
exits: { exits: {
@ -59,6 +62,7 @@ module.exports = {
'username', 'username',
'phone', 'phone',
'organization', 'organization',
'subscribeToOwnCards',
]); ]);
const user = await sails.helpers const user = await sails.helpers

View file

@ -32,6 +32,9 @@ module.exports = {
isNotEmptyString: true, isNotEmptyString: true,
allowNull: true, allowNull: true,
}, },
subscribeToOwnCards: {
type: 'boolean',
},
}, },
exits: { exits: {
@ -57,7 +60,14 @@ module.exports = {
throw Errors.USER_NOT_FOUND; 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); user = await sails.helpers.updateUser(user, values, this.req);

View file

@ -49,6 +49,17 @@ module.exports = {
boardId: inputs.list.boardId, boardId: inputs.list.boardId,
}).fetch(); }).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( sails.sockets.broadcast(
`board:${card.boardId}`, `board:${card.boardId}`,
'cardCreate', 'cardCreate',

View file

@ -52,6 +52,11 @@ module.exports = {
isNotEmptyString: true, isNotEmptyString: true,
allowNull: true, allowNull: true,
}, },
subscribeToOwnCards: {
type: 'boolean',
defaultsTo: false,
columnName: 'subscribe_to_own_cards',
},
deletedAt: { deletedAt: {
type: 'ref', type: 'ref',
columnName: 'deleted_at', columnName: 'deleted_at',

View file

@ -13,6 +13,7 @@ module.exports.up = (knex) =>
table.text('avatar'); table.text('avatar');
table.text('phone'); table.text('phone');
table.text('organization'); table.text('organization');
table.boolean('subscribe_to_own_cards').notNullable();
table.timestamp('created_at', true); table.timestamp('created_at', true);
table.timestamp('updated_at', true); table.timestamp('updated_at', true);

View file

@ -9,6 +9,7 @@ exports.seed = (knex) => {
isAdmin: true, isAdmin: true,
name: 'Demo Demo', name: 'Demo Demo',
username: 'demo', username: 'demo',
subscribeToOwnCards: false,
createdAt: date, createdAt: date,
updatedAt: date, updatedAt: date,
}); });