This commit is contained in:
mgabdev 2020-04-09 15:18:14 -04:00
parent 2b61a7c067
commit 7249143d9f
22 changed files with 166 additions and 440 deletions

View File

@ -7,27 +7,26 @@ import {
importFetchedStatus,
importFetchedStatuses,
} from './importer';
import { saveSettings } from './settings';
import { defineMessages } from 'react-intl';
import { List as ImmutableList } from 'immutable';
import { unescapeHTML } from '../utils/html';
import { getFilters, regexFromFilters } from '../selectors';
import { me } from '../initial_state';
export const NOTIFICATIONS_INITIALIZE = 'NOTIFICATIONS_INITIALIZE';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_INITIALIZE = 'NOTIFICATIONS_INITIALIZE';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE';
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE';
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE';
export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
export const NOTIFICATIONS_MARK_READ = 'NOTIFICATIONS_MARK_READ';
export const NOTIFICATIONS_MARK_READ = 'NOTIFICATIONS_MARK_READ';
export const MAX_QUEUED_NOTIFICATIONS = 40
@ -52,7 +51,7 @@ export function initializeNotifications() {
export function updateNotifications(notification, intlMessages, intlLocale) {
return (dispatch, getState) => {
const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
const showInColumn = getState().getIn(['notifications', 'filter', notification.type], true);
if (showInColumn) {
dispatch(importFetchedAccount(notification.account));
@ -73,7 +72,8 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
export function updateNotificationsQueue(notification, intlMessages, intlLocale, curPath) {
return (dispatch, getState) => {
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
// : todo :
// const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
const filters = getFilters(getState(), { contextType: 'notifications' });
let filtered = false;
@ -87,17 +87,18 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale,
}
// Desktop notifications
if (typeof window.Notification !== 'undefined' && showAlert && !filtered) {
const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
// : todo :
// if (typeof window.Notification !== 'undefined' && showAlert && !filtered) {
// const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
// const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
// const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
notify.addEventListener('click', () => {
window.focus();
notify.close();
});
}
// notify.addEventListener('click', () => {
// window.focus();
// notify.close();
// });
// }
if (isOnNotificationsPage) {
dispatch({
@ -134,22 +135,22 @@ export function dequeueNotifications() {
}
};
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
const excludeTypesFromFilter = filter => {
const allTypes = ImmutableList(['follow', 'favourite', 'reblog', 'mention', 'poll']);
return allTypes.filterNot(item => item === filter).toJS();
};
const noOp = () => {};
const noOp = () => { };
export function expandNotifications({ maxId } = {}, done = noOp) {
return (dispatch, getState) => {
if (!me) return;
if (!me) return
const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
const notifications = getState().get('notifications');
const isLoadingMore = !!maxId;
const onlyVerified = getState().getIn(['notifications', 'filter', 'onlyVerified'])
const onlyFollowing = getState().getIn(['notifications', 'filter', 'onlyFollowing'])
const activeFilter = getState().getIn(['notifications', 'filter', 'active'])
const notifications = getState().get('notifications')
const isLoadingMore = !!maxId
if (notifications.get('isLoading')) {
done();
@ -157,16 +158,16 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
}
console.log('activeFilter:', activeFilter)
console.log('excludeTypesFromSettings(getState()):', excludeTypesFromSettings(getState()))
// console.log('excludeTypesFromSettings(getState()):', excludeTypesFromSettings(getState()))
console.log('excludeTypesFromFilter(activeFilter):', excludeTypesFromFilter(activeFilter))
// : todo :
// filter verified and following here too
const params = {
max_id: maxId,
exclude_types: activeFilter === 'all'
? excludeTypesFromSettings(getState())
: excludeTypesFromFilter(activeFilter),
only_verified: onlyVerified,
only_following: onlyFollowing,
exclude_types: activeFilter === 'all' ? [] : excludeTypesFromFilter(activeFilter),
};
if (!maxId && notifications.get('items').size > 0) {
@ -237,17 +238,16 @@ export function scrollTopNotifications(top) {
}
};
export function setFilter (filterType) {
export function setFilter(path, value) {
return dispatch => {
dispatch({
type: NOTIFICATIONS_FILTER_SET,
path: ['notifications', 'quickFilter', 'active'],
value: filterType,
});
dispatch(expandNotifications());
dispatch(saveSettings());
};
};
path: path,
value: value,
})
dispatch(expandNotifications())
}
}
export function markReadNotifications() {
return (dispatch, getState) => {

View File

@ -1,10 +1,9 @@
import api from '../api';
import debounce from 'lodash.debounce';
// import { showAlertForError } from './alerts';
import { me } from '../initial_state';
import api from '../api'
import debounce from 'lodash.debounce'
import { me } from '../initial_state'
export const SETTING_CHANGE = 'SETTING_CHANGE';
export const SETTING_SAVE = 'SETTING_SAVE';
export const SETTING_CHANGE = 'SETTING_CHANGE'
export const SETTING_SAVE = 'SETTING_SAVE'
export function changeSetting(path, value) {
return dispatch => {
@ -12,26 +11,24 @@ export function changeSetting(path, value) {
type: SETTING_CHANGE,
path,
value,
});
})
dispatch(saveSettings());
};
};
dispatch(saveSettings())
}
}
const debouncedSave = debounce((dispatch, getState) => {
if (!me) return;
if (!me) return
if (getState().getIn(['settings', 'saved'])) {
return;
}
if (getState().getIn(['settings', 'saved'])) return
const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS();
const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS()
api().put('/api/web/settings', { data })
.then(() => dispatch({ type: SETTING_SAVE }))
// .catch(error => dispatch(showAlertForError(error)));
}, 5000, { trailing: true });
.catch(() => { /* */ })
}, 5000, { trailing: true })
export function saveSettings() {
return (dispatch, getState) => debouncedSave(dispatch, getState);
};
return (dispatch, getState) => debouncedSave(dispatch, getState)
}

View File

@ -0,0 +1,26 @@
const PencilIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 64 64',
title = 'Pencil',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d='M 22.609375 56.027344 L 8.085938 41.503906 L 43.214844 6.371094 L 57.742188 20.898438 Z M 6.726562 44.0625 L 20.050781 57.386719 L 0.0742188 64.039062 Z M 62.207031 16.449219 L 59.6875 18.96875 L 45.144531 4.421875 L 47.664062 1.90625 C 50.203125 -0.636719 54.316406 -0.636719 56.855469 1.90625 L 62.207031 7.257812 C 64.726562 9.804688 64.726562 13.902344 62.207031 16.449219 Z M 62.207031 16.449219' />
</g>
</svg>
)
export default PencilIcon

View File

@ -1,4 +1,3 @@
import Icon from './icon'
import Button from './button'
export default class FloatingActionButton extends PureComponent {
@ -21,13 +20,12 @@ export default class FloatingActionButton extends PureComponent {
<Button
onClick={onClick}
color='white'
className={[_s.positionFixed, _s.z4, _s.bottom0, _s.right0].join(' ')}
backgroundColor='brand'
className={[_s.positionFixed, _s.z4, _s.py15, _s.mb15, _s.mr15, _s.bottom0, _s.right0].join(' ')}
title={message}
aria-label={message}
>
<Icon id='compose' />
[...]
</Button>
icon='pencil'
/>
)
}
}

View File

@ -44,6 +44,7 @@ import MoreIcon from '../assets/more_icon'
import NotificationsIcon from '../assets/notifications_icon'
import OLListIcon from '../assets/ol_list_icon'
import PauseIcon from '../assets/pause_icon'
import PencilIcon from '../assets/pencil_icon'
import PinIcon from '../assets/pin_icon'
import PlayIcon from '../assets/play_icon'
import PollIcon from '../assets/poll_icon'
@ -111,6 +112,7 @@ const ICONS = {
'notifications': NotificationsIcon,
'ol-list': OLListIcon,
'pause': PauseIcon,
'pencil': PencilIcon,
'pin': PinIcon,
'play': PlayIcon,
'poll': PollIcon,

View File

@ -1,7 +1,7 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { changeSetting, saveSettings } from '../../actions/settings'
import { setFilter } from '../../actions/notifications'
import PanelLayout from './panel_layout'
import SettingSwitch from '../setting_switch'
@ -12,14 +12,13 @@ const messages = defineMessages({
})
const mapStateToProps = state => ({
settings: state.getIn(['settings', 'notifications']),
settings: state.getIn(['notifications', 'filter']),
})
const mapDispatchToProps = (dispatch) => {
return {
onChange(key, checked) {
dispatch(changeSetting(['notifications', ...key], checked))
dispatch(saveSettings())
onChange(path, value) {
dispatch(setFilter(path, value))
},
}
}
@ -43,7 +42,7 @@ class NotificationFilterPanel extends ImmutablePureComponent {
<SettingSwitch
prefix='notification'
settings={settings}
settingPath={['quickFilter', 'onlyVerifed']}
settingPath={'onlyVerified'}
onChange={onChange}
label={intl.formatMessage(messages.onlyVerified)}
/>
@ -51,7 +50,7 @@ class NotificationFilterPanel extends ImmutablePureComponent {
<SettingSwitch
prefix='notification'
settings={settings}
settingPath={['quickFilter', 'onlyFollowing']}
settingPath={'onlyFollowing'}
onChange={onChange}
label={intl.formatMessage(messages.onlyFollowing)}
/>

View File

@ -7,7 +7,10 @@ export default class SettingSwitch extends ImmutablePureComponent {
static propTypes = {
prefix: PropTypes.string,
settings: ImmutablePropTypes.map.isRequired,
settingPath: PropTypes.array.isRequired,
settingPath: PropTypes.oneOfType([
PropTypes.array,
PropTypes.string,
]).isRequired,
description: PropTypes.string,
label: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
@ -26,14 +29,17 @@ export default class SettingSwitch extends ImmutablePureComponent {
description
} = this.props
const id = ['setting-toggle', prefix, ...settingPath].filter(Boolean).join('-')
const isArray = Array.isArray(settingPath)
const checked = isArray ? settings.getIn(settingPath) : settings.get(settingPath)
const idVal = isArray ? settingPath.join('-') : settingPath
const id = ['setting-toggle', prefix, idVal].filter(Boolean).join('-')
return (
<Switch
description={description}
label={label}
id={id}
checked={settings.getIn(settingPath)}
checked={checked}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
/>

View File

@ -1,12 +1,14 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { injectIntl, defineMessages } from 'react-intl'
import * as Constants from '../constants'
import Button from './button'
import { closeSidebar } from '../actions/sidebar'
import { openModal } from '../actions/modal'
import { openPopover } from '../actions/popover'
import { me } from '../initial_state'
import { makeGetAccount } from '../selectors'
import Responsive from '../features/ui/util/responsive_component'
import SidebarSectionTitle from './sidebar_section_title'
import SidebarSectionItem from './sidebar_section_item'
import SidebarHeader from './sidebar_header'
@ -197,7 +199,7 @@ class Sidebar extends ImmutablePureComponent {
<header role='banner' className={[_s.default, _s.flexGrow1, _s.z3, _s.alignItemsEnd].join(' ')}>
<div className={[_s.default, _s.width240PX].join(' ')}>
<div className={[_s.default, _s.positionFixed, _s.top0, _s.height100PC].join(' ')}>
<div className={[_s.default, _s.height100PC, _s.width240PX, _s.pr15, _s.py10, _s.overflowYScroll].join(' ')}>
<div className={[_s.default, _s.height100PC, _s.alignItemsStart, _s.width240PX, _s.pr15, _s.py10, _s.overflowYScroll].join(' ')}>
<SidebarHeader />
@ -226,13 +228,23 @@ class Sidebar extends ImmutablePureComponent {
}
</nav>
<Button
block
onClick={this.handleOpenComposeModal}
className={[_s.py15, _s.fontSize15PX, _s.fontWeightBold].join(' ')}
>
Gab
</Button>
<Responsive min={Constants.BREAKPOINT_SMALL}>
<Button
block
onClick={this.handleOpenComposeModal}
className={[_s.py15, _s.fontSize15PX, _s.fontWeightBold].join(' ')}
>
Gab
</Button>
</Responsive>
<Responsive max={Constants.BREAKPOINT_SMALL}>
<Button
onClick={this.handleOpenComposeModal}
className={_s.py15}
icon='pencil'
/>
</Responsive>
</div>
</div>

View File

@ -387,6 +387,7 @@ class Status extends ImmutablePureComponent {
const containerClasses = cx({
default: 1,
radiusSmall: !borderless && !isChild,
mb15: !borderless && !isChild,
backgroundColorPrimary: 1,
pb15: featured,

View File

@ -57,6 +57,7 @@ class TabBarItem extends PureComponent {
justifyContentCenter: 1,
borderBottom2PX: 1,
py5: 1,
outlineNone: 1,
cursorPointer: 1,
backgroundTransparent: 1,
borderColorTransparent: !isActive,

View File

@ -3,12 +3,12 @@ import { injectIntl, defineMessages } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { HotKeys } from 'react-hotkeys'
import ImmutablePropTypes from 'react-immutable-proptypes'
import StatusContainer from '../../../../containers/status_container'
import AccountContainer from '../../../../containers/account_container'
import Avatar from '../../../../components/avatar'
import Icon from '../../../../components/icon'
import Text from '../../../../components/text'
import DisplayName from '../../../../components/display_name'
import StatusContainer from '../../../containers/status_container'
import AccountContainer from '../../../containers/account_container'
import Avatar from '../../../components/avatar'
import Icon from '../../../components/icon'
import Text from '../../../components/text'
import DisplayName from '../../../components/display_name'
const messages = defineMessages({
poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },

View File

@ -1 +0,0 @@
export { default } from './notification'

View File

@ -1,274 +0,0 @@
import { injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { HotKeys } from 'react-hotkeys';
import ImmutablePropTypes from 'react-immutable-proptypes';
import StatusContainer from '../../../../containers/status_container';
import AccountContainer from '../../../../containers/account_container';
import Button from '../../../../components/button'
import Icon from '../../../../components/icon';
const notificationForScreenReader = (intl, message, timestamp) => {
const output = [message];
output.push(intl.formatDate(timestamp, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }));
return output.join(', ');
};
export default
@injectIntl
class Notification extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
notification: ImmutablePropTypes.map.isRequired,
hidden: PropTypes.bool,
onMoveUp: PropTypes.func.isRequired,
onMoveDown: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired,
onFavorite: PropTypes.func.isRequired,
onRepost: PropTypes.func.isRequired,
onToggleHidden: PropTypes.func.isRequired,
status: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
getScrollPosition: PropTypes.func,
updateScrollBottom: PropTypes.func,
cacheMediaWidth: PropTypes.func,
cachedMediaWidth: PropTypes.number,
};
handleMoveUp = () => {
const { notification, onMoveUp } = this.props;
onMoveUp(notification.get('id'));
}
handleMoveDown = () => {
const { notification, onMoveDown } = this.props;
onMoveDown(notification.get('id'));
}
handleOpen = () => {
const { notification } = this.props;
if (notification.get('status')) {
this.context.router.history.push(`/${notification.getIn(['account', 'acct'])}/posts/${notification.get('status')}`);
} else {
this.handleOpenProfile();
}
}
handleOpenProfile = () => {
const { notification } = this.props;
this.context.router.history.push(`/${notification.getIn(['account', 'acct'])}`);
}
handleMention = e => {
e.preventDefault();
const { notification, onMention } = this.props;
onMention(notification.get('account'), this.context.router.history);
}
handleHotkeyFavorite = () => {
const { status } = this.props;
if (status) this.props.onFavorite(status);
}
handleHotkeyBoost = e => {
const { status } = this.props;
if (status) this.props.onRepost(status, e);
}
handleHotkeyToggleHidden = () => {
const { status } = this.props;
if (status) this.props.onToggleHidden(status);
}
getHandlers() {
return {
reply: this.handleMention,
favorite: this.handleHotkeyFavorite,
boost: this.handleHotkeyBoost,
mention: this.handleMention,
open: this.handleOpen,
openProfile: this.handleOpenProfile,
moveUp: this.handleMoveUp,
moveDown: this.handleMoveDown,
toggleHidden: this.handleHotkeyToggleHidden,
};
}
renderFollow(notification, account, link) {
const { intl } = this.props;
return (
<HotKeys handlers={this.getHandlers()}>
<div className='notification notification--follow focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow', defaultMessage: '{name} followed you' }, { name: account.get('acct') }), notification.get('created_at'))}>
<div className='notification__message'>
<div className='notification__favorite-icon-wrapper'>
<Icon id='user-plus' fixedWidth />
</div>
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} />
</span>
</div>
<AccountContainer id={account.get('id')} hidden={this.props.hidden} />
</div>
</HotKeys>
);
}
renderMention(notification) {
return (
<StatusContainer
id={notification.get('status')}
withDismiss
hidden={this.props.hidden}
onMoveDown={this.handleMoveDown}
onMoveUp={this.handleMoveUp}
contextType='notifications'
getScrollPosition={this.props.getScrollPosition}
updateScrollBottom={this.props.updateScrollBottom}
cachedMediaWidth={this.props.cachedMediaWidth}
cacheMediaWidth={this.props.cacheMediaWidth}
/>
);
}
renderFavorite(notification, link) {
const { intl } = this.props;
return (
<HotKeys handlers={this.getHandlers()}>
<div className='notification notification--favorite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.favorite', defaultMessage: '{name} favorited your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
<div className='notification__message'>
<div className='notification__favorite-icon-wrapper'>
<Icon id='star' className='star-icon' fixedWidth />
</div>
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.favorite' defaultMessage='{name} favorited your status' values={{ name: link }} />
</span>
</div>
{ /*
<StatusContainer
id={notification.get('status')}
account={notification.get('account')}
muted
withDismiss
hidden={!!this.props.hidden}
getScrollPosition={this.props.getScrollPosition}
updateScrollBottom={this.props.updateScrollBottom}
cachedMediaWidth={this.props.cachedMediaWidth}
cacheMediaWidth={this.props.cacheMediaWidth}
/> */ }
</div>
</HotKeys>
);
}
renderRepost(notification, link) {
const { intl } = this.props;
return (
<HotKeys handlers={this.getHandlers()}>
<div className='notification notification--repost focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.repost', defaultMessage: '{name} reposted your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
<div className='notification__message'>
<div className='notification__favorite-icon-wrapper'>
<Icon id='retweet' fixedWidth />
</div>
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.repost' defaultMessage='{name} reposted your status' values={{ name: link }} />
</span>
</div>
{ /*
<StatusContainer
id={notification.get('status')}
account={notification.get('account')}
muted
withDismiss
hidden={this.props.hidden}
getScrollPosition={this.props.getScrollPosition}
updateScrollBottom={this.props.updateScrollBottom}
cachedMediaWidth={this.props.cachedMediaWidth}
cacheMediaWidth={this.props.cacheMediaWidth}
/> */ }
</div>
</HotKeys>
);
}
renderPoll(notification) {
const { intl } = this.props;
return (
<HotKeys handlers={this.getHandlers()}>
<div className='notification notification--poll focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' }), notification.get('created_at'))}>
<div className='notification__message'>
<div className='notification__favorite-icon-wrapper'>
<Icon id='tasks' fixedWidth />
</div>
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.poll' defaultMessage='A poll you have voted in has ended' />
</span>
</div>
{ /*
<StatusContainer
id={notification.get('status')}
account={notification.get('account')}
muted
withDismiss
hidden={this.props.hidden}
getScrollPosition={this.props.getScrollPosition}
updateScrollBottom={this.props.updateScrollBottom}
cachedMediaWidth={this.props.cachedMediaWidth}
cacheMediaWidth={this.props.cacheMediaWidth}
/> */}
</div>
</HotKeys>
);
}
render() {
const { notification } = this.props;
const account = notification.get('account');
const displayNameHtml = { __html: account.get('display_name_html') };
const link = (
<bdi>
<Button
className='notification__display-name'
href={`/${account.get('acct')}`}
title={account.get('acct')}
to={`/${account.get('acct')}`}
dangerouslySetInnerHTML={displayNameHtml}
/>
</bdi>
);
// console.log("notification:", notification)
switch (notification.get('type')) {
// case 'follow':
// return this.renderFollow(notification, account, link);
// case 'mention':
// return this.renderMention(notification);
case 'favourite':
return this.renderFavorite(notification, link);
// case 'reblog':
// return this.renderRepost(notification, link);
// case 'poll':
// return this.renderPoll(notification);
}
return null;
}
}

View File

@ -1,43 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import ColumnSettings from '../components/column_settings';
import { changeSetting } from '../../../actions/settings';
import { setFilter } from '../../../actions/notifications';
import { clearNotifications } from '../../../actions/notifications';
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
import { openModal } from '../../../actions/modal';
const messages = defineMessages({
clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
});
const mapStateToProps = state => ({
settings: state.getIn(['settings', 'notifications']),
pushSettings: state.get('push_notifications'),
});
// : todo : put all notification settings actually IN settings
const mapDispatchToProps = (dispatch, { intl }) => ({
onChange (path, checked) {
if (path[0] === 'push') {
dispatch(changePushNotifications(path.slice(1), checked));
} else if (path[0] === 'quickFilter') {
dispatch(changeSetting(['notifications', ...path], checked));
dispatch(setFilter('all'));
} else {
dispatch(changeSetting(['notifications', ...path], checked));
}
},
onClear () {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.clearMessage),
confirm: intl.formatMessage(messages.clearConfirm),
onConfirm: () => dispatch(clearNotifications()),
}));
},
});
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ColumnSettings));

View File

@ -13,7 +13,7 @@ import {
} from '../../../actions/statuses'
import { boostModal } from '../../../initial_state'
import { makeGetNotification } from '../../../selectors'
import Notification from '../components/notification/notification-alt'
import Notification from '../components/notification'
const getAccountFromState = (state, accountId) => {
return state.getIn(['accounts', accountId])

View File

@ -17,12 +17,11 @@ import TimelineQueueButtonHeader from '../../components/timeline_queue_button_h
import Block from '../../components/block'
const getNotifications = createSelector([
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
state => state.getIn(['notifications', 'filter', 'active']),
state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
state => state.getIn(['notifications', 'items']),
], (showFilterBar, allowedType, excludedTypes, notifications) => {
if (!showFilterBar || allowedType === 'all') {
], (allowedType, excludedTypes, notifications) => {
if (allowedType === 'all') {
// used if user changed the notification settings after loading the notifications from the server
// otherwise a list of notifications will come pre-filtered from the backend
// we need to turn it off for FilterBar in order not to block ourselves from seeing a specific category
@ -32,7 +31,6 @@ const getNotifications = createSelector([
})
const mapStateToProps = state => ({
showFilterBar: state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
notifications: getNotifications(state),
isLoading: state.getIn(['notifications', 'isLoading'], true),
isUnread: state.getIn(['notifications', 'unread']) > 0,
@ -47,7 +45,6 @@ class Notifications extends ImmutablePureComponent {
static propTypes = {
notifications: ImmutablePropTypes.list.isRequired,
showFilterBar: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
isLoading: PropTypes.bool,
@ -125,7 +122,6 @@ class Notifications extends ImmutablePureComponent {
isLoading,
isUnread,
hasMore,
showFilterBar,
totalQueuedNotificationsCount
} = this.props

View File

@ -41,8 +41,6 @@ export default class Responsive extends PureComponent {
const shouldRender = this.shouldRender(min, max, width)
console.log("shouldRender:", min, max, width, shouldRender)
return shouldRender ? children : null
}
}

View File

@ -18,13 +18,12 @@ const messages = defineMessages({
});
const makeMapStateToProps = state => ({
selectedFilter: state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
selectedFilter: state.getIn(['notifications', 'filter', 'active']),
});
const mapDispatchToProps = (dispatch) => ({
selectFilter(newActiveFilter) {
console.log("newActiveFilter:", newActiveFilter)
dispatch(setFilter(newActiveFilter))
selectFilter(value) {
dispatch(setFilter('active', value))
},
});
@ -33,6 +32,10 @@ export default
@connect(makeMapStateToProps, mapDispatchToProps)
class NotificationsPage extends PureComponent {
static contextTypes = {
router: PropTypes.object.isRequired,
}
static propTypes = {
selectFilter: PropTypes.func.isRequired,
selectedFilter: PropTypes.string.isRequired,
@ -43,8 +46,16 @@ class NotificationsPage extends PureComponent {
document.title = 'Notifications - Gab'
}
// : todo : on pop change filter active type
onClick(notificationType) {
this.props.selectFilter(notificationType);
this.props.selectFilter(notificationType)
if (notificationType === 'all') {
this.context.router.history.push('/notifications')
} else {
this.context.router.history.push(`/notifications?only=${notificationType}`)
}
}
render() {

View File

@ -30,6 +30,11 @@ const initialState = ImmutableMap({
queuedNotifications: ImmutableList(), //max = MAX_QUEUED_NOTIFICATIONS
totalQueuedNotificationsCount: 0, //used for queuedItems overflow for MAX_QUEUED_NOTIFICATIONS+
lastRead: -1,
filter: ImmutableMap({
active: 'all',
onlyVerified: false,
onlyFollowing: false,
}),
});
const notificationToMap = notification => ImmutableMap({
@ -64,7 +69,7 @@ const expandNormalizedNotifications = (state, notifications, next) => {
let items = ImmutableList()
console.log("notifications:", notificationss)
console.log("notifications:", notifications)
notifications.forEach((n) => {
const notification = notificationToMap(n)
@ -211,7 +216,10 @@ export default function notifications(state = initialState, action) {
case NOTIFICATIONS_EXPAND_FAIL:
return state.set('isLoading', false);
case NOTIFICATIONS_FILTER_SET:
return state.set('items', ImmutableList()).set('hasMore', true);
return state.withMutations(mutable => {
mutable.set('items', ImmutableList()).set('hasMore', true)
mutable.setIn(['filter', action.path], action.value)
})
case NOTIFICATIONS_SCROLL_TOP:
return updateTop(state, action.top);
case NOTIFICATIONS_UPDATE:

View File

@ -1,5 +1,4 @@
import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings';
import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications';
import { STORE_HYDRATE } from '../actions/store';
import { EMOJI_USE } from '../actions/emojis';
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists';
@ -43,15 +42,6 @@ const initialState = ImmutableMap({
}),
}),
notifications: ImmutableMap({
// : todo : put all notification settings actually IN settings
quickFilter: ImmutableMap({
active: 'all',
onlyVerifed: false,
onlyFollowing: false,
}),
}),
community: ImmutableMap({
shows: ImmutableMap({
inSidebar: false,
@ -76,7 +66,6 @@ export default function settings(state = initialState, action) {
switch(action.type) {
case STORE_HYDRATE:
return hydrate(state, action.state.get('settings'));
case NOTIFICATIONS_FILTER_SET:
case SETTING_CHANGE:
return state
.setIn(action.path, action.value)

View File

@ -19,15 +19,15 @@ export function breakpointExtraLarge(width) {
}
export function breakpointLarge(width) {
return width > BREAKPOINT_MEDIUM && width < BREAKPOINT_LARGE
return width < BREAKPOINT_LARGE
}
export function breakpointMedium(width) {
return width > BREAKPOINT_SMALL && width < BREAKPOINT_MEDIUM
return width < BREAKPOINT_MEDIUM
}
export function breakpointSmall(width) {
return width > BREAKPOINT_EXTRA_SMALL && width < BREAKPOINT_SMALL
return width < BREAKPOINT_SMALL
}
export function breakpointExtraSmall(width) {

View File

@ -55,7 +55,7 @@ body {
flex-direction: column;
align-items: stretch;
flex-shrink: 0;
border: 0 solid black;
border: 0 solid #2d3436;
z-index: 0;
margin: 0;
padding: 0;
@ -313,7 +313,7 @@ body {
}
.backgroundColorBlack {
background-color: #3B3B3B;
background-color: #2d3436;
}
.backgroundColorBlackOpaque_onHover:hover {
@ -353,7 +353,7 @@ body {
}
.colorPrimary {
color: #000;
color: #2d3436;
}
.colorWhite {
@ -381,7 +381,7 @@ body {
}
.fillColorBlack {
fill: #000;
fill: #2d3436;
}
.fillColorWhite {