From 89bc4dff6f85e585713e777b4a860df77db2936e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 19 Apr 2021 14:45:15 +0200 Subject: [Glitch] Change onboarding by replacing tutorial with follow recommendations in web UI Port bf903dc51070f952ab64e43918e803fdaaa15e4d to glitch-soc Signed-off-by: Claire --- .../follow_recommendations/components/account.js | 85 +++++++++++++++++++ .../features/follow_recommendations/index.js | 95 ++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 app/javascript/flavours/glitch/features/follow_recommendations/components/account.js create mode 100644 app/javascript/flavours/glitch/features/follow_recommendations/index.js (limited to 'app/javascript/flavours/glitch/features/follow_recommendations') diff --git a/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js b/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js new file mode 100644 index 000000000..bee9f9384 --- /dev/null +++ b/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js @@ -0,0 +1,85 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { connect } from 'react-redux'; +import { makeGetAccount } from 'flavours/glitch/selectors'; +import Avatar from 'flavours/glitch/components/avatar'; +import DisplayName from 'flavours/glitch/components/display_name'; +import Permalink from 'flavours/glitch/components/permalink'; +import IconButton from 'flavours/glitch/components/icon_button'; +import { injectIntl, defineMessages } from 'react-intl'; +import { followAccount, unfollowAccount } from 'flavours/glitch/actions/accounts'; + +const messages = defineMessages({ + follow: { id: 'account.follow', defaultMessage: 'Follow' }, + unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, +}); + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, props) => ({ + account: getAccount(state, props.id), + }); + + return mapStateToProps; +}; + +const getFirstSentence = str => { + const arr = str.split(/(([\.\?!]+\s)|[.。?!])/); + + return arr[0]; +}; + +export default @connect(makeMapStateToProps) +@injectIntl +class Account extends ImmutablePureComponent { + + static propTypes = { + account: ImmutablePropTypes.map.isRequired, + intl: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, + }; + + handleFollow = () => { + const { account, dispatch } = this.props; + + if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) { + dispatch(unfollowAccount(account.get('id'))); + } else { + dispatch(followAccount(account.get('id'))); + } + } + + render () { + const { account, intl } = this.props; + + let button; + + if (account.getIn(['relationship', 'following'])) { + button = ; + } else { + button = ; + } + + return ( +
+
+ +
+ + + +
{getFirstSentence(account.get('note_plain'))}
+
+ +
+ {button} +
+
+
+ ); + } + +} diff --git a/app/javascript/flavours/glitch/features/follow_recommendations/index.js b/app/javascript/flavours/glitch/features/follow_recommendations/index.js new file mode 100644 index 000000000..ac75062e0 --- /dev/null +++ b/app/javascript/flavours/glitch/features/follow_recommendations/index.js @@ -0,0 +1,95 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; +import { FormattedMessage } from 'react-intl'; +import { fetchSuggestions } from 'flavours/glitch/actions/suggestions'; +import { changeSetting, saveSettings } from 'flavours/glitch/actions/settings'; +import { requestBrowserPermission } from 'flavours/glitch/actions/notifications'; +import Column from 'flavours/glitch/features/ui/components/column'; +import Account from './components/account'; +import Logo from 'flavours/glitch/components/logo'; +import imageGreeting from 'mastodon/../images/elephant_ui_greeting.svg'; +import Button from 'flavours/glitch/components/button'; + +const mapStateToProps = state => ({ + suggestions: state.getIn(['suggestions', 'items']), + isLoading: state.getIn(['suggestions', 'isLoading']), +}); + +export default @connect(mapStateToProps) +class FollowRecommendations extends ImmutablePureComponent { + + static contextTypes = { + router: PropTypes.object.isRequired, + }; + + static propTypes = { + dispatch: PropTypes.func.isRequired, + suggestions: ImmutablePropTypes.list, + isLoading: PropTypes.bool, + }; + + componentDidMount () { + const { dispatch, suggestions } = this.props; + + // Don't re-fetch if we're e.g. navigating backwards to this page, + // since we don't want followed accounts to disappear from the list + + if (suggestions.size === 0) { + dispatch(fetchSuggestions(true)); + } + } + + handleDone = () => { + const { dispatch } = this.props; + const { router } = this.context; + + dispatch(requestBrowserPermission((permission) => { + if (permission === 'granted') { + dispatch(changeSetting(['notifications', 'alerts', 'follow'], true)); + dispatch(changeSetting(['notifications', 'alerts', 'favourite'], true)); + dispatch(changeSetting(['notifications', 'alerts', 'reblog'], true)); + dispatch(changeSetting(['notifications', 'alerts', 'mention'], true)); + dispatch(changeSetting(['notifications', 'alerts', 'poll'], true)); + dispatch(changeSetting(['notifications', 'alerts', 'status'], true)); + dispatch(saveSettings()); + } + })); + + router.history.push('/timelines/home'); + } + + render () { + const { suggestions, isLoading } = this.props; + + return ( + +
+
+ +

+

+
+ + {!isLoading && ( + +
+ {suggestions.map(suggestion => ( + + ))} +
+ +
+ + +
+
+ )} +
+
+ ); + } + +} -- cgit From 0936ae413204a2b8e7da1b4b14a65eb856e30e74 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 20 Apr 2021 02:34:08 +0200 Subject: [Glitch] Fix newlines not being considered sentence separators in account note Port b5ac17c4b6bfa85494fd768bbf1af87ca79b622b to glitch-soc Signed-off-by: Claire --- .../glitch/features/follow_recommendations/components/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/javascript/flavours/glitch/features/follow_recommendations') diff --git a/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js b/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js index bee9f9384..046d03a9b 100644 --- a/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js +++ b/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js @@ -27,7 +27,7 @@ const makeMapStateToProps = () => { }; const getFirstSentence = str => { - const arr = str.split(/(([\.\?!]+\s)|[.。?!])/); + const arr = str.split(/(([\.\?!]+\s)|[.。?!\n•])/); return arr[0]; }; -- cgit From b571bc3278e39fda2caaa6663cdfd0015eb716b7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 5 May 2021 23:57:29 +0200 Subject: [Glitch] Add empty state message for follow recommendations in web UI Port 8d75bd002da5011237b537b522a5b6248e99b9d5 to glitch-soc Signed-off-by: Claire --- .../glitch/features/follow_recommendations/index.js | 10 +++++++--- .../flavours/glitch/styles/components/columns.scss | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'app/javascript/flavours/glitch/features/follow_recommendations') diff --git a/app/javascript/flavours/glitch/features/follow_recommendations/index.js b/app/javascript/flavours/glitch/features/follow_recommendations/index.js index ac75062e0..aea841bd6 100644 --- a/app/javascript/flavours/glitch/features/follow_recommendations/index.js +++ b/app/javascript/flavours/glitch/features/follow_recommendations/index.js @@ -75,10 +75,14 @@ class FollowRecommendations extends ImmutablePureComponent { {!isLoading && ( -
- {suggestions.map(suggestion => ( +
+ {suggestions.size > 0 ? suggestions.map(suggestion => ( - ))} + )) : ( +
+ +
+ )}
diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index c6045e96e..7f3d27dba 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -839,3 +839,19 @@ width: auto; } } + +.column-list { + margin: 0 20px; + border: 1px solid lighten($ui-base-color, 8%); + background: darken($ui-base-color, 2%); + border-radius: 4px; + + &__empty-message { + padding: 40px; + text-align: center; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color: $darker-text-color; + } +} -- cgit From d30dd5b1dbbc8e72cd7aefe5ee39684e068b7118 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 7 May 2021 14:33:57 +0200 Subject: [Glitch] Change home timeline to reload after follow recommendations in web UI Port 0ad240cb6b8662e31dfae6279cbee07a4c75b231 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/actions/timelines.js | 14 ++++++++++++++ .../glitch/features/follow_recommendations/index.js | 10 ++++++++++ .../flavours/glitch/features/home_timeline/index.js | 4 ++-- .../flavours/glitch/features/notifications/index.js | 2 +- app/javascript/flavours/glitch/reducers/timelines.js | 7 +++++++ 5 files changed, 34 insertions(+), 3 deletions(-) (limited to 'app/javascript/flavours/glitch/features/follow_recommendations') diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js index b19666e62..24cc0d63f 100644 --- a/app/javascript/flavours/glitch/actions/timelines.js +++ b/app/javascript/flavours/glitch/actions/timelines.js @@ -20,6 +20,8 @@ export const TIMELINE_LOAD_PENDING = 'TIMELINE_LOAD_PENDING'; export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'; export const TIMELINE_CONNECT = 'TIMELINE_CONNECT'; +export const TIMELINE_MARK_AS_PARTIAL = 'TIMELINE_MARK_AS_PARTIAL'; + export const loadPending = timeline => ({ type: TIMELINE_LOAD_PENDING, timeline, @@ -31,6 +33,13 @@ export function updateTimeline(timeline, status, accept) { return; } + if (getState().getIn(['timelines', timeline, 'isPartial'])) { + // Prevent new items from being added to a partial timeline, + // since it will be reloaded anyway + + return; + } + const filters = getFiltersRegex(getState(), { contextType: timeline }); const dropRegex = filters[0]; const regex = filters[1]; @@ -198,3 +207,8 @@ export const disconnectTimeline = timeline => ({ timeline, usePendingItems: preferPendingItems, }); + +export const markAsPartial = timeline => ({ + type: TIMELINE_MARK_AS_PARTIAL, + timeline, +}); diff --git a/app/javascript/flavours/glitch/features/follow_recommendations/index.js b/app/javascript/flavours/glitch/features/follow_recommendations/index.js index aea841bd6..8165c39a9 100644 --- a/app/javascript/flavours/glitch/features/follow_recommendations/index.js +++ b/app/javascript/flavours/glitch/features/follow_recommendations/index.js @@ -7,6 +7,7 @@ import { FormattedMessage } from 'react-intl'; import { fetchSuggestions } from 'flavours/glitch/actions/suggestions'; import { changeSetting, saveSettings } from 'flavours/glitch/actions/settings'; import { requestBrowserPermission } from 'flavours/glitch/actions/notifications'; +import { markAsPartial } from 'flavours/glitch/actions/timelines'; import Column from 'flavours/glitch/features/ui/components/column'; import Account from './components/account'; import Logo from 'flavours/glitch/components/logo'; @@ -42,6 +43,15 @@ class FollowRecommendations extends ImmutablePureComponent { } } + componentWillUnmount () { + const { dispatch } = this.props; + + // Force the home timeline to be reloaded when the user navigates + // to it; if the user is new, it would've been empty before + + dispatch(markAsPartial('home')); + } + handleDone = () => { const { dispatch } = this.props; const { router } = this.context; diff --git a/app/javascript/flavours/glitch/features/home_timeline/index.js b/app/javascript/flavours/glitch/features/home_timeline/index.js index cc8e4664c..19551d6b8 100644 --- a/app/javascript/flavours/glitch/features/home_timeline/index.js +++ b/app/javascript/flavours/glitch/features/home_timeline/index.js @@ -72,7 +72,7 @@ class HomeTimeline extends React.PureComponent { } componentDidMount () { - this.props.dispatch(fetchAnnouncements()); + setTimeout(() => this.props.dispatch(fetchAnnouncements()), 700); this._checkIfReloadNeeded(false, this.props.isPartial); } @@ -152,7 +152,7 @@ class HomeTimeline extends React.PureComponent { scrollKey={`home_timeline-${columnId}`} onLoadMore={this.handleLoadMore} timelineId='home' - emptyMessage={ }} />} + emptyMessage={ }} />} bindToDocument={!multiColumn} /> diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js index 842e02371..6fc951e37 100644 --- a/app/javascript/flavours/glitch/features/notifications/index.js +++ b/app/javascript/flavours/glitch/features/notifications/index.js @@ -224,7 +224,7 @@ class Notifications extends React.PureComponent { const { notifCleaning, notifCleaningActive } = this.props; const { animatingNCD } = this.state; const pinned = !!columnId; - const emptyMessage = ; + const emptyMessage = ; let scrollableContent = null; diff --git a/app/javascript/flavours/glitch/reducers/timelines.js b/app/javascript/flavours/glitch/reducers/timelines.js index 882b48790..7d815d850 100644 --- a/app/javascript/flavours/glitch/reducers/timelines.js +++ b/app/javascript/flavours/glitch/reducers/timelines.js @@ -9,6 +9,7 @@ import { TIMELINE_CONNECT, TIMELINE_DISCONNECT, TIMELINE_LOAD_PENDING, + TIMELINE_MARK_AS_PARTIAL, } from 'flavours/glitch/actions/timelines'; import { ACCOUNT_BLOCK_SUCCESS, @@ -173,6 +174,12 @@ export default function timelines(state = initialState, action) { initialTimeline, map => map.set('online', false).update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items), ); + case TIMELINE_MARK_AS_PARTIAL: + return state.update( + action.timeline, + initialTimeline, + map => map.set('isPartial', true).set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('unread', 0), + ); default: return state; } -- cgit