From f7117646afddb2676e9275d8efe90c3a20c59021 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 12 Apr 2021 12:37:14 +0200 Subject: Add cold-start follow recommendations (#15945) --- app/javascript/mastodon/actions/suggestions.js | 8 ++++---- .../mastodon/features/compose/components/search_results.js | 12 ++++++------ app/javascript/mastodon/reducers/suggestions.js | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/suggestions.js b/app/javascript/mastodon/actions/suggestions.js index b15bd916b..0bf959017 100644 --- a/app/javascript/mastodon/actions/suggestions.js +++ b/app/javascript/mastodon/actions/suggestions.js @@ -11,8 +11,8 @@ export function fetchSuggestions() { return (dispatch, getState) => { dispatch(fetchSuggestionsRequest()); - api(getState).get('/api/v1/suggestions').then(response => { - dispatch(importFetchedAccounts(response.data)); + api(getState).get('/api/v2/suggestions').then(response => { + dispatch(importFetchedAccounts(response.data.map(x => x.account))); dispatch(fetchSuggestionsSuccess(response.data)); }).catch(error => dispatch(fetchSuggestionsFail(error))); }; @@ -25,10 +25,10 @@ export function fetchSuggestionsRequest() { }; }; -export function fetchSuggestionsSuccess(accounts) { +export function fetchSuggestionsSuccess(suggestions) { return { type: SUGGESTIONS_FETCH_SUCCESS, - accounts, + suggestions, skipLoading: true, }; }; diff --git a/app/javascript/mastodon/features/compose/components/search_results.js b/app/javascript/mastodon/features/compose/components/search_results.js index 4b4cdff74..c4e160b8a 100644 --- a/app/javascript/mastodon/features/compose/components/search_results.js +++ b/app/javascript/mastodon/features/compose/components/search_results.js @@ -51,13 +51,13 @@ class SearchResults extends ImmutablePureComponent { - {suggestions && suggestions.map(accountId => ( + {suggestions && suggestions.map(suggestion => ( ))} diff --git a/app/javascript/mastodon/reducers/suggestions.js b/app/javascript/mastodon/reducers/suggestions.js index 834be728f..1a6e66ee7 100644 --- a/app/javascript/mastodon/reducers/suggestions.js +++ b/app/javascript/mastodon/reducers/suggestions.js @@ -19,18 +19,18 @@ export default function suggestionsReducer(state = initialState, action) { return state.set('isLoading', true); case SUGGESTIONS_FETCH_SUCCESS: return state.withMutations(map => { - map.set('items', fromJS(action.accounts.map(x => x.id))); + map.set('items', fromJS(action.suggestions.map(x => ({ ...x, account: x.account.id })))); map.set('isLoading', false); }); case SUGGESTIONS_FETCH_FAIL: return state.set('isLoading', false); case SUGGESTIONS_DISMISS: - return state.update('items', list => list.filterNot(id => id === action.id)); + return state.update('items', list => list.filterNot(x => x.account === action.id)); case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_MUTE_SUCCESS: - return state.update('items', list => list.filterNot(id => id === action.relationship.id)); + return state.update('items', list => list.filterNot(x => x.account === action.relationship.id)); case DOMAIN_BLOCK_SUCCESS: - return state.update('items', list => list.filterNot(id => action.accounts.includes(id))); + return state.update('items', list => list.filterNot(x => action.accounts.includes(x.account))); default: return state; } -- cgit From e78d06eecfb21de6aedf39fd7c63d9aa68f7033c Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Apr 2021 23:43:51 +0200 Subject: Add border to ๐Ÿšฒ emoji (#16035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/javascript/mastodon/features/emoji/emoji.js | 2 +- lib/tasks/emojis.rake | 2 +- public/emoji/1f6b2_border.svg | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 public/emoji/1f6b2_border.svg (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index 3de79ac9b..fb1a3804c 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -11,7 +11,7 @@ const emojiFilenames = (emojis) => { }; // Emoji requiring extra borders depending on theme -const darkEmoji = emojiFilenames(['๐ŸŽฑ', '๐Ÿœ', 'โšซ', '๐Ÿ–ค', 'โฌ›', 'โ—ผ๏ธ', 'โ—พ', 'โ—ผ๏ธ', 'โœ’๏ธ', 'โ–ช๏ธ', '๐Ÿ’ฃ', '๐ŸŽณ', '๐Ÿ“ท', '๐Ÿ“ธ', 'โ™ฃ๏ธ', '๐Ÿ•ถ๏ธ', 'โœด๏ธ', '๐Ÿ”Œ', '๐Ÿ’‚โ€โ™€๏ธ', '๐Ÿ“ฝ๏ธ', '๐Ÿณ', '๐Ÿฆ', '๐Ÿ’‚', '๐Ÿ”ช', '๐Ÿ•ณ๏ธ', '๐Ÿ•น๏ธ', '๐Ÿ•‹', '๐Ÿ–Š๏ธ', '๐Ÿ–‹๏ธ', '๐Ÿ’‚โ€โ™‚๏ธ', '๐ŸŽค', '๐ŸŽ“', '๐ŸŽฅ', '๐ŸŽผ', 'โ™ ๏ธ', '๐ŸŽฉ', '๐Ÿฆƒ', '๐Ÿ“ผ', '๐Ÿ“น', '๐ŸŽฎ', '๐Ÿƒ', '๐Ÿด', '๐Ÿž', '๐Ÿ•บ', '๐Ÿ“ฑ', '๐Ÿ“ฒ']); +const darkEmoji = emojiFilenames(['๐ŸŽฑ', '๐Ÿœ', 'โšซ', '๐Ÿ–ค', 'โฌ›', 'โ—ผ๏ธ', 'โ—พ', 'โ—ผ๏ธ', 'โœ’๏ธ', 'โ–ช๏ธ', '๐Ÿ’ฃ', '๐ŸŽณ', '๐Ÿ“ท', '๐Ÿ“ธ', 'โ™ฃ๏ธ', '๐Ÿ•ถ๏ธ', 'โœด๏ธ', '๐Ÿ”Œ', '๐Ÿ’‚โ€โ™€๏ธ', '๐Ÿ“ฝ๏ธ', '๐Ÿณ', '๐Ÿฆ', '๐Ÿ’‚', '๐Ÿ”ช', '๐Ÿ•ณ๏ธ', '๐Ÿ•น๏ธ', '๐Ÿ•‹', '๐Ÿ–Š๏ธ', '๐Ÿ–‹๏ธ', '๐Ÿ’‚โ€โ™‚๏ธ', '๐ŸŽค', '๐ŸŽ“', '๐ŸŽฅ', '๐ŸŽผ', 'โ™ ๏ธ', '๐ŸŽฉ', '๐Ÿฆƒ', '๐Ÿ“ผ', '๐Ÿ“น', '๐ŸŽฎ', '๐Ÿƒ', '๐Ÿด', '๐Ÿž', '๐Ÿ•บ', '๐Ÿ“ฑ', '๐Ÿ“ฒ', '๐Ÿšฒ']); const lightEmoji = emojiFilenames(['๐Ÿ‘ฝ', 'โšพ', '๐Ÿ”', 'โ˜๏ธ', '๐Ÿ’จ', '๐Ÿ•Š๏ธ', '๐Ÿ‘€', '๐Ÿฅ', '๐Ÿ‘ป', '๐Ÿ', 'โ•', 'โ”', 'โ›ธ๏ธ', '๐ŸŒฉ๏ธ', '๐Ÿ”Š', '๐Ÿ”‡', '๐Ÿ“ƒ', '๐ŸŒง๏ธ', '๐Ÿ', '๐Ÿš', '๐Ÿ™', '๐Ÿ“', '๐Ÿ‘', '๐Ÿ’€', 'โ˜ ๏ธ', '๐ŸŒจ๏ธ', '๐Ÿ”‰', '๐Ÿ”ˆ', '๐Ÿ’ฌ', '๐Ÿ’ญ', '๐Ÿ', '๐Ÿณ๏ธ', 'โšช', 'โฌœ', 'โ—ฝ', 'โ—ป๏ธ', 'โ–ซ๏ธ']); const emojiFilename = (filename) => { diff --git a/lib/tasks/emojis.rake b/lib/tasks/emojis.rake index c8655cc47..a373e7652 100644 --- a/lib/tasks/emojis.rake +++ b/lib/tasks/emojis.rake @@ -91,7 +91,7 @@ namespace :emojis do desc 'Generate emoji variants with white borders' task :generate_borders do src = Rails.root.join('app', 'javascript', 'mastodon', 'features', 'emoji', 'emoji_map.json') - emojis = '๐ŸŽฑ๐Ÿœโšซ๐Ÿ–คโฌ›โ—ผ๏ธโ—พโ—ผ๏ธโœ’๏ธโ–ช๏ธ๐Ÿ’ฃ๐ŸŽณ๐Ÿ“ท๐Ÿ“ธโ™ฃ๏ธ๐Ÿ•ถ๏ธโœด๏ธ๐Ÿ”Œ๐Ÿ’‚โ€โ™€๏ธ๐Ÿ“ฝ๏ธ๐Ÿณ๐Ÿฆ๐Ÿ’‚๐Ÿ”ช๐Ÿ•ณ๏ธ๐Ÿ•น๏ธ๐Ÿ•‹๐Ÿ–Š๏ธ๐Ÿ–‹๏ธ๐Ÿ’‚โ€โ™‚๏ธ๐ŸŽค๐ŸŽ“๐ŸŽฅ๐ŸŽผโ™ ๏ธ๐ŸŽฉ๐Ÿฆƒ๐Ÿ“ผ๐Ÿ“น๐ŸŽฎ๐Ÿƒ๐Ÿด๐Ÿž๐Ÿ•บ๐Ÿ“ฑ๐Ÿ“ฒ๐Ÿ‘ฝโšพ๐Ÿ”โ˜๏ธ๐Ÿ’จ๐Ÿ•Š๏ธ๐Ÿ‘€๐Ÿฅ๐Ÿ‘ป๐Ÿโ•โ”โ›ธ๏ธ๐ŸŒฉ๏ธ๐Ÿ”Š๐Ÿ”‡๐Ÿ“ƒ๐ŸŒง๏ธ๐Ÿ๐Ÿš๐Ÿ™๐Ÿ“๐Ÿ‘๐Ÿ’€โ˜ ๏ธ๐ŸŒจ๏ธ๐Ÿ”‰๐Ÿ”ˆ๐Ÿ’ฌ๐Ÿ’ญ๐Ÿ๐Ÿณ๏ธโšชโฌœโ—ฝโ—ป๏ธโ–ซ๏ธ' + emojis = '๐ŸŽฑ๐Ÿœโšซ๐Ÿ–คโฌ›โ—ผ๏ธโ—พโ—ผ๏ธโœ’๏ธโ–ช๏ธ๐Ÿ’ฃ๐ŸŽณ๐Ÿ“ท๐Ÿ“ธโ™ฃ๏ธ๐Ÿ•ถ๏ธโœด๏ธ๐Ÿ”Œ๐Ÿ’‚โ€โ™€๏ธ๐Ÿ“ฝ๏ธ๐Ÿณ๐Ÿฆ๐Ÿ’‚๐Ÿ”ช๐Ÿ•ณ๏ธ๐Ÿ•น๏ธ๐Ÿ•‹๐Ÿ–Š๏ธ๐Ÿ–‹๏ธ๐Ÿ’‚โ€โ™‚๏ธ๐ŸŽค๐ŸŽ“๐ŸŽฅ๐ŸŽผโ™ ๏ธ๐ŸŽฉ๐Ÿฆƒ๐Ÿ“ผ๐Ÿ“น๐ŸŽฎ๐Ÿƒ๐Ÿด๐Ÿž๐Ÿ•บ๐Ÿ“ฑ๐Ÿ“ฒ๐Ÿšฒ๐Ÿ‘ฝโšพ๐Ÿ”โ˜๏ธ๐Ÿ’จ๐Ÿ•Š๏ธ๐Ÿ‘€๐Ÿฅ๐Ÿ‘ป๐Ÿโ•โ”โ›ธ๏ธ๐ŸŒฉ๏ธ๐Ÿ”Š๐Ÿ”‡๐Ÿ“ƒ๐ŸŒง๏ธ๐Ÿ๐Ÿš๐Ÿ™๐Ÿ“๐Ÿ‘๐Ÿ’€โ˜ ๏ธ๐ŸŒจ๏ธ๐Ÿ”‰๐Ÿ”ˆ๐Ÿ’ฌ๐Ÿ’ญ๐Ÿ๐Ÿณ๏ธโšชโฌœโ—ฝโ—ป๏ธโ–ซ๏ธ' map = Oj.load(File.read(src)) diff --git a/public/emoji/1f6b2_border.svg b/public/emoji/1f6b2_border.svg new file mode 100644 index 000000000..0219841a1 --- /dev/null +++ b/public/emoji/1f6b2_border.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + -- cgit From 9bb334184900f1a4bb0a212cf46542faa0c544fd Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Fri, 16 Apr 2021 17:06:16 +0900 Subject: Fix to update suggestion list after dismiss (#16044) * Fix to update suggestion list after dismiss * Change to inline * Fix style --- app/javascript/mastodon/actions/suggestions.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/suggestions.js b/app/javascript/mastodon/actions/suggestions.js index 0bf959017..336c0c8f6 100644 --- a/app/javascript/mastodon/actions/suggestions.js +++ b/app/javascript/mastodon/actions/suggestions.js @@ -48,5 +48,12 @@ export const dismissSuggestion = accountId => (dispatch, getState) => { id: accountId, }); - api(getState).delete(`/api/v1/suggestions/${accountId}`); + api(getState).delete(`/api/v1/suggestions/${accountId}`).then(() => { + dispatch(fetchSuggestionsRequest()); + + api(getState).get('/api/v2/suggestions').then(response => { + dispatch(importFetchedAccounts(response.data.map(x => x.account))); + dispatch(fetchSuggestionsSuccess(response.data)); + }).catch(error => dispatch(fetchSuggestionsFail(error))); + }).catch(() => {}); }; -- cgit From baed52c2a7d8f91bae3c69150005fc528387785c Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Fri, 16 Apr 2021 17:06:42 +0900 Subject: Fix not to show follow button in global suggestion (#16045) * Fix not to show follow button in global suggestion * Fix style --- app/javascript/mastodon/components/account.js | 6 ++++-- .../mastodon/features/compose/components/search_results.js | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.js index 0e40ee1d6..a85d683a7 100644 --- a/app/javascript/mastodon/components/account.js +++ b/app/javascript/mastodon/components/account.js @@ -78,8 +78,10 @@ class Account extends ImmutablePureComponent { let buttons; - if (onActionClick && actionIcon) { - buttons = ; + if (actionIcon) { + if (onActionClick) { + buttons = ; + } } else if (account.get('id') !== me && account.get('relationship', null) !== null) { const following = account.getIn(['relationship', 'following']); const requested = account.getIn(['relationship', 'requested']); diff --git a/app/javascript/mastodon/features/compose/components/search_results.js b/app/javascript/mastodon/features/compose/components/search_results.js index c4e160b8a..a8b31b677 100644 --- a/app/javascript/mastodon/features/compose/components/search_results.js +++ b/app/javascript/mastodon/features/compose/components/search_results.js @@ -55,9 +55,9 @@ class SearchResults extends ImmutablePureComponent { ))} -- cgit From bf903dc51070f952ab64e43918e803fdaaa15e4d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 19 Apr 2021 14:45:15 +0200 Subject: Change onboarding by replacing tutorial with follow recommendations in web UI (#16060) --- .../mastodon/actions/importer/normalizer.js | 1 + app/javascript/mastodon/actions/onboarding.js | 13 --- app/javascript/mastodon/actions/suggestions.js | 7 +- app/javascript/mastodon/components/logo.js | 9 ++ app/javascript/mastodon/containers/mastodon.js | 47 +++-------- .../follow_recommendations/components/account.js | 85 +++++++++++++++++++ .../features/follow_recommendations/index.js | 95 ++++++++++++++++++++++ app/javascript/mastodon/features/ui/index.js | 11 +++ .../mastodon/features/ui/util/async-components.js | 4 + app/javascript/styles/mastodon/components.scss | 66 +++++++++++++++ 10 files changed, 287 insertions(+), 51 deletions(-) create mode 100644 app/javascript/mastodon/components/logo.js create mode 100644 app/javascript/mastodon/features/follow_recommendations/components/account.js create mode 100644 app/javascript/mastodon/features/follow_recommendations/index.js (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index dca44917a..087f26491 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -24,6 +24,7 @@ export function normalizeAccount(account) { account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap); account.note_emojified = emojify(account.note, emojiMap); + account.note_plain = unescapeHTML(account.note); if (account.fields) { account.fields = account.fields.map(pair => ({ diff --git a/app/javascript/mastodon/actions/onboarding.js b/app/javascript/mastodon/actions/onboarding.js index 42d8ea33f..a1dd3a731 100644 --- a/app/javascript/mastodon/actions/onboarding.js +++ b/app/javascript/mastodon/actions/onboarding.js @@ -1,21 +1,8 @@ import { changeSetting, saveSettings } from './settings'; -import { requestBrowserPermission } from './notifications'; export const INTRODUCTION_VERSION = 20181216044202; export const closeOnboarding = () => dispatch => { dispatch(changeSetting(['introductionVersion'], INTRODUCTION_VERSION)); dispatch(saveSettings()); - - 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()); - } - })); }; diff --git a/app/javascript/mastodon/actions/suggestions.js b/app/javascript/mastodon/actions/suggestions.js index 336c0c8f6..e3a549759 100644 --- a/app/javascript/mastodon/actions/suggestions.js +++ b/app/javascript/mastodon/actions/suggestions.js @@ -1,5 +1,6 @@ import api from '../api'; import { importFetchedAccounts } from './importer'; +import { fetchRelationships } from './accounts'; export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST'; export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS'; @@ -7,13 +8,17 @@ export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL'; export const SUGGESTIONS_DISMISS = 'SUGGESTIONS_DISMISS'; -export function fetchSuggestions() { +export function fetchSuggestions(withRelationships = false) { return (dispatch, getState) => { dispatch(fetchSuggestionsRequest()); api(getState).get('/api/v2/suggestions').then(response => { dispatch(importFetchedAccounts(response.data.map(x => x.account))); dispatch(fetchSuggestionsSuccess(response.data)); + + if (withRelationships) { + dispatch(fetchRelationships(response.data.map(item => item.account.id))); + } }).catch(error => dispatch(fetchSuggestionsFail(error))); }; }; diff --git a/app/javascript/mastodon/components/logo.js b/app/javascript/mastodon/components/logo.js new file mode 100644 index 000000000..d1c7f08a9 --- /dev/null +++ b/app/javascript/mastodon/components/logo.js @@ -0,0 +1,9 @@ +import React from 'react'; + +const Logo = () => ( + + + +); + +export default Logo; diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 3ac58cf7c..513b59908 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -1,12 +1,10 @@ import React from 'react'; -import { Provider, connect } from 'react-redux'; +import { Provider } from 'react-redux'; import PropTypes from 'prop-types'; import configureStore from '../store/configureStore'; -import { INTRODUCTION_VERSION } from '../actions/onboarding'; import { BrowserRouter, Route } from 'react-router-dom'; import { ScrollContext } from 'react-router-scroll-4'; import UI from '../features/ui'; -import Introduction from '../features/introduction'; import { fetchCustomEmojis } from '../actions/custom_emojis'; import { hydrateStore } from '../actions/store'; import { connectUserStream } from '../actions/streaming'; @@ -26,39 +24,6 @@ const hydrateAction = hydrateStore(initialState); store.dispatch(hydrateAction); store.dispatch(fetchCustomEmojis()); -const mapStateToProps = state => ({ - showIntroduction: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION, -}); - -@connect(mapStateToProps) -class MastodonMount extends React.PureComponent { - - static propTypes = { - showIntroduction: PropTypes.bool, - }; - - shouldUpdateScroll (_, { location }) { - return location.state !== previewMediaState && location.state !== previewVideoState; - } - - render () { - const { showIntroduction } = this.props; - - if (showIntroduction) { - return ; - } - - return ( - - - - - - ); - } - -} - export default class Mastodon extends React.PureComponent { static propTypes = { @@ -76,6 +41,10 @@ export default class Mastodon extends React.PureComponent { } } + shouldUpdateScroll (_, { location }) { + return location.state !== previewMediaState && location.state !== previewVideoState; + } + render () { const { locale } = this.props; @@ -83,7 +52,11 @@ export default class Mastodon extends React.PureComponent { - + + + + + diff --git a/app/javascript/mastodon/features/follow_recommendations/components/account.js b/app/javascript/mastodon/features/follow_recommendations/components/account.js new file mode 100644 index 000000000..23c03d1ff --- /dev/null +++ b/app/javascript/mastodon/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 'mastodon/selectors'; +import Avatar from 'mastodon/components/avatar'; +import DisplayName from 'mastodon/components/display_name'; +import Permalink from 'mastodon/components/permalink'; +import IconButton from 'mastodon/components/icon_button'; +import { injectIntl, defineMessages } from 'react-intl'; +import { followAccount, unfollowAccount } from 'mastodon/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/mastodon/features/follow_recommendations/index.js b/app/javascript/mastodon/features/follow_recommendations/index.js new file mode 100644 index 000000000..1231a27ea --- /dev/null +++ b/app/javascript/mastodon/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 'mastodon/actions/suggestions'; +import { changeSetting, saveSettings } from 'mastodon/actions/settings'; +import { requestBrowserPermission } from 'mastodon/actions/notifications'; +import Column from 'mastodon/features/ui/components/column'; +import Account from './components/account'; +import Logo from 'mastodon/components/logo'; +import imageGreeting from 'mastodon/../images/elephant_ui_greeting.svg'; +import Button from 'mastodon/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 => ( + + ))} +
+ +
+ + +
+
+ )} +
+
+ ); + } + +} diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 507ac1df1..078a69f0f 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -51,10 +51,12 @@ import { Lists, Search, Directory, + FollowRecommendations, } from './util/async-components'; import { me } from '../../initial_state'; import { previewState as previewMediaState } from './components/media_modal'; import { previewState as previewVideoState } from './components/video_modal'; +import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. @@ -71,6 +73,7 @@ const mapStateToProps = state => ({ hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0, canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4, dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null, + firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION, }); const keyMap = { @@ -167,6 +170,7 @@ class SwitchingColumnsArea extends React.PureComponent { + @@ -215,6 +219,7 @@ class UI extends React.PureComponent { intl: PropTypes.object.isRequired, dropdownMenuIsOpen: PropTypes.bool, layout: PropTypes.string.isRequired, + firstLaunch: PropTypes.bool, }; state = { @@ -350,6 +355,12 @@ class UI extends React.PureComponent { navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage); } + // On first launch, redirect to the follow recommendations page + if (this.props.firstLaunch) { + this.context.router.history.replace('/start'); + this.props.dispatch(closeOnboarding()); + } + this.props.dispatch(fetchMarkers()); this.props.dispatch(expandHomeTimeline()); this.props.dispatch(expandNotifications()); diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 986efda1e..aa90b226a 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -153,3 +153,7 @@ export function Audio () { export function Directory () { return import(/* webpackChunkName: "features/directory" */'../../directory'); } + +export function FollowRecommendations () { + return import(/* webpackChunkName: "features/follow_recommendations" */'../../follow_recommendations'); +} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 49432b864..f151224ae 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1307,6 +1307,29 @@ overflow: hidden; text-decoration: none; font-size: 14px; + + &--with-note { + strong { + display: inline; + } + } + } + + &__note { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: $ui-secondary-color; + } +} + +.follow-recommendations-account { + .icon-button { + color: $ui-primary-color; + + &.active { + color: $valid-value-color; + } } } @@ -2459,6 +2482,49 @@ a.account__display-name { border-color: darken($ui-base-color, 8%); } +.column-title { + text-align: center; + padding: 40px; + + .logo { + fill: $primary-text-color; + width: 50px; + margin: 0 auto; + margin-bottom: 40px; + } + + h3 { + font-size: 24px; + line-height: 1.5; + font-weight: 700; + margin-bottom: 10px; + } + + p { + font-size: 16px; + line-height: 24px; + font-weight: 400; + color: $darker-text-color; + } +} + +.column-actions { + display: flex; + align-items: center; + justify-content: center; + padding: 40px; + padding-top: 40px; + padding-bottom: 200px; + + &__background { + position: absolute; + left: 0; + bottom: 0; + height: 220px; + width: auto; + } +} + .compose-panel { width: 285px; margin-top: 10px; -- cgit From b5ac17c4b6bfa85494fd768bbf1af87ca79b622b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 20 Apr 2021 02:34:08 +0200 Subject: Fix newlines not being considered sentence separators in account note (#16079) Also bullets --- .../mastodon/features/follow_recommendations/components/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/follow_recommendations/components/account.js b/app/javascript/mastodon/features/follow_recommendations/components/account.js index 23c03d1ff..bd855aab0 100644 --- a/app/javascript/mastodon/features/follow_recommendations/components/account.js +++ b/app/javascript/mastodon/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