[Glitch] Add option to be notified when a followed user posts
Port 974b1b79ce
to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
pull/1430/head
parent
0a069bffd9
commit
14869ee656
|
@ -126,14 +126,14 @@ export function fetchAccountFail(id, error) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function followAccount(id, reblogs = true) {
|
export function followAccount(id, options = { reblogs: true }) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const alreadyFollowing = getState().getIn(['relationships', id, 'following']);
|
const alreadyFollowing = getState().getIn(['relationships', id, 'following']);
|
||||||
const locked = getState().getIn(['accounts', id, 'locked'], false);
|
const locked = getState().getIn(['accounts', id, 'locked'], false);
|
||||||
|
|
||||||
dispatch(followAccountRequest(id, locked));
|
dispatch(followAccountRequest(id, locked));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then(response => {
|
api(getState).post(`/api/v1/accounts/${id}/follow`, options).then(response => {
|
||||||
dispatch(followAccountSuccess(response.data, alreadyFollowing));
|
dispatch(followAccountSuccess(response.data, alreadyFollowing));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(followAccountFail(error, locked));
|
dispatch(followAccountFail(error, locked));
|
||||||
|
|
|
@ -73,7 +73,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
|
||||||
|
|
||||||
let filtered = false;
|
let filtered = false;
|
||||||
|
|
||||||
if (notification.type === 'mention') {
|
if (['mention', 'status'].includes(notification.type)) {
|
||||||
const dropRegex = filters[0];
|
const dropRegex = filters[0];
|
||||||
const regex = filters[1];
|
const regex = filters[1];
|
||||||
const searchIndex = searchTextFromRawStatus(notification.status);
|
const searchIndex = searchTextFromRawStatus(notification.status);
|
||||||
|
|
|
@ -680,6 +680,7 @@ class Status extends ImmutablePureComponent {
|
||||||
favourite: 'favourited',
|
favourite: 'favourited',
|
||||||
reblog: 'boosted',
|
reblog: 'boosted',
|
||||||
reblogged_by: 'boosted',
|
reblogged_by: 'boosted',
|
||||||
|
status: 'posted',
|
||||||
}[prepend];
|
}[prepend];
|
||||||
|
|
||||||
selectorAttribs[`data-${notifKind}-by`] = `@${account.get('acct')}`;
|
selectorAttribs[`data-${notifKind}-by`] = `@${account.get('acct')}`;
|
||||||
|
|
|
@ -64,6 +64,14 @@ export default class StatusPrepend extends React.PureComponent {
|
||||||
values={{ name : link }}
|
values={{ name : link }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case 'status':
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='notification.status'
|
||||||
|
defaultMessage='{name} just posted'
|
||||||
|
values={{ name: link }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case 'poll':
|
case 'poll':
|
||||||
if (me === account.get('id')) {
|
if (me === account.get('id')) {
|
||||||
return (
|
return (
|
||||||
|
@ -88,12 +96,32 @@ export default class StatusPrepend extends React.PureComponent {
|
||||||
const { Message } = this;
|
const { Message } = this;
|
||||||
const { type } = this.props;
|
const { type } = this.props;
|
||||||
|
|
||||||
|
let iconId;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case 'favourite':
|
||||||
|
iconId = 'star';
|
||||||
|
break;
|
||||||
|
case 'featured':
|
||||||
|
iconId = 'thumb-tack';
|
||||||
|
break;
|
||||||
|
case 'poll':
|
||||||
|
iconId = 'tasks';
|
||||||
|
break;
|
||||||
|
case 'reblogged_by':
|
||||||
|
iconId = 'retweet';
|
||||||
|
break;
|
||||||
|
case 'status':
|
||||||
|
iconId = 'bell';
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
return !type ? null : (
|
return !type ? null : (
|
||||||
<aside className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend' : 'notification__message'}>
|
<aside className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend' : 'notification__message'}>
|
||||||
<div className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}>
|
<div className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}>
|
||||||
<Icon
|
<Icon
|
||||||
className={`status__prepend-icon ${type === 'favourite' ? 'star-icon' : ''}`}
|
className={`status__prepend-icon ${type === 'favourite' ? 'star-icon' : ''}`}
|
||||||
id={type === 'favourite' ? 'star' : (type === 'featured' ? 'thumb-tack' : (type === 'poll' ? 'tasks' : 'retweet'))}
|
id={iconId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Message />
|
<Message />
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { autoPlayGif, me, isStaff } from 'flavours/glitch/util/initial_state';
|
||||||
import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/util/backend_links';
|
import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/util/backend_links';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icon from 'flavours/glitch/components/icon';
|
import Icon from 'flavours/glitch/components/icon';
|
||||||
|
import IconButton from 'flavours/glitch/components/icon_button';
|
||||||
import Avatar from 'flavours/glitch/components/avatar';
|
import Avatar from 'flavours/glitch/components/avatar';
|
||||||
import Button from 'flavours/glitch/components/button';
|
import Button from 'flavours/glitch/components/button';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
@ -34,6 +35,8 @@ const messages = defineMessages({
|
||||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||||
hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
|
hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
|
||||||
showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
|
showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
|
||||||
|
enableNotifications: { id: 'account.enable_notifications', defaultMessage: 'Notify me when @{name} posts' },
|
||||||
|
disableNotifications: { id: 'account.disable_notifications', defaultMessage: 'Stop notifying me when @{name} posts' },
|
||||||
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
|
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||||
|
@ -68,8 +71,9 @@ class Header extends ImmutablePureComponent {
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention: PropTypes.func.isRequired,
|
||||||
onDirect: PropTypes.func.isRequired,
|
onDirect: PropTypes.func.isRequired,
|
||||||
onReport: PropTypes.func.isRequired,
|
|
||||||
onReblogToggle: PropTypes.func.isRequired,
|
onReblogToggle: PropTypes.func.isRequired,
|
||||||
|
onNotifyToggle: PropTypes.func.isRequired,
|
||||||
|
onReport: PropTypes.func.isRequired,
|
||||||
onMute: PropTypes.func.isRequired,
|
onMute: PropTypes.func.isRequired,
|
||||||
onBlockDomain: PropTypes.func.isRequired,
|
onBlockDomain: PropTypes.func.isRequired,
|
||||||
onUnblockDomain: PropTypes.func.isRequired,
|
onUnblockDomain: PropTypes.func.isRequired,
|
||||||
|
@ -138,6 +142,7 @@ class Header extends ImmutablePureComponent {
|
||||||
|
|
||||||
let info = [];
|
let info = [];
|
||||||
let actionBtn = '';
|
let actionBtn = '';
|
||||||
|
let bellBtn = '';
|
||||||
let lockedIcon = '';
|
let lockedIcon = '';
|
||||||
let menu = [];
|
let menu = [];
|
||||||
|
|
||||||
|
@ -168,6 +173,10 @@ class Header extends ImmutablePureComponent {
|
||||||
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.edit_profile)} onClick={this.openEditProfile} />;
|
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.edit_profile)} onClick={this.openEditProfile} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
|
||||||
|
bellBtn = <IconButton icon='bell-o' size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
|
if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
|
||||||
actionBtn = '';
|
actionBtn = '';
|
||||||
}
|
}
|
||||||
|
@ -289,6 +298,7 @@ class Header extends ImmutablePureComponent {
|
||||||
|
|
||||||
<div className='account__header__tabs__buttons'>
|
<div className='account__header__tabs__buttons'>
|
||||||
{actionBtn}
|
{actionBtn}
|
||||||
|
{bellBtn}
|
||||||
|
|
||||||
<DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' />
|
<DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -56,6 +56,10 @@ export default class Header extends ImmutablePureComponent {
|
||||||
this.props.onReblogToggle(this.props.account);
|
this.props.onReblogToggle(this.props.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNotifyToggle = () => {
|
||||||
|
this.props.onNotifyToggle(this.props.account);
|
||||||
|
}
|
||||||
|
|
||||||
handleMute = () => {
|
handleMute = () => {
|
||||||
this.props.onMute(this.props.account);
|
this.props.onMute(this.props.account);
|
||||||
}
|
}
|
||||||
|
@ -107,6 +111,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onMention={this.handleMention}
|
onMention={this.handleMention}
|
||||||
onDirect={this.handleDirect}
|
onDirect={this.handleDirect}
|
||||||
onReblogToggle={this.handleReblogToggle}
|
onReblogToggle={this.handleReblogToggle}
|
||||||
|
onNotifyToggle={this.handleNotifyToggle}
|
||||||
onReport={this.handleReport}
|
onReport={this.handleReport}
|
||||||
onMute={this.handleMute}
|
onMute={this.handleMute}
|
||||||
onBlockDomain={this.handleBlockDomain}
|
onBlockDomain={this.handleBlockDomain}
|
||||||
|
|
|
@ -81,9 +81,9 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
|
|
||||||
onReblogToggle (account) {
|
onReblogToggle (account) {
|
||||||
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
||||||
dispatch(followAccount(account.get('id'), false));
|
dispatch(followAccount(account.get('id'), { reblogs: false }));
|
||||||
} else {
|
} else {
|
||||||
dispatch(followAccount(account.get('id'), true));
|
dispatch(followAccount(account.get('id'), { reblogs: true }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -95,6 +95,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onNotifyToggle (account) {
|
||||||
|
if (account.getIn(['relationship', 'notifying'])) {
|
||||||
|
dispatch(followAccount(account.get('id'), { notify: false }));
|
||||||
|
} else {
|
||||||
|
dispatch(followAccount(account.get('id'), { notify: true }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onReport (account) {
|
onReport (account) {
|
||||||
dispatch(initReport(account));
|
dispatch(initReport(account));
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,7 @@ const tooltips = defineMessages({
|
||||||
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
||||||
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
||||||
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||||
|
statuses: { id: 'notifications.filter.statuses', defaultMessage: 'Updates from people you follow' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @injectIntl
|
export default @injectIntl
|
||||||
|
@ -87,6 +88,13 @@ class FilterBar extends React.PureComponent {
|
||||||
>
|
>
|
||||||
<Icon id='tasks' fixedWidth />
|
<Icon id='tasks' fixedWidth />
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'status' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('status')}
|
||||||
|
title={intl.formatMessage(tooltips.statuses)}
|
||||||
|
>
|
||||||
|
<Icon id='home' fixedWidth />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className={selectedFilter === 'follow' ? 'active' : ''}
|
className={selectedFilter === 'follow' ? 'active' : ''}
|
||||||
onClick={this.onClick('follow')}
|
onClick={this.onClick('follow')}
|
||||||
|
|
|
@ -83,6 +83,28 @@ export default class Notification extends ImmutablePureComponent {
|
||||||
unread={this.props.unread}
|
unread={this.props.unread}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case 'status':
|
||||||
|
return (
|
||||||
|
<StatusContainer
|
||||||
|
containerId={notification.get('id')}
|
||||||
|
hidden={hidden}
|
||||||
|
id={notification.get('status')}
|
||||||
|
account={notification.get('account')}
|
||||||
|
prepend='status'
|
||||||
|
muted
|
||||||
|
notification={notification}
|
||||||
|
onMoveDown={onMoveDown}
|
||||||
|
onMoveUp={onMoveUp}
|
||||||
|
onMention={onMention}
|
||||||
|
getScrollPosition={getScrollPosition}
|
||||||
|
updateScrollBottom={updateScrollBottom}
|
||||||
|
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||||
|
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||||
|
onUnmount={this.props.onUnmount}
|
||||||
|
withDismiss
|
||||||
|
unread={this.props.unread}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case 'favourite':
|
case 'favourite':
|
||||||
return (
|
return (
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
|
|
|
@ -620,6 +620,10 @@
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .icon-button {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
margin: 0 8px;
|
margin: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue