From 41b01382ac4a3267f2abc037bed4f9104b0e5d17 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 14 May 2022 12:35:12 -0500 Subject: [PATCH 01/38] SearchResults: convert to TSX --- .../compose/components/search_results.js | 174 ---------------- .../compose/components/search_results.tsx | 187 ++++++++++++++++++ 2 files changed, 187 insertions(+), 174 deletions(-) delete mode 100644 app/soapbox/features/compose/components/search_results.js create mode 100644 app/soapbox/features/compose/components/search_results.tsx diff --git a/app/soapbox/features/compose/components/search_results.js b/app/soapbox/features/compose/components/search_results.js deleted file mode 100644 index 05cbe2663..000000000 --- a/app/soapbox/features/compose/components/search_results.js +++ /dev/null @@ -1,174 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { FormattedMessage } from 'react-intl'; -import { defineMessages, injectIntl } from 'react-intl'; - -import ScrollableList from 'soapbox/components/scrollable_list'; -import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account'; -import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder_hashtag'; -import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status'; - -import Hashtag from '../../../components/hashtag'; -import { Tabs } from '../../../components/ui'; -import AccountContainer from '../../../containers/account_container'; -import StatusContainer from '../../../containers/status_container'; - -const messages = defineMessages({ - accounts: { id: 'search_results.accounts', defaultMessage: 'People' }, - statuses: { id: 'search_results.statuses', defaultMessage: 'Posts' }, - hashtags: { id: 'search_results.hashtags', defaultMessage: 'Hashtags' }, -}); - -export default @injectIntl -class SearchResults extends ImmutablePureComponent { - - static propTypes = { - value: PropTypes.string, - results: ImmutablePropTypes.map.isRequired, - submitted: PropTypes.bool, - expandSearch: PropTypes.func.isRequired, - selectedFilter: PropTypes.string.isRequired, - selectFilter: PropTypes.func.isRequired, - features: PropTypes.object.isRequired, - suggestions: ImmutablePropTypes.list, - trendingStatuses: ImmutablePropTypes.list, - trends: ImmutablePropTypes.list, - intl: PropTypes.object.isRequired, - }; - - handleLoadMore = () => this.props.expandSearch(this.props.selectedFilter); - - handleSelectFilter = newActiveFilter => this.props.selectFilter(newActiveFilter); - - componentDidMount() { - this.props.fetchTrendingStatuses(); - } - - renderFilterBar() { - const { intl, selectedFilter } = this.props; - - const items = [ - { - text: intl.formatMessage(messages.accounts), - action: () => this.handleSelectFilter('accounts'), - name: 'accounts', - }, - { - text: intl.formatMessage(messages.statuses), - action: () => this.handleSelectFilter('statuses'), - name: 'statuses', - }, - { - text: intl.formatMessage(messages.hashtags), - action: () => this.handleSelectFilter('hashtags'), - name: 'hashtags', - }, - ]; - - return ; - } - - render() { - const { value, results, submitted, selectedFilter, suggestions, trendingStatuses, trends } = this.props; - - let searchResults; - let hasMore = false; - let loaded; - let noResultsMessage; - let placeholderComponent = PlaceholderStatus; - - if (selectedFilter === 'accounts') { - hasMore = results.get('accountsHasMore'); - loaded = results.get('accountsLoaded'); - placeholderComponent = PlaceholderAccount; - - if (results.get('accounts') && results.get('accounts').size > 0) { - searchResults = results.get('accounts').map(accountId => ); - } else if (!submitted && suggestions && !suggestions.isEmpty()) { - searchResults = suggestions.map(suggestion => ); - } else if (loaded) { - noResultsMessage = ( -
- -
- ); - } - } - - if (selectedFilter === 'statuses') { - hasMore = results.get('statusesHasMore'); - loaded = results.get('statusesLoaded'); - - if (results.get('statuses') && results.get('statuses').size > 0) { - searchResults = results.get('statuses').map(statusId => ); - } else if (!submitted && trendingStatuses && !trendingStatuses.isEmpty()) { - searchResults = trendingStatuses.map(statusId => ); - } else if (loaded) { - noResultsMessage = ( -
- -
- ); - } - } - - if (selectedFilter === 'hashtags') { - hasMore = results.get('hashtagsHasMore'); - loaded = results.get('hashtagsLoaded'); - placeholderComponent = PlaceholderHashtag; - - if (results.get('hashtags') && results.get('hashtags').size > 0) { - searchResults = results.get('hashtags').map(hashtag => ); - } else if (!submitted && suggestions && !suggestions.isEmpty()) { - searchResults = trends.map(hashtag => ); - } else if (loaded) { - noResultsMessage = ( -
- -
- ); - } - } - - return ( - <> - {this.renderFilterBar()} - - {noResultsMessage || ( - - {searchResults} - - )} - - ); - } - -} diff --git a/app/soapbox/features/compose/components/search_results.tsx b/app/soapbox/features/compose/components/search_results.tsx new file mode 100644 index 000000000..1dc244920 --- /dev/null +++ b/app/soapbox/features/compose/components/search_results.tsx @@ -0,0 +1,187 @@ +import classNames from 'classnames'; +import React, { useEffect } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; + +import { fetchTrendingStatuses } from 'soapbox/actions/trending_statuses'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account'; +import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder_hashtag'; +import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status'; +import { useAppDispatch } from 'soapbox/hooks'; + +import Hashtag from '../../../components/hashtag'; +import { Tabs } from '../../../components/ui'; +import AccountContainer from '../../../containers/account_container'; +import StatusContainer from '../../../containers/status_container'; + +import type { Map as ImmutableMap, List as ImmutableList } from 'immutable'; + +const messages = defineMessages({ + accounts: { id: 'search_results.accounts', defaultMessage: 'People' }, + statuses: { id: 'search_results.statuses', defaultMessage: 'Posts' }, + hashtags: { id: 'search_results.hashtags', defaultMessage: 'Hashtags' }, +}); + +type SearchFilter = 'accounts' | 'statuses' | 'hashtags'; + +interface ISearchResults { + value: string, + results: ImmutableMap, + submitted: boolean, + expandSearch: (filter: SearchFilter) => void, + selectedFilter: SearchFilter, + selectFilter: (filter: SearchFilter) => void, + suggestions: ImmutableList, + trendingStatuses: ImmutableList, + trends: ImmutableList, +} + +/** Displays search results depending on the active tab. */ +const SearchResults: React.FC = ({ + value, + results, + submitted, + expandSearch, + selectedFilter, + selectFilter, + suggestions, + trendingStatuses, + trends, +}) => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const handleLoadMore = () => expandSearch(selectedFilter); + const handleSelectFilter = (newActiveFilter: SearchFilter) => selectFilter(newActiveFilter); + + useEffect(() => { + dispatch(fetchTrendingStatuses()); + }, []); + + const renderFilterBar = () => { + const items = [ + { + text: intl.formatMessage(messages.accounts), + action: () => handleSelectFilter('accounts'), + name: 'accounts', + }, + { + text: intl.formatMessage(messages.statuses), + action: () => handleSelectFilter('statuses'), + name: 'statuses', + }, + { + text: intl.formatMessage(messages.hashtags), + action: () => handleSelectFilter('hashtags'), + name: 'hashtags', + }, + ]; + + return ; + }; + + let searchResults; + let hasMore = false; + let loaded; + let noResultsMessage; + let placeholderComponent: React.ComponentType = PlaceholderStatus; + + if (selectedFilter === 'accounts') { + hasMore = results.get('accountsHasMore'); + loaded = results.get('accountsLoaded'); + placeholderComponent = PlaceholderAccount; + + if (results.get('accounts') && results.get('accounts').size > 0) { + searchResults = results.get('accounts').map((accountId: string) => ); + } else if (!submitted && suggestions && !suggestions.isEmpty()) { + searchResults = suggestions.map(suggestion => ); + } else if (loaded) { + noResultsMessage = ( +
+ +
+ ); + } + } + + if (selectedFilter === 'statuses') { + hasMore = results.get('statusesHasMore'); + loaded = results.get('statusesLoaded'); + + if (results.get('statuses') && results.get('statuses').size > 0) { + searchResults = results.get('statuses').map((statusId: string) => ( + // @ts-ignore + + )); + } else if (!submitted && trendingStatuses && !trendingStatuses.isEmpty()) { + searchResults = trendingStatuses.map(statusId => ( + // @ts-ignore + + )); + } else if (loaded) { + noResultsMessage = ( +
+ +
+ ); + } + } + + if (selectedFilter === 'hashtags') { + hasMore = results.get('hashtagsHasMore'); + loaded = results.get('hashtagsLoaded'); + placeholderComponent = PlaceholderHashtag; + + if (results.get('hashtags') && results.get('hashtags').size > 0) { + searchResults = results.get('hashtags').map((hashtag: ImmutableMap) => ); + } else if (!submitted && suggestions && !suggestions.isEmpty()) { + searchResults = trends.map(hashtag => ); + } else if (loaded) { + noResultsMessage = ( +
+ +
+ ); + } + } + + return ( + <> + {renderFilterBar()} + + {noResultsMessage || ( + + {searchResults} + + )} + + ); +}; + +export default SearchResults; From 5bbfb0cc398e4e076c0458b42f71f3e168e876ee Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 14 May 2022 13:07:32 -0500 Subject: [PATCH 02/38] Convert Trends, Suggestions, and TrendingStatuses reducers to TypeScript --- .../compose/components/search_results.tsx | 32 ++++++------ .../follow_recommendations_list.tsx | 4 +- .../{suggestions.js => suggestions.ts} | 50 ++++++++++++++----- app/soapbox/reducers/trending_statuses.js | 31 ------------ app/soapbox/reducers/trending_statuses.ts | 37 ++++++++++++++ app/soapbox/reducers/{trends.js => trends.ts} | 16 ++++-- 6 files changed, 105 insertions(+), 65 deletions(-) rename app/soapbox/reducers/{suggestions.js => suggestions.ts} (54%) delete mode 100644 app/soapbox/reducers/trending_statuses.js create mode 100644 app/soapbox/reducers/trending_statuses.ts rename app/soapbox/reducers/{trends.js => trends.ts} (53%) diff --git a/app/soapbox/features/compose/components/search_results.tsx b/app/soapbox/features/compose/components/search_results.tsx index 1dc244920..d24c7631d 100644 --- a/app/soapbox/features/compose/components/search_results.tsx +++ b/app/soapbox/features/compose/components/search_results.tsx @@ -3,19 +3,23 @@ import React, { useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl'; +import { expandSearch, setFilter } from 'soapbox/actions/search'; import { fetchTrendingStatuses } from 'soapbox/actions/trending_statuses'; import ScrollableList from 'soapbox/components/scrollable_list'; import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account'; import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder_hashtag'; import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status'; -import { useAppDispatch } from 'soapbox/hooks'; +import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; import Hashtag from '../../../components/hashtag'; import { Tabs } from '../../../components/ui'; import AccountContainer from '../../../containers/account_container'; import StatusContainer from '../../../containers/status_container'; -import type { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import type { + Map as ImmutableMap, + List as ImmutableList, +} from 'immutable'; const messages = defineMessages({ accounts: { id: 'search_results.accounts', defaultMessage: 'People' }, @@ -38,22 +42,20 @@ interface ISearchResults { } /** Displays search results depending on the active tab. */ -const SearchResults: React.FC = ({ - value, - results, - submitted, - expandSearch, - selectedFilter, - selectFilter, - suggestions, - trendingStatuses, - trends, -}) => { +const SearchResults: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); - const handleLoadMore = () => expandSearch(selectedFilter); - const handleSelectFilter = (newActiveFilter: SearchFilter) => selectFilter(newActiveFilter); + const value = useAppSelector(state => state.search.get('submittedValue')); + const results = useAppSelector(state => state.search.get('results')); + const suggestions = useAppSelector(state => state.suggestions.items); + const trendingStatuses = useAppSelector(state => state.trending_statuses.items); + const trends = useAppSelector(state => state.trends.items); + const submitted = useAppSelector(state => state.search.get('submitted')); + const selectedFilter = useAppSelector(state => state.search.get('filter')); + + const handleLoadMore = () => dispatch(expandSearch(selectedFilter)); + const handleSelectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter)); useEffect(() => { dispatch(fetchTrendingStatuses()); diff --git a/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx b/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx index 4ca838072..109febdf4 100644 --- a/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx +++ b/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx @@ -31,8 +31,8 @@ const FollowRecommendationsList: React.FC = () => { return (
- {suggestions.size > 0 ? suggestions.map((suggestion: { account: string }) => ( - + {suggestions.size > 0 ? suggestions.map(suggestion => ( + )) : (
diff --git a/app/soapbox/reducers/suggestions.js b/app/soapbox/reducers/suggestions.ts similarity index 54% rename from app/soapbox/reducers/suggestions.js rename to app/soapbox/reducers/suggestions.ts index ac00eecf1..bca64c9e2 100644 --- a/app/soapbox/reducers/suggestions.js +++ b/app/soapbox/reducers/suggestions.ts @@ -1,4 +1,8 @@ -import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; +import { + Map as ImmutableMap, + List as ImmutableList, + Record as ImmutableRecord, +} from 'immutable'; import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'soapbox/actions/accounts'; import { DOMAIN_BLOCK_SUCCESS } from 'soapbox/actions/domain_blocks'; @@ -13,42 +17,64 @@ import { SUGGESTIONS_V2_FETCH_FAIL, } from '../actions/suggestions'; -const initialState = ImmutableMap({ - items: ImmutableList(), +import type { AnyAction } from 'redux'; + +type SuggestionSource = 'past_interactions' | 'staff' | 'global'; + +type ReducerSuggestion = { + source: SuggestionSource, + account: string, +} + +type SuggestionAccount = { + id: string, +} + +type Suggestion = { + source: SuggestionSource, + account: SuggestionAccount, +} + +const ReducerRecord = ImmutableRecord({ + items: ImmutableList>(), isLoading: false, }); -// Convert a v1 account into a v2 suggestion -const accountToSuggestion = account => { +type State = ReturnType; + +/** Convert a v1 account into a v2 suggestion. */ +const accountToSuggestion = (account: SuggestionAccount): ReducerSuggestion => { return { source: 'past_interactions', account: account.id, }; }; -const importAccounts = (state, accounts) => { +/** Import plain accounts into the reducer (legacy). */ +const importAccounts = (state: State, accounts: SuggestionAccount[]): State => { return state.withMutations(state => { - state.set('items', fromJS(accounts.map(accountToSuggestion))); + state.set('items', ImmutableList(accounts.map(account => ImmutableMap(accountToSuggestion(account))))); state.set('isLoading', false); }); }; -const importSuggestions = (state, suggestions) => { +/** Import full suggestion objects. */ +const importSuggestions = (state: State, suggestions: Suggestion[]): State => { return state.withMutations(state => { - state.set('items', fromJS(suggestions.map(x => ({ ...x, account: x.account.id })))); + state.set('items', ImmutableList(suggestions.map(x => ImmutableMap({ ...x, account: x.account.id })))); state.set('isLoading', false); }); }; -const dismissAccount = (state, accountId) => { +const dismissAccount = (state: State, accountId: string): State => { return state.update('items', items => items.filterNot(item => item.get('account') === accountId)); }; -const dismissAccounts = (state, accountIds) => { +const dismissAccounts = (state: State, accountIds: string[]): State => { return state.update('items', items => items.filterNot(item => accountIds.includes(item.get('account')))); }; -export default function suggestionsReducer(state = initialState, action) { +export default function suggestionsReducer(state = ReducerRecord(), action: AnyAction) { switch (action.type) { case SUGGESTIONS_FETCH_REQUEST: case SUGGESTIONS_V2_FETCH_REQUEST: diff --git a/app/soapbox/reducers/trending_statuses.js b/app/soapbox/reducers/trending_statuses.js deleted file mode 100644 index ee4fa234b..000000000 --- a/app/soapbox/reducers/trending_statuses.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable'; - -import { - TRENDING_STATUSES_FETCH_REQUEST, - TRENDING_STATUSES_FETCH_SUCCESS, -} from 'soapbox/actions/trending_statuses'; - -const initialState = ImmutableMap({ - items: ImmutableOrderedSet(), - isLoading: false, -}); - -const toIds = items => ImmutableOrderedSet(items.map(item => item.id)); - -const importStatuses = (state, statuses) => { - return state.withMutations(state => { - state.set('items', toIds(statuses)); - state.set('isLoading', false); - }); -}; - -export default function trending_statuses(state = initialState, action) { - switch (action.type) { - case TRENDING_STATUSES_FETCH_REQUEST: - return state.set('isLoading', true); - case TRENDING_STATUSES_FETCH_SUCCESS: - return importStatuses(state, action.statuses); - default: - return state; - } -} diff --git a/app/soapbox/reducers/trending_statuses.ts b/app/soapbox/reducers/trending_statuses.ts new file mode 100644 index 000000000..afaf5d9af --- /dev/null +++ b/app/soapbox/reducers/trending_statuses.ts @@ -0,0 +1,37 @@ +import { Record as ImmutableRecord, OrderedSet as ImmutableOrderedSet } from 'immutable'; + +import { + TRENDING_STATUSES_FETCH_REQUEST, + TRENDING_STATUSES_FETCH_SUCCESS, +} from 'soapbox/actions/trending_statuses'; + +import type { AnyAction } from 'redux'; + +const ReducerRecord = ImmutableRecord({ + items: ImmutableOrderedSet(), + isLoading: false, +}); + +type State = ReturnType; + +type IdEntity = { id: string }; + +const toIds = (items: IdEntity[]) => ImmutableOrderedSet(items.map(item => item.id)); + +const importStatuses = (state: State, statuses: IdEntity[]): State => { + return state.withMutations(state => { + state.set('items', toIds(statuses)); + state.set('isLoading', false); + }); +}; + +export default function trending_statuses(state = ReducerRecord(), action: AnyAction) { + switch (action.type) { + case TRENDING_STATUSES_FETCH_REQUEST: + return state.set('isLoading', true); + case TRENDING_STATUSES_FETCH_SUCCESS: + return importStatuses(state, action.statuses); + default: + return state; + } +} diff --git a/app/soapbox/reducers/trends.js b/app/soapbox/reducers/trends.ts similarity index 53% rename from app/soapbox/reducers/trends.js rename to app/soapbox/reducers/trends.ts index eb7df59bf..cb061c7b7 100644 --- a/app/soapbox/reducers/trends.js +++ b/app/soapbox/reducers/trends.ts @@ -1,4 +1,8 @@ -import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; +import { + Map as ImmutableMap, + Record as ImmutableRecord, + List as ImmutableList, +} from 'immutable'; import { TRENDS_FETCH_REQUEST, @@ -6,18 +10,20 @@ import { TRENDS_FETCH_FAIL, } from '../actions/trends'; -const initialState = ImmutableMap({ - items: ImmutableList(), +import type { AnyAction } from 'redux'; + +const ReducerRecord = ImmutableRecord({ + items: ImmutableList>(), isLoading: false, }); -export default function trendsReducer(state = initialState, action) { +export default function trendsReducer(state = ReducerRecord(), action: AnyAction) { switch (action.type) { case TRENDS_FETCH_REQUEST: return state.set('isLoading', true); case TRENDS_FETCH_SUCCESS: return state.withMutations(map => { - map.set('items', fromJS(action.tags.map((x => x)))); + map.set('items', ImmutableList(action.tags.map(ImmutableMap))); map.set('isLoading', false); }); case TRENDS_FETCH_FAIL: From 88404f32b6526329b2d8f492f0ac8f7c1842811e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 14 May 2022 13:18:31 -0500 Subject: [PATCH 03/38] Search reducer: convert to TypeScript --- .../compose/components/search_results.tsx | 8 ++-- app/soapbox/reducers/{search.js => search.ts} | 43 ++++++++++++++----- 2 files changed, 36 insertions(+), 15 deletions(-) rename app/soapbox/reducers/{search.js => search.ts} (69%) diff --git a/app/soapbox/features/compose/components/search_results.tsx b/app/soapbox/features/compose/components/search_results.tsx index d24c7631d..d3fa555fa 100644 --- a/app/soapbox/features/compose/components/search_results.tsx +++ b/app/soapbox/features/compose/components/search_results.tsx @@ -46,13 +46,13 @@ const SearchResults: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); - const value = useAppSelector(state => state.search.get('submittedValue')); - const results = useAppSelector(state => state.search.get('results')); + const value = useAppSelector(state => state.search.submittedValue); + const results = useAppSelector(state => state.search.results); const suggestions = useAppSelector(state => state.suggestions.items); const trendingStatuses = useAppSelector(state => state.trending_statuses.items); const trends = useAppSelector(state => state.trends.items); - const submitted = useAppSelector(state => state.search.get('submitted')); - const selectedFilter = useAppSelector(state => state.search.get('filter')); + const submitted = useAppSelector(state => state.search.submitted); + const selectedFilter = useAppSelector(state => state.search.filter); const handleLoadMore = () => dispatch(expandSearch(selectedFilter)); const handleSelectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter)); diff --git a/app/soapbox/reducers/search.js b/app/soapbox/reducers/search.ts similarity index 69% rename from app/soapbox/reducers/search.js rename to app/soapbox/reducers/search.ts index e086ef4a0..f315cbb90 100644 --- a/app/soapbox/reducers/search.js +++ b/app/soapbox/reducers/search.ts @@ -1,4 +1,10 @@ -import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; +import { + Map as ImmutableMap, + Record as ImmutableRecord, + List as ImmutableList, + OrderedSet as ImmutableOrderedSet, + fromJS, +} from 'immutable'; import { COMPOSE_MENTION, @@ -17,26 +23,39 @@ import { SEARCH_EXPAND_SUCCESS, } from '../actions/search'; -const initialState = ImmutableMap({ +import type { AnyAction } from 'redux'; + +const ReducerRecord = ImmutableRecord({ value: '', submitted: false, submittedValue: '', hidden: false, - results: ImmutableMap(), + results: ImmutableMap(), filter: 'accounts', }); -const toIds = items => { +type State = ReturnType; + +type IdEntity = { id: string }; +type SearchType = 'accounts' | 'statuses' | 'hashtags'; + +type Results = { + accounts: IdEntity[], + statuses: IdEntity[], + hashtags: Record[], +} + +const toIds = (items: IdEntity[]) => { return ImmutableOrderedSet(items.map(item => item.id)); }; -const importResults = (state, results, searchTerm, searchType) => { +const importResults = (state: State, results: Results, searchTerm: string, searchType: SearchType): State => { return state.withMutations(state => { if (state.get('value') === searchTerm && state.get('filter') === searchType) { state.set('results', ImmutableMap({ accounts: toIds(results.accounts), statuses: toIds(results.statuses), - hashtags: fromJS(results.hashtags), // it's a list of maps + hashtags: ImmutableList(results.hashtags.map(ImmutableMap)), // it's a list of maps accountsHasMore: results.accounts.length >= 20, statusesHasMore: results.statuses.length >= 20, hashtagsHasMore: results.hashtags.length >= 20, @@ -50,17 +69,19 @@ const importResults = (state, results, searchTerm, searchType) => { }); }; -const paginateResults = (state, searchType, results, searchTerm) => { +const paginateResults = (state: State, searchType: SearchType, results: Results, searchTerm: string): State => { return state.withMutations(state => { - if (state.get('value') === searchTerm) { + if (state.value === searchTerm) { state.setIn(['results', `${searchType}HasMore`], results[searchType].length >= 20); state.setIn(['results', `${searchType}Loaded`], true); state.updateIn(['results', searchType], items => { const data = results[searchType]; // Hashtags are a list of maps. Others are IDs. if (searchType === 'hashtags') { + // @ts-ignore return items.concat(fromJS(data)); } else { + // @ts-ignore return items.concat(toIds(data)); } }); @@ -68,7 +89,7 @@ const paginateResults = (state, searchType, results, searchTerm) => { }); }; -const handleSubmitted = (state, value) => { +const handleSubmitted = (state: State, value: string): State => { return state.withMutations(state => { state.set('results', ImmutableMap()); state.set('submitted', true); @@ -76,12 +97,12 @@ const handleSubmitted = (state, value) => { }); }; -export default function search(state = initialState, action) { +export default function search(state = ReducerRecord(), action: AnyAction) { switch (action.type) { case SEARCH_CHANGE: return state.set('value', action.value); case SEARCH_CLEAR: - return initialState; + return ReducerRecord(); case SEARCH_SHOW: return state.set('hidden', false); case COMPOSE_REPLY: From 640a6ab8eb00785123e9a734c8ded0f59e8acabd Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 14 May 2022 13:20:21 -0500 Subject: [PATCH 04/38] Delete SearchResultsContainer --- .../compose/components/search_results.tsx | 14 +------- .../containers/search_results_container.js | 33 ------------------- app/soapbox/features/search/index.tsx | 4 +-- 3 files changed, 3 insertions(+), 48 deletions(-) delete mode 100644 app/soapbox/features/compose/containers/search_results_container.js diff --git a/app/soapbox/features/compose/components/search_results.tsx b/app/soapbox/features/compose/components/search_results.tsx index d3fa555fa..2543e3626 100644 --- a/app/soapbox/features/compose/components/search_results.tsx +++ b/app/soapbox/features/compose/components/search_results.tsx @@ -29,20 +29,8 @@ const messages = defineMessages({ type SearchFilter = 'accounts' | 'statuses' | 'hashtags'; -interface ISearchResults { - value: string, - results: ImmutableMap, - submitted: boolean, - expandSearch: (filter: SearchFilter) => void, - selectedFilter: SearchFilter, - selectFilter: (filter: SearchFilter) => void, - suggestions: ImmutableList, - trendingStatuses: ImmutableList, - trends: ImmutableList, -} - /** Displays search results depending on the active tab. */ -const SearchResults: React.FC = () => { +const SearchResults: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); diff --git a/app/soapbox/features/compose/containers/search_results_container.js b/app/soapbox/features/compose/containers/search_results_container.js deleted file mode 100644 index 4ab9b712f..000000000 --- a/app/soapbox/features/compose/containers/search_results_container.js +++ /dev/null @@ -1,33 +0,0 @@ -import { connect } from 'react-redux'; - -import { fetchTrendingStatuses } from 'soapbox/actions/trending_statuses'; -import { getFeatures } from 'soapbox/utils/features'; - -import { expandSearch, setFilter } from '../../../actions/search'; -import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestions'; -import SearchResults from '../components/search_results'; - -const mapStateToProps = state => { - const instance = state.get('instance'); - - return { - value: state.getIn(['search', 'submittedValue']), - results: state.getIn(['search', 'results']), - suggestions: state.getIn(['suggestions', 'items']), - trendingStatuses: state.getIn(['trending_statuses', 'items']), - trends: state.getIn(['trends', 'items']), - submitted: state.getIn(['search', 'submitted']), - selectedFilter: state.getIn(['search', 'filter']), - features: getFeatures(instance), - }; -}; - -const mapDispatchToProps = dispatch => ({ - fetchSuggestions: () => dispatch(fetchSuggestions()), - fetchTrendingStatuses: () => dispatch(fetchTrendingStatuses()), - expandSearch: type => dispatch(expandSearch(type)), - dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))), - selectFilter: newActiveFilter => dispatch(setFilter(newActiveFilter)), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(SearchResults); diff --git a/app/soapbox/features/search/index.tsx b/app/soapbox/features/search/index.tsx index 3fdf625f6..c17691c37 100644 --- a/app/soapbox/features/search/index.tsx +++ b/app/soapbox/features/search/index.tsx @@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { Column } from 'soapbox/components/ui'; import Search from 'soapbox/features/compose/components/search'; -import SearchResultsContainer from 'soapbox/features/compose/containers/search_results_container'; +import SearchResults from 'soapbox/features/compose/components/search_results'; const messages = defineMessages({ heading: { id: 'column.search', defaultMessage: 'Search' }, @@ -16,7 +16,7 @@ const SearchPage = () => {
- +
); From 295eb40f14cb205a02e82b84cd9f7fc1efcd749a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 14 May 2022 13:23:37 -0500 Subject: [PATCH 05/38] SearchResults: remove unused import --- app/soapbox/features/compose/components/search_results.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/soapbox/features/compose/components/search_results.tsx b/app/soapbox/features/compose/components/search_results.tsx index 2543e3626..66d8f2062 100644 --- a/app/soapbox/features/compose/components/search_results.tsx +++ b/app/soapbox/features/compose/components/search_results.tsx @@ -16,10 +16,7 @@ import { Tabs } from '../../../components/ui'; import AccountContainer from '../../../containers/account_container'; import StatusContainer from '../../../containers/status_container'; -import type { - Map as ImmutableMap, - List as ImmutableList, -} from 'immutable'; +import type { Map as ImmutableMap } from 'immutable'; const messages = defineMessages({ accounts: { id: 'search_results.accounts', defaultMessage: 'People' }, From 2e192153ed6b3413d388cf0bc9b1d3e1e9d5beb0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 14 May 2022 14:13:58 -0500 Subject: [PATCH 06/38] Fix reducer tests --- app/soapbox/reducers/__tests__/search-test.js | 14 +++++++------- app/soapbox/reducers/__tests__/suggestions-test.js | 13 ++++++++----- app/soapbox/reducers/__tests__/trends-test.js | 10 +++++----- app/soapbox/reducers/search.ts | 2 +- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/soapbox/reducers/__tests__/search-test.js b/app/soapbox/reducers/__tests__/search-test.js index b1138b663..c3b8d1ea0 100644 --- a/app/soapbox/reducers/__tests__/search-test.js +++ b/app/soapbox/reducers/__tests__/search-test.js @@ -6,11 +6,11 @@ import { SEARCH_EXPAND_SUCCESS, } from 'soapbox/actions/search'; -import reducer from '../search'; +import reducer, { ReducerRecord } from '../search'; describe('search reducer', () => { it('should return the initial state', () => { - expect(reducer(undefined, {})).toEqual(ImmutableMap({ + expect(reducer(undefined, {})).toEqual(ReducerRecord({ value: '', submitted: false, submittedValue: '', @@ -22,7 +22,7 @@ describe('search reducer', () => { describe('SEARCH_CHANGE', () => { it('sets the value', () => { - const state = ImmutableMap({ value: 'hell' }); + const state = ReducerRecord({ value: 'hell' }); const action = { type: SEARCH_CHANGE, value: 'hello' }; expect(reducer(state, action).get('value')).toEqual('hello'); }); @@ -30,7 +30,7 @@ describe('search reducer', () => { describe('SEARCH_CLEAR', () => { it('resets the state', () => { - const state = ImmutableMap({ + const state = ReducerRecord({ value: 'hello world', submitted: true, submittedValue: 'hello world', @@ -41,7 +41,7 @@ describe('search reducer', () => { const action = { type: SEARCH_CLEAR }; - const expected = ImmutableMap({ + const expected = ReducerRecord({ value: '', submitted: false, submittedValue: '', @@ -56,7 +56,7 @@ describe('search reducer', () => { describe(SEARCH_EXPAND_SUCCESS, () => { it('imports hashtags as maps', () => { - const state = ImmutableMap({ + const state = ReducerRecord({ value: 'artist', submitted: true, submittedValue: 'artist', @@ -82,7 +82,7 @@ describe('search reducer', () => { searchType: 'hashtags', }; - const expected = ImmutableMap({ + const expected = ReducerRecord({ value: 'artist', submitted: true, submittedValue: 'artist', diff --git a/app/soapbox/reducers/__tests__/suggestions-test.js b/app/soapbox/reducers/__tests__/suggestions-test.js index 7da0b7f75..3a11b5b56 100644 --- a/app/soapbox/reducers/__tests__/suggestions-test.js +++ b/app/soapbox/reducers/__tests__/suggestions-test.js @@ -1,4 +1,7 @@ -import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; +import { + Record as ImmutableRecord, + fromJS, +} from 'immutable'; import { SUGGESTIONS_DISMISS } from 'soapbox/actions/suggestions'; @@ -6,10 +9,10 @@ import reducer from '../suggestions'; describe('suggestions reducer', () => { it('should return the initial state', () => { - expect(reducer(undefined, {})).toEqual(ImmutableMap({ - items: ImmutableList(), - isLoading: false, - })); + const result = reducer(undefined, {}); + expect(ImmutableRecord.isRecord(result)).toBe(true); + expect(result.items.isEmpty()).toBe(true); + expect(result.isLoading).toBe(false); }); describe('SUGGESTIONS_DISMISS', () => { diff --git a/app/soapbox/reducers/__tests__/trends-test.js b/app/soapbox/reducers/__tests__/trends-test.js index 5ebeabb31..22d7d34e4 100644 --- a/app/soapbox/reducers/__tests__/trends-test.js +++ b/app/soapbox/reducers/__tests__/trends-test.js @@ -1,12 +1,12 @@ -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { Record as ImmutableRecord } from 'immutable'; import reducer from '../trends'; describe('trends reducer', () => { it('should return the initial state', () => { - expect(reducer(undefined, {})).toEqual(ImmutableMap({ - items: ImmutableList(), - isLoading: false, - })); + const result = reducer(undefined, {}); + expect(ImmutableRecord.isRecord(result)).toBe(true); + expect(result.items.isEmpty()).toBe(true); + expect(result.isLoading).toBe(false); }); }); diff --git a/app/soapbox/reducers/search.ts b/app/soapbox/reducers/search.ts index f315cbb90..64d2a6a8c 100644 --- a/app/soapbox/reducers/search.ts +++ b/app/soapbox/reducers/search.ts @@ -25,7 +25,7 @@ import { import type { AnyAction } from 'redux'; -const ReducerRecord = ImmutableRecord({ +export const ReducerRecord = ImmutableRecord({ value: '', submitted: false, submittedValue: '', From 6bf0d5847b6be949cbe521ce0d2ca3f127bdeed4 Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Fri, 13 May 2022 12:12:19 -0400 Subject: [PATCH 07/38] Convert display-name to TS --- .../components/__tests__/display_name.test.js | 2 +- app/soapbox/components/display-name.tsx | 51 +++++++++++ app/soapbox/components/display_name.js | 85 ------------------- .../components/moved_note.tsx | 2 +- .../features/aliases/components/account.js | 2 +- app/soapbox/features/birthdays/account.tsx | 2 +- .../features/chats/components/chat.tsx | 2 +- .../directory/components/account_card.js | 4 +- .../components/account.tsx | 2 +- .../components/account_authorize.tsx | 2 +- .../features/list_adder/components/account.js | 2 +- .../list_editor/components/account.js | 2 +- .../components/follow_request.js | 2 +- .../features/reply_mentions/account.js | 2 +- .../components/scheduled_status.js | 2 +- 15 files changed, 65 insertions(+), 99 deletions(-) create mode 100644 app/soapbox/components/display-name.tsx delete mode 100644 app/soapbox/components/display_name.js diff --git a/app/soapbox/components/__tests__/display_name.test.js b/app/soapbox/components/__tests__/display_name.test.js index 00e175d40..6d0a05ed5 100644 --- a/app/soapbox/components/__tests__/display_name.test.js +++ b/app/soapbox/components/__tests__/display_name.test.js @@ -3,7 +3,7 @@ import React from 'react'; import { normalizeAccount } from 'soapbox/normalizers'; import { render, screen } from '../../jest/test-helpers'; -import DisplayName from '../display_name'; +import DisplayName from '../display-name'; describe('', () => { it('renders display name + account name', () => { diff --git a/app/soapbox/components/display-name.tsx b/app/soapbox/components/display-name.tsx new file mode 100644 index 000000000..d27d0efc7 --- /dev/null +++ b/app/soapbox/components/display-name.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; + +import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; +import { useSoapboxConfig } from 'soapbox/hooks'; + +import { getAcct } from '../utils/accounts'; + +import Icon from './icon'; +import RelativeTimestamp from './relative_timestamp'; +import VerificationBadge from './verification_badge'; + +import type { Account } from 'soapbox/types/entities'; + +interface IDisplayName { + account: Account + withDate?: boolean +} + +const DisplayName: React.FC = ({ account, children, withDate = false }) => { + const { displayFqn = false } = useSoapboxConfig(); + const { created_at: createdAt, verified } = account; + + const joinedAt = createdAt ? ( +
+ + +
+ ) : null; + + const displayName = ( + + + {verified && } + {withDate && joinedAt} + + ); + + const suffix = (@{getAcct(account, displayFqn)}); + + return ( + + + {displayName} + + {suffix} + {children} + + ); +}; + +export default DisplayName; diff --git a/app/soapbox/components/display_name.js b/app/soapbox/components/display_name.js deleted file mode 100644 index 141988697..000000000 --- a/app/soapbox/components/display_name.js +++ /dev/null @@ -1,85 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { connect } from 'react-redux'; - -import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper'; -import { displayFqn } from 'soapbox/utils/state'; - -import { getAcct } from '../utils/accounts'; - -import Icon from './icon'; -import RelativeTimestamp from './relative_timestamp'; -import VerificationBadge from './verification_badge'; - -const mapStateToProps = state => { - return { - displayFqn: displayFqn(state), - }; -}; - -export default @connect(mapStateToProps) -class DisplayName extends React.PureComponent { - - static propTypes = { - account: ImmutablePropTypes.record.isRequired, - displayFqn: PropTypes.bool, - others: ImmutablePropTypes.list, - children: PropTypes.node, - withDate: PropTypes.bool, - }; - - static defaultProps = { - withDate: false, - } - - render() { - const { account, displayFqn, others, children, withDate } = this.props; - - let displayName, suffix; - const verified = account.get('verified'); - - const createdAt = account.get('created_at'); - - const joinedAt = createdAt ? ( -
- - -
- ) : null; - - if (others?.size > 1) { - displayName = others.take(2).map(a => ( - - - {verified && } - {withDate && joinedAt} - - )).reduce((prev, cur) => [prev, ', ', cur]); - - if (others.size - 2 > 0) { - suffix = `+${others.size - 2}`; - } - } else { - displayName = ( - - - {verified && } - {withDate && joinedAt} - - ); - suffix = @{getAcct(account, displayFqn)}; - } - - return ( - - - {displayName} - - {suffix} - {children} - - ); - } - -} diff --git a/app/soapbox/features/account_timeline/components/moved_note.tsx b/app/soapbox/features/account_timeline/components/moved_note.tsx index 6e901252d..0ba6d8da5 100644 --- a/app/soapbox/features/account_timeline/components/moved_note.tsx +++ b/app/soapbox/features/account_timeline/components/moved_note.tsx @@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl'; import { NavLink } from 'react-router-dom'; import AvatarOverlay from 'soapbox/components/avatar_overlay'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import Icon from 'soapbox/components/icon'; import type { Account as AccountEntity } from 'soapbox/types/entities'; diff --git a/app/soapbox/features/aliases/components/account.js b/app/soapbox/features/aliases/components/account.js index 38bce11bb..8d0ccfbe3 100644 --- a/app/soapbox/features/aliases/components/account.js +++ b/app/soapbox/features/aliases/components/account.js @@ -7,7 +7,7 @@ import { connect } from 'react-redux'; import { addToAliases } from 'soapbox/actions/aliases'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import IconButton from 'soapbox/components/icon_button'; import { makeGetAccount } from 'soapbox/selectors'; import { getFeatures } from 'soapbox/utils/features'; diff --git a/app/soapbox/features/birthdays/account.tsx b/app/soapbox/features/birthdays/account.tsx index 94b7e08a6..eea3266e2 100644 --- a/app/soapbox/features/birthdays/account.tsx +++ b/app/soapbox/features/birthdays/account.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import Icon from 'soapbox/components/icon'; import Permalink from 'soapbox/components/permalink'; import { useAppSelector } from 'soapbox/hooks'; diff --git a/app/soapbox/features/chats/components/chat.tsx b/app/soapbox/features/chats/components/chat.tsx index afb1d9df8..8e10a2f91 100644 --- a/app/soapbox/features/chats/components/chat.tsx +++ b/app/soapbox/features/chats/components/chat.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import Icon from 'soapbox/components/icon'; import emojify from 'soapbox/features/emoji/emoji'; import { useAppSelector } from 'soapbox/hooks'; diff --git a/app/soapbox/features/directory/components/account_card.js b/app/soapbox/features/directory/components/account_card.js index 83a48b256..1db12c96a 100644 --- a/app/soapbox/features/directory/components/account_card.js +++ b/app/soapbox/features/directory/components/account_card.js @@ -8,7 +8,7 @@ import { connect } from 'react-redux'; import { getSettings } from 'soapbox/actions/settings'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import Permalink from 'soapbox/components/permalink'; import RelativeTimestamp from 'soapbox/components/relative_timestamp'; import { Text } from 'soapbox/components/ui'; @@ -82,4 +82,4 @@ class AccountCard extends ImmutablePureComponent { ); } -} \ No newline at end of file +} diff --git a/app/soapbox/features/follow_recommendations/components/account.tsx b/app/soapbox/features/follow_recommendations/components/account.tsx index 8ba56b497..3371a971f 100644 --- a/app/soapbox/features/follow_recommendations/components/account.tsx +++ b/app/soapbox/features/follow_recommendations/components/account.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import Permalink from 'soapbox/components/permalink'; import ActionButton from 'soapbox/features/ui/components/action_button'; import { useAppSelector } from 'soapbox/hooks'; diff --git a/app/soapbox/features/follow_requests/components/account_authorize.tsx b/app/soapbox/features/follow_requests/components/account_authorize.tsx index cd46f7817..ff66df26b 100644 --- a/app/soapbox/features/follow_requests/components/account_authorize.tsx +++ b/app/soapbox/features/follow_requests/components/account_authorize.tsx @@ -4,7 +4,7 @@ import { useDispatch } from 'react-redux'; import { authorizeFollowRequest, rejectFollowRequest } from 'soapbox/actions/accounts'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import IconButton from 'soapbox/components/icon_button'; import Permalink from 'soapbox/components/permalink'; import { useAppSelector } from 'soapbox/hooks'; diff --git a/app/soapbox/features/list_adder/components/account.js b/app/soapbox/features/list_adder/components/account.js index 014f3ac22..184339572 100644 --- a/app/soapbox/features/list_adder/components/account.js +++ b/app/soapbox/features/list_adder/components/account.js @@ -5,7 +5,7 @@ import { injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import Avatar from '../../../components/avatar'; -import DisplayName from '../../../components/display_name'; +import DisplayName from '../../../components/display-name'; import { makeGetAccount } from '../../../selectors'; const makeMapStateToProps = () => { diff --git a/app/soapbox/features/list_editor/components/account.js b/app/soapbox/features/list_editor/components/account.js index 8054bb52f..e73a465ca 100644 --- a/app/soapbox/features/list_editor/components/account.js +++ b/app/soapbox/features/list_editor/components/account.js @@ -7,7 +7,7 @@ import { connect } from 'react-redux'; import { removeFromListEditor, addToListEditor } from '../../../actions/lists'; import Avatar from '../../../components/avatar'; -import DisplayName from '../../../components/display_name'; +import DisplayName from '../../../components/display-name'; import IconButton from '../../../components/icon_button'; import { makeGetAccount } from '../../../selectors'; diff --git a/app/soapbox/features/notifications/components/follow_request.js b/app/soapbox/features/notifications/components/follow_request.js index 2bf25131b..4c386227e 100644 --- a/app/soapbox/features/notifications/components/follow_request.js +++ b/app/soapbox/features/notifications/components/follow_request.js @@ -5,7 +5,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { defineMessages, injectIntl } from 'react-intl'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import IconButton from 'soapbox/components/icon_button'; import Permalink from 'soapbox/components/permalink'; diff --git a/app/soapbox/features/reply_mentions/account.js b/app/soapbox/features/reply_mentions/account.js index 1ef60ddf6..b9bf62d51 100644 --- a/app/soapbox/features/reply_mentions/account.js +++ b/app/soapbox/features/reply_mentions/account.js @@ -7,7 +7,7 @@ import { connect } from 'react-redux'; import { fetchAccount } from 'soapbox/actions/accounts'; import { addToMentions, removeFromMentions } from 'soapbox/actions/compose'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import IconButton from 'soapbox/components/icon_button'; import { makeGetAccount } from 'soapbox/selectors'; diff --git a/app/soapbox/features/scheduled_statuses/components/scheduled_status.js b/app/soapbox/features/scheduled_statuses/components/scheduled_status.js index f5efdd406..4b3a890b0 100644 --- a/app/soapbox/features/scheduled_statuses/components/scheduled_status.js +++ b/app/soapbox/features/scheduled_statuses/components/scheduled_status.js @@ -6,7 +6,7 @@ import { Link, NavLink } from 'react-router-dom'; import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display_name'; +import DisplayName from 'soapbox/components/display-name'; import RelativeTimestamp from 'soapbox/components/relative_timestamp'; import StatusContent from 'soapbox/components/status_content'; import StatusReplyMentions from 'soapbox/components/status_reply_mentions'; From eecbbb839a508cfbc18eabbb7f3d48a87a6e7769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 16 May 2022 20:30:42 +0200 Subject: [PATCH 08/38] Use FormattedList for 'Replying to' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../components/status_reply_mentions.js | 33 +++++++++++-------- .../compose/components/reply_mentions.tsx | 17 +++++++--- .../status/components/quoted_status.tsx | 18 ++++++---- app/soapbox/locales/ar.json | 2 -- app/soapbox/locales/ast.json | 2 -- app/soapbox/locales/bg.json | 2 -- app/soapbox/locales/bn.json | 2 -- app/soapbox/locales/br.json | 2 -- app/soapbox/locales/ca.json | 2 -- app/soapbox/locales/co.json | 2 -- app/soapbox/locales/cs.json | 2 -- app/soapbox/locales/cy.json | 2 -- app/soapbox/locales/da.json | 2 -- app/soapbox/locales/de.json | 4 +-- app/soapbox/locales/el.json | 2 -- app/soapbox/locales/en-Shaw.json | 4 +-- app/soapbox/locales/en.json | 2 -- app/soapbox/locales/eo.json | 2 -- app/soapbox/locales/es-AR.json | 2 -- app/soapbox/locales/es.json | 2 -- app/soapbox/locales/et.json | 2 -- app/soapbox/locales/eu.json | 2 -- app/soapbox/locales/fa.json | 2 -- app/soapbox/locales/fi.json | 2 -- app/soapbox/locales/fr.json | 2 -- app/soapbox/locales/ga.json | 2 -- app/soapbox/locales/gl.json | 2 -- app/soapbox/locales/he.json | 4 +-- app/soapbox/locales/hi.json | 2 -- app/soapbox/locales/hr.json | 2 -- app/soapbox/locales/hu.json | 2 -- app/soapbox/locales/hy.json | 2 -- app/soapbox/locales/id.json | 2 -- app/soapbox/locales/io.json | 2 -- app/soapbox/locales/is.json | 4 +-- app/soapbox/locales/it.json | 4 +-- app/soapbox/locales/ja.json | 2 -- app/soapbox/locales/ka.json | 2 -- app/soapbox/locales/kk.json | 2 -- app/soapbox/locales/ko.json | 2 -- app/soapbox/locales/lt.json | 2 -- app/soapbox/locales/lv.json | 2 -- app/soapbox/locales/mk.json | 2 -- app/soapbox/locales/ms.json | 2 -- app/soapbox/locales/nl.json | 2 -- app/soapbox/locales/nn.json | 2 -- app/soapbox/locales/no.json | 2 -- app/soapbox/locales/oc.json | 2 -- app/soapbox/locales/pl.json | 4 +-- app/soapbox/locales/pt-BR.json | 2 -- app/soapbox/locales/pt.json | 2 -- app/soapbox/locales/ro.json | 2 -- app/soapbox/locales/ru.json | 2 -- app/soapbox/locales/sk.json | 2 -- app/soapbox/locales/sl.json | 2 -- app/soapbox/locales/sq.json | 2 -- app/soapbox/locales/sr-Latn.json | 2 -- app/soapbox/locales/sr.json | 2 -- app/soapbox/locales/sv.json | 2 -- app/soapbox/locales/ta.json | 2 -- app/soapbox/locales/te.json | 2 -- app/soapbox/locales/th.json | 2 -- app/soapbox/locales/tr.json | 2 -- app/soapbox/locales/uk.json | 2 -- app/soapbox/locales/zh-CN.json | 2 -- app/soapbox/locales/zh-HK.json | 2 -- app/soapbox/locales/zh-TW.json | 2 -- 67 files changed, 56 insertions(+), 152 deletions(-) diff --git a/app/soapbox/components/status_reply_mentions.js b/app/soapbox/components/status_reply_mentions.js index 76a48d5df..0809d6085 100644 --- a/app/soapbox/components/status_reply_mentions.js +++ b/app/soapbox/components/status_reply_mentions.js @@ -1,8 +1,9 @@ +import { List as ImmutableList } from 'immutable'; import PropTypes from 'prop-types'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { FormattedMessage, injectIntl } from 'react-intl'; +import { FormattedList, FormattedMessage, injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; @@ -42,7 +43,7 @@ class StatusReplyMentions extends ImmutablePureComponent { return null; } - const to = status.get('mentions', []); + const to = status.get('mentions', ImmutableList()); // The post is a reply, but it has no mentions. // Rare, but it can happen. @@ -58,23 +59,27 @@ class StatusReplyMentions extends ImmutablePureComponent { } // The typical case with a reply-to and a list of mentions. + const accounts = to.slice(0, 2).map(account => ( + + @{account.get('username')} + + )).toArray(); + + if (to.size > 2) { + accounts.push( + + + , + ); + } + return (
(<> - - @{account.get('username')} - - {' '} - )), - more: to.size > 2 && ( - - - - ), + accounts: , }} />
diff --git a/app/soapbox/features/compose/components/reply_mentions.tsx b/app/soapbox/features/compose/components/reply_mentions.tsx index 2d09134e0..c25c2f3df 100644 --- a/app/soapbox/features/compose/components/reply_mentions.tsx +++ b/app/soapbox/features/compose/components/reply_mentions.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { FormattedMessage } from 'react-intl'; +import { FormattedList, FormattedMessage } from 'react-intl'; import { useDispatch } from 'react-redux'; import { openModal } from 'soapbox/actions/modals'; @@ -47,14 +47,23 @@ const ReplyMentions: React.FC = () => { ); } + const accounts = to.slice(0, 2).map((acct: string) => ( + @{acct.split('@')[0]} + )).toArray(); + + if (to.size > 2) { + accounts.push( + , + ); + } + return ( <>@{acct.split('@')[0]}{' '}), - more: to.size > 2 && , + accounts: , }} /> diff --git a/app/soapbox/features/status/components/quoted_status.tsx b/app/soapbox/features/status/components/quoted_status.tsx index aff5940be..c91dd616d 100644 --- a/app/soapbox/features/status/components/quoted_status.tsx +++ b/app/soapbox/features/status/components/quoted_status.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames'; import { History } from 'history'; import React from 'react'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl, FormattedMessage, IntlShape } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage, IntlShape, FormattedList } from 'react-intl'; import { withRouter } from 'react-router-dom'; import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; @@ -67,10 +67,9 @@ class QuotedStatus extends ImmutablePureComponent {
@@ -84,14 +83,21 @@ class QuotedStatus extends ImmutablePureComponent { } } + const accounts = to.slice(0, 2).map(account => <>@{account.username}).toArray(); + + if (to.size > 2) { + accounts.push( + , + ); + } + return (
`@${account.username} `), - more: to.size > 2 && , + accounts: , }} />
diff --git a/app/soapbox/locales/ar.json b/app/soapbox/locales/ar.json index 5031eb79d..d1e705697 100644 --- a/app/soapbox/locales/ar.json +++ b/app/soapbox/locales/ar.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "إلغاء", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/ast.json b/app/soapbox/locales/ast.json index 18f0c07df..ef5c65bd9 100644 --- a/app/soapbox/locales/ast.json +++ b/app/soapbox/locales/ast.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Encaboxar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/bg.json b/app/soapbox/locales/bg.json index b9f8ae1e1..7a62a52f0 100644 --- a/app/soapbox/locales/bg.json +++ b/app/soapbox/locales/bg.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Отказ", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/bn.json b/app/soapbox/locales/bn.json index 54b6a65eb..c030722fa 100644 --- a/app/soapbox/locales/bn.json +++ b/app/soapbox/locales/bn.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "বাতিল করতে", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/br.json b/app/soapbox/locales/br.json index af5a58c8d..1a0d58e9a 100644 --- a/app/soapbox/locales/br.json +++ b/app/soapbox/locales/br.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/ca.json b/app/soapbox/locales/ca.json index 0a3b7fa54..e36ad89bc 100644 --- a/app/soapbox/locales/ca.json +++ b/app/soapbox/locales/ca.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel·lar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Bloquejar {target}", "report.block_hint": "També vols bloquejar aquest compte?", diff --git a/app/soapbox/locales/co.json b/app/soapbox/locales/co.json index 390ef9764..79214836a 100644 --- a/app/soapbox/locales/co.json +++ b/app/soapbox/locales/co.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Annullà", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/cs.json b/app/soapbox/locales/cs.json index 96d275865..155e1fa95 100644 --- a/app/soapbox/locales/cs.json +++ b/app/soapbox/locales/cs.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Zrušit", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Zablokovat {target}", "report.block_hint": "Chcete zablokovat tento účet?", diff --git a/app/soapbox/locales/cy.json b/app/soapbox/locales/cy.json index a087fd35c..bfc9c701b 100644 --- a/app/soapbox/locales/cy.json +++ b/app/soapbox/locales/cy.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Canslo", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/da.json b/app/soapbox/locales/da.json index b879b4a51..a935d51cf 100644 --- a/app/soapbox/locales/da.json +++ b/app/soapbox/locales/da.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Annuller", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/de.json b/app/soapbox/locales/de.json index 95bce86fc..67067f2bf 100644 --- a/app/soapbox/locales/de.json +++ b/app/soapbox/locales/de.json @@ -859,8 +859,8 @@ "reply_indicator.cancel": "Abbrechen", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "und {count, plural, one {einen weiteren Nutzer} other {# weitere Nutzer}}", - "reply_mentions.reply": "Antwort an {accounts}{more}", + "reply_mentions.more": "{count, plural, one {einen weiteren Nutzer} other {# weitere Nutzer}}", + "reply_mentions.reply": "Antwort an {accounts}", "reply_mentions.reply_empty": "Antwort auf einen Beitrag", "report.block": "{target} blockieren.", "report.block_hint": "Soll dieses Konto zusammen mit der Meldung auch gleich blockiert werden?", diff --git a/app/soapbox/locales/el.json b/app/soapbox/locales/el.json index 4aba909aa..b601e68e8 100644 --- a/app/soapbox/locales/el.json +++ b/app/soapbox/locales/el.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Άκυρο", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/en-Shaw.json b/app/soapbox/locales/en-Shaw.json index 50f8c36ae..a0c457f11 100644 --- a/app/soapbox/locales/en-Shaw.json +++ b/app/soapbox/locales/en-Shaw.json @@ -859,8 +859,8 @@ "reply_indicator.cancel": "𐑒𐑨𐑯𐑕𐑩𐑤", "reply_mentions.account.add": "𐑨𐑛 𐑑 𐑥𐑧𐑯𐑖𐑩𐑯𐑟", "reply_mentions.account.remove": "𐑮𐑦𐑥𐑵𐑝 𐑓𐑮𐑪𐑥 𐑥𐑧𐑯𐑖𐑩𐑯𐑟", - "reply_mentions.more": "𐑯 {count} 𐑥𐑹", - "reply_mentions.reply": "𐑮𐑦𐑐𐑤𐑲𐑦𐑙 𐑑 {accounts}{more}", + "reply_mentions.more": "{count} 𐑥𐑹", + "reply_mentions.reply": "𐑮𐑦𐑐𐑤𐑲𐑦𐑙 𐑑 {accounts}", "reply_mentions.reply_empty": "𐑮𐑦𐑐𐑤𐑲𐑦𐑙 𐑑 𐑐𐑴𐑕𐑑", "report.block": "𐑚𐑤𐑪𐑒 {target}", "report.block_hint": "𐑛𐑵 𐑿 𐑷𐑤𐑕𐑴 𐑢𐑪𐑯𐑑 𐑑 𐑚𐑤𐑪𐑒 𐑞𐑦𐑕 𐑩𐑒𐑬𐑯𐑑?", diff --git a/app/soapbox/locales/en.json b/app/soapbox/locales/en.json index a7c1d1ce1..e6267ac94 100644 --- a/app/soapbox/locales/en.json +++ b/app/soapbox/locales/en.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/eo.json b/app/soapbox/locales/eo.json index 879fdde3b..4c3bd6166 100644 --- a/app/soapbox/locales/eo.json +++ b/app/soapbox/locales/eo.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Nuligi", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/es-AR.json b/app/soapbox/locales/es-AR.json index e1fa60e68..eb56879a0 100644 --- a/app/soapbox/locales/es-AR.json +++ b/app/soapbox/locales/es-AR.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancelar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/es.json b/app/soapbox/locales/es.json index 2e0ab4ec9..c4a3e99cb 100644 --- a/app/soapbox/locales/es.json +++ b/app/soapbox/locales/es.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancelar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/et.json b/app/soapbox/locales/et.json index 5a4eb43c6..636be30e0 100644 --- a/app/soapbox/locales/et.json +++ b/app/soapbox/locales/et.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Tühista", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/eu.json b/app/soapbox/locales/eu.json index 3a3881aca..a16e4332b 100644 --- a/app/soapbox/locales/eu.json +++ b/app/soapbox/locales/eu.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Utzi", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/fa.json b/app/soapbox/locales/fa.json index 863046301..b9e9da44d 100644 --- a/app/soapbox/locales/fa.json +++ b/app/soapbox/locales/fa.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "لغو", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/fi.json b/app/soapbox/locales/fi.json index 2a29e6c20..d806c2fc6 100644 --- a/app/soapbox/locales/fi.json +++ b/app/soapbox/locales/fi.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Peruuta", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/fr.json b/app/soapbox/locales/fr.json index 743341596..373c89021 100644 --- a/app/soapbox/locales/fr.json +++ b/app/soapbox/locales/fr.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Annuler", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/ga.json b/app/soapbox/locales/ga.json index 5e6cb3a8b..eec9f2971 100644 --- a/app/soapbox/locales/ga.json +++ b/app/soapbox/locales/ga.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/gl.json b/app/soapbox/locales/gl.json index a5a04c57c..3ea35cb9f 100644 --- a/app/soapbox/locales/gl.json +++ b/app/soapbox/locales/gl.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancelar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/he.json b/app/soapbox/locales/he.json index 69ae4d20a..21717e89f 100644 --- a/app/soapbox/locales/he.json +++ b/app/soapbox/locales/he.json @@ -859,8 +859,8 @@ "reply_indicator.cancel": "ביטול", "reply_mentions.account.add": "הוסף לאזכורים", "reply_mentions.account.remove": "הסר מהאזכורים", - "reply_mentions.more": "ו-{count} עוד", - "reply_mentions.reply": "משיב ל-{accounts}{more}", + "reply_mentions.more": "{count} עוד", + "reply_mentions.reply": "משיב ל-{accounts}", "reply_mentions.reply_empty": "משיב לפוסט", "report.block": "חסום {target}", "report.block_hint": "האם גם אתה רוצה לחסום את החשבון הזה?", diff --git a/app/soapbox/locales/hi.json b/app/soapbox/locales/hi.json index 2080a931a..292d6a033 100644 --- a/app/soapbox/locales/hi.json +++ b/app/soapbox/locales/hi.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/hr.json b/app/soapbox/locales/hr.json index e78476954..ca5451121 100644 --- a/app/soapbox/locales/hr.json +++ b/app/soapbox/locales/hr.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Otkaži", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/hu.json b/app/soapbox/locales/hu.json index 031c6c0a7..0d5b56411 100644 --- a/app/soapbox/locales/hu.json +++ b/app/soapbox/locales/hu.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Mégsem", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/hy.json b/app/soapbox/locales/hy.json index e023bb148..c2638e2d4 100644 --- a/app/soapbox/locales/hy.json +++ b/app/soapbox/locales/hy.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Չեղարկել", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/id.json b/app/soapbox/locales/id.json index 92bd85bdd..904f73cc5 100644 --- a/app/soapbox/locales/id.json +++ b/app/soapbox/locales/id.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Batal", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/io.json b/app/soapbox/locales/io.json index 0a5bffb3c..f85f36a5a 100644 --- a/app/soapbox/locales/io.json +++ b/app/soapbox/locales/io.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Nihiligar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/is.json b/app/soapbox/locales/is.json index e3eea2422..49e906eb5 100644 --- a/app/soapbox/locales/is.json +++ b/app/soapbox/locales/is.json @@ -788,8 +788,8 @@ "reply_indicator.cancel": "Hætta við", "reply_mentions.account.add": "Bæta við í tilvísanirnar", "reply_mentions.account.remove": "Fjarlægja úr tilvísunum", - "reply_mentions.more": "og {count} fleirum", - "reply_mentions.reply": "Að svara {accounts}{more}", + "reply_mentions.more": "{count} fleirum", + "reply_mentions.reply": "Að svara {accounts}", "reply_mentions.reply_empty": "Að svara færslu", "report.block": "Loka á {target}", "report.block_hint": "Viltu líka loka á þennan reikning?", diff --git a/app/soapbox/locales/it.json b/app/soapbox/locales/it.json index 57e54f718..e565fb5d6 100644 --- a/app/soapbox/locales/it.json +++ b/app/soapbox/locales/it.json @@ -859,8 +859,8 @@ "reply_indicator.cancel": "Annulla", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "e ancora {count}", - "reply_mentions.reply": "Risponde a {accounts}{more}", + "reply_mentions.more": "ancora {count}", + "reply_mentions.reply": "Risponde a {accounts}", "reply_mentions.reply_empty": "Rispondendo al contenuto", "report.block": "Blocca {target}", "report.block_hint": "Vuoi anche bloccare questa persona?", diff --git a/app/soapbox/locales/ja.json b/app/soapbox/locales/ja.json index ea90e3e07..931c89326 100644 --- a/app/soapbox/locales/ja.json +++ b/app/soapbox/locales/ja.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "キャンセル", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "{target}さんをブロック", "report.block_hint": "このアカウントをブロックしますか?", diff --git a/app/soapbox/locales/ka.json b/app/soapbox/locales/ka.json index 05f781c9b..950b4e69c 100644 --- a/app/soapbox/locales/ka.json +++ b/app/soapbox/locales/ka.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "უარყოფა", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/kk.json b/app/soapbox/locales/kk.json index 0bf4c7faf..40f4f25ac 100644 --- a/app/soapbox/locales/kk.json +++ b/app/soapbox/locales/kk.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Қайтып алу", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/ko.json b/app/soapbox/locales/ko.json index 6e56a125d..55bc1e1bb 100644 --- a/app/soapbox/locales/ko.json +++ b/app/soapbox/locales/ko.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "취소", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/lt.json b/app/soapbox/locales/lt.json index a369a6ec5..44b913afb 100644 --- a/app/soapbox/locales/lt.json +++ b/app/soapbox/locales/lt.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/lv.json b/app/soapbox/locales/lv.json index a8fb3a793..c97bca375 100644 --- a/app/soapbox/locales/lv.json +++ b/app/soapbox/locales/lv.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/mk.json b/app/soapbox/locales/mk.json index 501a56cd9..c760d08e0 100644 --- a/app/soapbox/locales/mk.json +++ b/app/soapbox/locales/mk.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/ms.json b/app/soapbox/locales/ms.json index 880fd2938..eaafcd52f 100644 --- a/app/soapbox/locales/ms.json +++ b/app/soapbox/locales/ms.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/nl.json b/app/soapbox/locales/nl.json index 1c1c00c3f..47795ef3d 100644 --- a/app/soapbox/locales/nl.json +++ b/app/soapbox/locales/nl.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Annuleren", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/nn.json b/app/soapbox/locales/nn.json index 41a2b7f85..26e4c3134 100644 --- a/app/soapbox/locales/nn.json +++ b/app/soapbox/locales/nn.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancel", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/no.json b/app/soapbox/locales/no.json index 77ac02363..fe86be051 100644 --- a/app/soapbox/locales/no.json +++ b/app/soapbox/locales/no.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Avbryt", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/oc.json b/app/soapbox/locales/oc.json index c952d6eea..64b471abc 100644 --- a/app/soapbox/locales/oc.json +++ b/app/soapbox/locales/oc.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Anullar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index e9a38c69c..d5c8b15e5 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -876,8 +876,8 @@ "reply_indicator.cancel": "Anuluj", "reply_mentions.account.add": "Dodaj do wspomnianych", "reply_mentions.account.remove": "Usuń z wspomnianych", - "reply_mentions.more": "i {count} więcej", - "reply_mentions.reply": "W odpowiedzi do {accounts}{more}", + "reply_mentions.more": "{count} więcej", + "reply_mentions.reply": "W odpowiedzi do {accounts}", "reply_mentions.reply_empty": "W odpowiedzi na wpis", "report.block": "Zablokuj {target}", "report.block_hint": "Czy chcesz też zablokować to konto?", diff --git a/app/soapbox/locales/pt-BR.json b/app/soapbox/locales/pt-BR.json index 79701f4e8..f0b0a19df 100644 --- a/app/soapbox/locales/pt-BR.json +++ b/app/soapbox/locales/pt-BR.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancelar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/pt.json b/app/soapbox/locales/pt.json index 6eadee36d..4cc86daad 100644 --- a/app/soapbox/locales/pt.json +++ b/app/soapbox/locales/pt.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Cancelar", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Bloquear {target}", "report.block_hint": "Desejas também bloquear esta conta?", diff --git a/app/soapbox/locales/ro.json b/app/soapbox/locales/ro.json index 46c068630..2f49f24ce 100644 --- a/app/soapbox/locales/ro.json +++ b/app/soapbox/locales/ro.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Anulează", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/ru.json b/app/soapbox/locales/ru.json index 76e4c0eb2..b4d0b3e2d 100644 --- a/app/soapbox/locales/ru.json +++ b/app/soapbox/locales/ru.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Отмена", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/sk.json b/app/soapbox/locales/sk.json index 368c8d9b4..7a81a02c4 100644 --- a/app/soapbox/locales/sk.json +++ b/app/soapbox/locales/sk.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Zrušiť", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/sl.json b/app/soapbox/locales/sl.json index 5a5e08eaf..d1710325d 100644 --- a/app/soapbox/locales/sl.json +++ b/app/soapbox/locales/sl.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Prekliči", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/sq.json b/app/soapbox/locales/sq.json index ca6dc72cb..1c0a23d21 100644 --- a/app/soapbox/locales/sq.json +++ b/app/soapbox/locales/sq.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Anuloje", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/sr-Latn.json b/app/soapbox/locales/sr-Latn.json index f04b9bef5..ccf0c8ef1 100644 --- a/app/soapbox/locales/sr-Latn.json +++ b/app/soapbox/locales/sr-Latn.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Poništi", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/sr.json b/app/soapbox/locales/sr.json index efe2c4aa6..140a2738c 100644 --- a/app/soapbox/locales/sr.json +++ b/app/soapbox/locales/sr.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Поништи", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/sv.json b/app/soapbox/locales/sv.json index dd56ef574..a57d57e8f 100644 --- a/app/soapbox/locales/sv.json +++ b/app/soapbox/locales/sv.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Ångra", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/ta.json b/app/soapbox/locales/ta.json index 189eda2bb..69c44f84f 100644 --- a/app/soapbox/locales/ta.json +++ b/app/soapbox/locales/ta.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "எதிராணை", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/te.json b/app/soapbox/locales/te.json index 04f5c1931..6b34989f9 100644 --- a/app/soapbox/locales/te.json +++ b/app/soapbox/locales/te.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "రద్దు చెయ్యి", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/th.json b/app/soapbox/locales/th.json index 72d984068..c509dd4a3 100644 --- a/app/soapbox/locales/th.json +++ b/app/soapbox/locales/th.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "ยกเลิก", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/tr.json b/app/soapbox/locales/tr.json index 133b7c649..45c7c02b0 100644 --- a/app/soapbox/locales/tr.json +++ b/app/soapbox/locales/tr.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "İptal", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/uk.json b/app/soapbox/locales/uk.json index f88236624..bf601a95b 100644 --- a/app/soapbox/locales/uk.json +++ b/app/soapbox/locales/uk.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "Відмінити", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/zh-CN.json b/app/soapbox/locales/zh-CN.json index 25d9a28f7..0ec6a2e0f 100644 --- a/app/soapbox/locales/zh-CN.json +++ b/app/soapbox/locales/zh-CN.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "取消", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "屏蔽帐号 {target}", "report.block_hint": "你是否也要屏蔽这个帐号呢?", diff --git a/app/soapbox/locales/zh-HK.json b/app/soapbox/locales/zh-HK.json index b4143a647..b163f6675 100644 --- a/app/soapbox/locales/zh-HK.json +++ b/app/soapbox/locales/zh-HK.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "取消", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", diff --git a/app/soapbox/locales/zh-TW.json b/app/soapbox/locales/zh-TW.json index 44db7da27..5eade39d5 100644 --- a/app/soapbox/locales/zh-TW.json +++ b/app/soapbox/locales/zh-TW.json @@ -859,8 +859,6 @@ "reply_indicator.cancel": "取消", "reply_mentions.account.add": "Add to mentions", "reply_mentions.account.remove": "Remove from mentions", - "reply_mentions.more": "and {count} more", - "reply_mentions.reply": "Replying to {accounts}{more}", "reply_mentions.reply_empty": "Replying to post", "report.block": "Block {target}", "report.block_hint": "Do you also want to block this account?", From e21ec04be614597611e1938890d2829db17343d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 17 May 2022 13:05:01 +0200 Subject: [PATCH 09/38] Add 'Remove from followers' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/accounts.js | 40 +++++++++++++++++++ .../features/account/components/header.js | 9 +++++ .../account_timeline/components/header.js | 6 +++ .../containers/header_container.js | 17 ++++++++ app/soapbox/reducers/relationships.js | 2 + app/soapbox/utils/features.ts | 9 +++++ 6 files changed, 83 insertions(+) diff --git a/app/soapbox/actions/accounts.js b/app/soapbox/actions/accounts.js index 1005af838..4df56417e 100644 --- a/app/soapbox/actions/accounts.js +++ b/app/soapbox/actions/accounts.js @@ -57,6 +57,10 @@ export const ACCOUNT_UNPIN_REQUEST = 'ACCOUNT_UNPIN_REQUEST'; export const ACCOUNT_UNPIN_SUCCESS = 'ACCOUNT_UNPIN_SUCCESS'; export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL'; +export const ACCOUNT_REMOVE_FROM_FOLLOWERS_REQUEST = 'ACCOUNT_REMOVE_FROM_FOLLOWERS_REQUEST'; +export const ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS = 'ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS'; +export const ACCOUNT_REMOVE_FROM_FOLLOWERS_FAIL = 'ACCOUNT_REMOVE_FROM_FOLLOWERS_FAIL'; + export const PINNED_ACCOUNTS_FETCH_REQUEST = 'PINNED_ACCOUNTS_FETCH_REQUEST'; export const PINNED_ACCOUNTS_FETCH_SUCCESS = 'PINNED_ACCOUNTS_FETCH_SUCCESS'; export const PINNED_ACCOUNTS_FETCH_FAIL = 'PINNED_ACCOUNTS_FETCH_FAIL'; @@ -520,6 +524,42 @@ export function unsubscribeAccountFail(error) { }; } + +export function removeFromFollowers(id) { + return (dispatch, getState) => { + if (!isLoggedIn(getState)) return; + + dispatch(muteAccountRequest(id)); + + api(getState).post(`/api/v1/accounts/${id}/remove_from_followers`).then(response => { + dispatch(removeFromFollowersSuccess(response.data)); + }).catch(error => { + dispatch(removeFromFollowersFail(id, error)); + }); + }; +} + +export function removeFromFollowersRequest(id) { + return { + type: ACCOUNT_REMOVE_FROM_FOLLOWERS_REQUEST, + id, + }; +} + +export function removeFromFollowersSuccess(relationship) { + return { + type: ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS, + relationship, + }; +} + +export function removeFromFollowersFail(error) { + return { + type: ACCOUNT_REMOVE_FROM_FOLLOWERS_FAIL, + error, + }; +} + export function fetchFollowers(id) { return (dispatch, getState) => { dispatch(fetchFollowersRequest(id)); diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index 47c1172c7..7a65b0bef 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -48,6 +48,7 @@ const messages = defineMessages({ mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' }, unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' }, + removeFromFollowers: { id: 'account.remove_from_followers', defaultMessage: 'Remove this follower' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' }, deactivateUser: { id: 'admin.users.actions.deactivate_user', defaultMessage: 'Deactivate @{name}' }, @@ -283,6 +284,14 @@ class Header extends ImmutablePureComponent { }); } + if (features.removeFromFollowers && account.getIn(['relationship', 'followed_by'])) { + menu.push({ + text: intl.formatMessage(messages.removeFromFollowers), + action: this.props.onRemoveFromFollowers, + icon: require('@tabler/icons/icons/user-x.svg'), + }); + } + if (account.getIn(['relationship', 'muting'])) { menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), diff --git a/app/soapbox/features/account_timeline/components/header.js b/app/soapbox/features/account_timeline/components/header.js index b969e0b61..bba6bbcd5 100644 --- a/app/soapbox/features/account_timeline/components/header.js +++ b/app/soapbox/features/account_timeline/components/header.js @@ -25,6 +25,7 @@ class Header extends ImmutablePureComponent { onUnblockDomain: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired, + onRemoveFromFollowers: PropTypes.func.isRequired, username: PropTypes.string, history: PropTypes.object, }; @@ -141,6 +142,10 @@ class Header extends ImmutablePureComponent { this.props.onShowNote(this.props.account); } + handleRemoveFromFollowers = () => { + this.props.onRemoveFromFollowers(this.props.account); + } + render() { const { account } = this.props; const moved = (account) ? account.get('moved') : false; @@ -177,6 +182,7 @@ class Header extends ImmutablePureComponent { onSuggestUser={this.handleSuggestUser} onUnsuggestUser={this.handleUnsuggestUser} onShowNote={this.handleShowNote} + onRemoveFromFollowers={this.handleRemoveFromFollowers} username={this.props.username} /> diff --git a/app/soapbox/features/account_timeline/containers/header_container.js b/app/soapbox/features/account_timeline/containers/header_container.js index 43ceceb2f..baf0ccb17 100644 --- a/app/soapbox/features/account_timeline/containers/header_container.js +++ b/app/soapbox/features/account_timeline/containers/header_container.js @@ -13,6 +13,7 @@ import { unpinAccount, subscribeAccount, unsubscribeAccount, + removeFromFollowers, } from 'soapbox/actions/accounts'; import { verifyUser, @@ -56,6 +57,7 @@ const messages = defineMessages({ demotedToUser: { id: 'admin.users.actions.demote_to_user_message', defaultMessage: '@{acct} was demoted to a regular user' }, userSuggested: { id: 'admin.users.user_suggested_message', defaultMessage: '@{acct} was suggested' }, userUnsuggested: { id: 'admin.users.user_unsuggested_message', defaultMessage: '@{acct} was unsuggested' }, + removeFromFollowersConfirm: { id: 'confirmations.remove_from_followers.confirm', defaultMessage: 'Remove' }, }); const makeMapStateToProps = () => { @@ -269,6 +271,21 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onShowNote(account) { dispatch(initAccountNoteModal(account)); }, + + onRemoveFromFollowers(account) { + dispatch((_, getState) => { + const unfollowModal = getSettings(getState()).get('unfollowModal'); + if (unfollowModal) { + dispatch(openModal('CONFIRM', { + message: @{account.get('acct')}
}} />, + confirm: intl.formatMessage(messages.removeFromFollowersConfirm), + onConfirm: () => dispatch(removeFromFollowers(account.get('id'))), + })); + } else { + dispatch(removeFromFollowers(account.get('id'))); + } + }); + }, }); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header)); diff --git a/app/soapbox/reducers/relationships.js b/app/soapbox/reducers/relationships.js index c63ee978e..80754842c 100644 --- a/app/soapbox/reducers/relationships.js +++ b/app/soapbox/reducers/relationships.js @@ -19,6 +19,7 @@ import { ACCOUNT_UNSUBSCRIBE_SUCCESS, ACCOUNT_PIN_SUCCESS, ACCOUNT_UNPIN_SUCCESS, + ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS, RELATIONSHIPS_FETCH_SUCCESS, } from '../actions/accounts'; import { @@ -108,6 +109,7 @@ export default function relationships(state = initialState, action) { case ACCOUNT_PIN_SUCCESS: case ACCOUNT_UNPIN_SUCCESS: case ACCOUNT_NOTE_SUBMIT_SUCCESS: + case ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS: return normalizeRelationship(state, action.relationship); case RELATIONSHIPS_FETCH_SUCCESS: return normalizeRelationships(state, action.relationships); diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 1e4d76266..7819b2554 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -420,6 +420,15 @@ const getInstanceFeatures = (instance: Instance) => { */ remoteInteractionsAPI: v.software === PLEROMA && gte(v.version, '2.4.50'), + /** + * Ability to remove an account from your followers. + * @see POST /api/v1/accounts/:id/remove_from_followers + */ + removeFromFollowers: any([ + v.software === MASTODON && gte(v.compatVersion, '3.5.0'), + v.software === PLEROMA && v.build === SOAPBOX && gte(v.version, '2.4.50'), + ]), + reportMultipleStatuses: any([ v.software === MASTODON, v.software === PLEROMA, From 677ccfdf7e6ef7c405f49adf40f92a68933127bb Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 07:39:59 -0400 Subject: [PATCH 10/38] Add 'install' step and cache it --- .gitlab-ci.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d140029f..49d9d0e20 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,17 +8,22 @@ cache: files: - yarn.lock paths: - - node_modules + - node_modules/ stages: + - install - lint - test - build - deploy -before_script: - - env - - yarn +install_dependencies: + stage: install + script: + - yarn install --ignore-scripts + artifacts: + paths: + - node_modules/ lint-js: stage: lint From 492c227cc36280779bffddeb50e7722b78f68932 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 07:42:37 -0400 Subject: [PATCH 11/38] Rename to dashes --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 49d9d0e20..d78f27a52 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,7 @@ stages: - build - deploy -install_dependencies: +install-dependencies: stage: install script: - yarn install --ignore-scripts From 76d7cc64479a481859a37d522a4f146fae035d58 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 09:32:27 -0400 Subject: [PATCH 12/38] Improve design on deleting your account --- app/soapbox/features/settings/index.tsx | 30 ++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/app/soapbox/features/settings/index.tsx b/app/soapbox/features/settings/index.tsx index c43f4fc3d..1bb6722a8 100644 --- a/app/soapbox/features/settings/index.tsx +++ b/app/soapbox/features/settings/index.tsx @@ -1,11 +1,11 @@ -import * as React from 'react'; +import React, { useEffect } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { fetchMfa } from 'soapbox/actions/mfa'; import List, { ListItem } from 'soapbox/components/list'; -import { Button, Card, CardBody, CardHeader, CardTitle, Column } from 'soapbox/components/ui'; +import { Card, CardBody, CardHeader, CardTitle, Column } from 'soapbox/components/ui'; import { useAppSelector, useOwnAccount } from 'soapbox/hooks'; import { getFeatures } from 'soapbox/utils/features'; @@ -22,6 +22,7 @@ const messages = defineMessages({ configureMfa: { id: 'settings.configure_mfa', defaultMessage: 'Configure MFA' }, sessions: { id: 'settings.sessions', defaultMessage: 'Active sessions' }, deleteAccount: { id: 'settings.delete_account', defaultMessage: 'Delete Account' }, + other: { id: 'settings.other', defaultMessage: 'Other options' }, }); /** User settings page. */ @@ -34,15 +35,16 @@ const Settings = () => { const features = useAppSelector((state) => getFeatures(state.instance)); const account = useOwnAccount(); - const navigateToChangeEmail = React.useCallback(() => history.push('/settings/email'), [history]); - const navigateToChangePassword = React.useCallback(() => history.push('/settings/password'), [history]); - const navigateToMfa = React.useCallback(() => history.push('/settings/mfa'), [history]); - const navigateToSessions = React.useCallback(() => history.push('/settings/tokens'), [history]); - const navigateToEditProfile = React.useCallback(() => history.push('/settings/profile'), [history]); + const navigateToChangeEmail = () => history.push('/settings/email'); + const navigateToChangePassword = () => history.push('/settings/password'); + const navigateToMfa = () => history.push('/settings/mfa'); + const navigateToSessions = () => history.push('/settings/tokens'); + const navigateToEditProfile = () => history.push('/settings/profile'); + const navigateToDeleteAccount = () => history.push('/settings/account'); const isMfaEnabled = mfa.getIn(['settings', 'totp']); - React.useEffect(() => { + useEffect(() => { dispatch(fetchMfa()); }, [dispatch]); @@ -92,12 +94,14 @@ const Settings = () => { + + + + -
- -
+ + +
From 4c5bff2fb588497f7ac41bc8c6f34685717df5f7 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 09:46:55 -0400 Subject: [PATCH 13/38] Remove unused ColumnBackButton component --- .../__tests__/column_back_button.test.js | 12 ------ app/soapbox/components/column_back_button.js | 41 ------------------- app/soapbox/features/chats/chat_room.js | 1 - 3 files changed, 54 deletions(-) delete mode 100644 app/soapbox/components/__tests__/column_back_button.test.js delete mode 100644 app/soapbox/components/column_back_button.js diff --git a/app/soapbox/components/__tests__/column_back_button.test.js b/app/soapbox/components/__tests__/column_back_button.test.js deleted file mode 100644 index 6ebc95f40..000000000 --- a/app/soapbox/components/__tests__/column_back_button.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; - -import { render, screen } from '../../jest/test-helpers'; -import ColumnBackButton from '../column_back_button'; - -describe('', () => { - it('renders correctly', () => { - render(); - - expect(screen.getByRole('button')).toHaveTextContent('Back'); - }); -}); diff --git a/app/soapbox/components/column_back_button.js b/app/soapbox/components/column_back_button.js deleted file mode 100644 index 75d526992..000000000 --- a/app/soapbox/components/column_back_button.js +++ /dev/null @@ -1,41 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { withRouter } from 'react-router-dom'; - -import Icon from 'soapbox/components/icon'; - -export default @withRouter -class ColumnBackButton extends React.PureComponent { - - static propTypes = { - to: PropTypes.string, - history: PropTypes.object, - }; - - handleClick = () => { - const { to } = this.props; - - if (window.history?.length === 1) { - this.props.history.push(to ? to : '/'); - } else { - this.props.history.goBack(); - } - } - - handleKeyUp = (e) => { - if (e.key === 'Enter') { - this.handleClick(); - } - } - - render() { - return ( - - ); - } - -} diff --git a/app/soapbox/features/chats/chat_room.js b/app/soapbox/features/chats/chat_room.js index 5ea275b5e..4d9140650 100644 --- a/app/soapbox/features/chats/chat_room.js +++ b/app/soapbox/features/chats/chat_room.js @@ -77,7 +77,6 @@ class ChatRoom extends ImmutablePureComponent { return ( {/*
-
From 990214d02c7575c5bbe429443d43900705095273 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 10:03:37 -0400 Subject: [PATCH 14/38] Convert 'AttachmentThumbs' to TSX --- app/soapbox/components/attachment-thumbs.tsx | 44 ++++++++++++++++ app/soapbox/components/attachment_thumbs.js | 52 ------------------- app/soapbox/components/status.tsx | 4 +- .../compose/components/reply_indicator.js | 5 +- .../components/scheduled_status.js | 3 +- .../status/components/quoted_status.tsx | 3 +- .../ui/components/compare_history_modal.tsx | 7 +-- .../modals/report-modal/report-modal.tsx | 3 +- 8 files changed, 53 insertions(+), 68 deletions(-) create mode 100644 app/soapbox/components/attachment-thumbs.tsx delete mode 100644 app/soapbox/components/attachment_thumbs.js diff --git a/app/soapbox/components/attachment-thumbs.tsx b/app/soapbox/components/attachment-thumbs.tsx new file mode 100644 index 000000000..37b9fc9c6 --- /dev/null +++ b/app/soapbox/components/attachment-thumbs.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { useDispatch } from 'react-redux'; + +import { openModal } from 'soapbox/actions/modals'; +import Bundle from 'soapbox/features/ui/components/bundle'; +import { MediaGallery } from 'soapbox/features/ui/util/async-components'; + +import type { List as ImmutableList } from 'immutable'; + +interface IAttachmentThumbs { + media: ImmutableList> + onClick?(): void + sensitive?: boolean +} + +const AttachmentThumbs = (props: IAttachmentThumbs) => { + const { media, onClick, sensitive } = props; + const dispatch = useDispatch(); + + const renderLoading = () =>
; + const onOpenMedia = (media: Immutable.Record, index: number) => dispatch(openModal('MEDIA', { media, index })); + + return ( +
+ + {(Component: any) => ( + + )} + + + {onClick && ( +
+ )} +
+ ); +}; + +export default AttachmentThumbs; diff --git a/app/soapbox/components/attachment_thumbs.js b/app/soapbox/components/attachment_thumbs.js deleted file mode 100644 index 6c998e744..000000000 --- a/app/soapbox/components/attachment_thumbs.js +++ /dev/null @@ -1,52 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { connect } from 'react-redux'; - -import { openModal } from 'soapbox/actions/modals'; -import Bundle from 'soapbox/features/ui/components/bundle'; -import { MediaGallery } from 'soapbox/features/ui/util/async-components'; - -export default @connect() -class AttachmentThumbs extends ImmutablePureComponent { - - static propTypes = { - dispatch: PropTypes.func.isRequired, - media: ImmutablePropTypes.list.isRequired, - onClick: PropTypes.func, - sensitive: PropTypes.bool, - }; - - renderLoading() { - return
; - } - - onOpenMedia = (media, index) => { - this.props.dispatch(openModal('MEDIA', { media, index })); - } - - render() { - const { media, onClick, sensitive } = this.props; - - return ( -
- - {Component => ( - - )} - - {onClick && ( -
- )} -
- ); - } - -} diff --git a/app/soapbox/components/status.tsx b/app/soapbox/components/status.tsx index f2b804fbe..3396b2d76 100644 --- a/app/soapbox/components/status.tsx +++ b/app/soapbox/components/status.tsx @@ -14,7 +14,7 @@ import Card from '../features/status/components/card'; import Bundle from '../features/ui/components/bundle'; import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; -import AttachmentThumbs from './attachment_thumbs'; +import AttachmentThumbs from './attachment-thumbs'; import StatusActionBar from './status_action_bar'; import StatusContent from './status_content'; import StatusReplyMentions from './status_reply_mentions'; @@ -160,7 +160,7 @@ class Status extends ImmutablePureComponent { // Compensate height changes componentDidUpdate(_prevProps: IStatus, _prevState: IStatusState, snapshot?: ScrollPosition): void { - const doShowCard: boolean = Boolean(!this.props.muted && !this.props.hidden && this.props.status && this.props.status.card); + const doShowCard: boolean = Boolean(!this.props.muted && !this.props.hidden && this.props.status && this.props.status.card); if (doShowCard && !this.didShowCard) { this.didShowCard = true; diff --git a/app/soapbox/features/compose/components/reply_indicator.js b/app/soapbox/features/compose/components/reply_indicator.js index 7063970b3..faa86ea9a 100644 --- a/app/soapbox/features/compose/components/reply_indicator.js +++ b/app/soapbox/features/compose/components/reply_indicator.js @@ -3,7 +3,7 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; +import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import { Stack, Text } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; @@ -28,7 +28,7 @@ export default class ReplyIndicator extends ImmutablePureComponent { return null; } - const style = { + const style = { direction: isRtl(status.get('search_index')) ? 'rtl' : 'ltr', }; @@ -59,7 +59,6 @@ export default class ReplyIndicator extends ImmutablePureComponent { {status.get('media_attachments').size > 0 && ( diff --git a/app/soapbox/features/scheduled_statuses/components/scheduled_status.js b/app/soapbox/features/scheduled_statuses/components/scheduled_status.js index f5efdd406..6c05d8e2a 100644 --- a/app/soapbox/features/scheduled_statuses/components/scheduled_status.js +++ b/app/soapbox/features/scheduled_statuses/components/scheduled_status.js @@ -4,7 +4,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; import { Link, NavLink } from 'react-router-dom'; -import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; +import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import Avatar from 'soapbox/components/avatar'; import DisplayName from 'soapbox/components/display_name'; import RelativeTimestamp from 'soapbox/components/relative_timestamp'; @@ -74,7 +74,6 @@ class ScheduledStatus extends ImmutablePureComponent { {status.get('media_attachments').size > 0 && ( diff --git a/app/soapbox/features/status/components/quoted_status.tsx b/app/soapbox/features/status/components/quoted_status.tsx index aff5940be..9be046724 100644 --- a/app/soapbox/features/status/components/quoted_status.tsx +++ b/app/soapbox/features/status/components/quoted_status.tsx @@ -5,7 +5,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { defineMessages, injectIntl, FormattedMessage, IntlShape } from 'react-intl'; import { withRouter } from 'react-router-dom'; -import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; +import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import { Stack, Text } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; @@ -143,7 +143,6 @@ class QuotedStatus extends ImmutablePureComponent { {status.media_attachments.size > 0 && ( diff --git a/app/soapbox/features/ui/components/compare_history_modal.tsx b/app/soapbox/features/ui/components/compare_history_modal.tsx index d0ab73d55..648c0709f 100644 --- a/app/soapbox/features/ui/components/compare_history_modal.tsx +++ b/app/soapbox/features/ui/components/compare_history_modal.tsx @@ -4,7 +4,7 @@ import React, { useEffect } from 'react'; import { FormattedDate, FormattedMessage } from 'react-intl'; import { fetchHistory } from 'soapbox/actions/history'; -import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; +import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import { HStack, Modal, Spinner, Stack, Text } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; @@ -75,10 +75,7 @@ const CompareHistoryModal: React.FC = ({ onClose, statusId )} {version.media_attachments.size > 0 && ( - + )} diff --git a/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx b/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx index 536f680ee..997abb1cd 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx @@ -6,7 +6,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { blockAccount } from 'soapbox/actions/accounts'; import { submitReport, submitReportSuccess, submitReportFail } from 'soapbox/actions/reports'; import { expandAccountTimeline } from 'soapbox/actions/timelines'; -import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; +import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import StatusContent from 'soapbox/components/status_content'; import { Modal, ProgressBar, Stack, Text } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; @@ -61,7 +61,6 @@ const SelectedStatus = ({ statusId }: { statusId: string }) => { {status.get('media_attachments').size > 0 && ( From 893474e78827968981090be774ee39bb0466b25d Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 11:27:04 -0400 Subject: [PATCH 15/38] Fix verified flag --- .../features/edit_profile/components/profile-preview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/features/edit_profile/components/profile-preview.tsx b/app/soapbox/features/edit_profile/components/profile-preview.tsx index 2f355fe64..9970cfaf1 100644 --- a/app/soapbox/features/edit_profile/components/profile-preview.tsx +++ b/app/soapbox/features/edit_profile/components/profile-preview.tsx @@ -29,7 +29,7 @@ const ProfilePreview: React.FC = ({ account }) => {
- {!account.verified &&
} + {account.verified &&
}
From a20328b66c286a02ebdfaa2e245b3ac905308492 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 17 May 2022 15:37:16 +0000 Subject: [PATCH 16/38] Revert "Merge branch 'search-results-tsx' into 'develop'" This reverts merge request !1384 --- .../compose/components/search_results.js | 174 ++++++++++++++++++ .../compose/components/search_results.tsx | 174 ------------------ .../containers/search_results_container.js | 33 ++++ .../follow_recommendations_list.tsx | 4 +- app/soapbox/features/search/index.tsx | 4 +- app/soapbox/reducers/__tests__/search-test.js | 14 +- .../reducers/__tests__/suggestions-test.js | 13 +- app/soapbox/reducers/__tests__/trends-test.js | 10 +- app/soapbox/reducers/{search.ts => search.js} | 43 ++--- .../{suggestions.ts => suggestions.js} | 50 ++--- app/soapbox/reducers/trending_statuses.js | 31 ++++ app/soapbox/reducers/trending_statuses.ts | 37 ---- app/soapbox/reducers/{trends.ts => trends.js} | 16 +- 13 files changed, 287 insertions(+), 316 deletions(-) create mode 100644 app/soapbox/features/compose/components/search_results.js delete mode 100644 app/soapbox/features/compose/components/search_results.tsx create mode 100644 app/soapbox/features/compose/containers/search_results_container.js rename app/soapbox/reducers/{search.ts => search.js} (69%) rename app/soapbox/reducers/{suggestions.ts => suggestions.js} (54%) create mode 100644 app/soapbox/reducers/trending_statuses.js delete mode 100644 app/soapbox/reducers/trending_statuses.ts rename app/soapbox/reducers/{trends.ts => trends.js} (53%) diff --git a/app/soapbox/features/compose/components/search_results.js b/app/soapbox/features/compose/components/search_results.js new file mode 100644 index 000000000..05cbe2663 --- /dev/null +++ b/app/soapbox/features/compose/components/search_results.js @@ -0,0 +1,174 @@ +import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; + +import ScrollableList from 'soapbox/components/scrollable_list'; +import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account'; +import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder_hashtag'; +import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status'; + +import Hashtag from '../../../components/hashtag'; +import { Tabs } from '../../../components/ui'; +import AccountContainer from '../../../containers/account_container'; +import StatusContainer from '../../../containers/status_container'; + +const messages = defineMessages({ + accounts: { id: 'search_results.accounts', defaultMessage: 'People' }, + statuses: { id: 'search_results.statuses', defaultMessage: 'Posts' }, + hashtags: { id: 'search_results.hashtags', defaultMessage: 'Hashtags' }, +}); + +export default @injectIntl +class SearchResults extends ImmutablePureComponent { + + static propTypes = { + value: PropTypes.string, + results: ImmutablePropTypes.map.isRequired, + submitted: PropTypes.bool, + expandSearch: PropTypes.func.isRequired, + selectedFilter: PropTypes.string.isRequired, + selectFilter: PropTypes.func.isRequired, + features: PropTypes.object.isRequired, + suggestions: ImmutablePropTypes.list, + trendingStatuses: ImmutablePropTypes.list, + trends: ImmutablePropTypes.list, + intl: PropTypes.object.isRequired, + }; + + handleLoadMore = () => this.props.expandSearch(this.props.selectedFilter); + + handleSelectFilter = newActiveFilter => this.props.selectFilter(newActiveFilter); + + componentDidMount() { + this.props.fetchTrendingStatuses(); + } + + renderFilterBar() { + const { intl, selectedFilter } = this.props; + + const items = [ + { + text: intl.formatMessage(messages.accounts), + action: () => this.handleSelectFilter('accounts'), + name: 'accounts', + }, + { + text: intl.formatMessage(messages.statuses), + action: () => this.handleSelectFilter('statuses'), + name: 'statuses', + }, + { + text: intl.formatMessage(messages.hashtags), + action: () => this.handleSelectFilter('hashtags'), + name: 'hashtags', + }, + ]; + + return ; + } + + render() { + const { value, results, submitted, selectedFilter, suggestions, trendingStatuses, trends } = this.props; + + let searchResults; + let hasMore = false; + let loaded; + let noResultsMessage; + let placeholderComponent = PlaceholderStatus; + + if (selectedFilter === 'accounts') { + hasMore = results.get('accountsHasMore'); + loaded = results.get('accountsLoaded'); + placeholderComponent = PlaceholderAccount; + + if (results.get('accounts') && results.get('accounts').size > 0) { + searchResults = results.get('accounts').map(accountId => ); + } else if (!submitted && suggestions && !suggestions.isEmpty()) { + searchResults = suggestions.map(suggestion => ); + } else if (loaded) { + noResultsMessage = ( +
+ +
+ ); + } + } + + if (selectedFilter === 'statuses') { + hasMore = results.get('statusesHasMore'); + loaded = results.get('statusesLoaded'); + + if (results.get('statuses') && results.get('statuses').size > 0) { + searchResults = results.get('statuses').map(statusId => ); + } else if (!submitted && trendingStatuses && !trendingStatuses.isEmpty()) { + searchResults = trendingStatuses.map(statusId => ); + } else if (loaded) { + noResultsMessage = ( +
+ +
+ ); + } + } + + if (selectedFilter === 'hashtags') { + hasMore = results.get('hashtagsHasMore'); + loaded = results.get('hashtagsLoaded'); + placeholderComponent = PlaceholderHashtag; + + if (results.get('hashtags') && results.get('hashtags').size > 0) { + searchResults = results.get('hashtags').map(hashtag => ); + } else if (!submitted && suggestions && !suggestions.isEmpty()) { + searchResults = trends.map(hashtag => ); + } else if (loaded) { + noResultsMessage = ( +
+ +
+ ); + } + } + + return ( + <> + {this.renderFilterBar()} + + {noResultsMessage || ( + + {searchResults} + + )} + + ); + } + +} diff --git a/app/soapbox/features/compose/components/search_results.tsx b/app/soapbox/features/compose/components/search_results.tsx deleted file mode 100644 index 66d8f2062..000000000 --- a/app/soapbox/features/compose/components/search_results.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import classNames from 'classnames'; -import React, { useEffect } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { defineMessages, useIntl } from 'react-intl'; - -import { expandSearch, setFilter } from 'soapbox/actions/search'; -import { fetchTrendingStatuses } from 'soapbox/actions/trending_statuses'; -import ScrollableList from 'soapbox/components/scrollable_list'; -import PlaceholderAccount from 'soapbox/features/placeholder/components/placeholder_account'; -import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder_hashtag'; -import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status'; -import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; - -import Hashtag from '../../../components/hashtag'; -import { Tabs } from '../../../components/ui'; -import AccountContainer from '../../../containers/account_container'; -import StatusContainer from '../../../containers/status_container'; - -import type { Map as ImmutableMap } from 'immutable'; - -const messages = defineMessages({ - accounts: { id: 'search_results.accounts', defaultMessage: 'People' }, - statuses: { id: 'search_results.statuses', defaultMessage: 'Posts' }, - hashtags: { id: 'search_results.hashtags', defaultMessage: 'Hashtags' }, -}); - -type SearchFilter = 'accounts' | 'statuses' | 'hashtags'; - -/** Displays search results depending on the active tab. */ -const SearchResults: React.FC = () => { - const intl = useIntl(); - const dispatch = useAppDispatch(); - - const value = useAppSelector(state => state.search.submittedValue); - const results = useAppSelector(state => state.search.results); - const suggestions = useAppSelector(state => state.suggestions.items); - const trendingStatuses = useAppSelector(state => state.trending_statuses.items); - const trends = useAppSelector(state => state.trends.items); - const submitted = useAppSelector(state => state.search.submitted); - const selectedFilter = useAppSelector(state => state.search.filter); - - const handleLoadMore = () => dispatch(expandSearch(selectedFilter)); - const handleSelectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter)); - - useEffect(() => { - dispatch(fetchTrendingStatuses()); - }, []); - - const renderFilterBar = () => { - const items = [ - { - text: intl.formatMessage(messages.accounts), - action: () => handleSelectFilter('accounts'), - name: 'accounts', - }, - { - text: intl.formatMessage(messages.statuses), - action: () => handleSelectFilter('statuses'), - name: 'statuses', - }, - { - text: intl.formatMessage(messages.hashtags), - action: () => handleSelectFilter('hashtags'), - name: 'hashtags', - }, - ]; - - return ; - }; - - let searchResults; - let hasMore = false; - let loaded; - let noResultsMessage; - let placeholderComponent: React.ComponentType = PlaceholderStatus; - - if (selectedFilter === 'accounts') { - hasMore = results.get('accountsHasMore'); - loaded = results.get('accountsLoaded'); - placeholderComponent = PlaceholderAccount; - - if (results.get('accounts') && results.get('accounts').size > 0) { - searchResults = results.get('accounts').map((accountId: string) => ); - } else if (!submitted && suggestions && !suggestions.isEmpty()) { - searchResults = suggestions.map(suggestion => ); - } else if (loaded) { - noResultsMessage = ( -
- -
- ); - } - } - - if (selectedFilter === 'statuses') { - hasMore = results.get('statusesHasMore'); - loaded = results.get('statusesLoaded'); - - if (results.get('statuses') && results.get('statuses').size > 0) { - searchResults = results.get('statuses').map((statusId: string) => ( - // @ts-ignore - - )); - } else if (!submitted && trendingStatuses && !trendingStatuses.isEmpty()) { - searchResults = trendingStatuses.map(statusId => ( - // @ts-ignore - - )); - } else if (loaded) { - noResultsMessage = ( -
- -
- ); - } - } - - if (selectedFilter === 'hashtags') { - hasMore = results.get('hashtagsHasMore'); - loaded = results.get('hashtagsLoaded'); - placeholderComponent = PlaceholderHashtag; - - if (results.get('hashtags') && results.get('hashtags').size > 0) { - searchResults = results.get('hashtags').map((hashtag: ImmutableMap) => ); - } else if (!submitted && suggestions && !suggestions.isEmpty()) { - searchResults = trends.map(hashtag => ); - } else if (loaded) { - noResultsMessage = ( -
- -
- ); - } - } - - return ( - <> - {renderFilterBar()} - - {noResultsMessage || ( - - {searchResults} - - )} - - ); -}; - -export default SearchResults; diff --git a/app/soapbox/features/compose/containers/search_results_container.js b/app/soapbox/features/compose/containers/search_results_container.js new file mode 100644 index 000000000..4ab9b712f --- /dev/null +++ b/app/soapbox/features/compose/containers/search_results_container.js @@ -0,0 +1,33 @@ +import { connect } from 'react-redux'; + +import { fetchTrendingStatuses } from 'soapbox/actions/trending_statuses'; +import { getFeatures } from 'soapbox/utils/features'; + +import { expandSearch, setFilter } from '../../../actions/search'; +import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestions'; +import SearchResults from '../components/search_results'; + +const mapStateToProps = state => { + const instance = state.get('instance'); + + return { + value: state.getIn(['search', 'submittedValue']), + results: state.getIn(['search', 'results']), + suggestions: state.getIn(['suggestions', 'items']), + trendingStatuses: state.getIn(['trending_statuses', 'items']), + trends: state.getIn(['trends', 'items']), + submitted: state.getIn(['search', 'submitted']), + selectedFilter: state.getIn(['search', 'filter']), + features: getFeatures(instance), + }; +}; + +const mapDispatchToProps = dispatch => ({ + fetchSuggestions: () => dispatch(fetchSuggestions()), + fetchTrendingStatuses: () => dispatch(fetchTrendingStatuses()), + expandSearch: type => dispatch(expandSearch(type)), + dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))), + selectFilter: newActiveFilter => dispatch(setFilter(newActiveFilter)), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(SearchResults); diff --git a/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx b/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx index 109febdf4..4ca838072 100644 --- a/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx +++ b/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx @@ -31,8 +31,8 @@ const FollowRecommendationsList: React.FC = () => { return (
- {suggestions.size > 0 ? suggestions.map(suggestion => ( - + {suggestions.size > 0 ? suggestions.map((suggestion: { account: string }) => ( + )) : (
diff --git a/app/soapbox/features/search/index.tsx b/app/soapbox/features/search/index.tsx index c17691c37..3fdf625f6 100644 --- a/app/soapbox/features/search/index.tsx +++ b/app/soapbox/features/search/index.tsx @@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { Column } from 'soapbox/components/ui'; import Search from 'soapbox/features/compose/components/search'; -import SearchResults from 'soapbox/features/compose/components/search_results'; +import SearchResultsContainer from 'soapbox/features/compose/containers/search_results_container'; const messages = defineMessages({ heading: { id: 'column.search', defaultMessage: 'Search' }, @@ -16,7 +16,7 @@ const SearchPage = () => {
- +
); diff --git a/app/soapbox/reducers/__tests__/search-test.js b/app/soapbox/reducers/__tests__/search-test.js index c3b8d1ea0..b1138b663 100644 --- a/app/soapbox/reducers/__tests__/search-test.js +++ b/app/soapbox/reducers/__tests__/search-test.js @@ -6,11 +6,11 @@ import { SEARCH_EXPAND_SUCCESS, } from 'soapbox/actions/search'; -import reducer, { ReducerRecord } from '../search'; +import reducer from '../search'; describe('search reducer', () => { it('should return the initial state', () => { - expect(reducer(undefined, {})).toEqual(ReducerRecord({ + expect(reducer(undefined, {})).toEqual(ImmutableMap({ value: '', submitted: false, submittedValue: '', @@ -22,7 +22,7 @@ describe('search reducer', () => { describe('SEARCH_CHANGE', () => { it('sets the value', () => { - const state = ReducerRecord({ value: 'hell' }); + const state = ImmutableMap({ value: 'hell' }); const action = { type: SEARCH_CHANGE, value: 'hello' }; expect(reducer(state, action).get('value')).toEqual('hello'); }); @@ -30,7 +30,7 @@ describe('search reducer', () => { describe('SEARCH_CLEAR', () => { it('resets the state', () => { - const state = ReducerRecord({ + const state = ImmutableMap({ value: 'hello world', submitted: true, submittedValue: 'hello world', @@ -41,7 +41,7 @@ describe('search reducer', () => { const action = { type: SEARCH_CLEAR }; - const expected = ReducerRecord({ + const expected = ImmutableMap({ value: '', submitted: false, submittedValue: '', @@ -56,7 +56,7 @@ describe('search reducer', () => { describe(SEARCH_EXPAND_SUCCESS, () => { it('imports hashtags as maps', () => { - const state = ReducerRecord({ + const state = ImmutableMap({ value: 'artist', submitted: true, submittedValue: 'artist', @@ -82,7 +82,7 @@ describe('search reducer', () => { searchType: 'hashtags', }; - const expected = ReducerRecord({ + const expected = ImmutableMap({ value: 'artist', submitted: true, submittedValue: 'artist', diff --git a/app/soapbox/reducers/__tests__/suggestions-test.js b/app/soapbox/reducers/__tests__/suggestions-test.js index 3a11b5b56..7da0b7f75 100644 --- a/app/soapbox/reducers/__tests__/suggestions-test.js +++ b/app/soapbox/reducers/__tests__/suggestions-test.js @@ -1,7 +1,4 @@ -import { - Record as ImmutableRecord, - fromJS, -} from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { SUGGESTIONS_DISMISS } from 'soapbox/actions/suggestions'; @@ -9,10 +6,10 @@ import reducer from '../suggestions'; describe('suggestions reducer', () => { it('should return the initial state', () => { - const result = reducer(undefined, {}); - expect(ImmutableRecord.isRecord(result)).toBe(true); - expect(result.items.isEmpty()).toBe(true); - expect(result.isLoading).toBe(false); + expect(reducer(undefined, {})).toEqual(ImmutableMap({ + items: ImmutableList(), + isLoading: false, + })); }); describe('SUGGESTIONS_DISMISS', () => { diff --git a/app/soapbox/reducers/__tests__/trends-test.js b/app/soapbox/reducers/__tests__/trends-test.js index 22d7d34e4..5ebeabb31 100644 --- a/app/soapbox/reducers/__tests__/trends-test.js +++ b/app/soapbox/reducers/__tests__/trends-test.js @@ -1,12 +1,12 @@ -import { Record as ImmutableRecord } from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import reducer from '../trends'; describe('trends reducer', () => { it('should return the initial state', () => { - const result = reducer(undefined, {}); - expect(ImmutableRecord.isRecord(result)).toBe(true); - expect(result.items.isEmpty()).toBe(true); - expect(result.isLoading).toBe(false); + expect(reducer(undefined, {})).toEqual(ImmutableMap({ + items: ImmutableList(), + isLoading: false, + })); }); }); diff --git a/app/soapbox/reducers/search.ts b/app/soapbox/reducers/search.js similarity index 69% rename from app/soapbox/reducers/search.ts rename to app/soapbox/reducers/search.js index 64d2a6a8c..e086ef4a0 100644 --- a/app/soapbox/reducers/search.ts +++ b/app/soapbox/reducers/search.js @@ -1,10 +1,4 @@ -import { - Map as ImmutableMap, - Record as ImmutableRecord, - List as ImmutableList, - OrderedSet as ImmutableOrderedSet, - fromJS, -} from 'immutable'; +import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; import { COMPOSE_MENTION, @@ -23,39 +17,26 @@ import { SEARCH_EXPAND_SUCCESS, } from '../actions/search'; -import type { AnyAction } from 'redux'; - -export const ReducerRecord = ImmutableRecord({ +const initialState = ImmutableMap({ value: '', submitted: false, submittedValue: '', hidden: false, - results: ImmutableMap(), + results: ImmutableMap(), filter: 'accounts', }); -type State = ReturnType; - -type IdEntity = { id: string }; -type SearchType = 'accounts' | 'statuses' | 'hashtags'; - -type Results = { - accounts: IdEntity[], - statuses: IdEntity[], - hashtags: Record[], -} - -const toIds = (items: IdEntity[]) => { +const toIds = items => { return ImmutableOrderedSet(items.map(item => item.id)); }; -const importResults = (state: State, results: Results, searchTerm: string, searchType: SearchType): State => { +const importResults = (state, results, searchTerm, searchType) => { return state.withMutations(state => { if (state.get('value') === searchTerm && state.get('filter') === searchType) { state.set('results', ImmutableMap({ accounts: toIds(results.accounts), statuses: toIds(results.statuses), - hashtags: ImmutableList(results.hashtags.map(ImmutableMap)), // it's a list of maps + hashtags: fromJS(results.hashtags), // it's a list of maps accountsHasMore: results.accounts.length >= 20, statusesHasMore: results.statuses.length >= 20, hashtagsHasMore: results.hashtags.length >= 20, @@ -69,19 +50,17 @@ const importResults = (state: State, results: Results, searchTerm: string, searc }); }; -const paginateResults = (state: State, searchType: SearchType, results: Results, searchTerm: string): State => { +const paginateResults = (state, searchType, results, searchTerm) => { return state.withMutations(state => { - if (state.value === searchTerm) { + if (state.get('value') === searchTerm) { state.setIn(['results', `${searchType}HasMore`], results[searchType].length >= 20); state.setIn(['results', `${searchType}Loaded`], true); state.updateIn(['results', searchType], items => { const data = results[searchType]; // Hashtags are a list of maps. Others are IDs. if (searchType === 'hashtags') { - // @ts-ignore return items.concat(fromJS(data)); } else { - // @ts-ignore return items.concat(toIds(data)); } }); @@ -89,7 +68,7 @@ const paginateResults = (state: State, searchType: SearchType, results: Results, }); }; -const handleSubmitted = (state: State, value: string): State => { +const handleSubmitted = (state, value) => { return state.withMutations(state => { state.set('results', ImmutableMap()); state.set('submitted', true); @@ -97,12 +76,12 @@ const handleSubmitted = (state: State, value: string): State => { }); }; -export default function search(state = ReducerRecord(), action: AnyAction) { +export default function search(state = initialState, action) { switch (action.type) { case SEARCH_CHANGE: return state.set('value', action.value); case SEARCH_CLEAR: - return ReducerRecord(); + return initialState; case SEARCH_SHOW: return state.set('hidden', false); case COMPOSE_REPLY: diff --git a/app/soapbox/reducers/suggestions.ts b/app/soapbox/reducers/suggestions.js similarity index 54% rename from app/soapbox/reducers/suggestions.ts rename to app/soapbox/reducers/suggestions.js index bca64c9e2..ac00eecf1 100644 --- a/app/soapbox/reducers/suggestions.ts +++ b/app/soapbox/reducers/suggestions.js @@ -1,8 +1,4 @@ -import { - Map as ImmutableMap, - List as ImmutableList, - Record as ImmutableRecord, -} from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'soapbox/actions/accounts'; import { DOMAIN_BLOCK_SUCCESS } from 'soapbox/actions/domain_blocks'; @@ -17,64 +13,42 @@ import { SUGGESTIONS_V2_FETCH_FAIL, } from '../actions/suggestions'; -import type { AnyAction } from 'redux'; - -type SuggestionSource = 'past_interactions' | 'staff' | 'global'; - -type ReducerSuggestion = { - source: SuggestionSource, - account: string, -} - -type SuggestionAccount = { - id: string, -} - -type Suggestion = { - source: SuggestionSource, - account: SuggestionAccount, -} - -const ReducerRecord = ImmutableRecord({ - items: ImmutableList>(), +const initialState = ImmutableMap({ + items: ImmutableList(), isLoading: false, }); -type State = ReturnType; - -/** Convert a v1 account into a v2 suggestion. */ -const accountToSuggestion = (account: SuggestionAccount): ReducerSuggestion => { +// Convert a v1 account into a v2 suggestion +const accountToSuggestion = account => { return { source: 'past_interactions', account: account.id, }; }; -/** Import plain accounts into the reducer (legacy). */ -const importAccounts = (state: State, accounts: SuggestionAccount[]): State => { +const importAccounts = (state, accounts) => { return state.withMutations(state => { - state.set('items', ImmutableList(accounts.map(account => ImmutableMap(accountToSuggestion(account))))); + state.set('items', fromJS(accounts.map(accountToSuggestion))); state.set('isLoading', false); }); }; -/** Import full suggestion objects. */ -const importSuggestions = (state: State, suggestions: Suggestion[]): State => { +const importSuggestions = (state, suggestions) => { return state.withMutations(state => { - state.set('items', ImmutableList(suggestions.map(x => ImmutableMap({ ...x, account: x.account.id })))); + state.set('items', fromJS(suggestions.map(x => ({ ...x, account: x.account.id })))); state.set('isLoading', false); }); }; -const dismissAccount = (state: State, accountId: string): State => { +const dismissAccount = (state, accountId) => { return state.update('items', items => items.filterNot(item => item.get('account') === accountId)); }; -const dismissAccounts = (state: State, accountIds: string[]): State => { +const dismissAccounts = (state, accountIds) => { return state.update('items', items => items.filterNot(item => accountIds.includes(item.get('account')))); }; -export default function suggestionsReducer(state = ReducerRecord(), action: AnyAction) { +export default function suggestionsReducer(state = initialState, action) { switch (action.type) { case SUGGESTIONS_FETCH_REQUEST: case SUGGESTIONS_V2_FETCH_REQUEST: diff --git a/app/soapbox/reducers/trending_statuses.js b/app/soapbox/reducers/trending_statuses.js new file mode 100644 index 000000000..ee4fa234b --- /dev/null +++ b/app/soapbox/reducers/trending_statuses.js @@ -0,0 +1,31 @@ +import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable'; + +import { + TRENDING_STATUSES_FETCH_REQUEST, + TRENDING_STATUSES_FETCH_SUCCESS, +} from 'soapbox/actions/trending_statuses'; + +const initialState = ImmutableMap({ + items: ImmutableOrderedSet(), + isLoading: false, +}); + +const toIds = items => ImmutableOrderedSet(items.map(item => item.id)); + +const importStatuses = (state, statuses) => { + return state.withMutations(state => { + state.set('items', toIds(statuses)); + state.set('isLoading', false); + }); +}; + +export default function trending_statuses(state = initialState, action) { + switch (action.type) { + case TRENDING_STATUSES_FETCH_REQUEST: + return state.set('isLoading', true); + case TRENDING_STATUSES_FETCH_SUCCESS: + return importStatuses(state, action.statuses); + default: + return state; + } +} diff --git a/app/soapbox/reducers/trending_statuses.ts b/app/soapbox/reducers/trending_statuses.ts deleted file mode 100644 index afaf5d9af..000000000 --- a/app/soapbox/reducers/trending_statuses.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Record as ImmutableRecord, OrderedSet as ImmutableOrderedSet } from 'immutable'; - -import { - TRENDING_STATUSES_FETCH_REQUEST, - TRENDING_STATUSES_FETCH_SUCCESS, -} from 'soapbox/actions/trending_statuses'; - -import type { AnyAction } from 'redux'; - -const ReducerRecord = ImmutableRecord({ - items: ImmutableOrderedSet(), - isLoading: false, -}); - -type State = ReturnType; - -type IdEntity = { id: string }; - -const toIds = (items: IdEntity[]) => ImmutableOrderedSet(items.map(item => item.id)); - -const importStatuses = (state: State, statuses: IdEntity[]): State => { - return state.withMutations(state => { - state.set('items', toIds(statuses)); - state.set('isLoading', false); - }); -}; - -export default function trending_statuses(state = ReducerRecord(), action: AnyAction) { - switch (action.type) { - case TRENDING_STATUSES_FETCH_REQUEST: - return state.set('isLoading', true); - case TRENDING_STATUSES_FETCH_SUCCESS: - return importStatuses(state, action.statuses); - default: - return state; - } -} diff --git a/app/soapbox/reducers/trends.ts b/app/soapbox/reducers/trends.js similarity index 53% rename from app/soapbox/reducers/trends.ts rename to app/soapbox/reducers/trends.js index cb061c7b7..eb7df59bf 100644 --- a/app/soapbox/reducers/trends.ts +++ b/app/soapbox/reducers/trends.js @@ -1,8 +1,4 @@ -import { - Map as ImmutableMap, - Record as ImmutableRecord, - List as ImmutableList, -} from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { TRENDS_FETCH_REQUEST, @@ -10,20 +6,18 @@ import { TRENDS_FETCH_FAIL, } from '../actions/trends'; -import type { AnyAction } from 'redux'; - -const ReducerRecord = ImmutableRecord({ - items: ImmutableList>(), +const initialState = ImmutableMap({ + items: ImmutableList(), isLoading: false, }); -export default function trendsReducer(state = ReducerRecord(), action: AnyAction) { +export default function trendsReducer(state = initialState, action) { switch (action.type) { case TRENDS_FETCH_REQUEST: return state.set('isLoading', true); case TRENDS_FETCH_SUCCESS: return state.withMutations(map => { - map.set('items', ImmutableList(action.tags.map(ImmutableMap))); + map.set('items', fromJS(action.tags.map((x => x)))); map.set('isLoading', false); }); case TRENDS_FETCH_FAIL: From 417ac96ffbefe4d6a6c58474585f0e0d9d5bc21d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 17 May 2022 15:45:38 +0000 Subject: [PATCH 17/38] Restyle hotkeys modal --- app/soapbox/components/ui/modal/modal.tsx | 17 ++- .../features/ui/components/hotkeys_modal.tsx | 125 ++++++++++-------- app/styles/application.scss | 1 - app/styles/components/hotkeys-modal.scss | 51 ------- 4 files changed, 85 insertions(+), 109 deletions(-) delete mode 100644 app/styles/components/hotkeys-modal.scss diff --git a/app/soapbox/components/ui/modal/modal.tsx b/app/soapbox/components/ui/modal/modal.tsx index 5ac16ca14..5c01c513a 100644 --- a/app/soapbox/components/ui/modal/modal.tsx +++ b/app/soapbox/components/ui/modal/modal.tsx @@ -10,6 +10,19 @@ const messages = defineMessages({ confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, }); +type Widths = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' + +const widths = { + xs: 'max-w-xs', + sm: 'max-w-sm', + md: 'max-w-base', + lg: 'max-w-lg', + xl: 'max-w-xl', + '2xl': 'max-w-2xl', + '3xl': 'max-w-3xl', + '4xl': 'max-w-4xl', +}; + interface IModal { /** Callback when the modal is cancelled. */ cancelAction?: () => void, @@ -37,6 +50,7 @@ interface IModal { skipFocus?: boolean, /** Title text for the modal. */ title: string | React.ReactNode, + width?: Widths, } /** Displays a modal dialog box. */ @@ -55,6 +69,7 @@ const Modal: React.FC = ({ secondaryText, skipFocus = false, title, + width = 'xl', }) => { const intl = useIntl(); const buttonRef = React.useRef(null); @@ -66,7 +81,7 @@ const Modal: React.FC = ({ }, [skipFocus, buttonRef]); return ( -
+
void, } +const Hotkey: React.FC<{ children: React.ReactNode }> = ({ children }) => ( + + {children} + +); + +const TableCell: React.FC<{ children: React.ReactNode }> = ({ children }) => ( + + {children} + +); + const HotkeysModal: React.FC = ({ onClose }) => { const features = useAppSelector((state) => getFeatures(state.instance)); @@ -16,142 +28,143 @@ const HotkeysModal: React.FC = ({ onClose }) => { } onClose={onClose} + width='4xl' > -
+
- + - - + r + - - + m + - - + p + - - + f + {features.emojiReacts && ( - - + e + )} - - + b + - - + enter, o + - - + a +
r
m
p
f
e
b
enter, o
a
- + {features.spoilers && ( - - + x + )} {features.spoilers && ( - - + h + )} - - + up, k + - - + down, j + - - + n + - - + alt + n + - - + backspace + - - + s + - - + esc +
x
h
up, k
down, j
n
alt + n
backspace
s
esc
- + - - + g + h + - - + g + n + - - + g + f + - - + g + p + - - + g + u + - - + g + b + - - + g + m + - - + g + r + - - + ? +
g + h
g + n
g + f
g + p
g + u
g + b
g + m
g + r
?
diff --git a/app/styles/application.scss b/app/styles/application.scss index dd59d8754..03f8ff65d 100644 --- a/app/styles/application.scss +++ b/app/styles/application.scss @@ -43,7 +43,6 @@ @import 'components/user-panel'; @import 'components/compose-form'; @import 'components/sidebar-menu'; -@import 'components/hotkeys-modal'; @import 'components/emoji-reacts'; @import 'components/status'; @import 'components/reply-indicator'; diff --git a/app/styles/components/hotkeys-modal.scss b/app/styles/components/hotkeys-modal.scss deleted file mode 100644 index b246e115a..000000000 --- a/app/styles/components/hotkeys-modal.scss +++ /dev/null @@ -1,51 +0,0 @@ -.hotkeys-modal { - padding: 8px 0 0; - overflow: hidden; - background-color: var(--background-color); - border-radius: 6px; - flex-direction: column; - - @media screen and (max-width: 960px) { - height: 90vh; - } - - .compose-modal__content { - background-color: var(--background-color); - margin: 5px; - - @media screen and (max-width: 960px) { - flex-direction: column; - overflow: hidden; - overflow-y: scroll; - height: calc(100% - 80px); - -webkit-overflow-scrolling: touch; - } - } - - table { - thead { - display: block; - padding-left: 10px; - margin-bottom: 10px; - color: var(--primary-text-color); - font-size: 16px; - font-weight: 600; - } - - tr { - font-size: 12px; - } - - td { - padding: 0 10px 8px; - } - - kbd { - display: inline-block; - padding: 2px 8px; - background-color: var(--brand-color--med); - border: 1px solid var(--brand-color--med); - border-radius: 4px; - } - } -} From 330bf8e74d7bc722da54d9b376f93cd2162f5944 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 12:47:32 -0400 Subject: [PATCH 18/38] Improve Trends test coverage --- app/soapbox/components/hashtag.tsx | 2 +- .../ui/components/__tests__/trends-panel.test.tsx | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/soapbox/components/hashtag.tsx b/app/soapbox/components/hashtag.tsx index 06d44d467..c8e45a084 100644 --- a/app/soapbox/components/hashtag.tsx +++ b/app/soapbox/components/hashtag.tsx @@ -42,7 +42,7 @@ const Hashtag: React.FC = ({ hashtag }) => { {hashtag.get('history') && ( -
+
', () => { trends: ImmutableMap({ items: fromJS([{ name: 'hashtag 1', - history: [{ accounts: [] }], + history: [{ + day: '1652745600', + uses: '294', + accounts: '180', + }], }]), }), }; render(, null, store); expect(screen.getByTestId('hashtag')).toHaveTextContent(/hashtag 1/i); + expect(screen.getByTestId('hashtag')).toHaveTextContent(/180 people talking/i); + expect(screen.getByTestId('sparklines')).toBeInTheDocument(); }); it('renders multiple trends', () => { From 506b962421191776431e1e17d9f6cfe48601d1ed Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 17 May 2022 13:10:40 -0400 Subject: [PATCH 19/38] Improve Account test coverage --- .../components/__tests__/account.test.tsx | 67 +++++++++++++++++++ app/soapbox/components/verification_badge.tsx | 2 +- 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 app/soapbox/components/__tests__/account.test.tsx diff --git a/app/soapbox/components/__tests__/account.test.tsx b/app/soapbox/components/__tests__/account.test.tsx new file mode 100644 index 000000000..e7af7b8f0 --- /dev/null +++ b/app/soapbox/components/__tests__/account.test.tsx @@ -0,0 +1,67 @@ +import { Map as ImmutableMap } from 'immutable'; +import React from 'react'; + +import { render, screen } from '../../jest/test-helpers'; +import { normalizeAccount } from '../../normalizers'; +import Account from '../account'; + +describe('', () => { + it('renders account name and username', () => { + const account = normalizeAccount({ + id: '1', + acct: 'justin-username', + display_name: 'Justin L', + avatar: 'test.jpg', + }); + + const store = { + accounts: ImmutableMap({ + '1': account, + }), + }; + + render(, null, store); + expect(screen.getByTestId('account')).toHaveTextContent('Justin L'); + expect(screen.getByTestId('account')).toHaveTextContent(/justin-username/i); + }); + + describe('verification badge', () => { + it('renders verification badge', () => { + const account = normalizeAccount({ + id: '1', + acct: 'justin-username', + display_name: 'Justin L', + avatar: 'test.jpg', + verified: true, + }); + + const store = { + accounts: ImmutableMap({ + '1': account, + }), + }; + + render(, null, store); + expect(screen.getByTestId('verified-badge')).toBeInTheDocument(); + }); + + it('does not render verification badge', () => { + const account = normalizeAccount({ + id: '1', + acct: 'justin-username', + display_name: 'Justin L', + avatar: 'test.jpg', + verified: false, + }); + + const store = { + accounts: ImmutableMap({ + '1': account, + }), + }; + + render(, null, store); + expect(screen.queryAllByTestId('verified-badge')).toHaveLength(0); + }); + }); +}); diff --git a/app/soapbox/components/verification_badge.tsx b/app/soapbox/components/verification_badge.tsx index a35597c07..987b1ad8a 100644 --- a/app/soapbox/components/verification_badge.tsx +++ b/app/soapbox/components/verification_badge.tsx @@ -24,7 +24,7 @@ const VerificationBadge: React.FC = ({ className }) => { const Element = icon.endsWith('.svg') ? Icon : 'img'; return ( - + ); From 2bdd3415e2093aee099cc903c5b7cd552e227bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 17 May 2022 23:03:10 +0200 Subject: [PATCH 20/38] Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/locales/pl.json | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index e9a38c69c..7af8d9aba 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -78,7 +78,7 @@ "admin.dashcounters.user_count_label": "użytkownicy łącznie", "admin.dashwidgets.email_list_header": "Lista adresów e-mail", "admin.dashwidgets.software_header": "Oprogramowanie", - "admin.latest_accounts_panel.more": "Click to see {count} {count, plural, one {account} other {accounts}}", + "admin.latest_accounts_panel.more": "Naciśnij, aby zobaczyć {count} {count, plural, one {konto} few {konta} many {kont}}", "admin.latest_accounts_panel.title": "Najnowsze konta", "admin.moderation_log.empty_message": "Nie wykonałeś(-aś) jeszcze żadnych działań moderacyjnych. Kiedy jakieś zostaną wykonane, ich historia pojawi się tutaj.", "admin.reports.actions.close": "Zamknij", @@ -104,14 +104,14 @@ "admin.users.actions.promote_to_admin_message": "Mianowano @{acct} administratorem", "admin.users.actions.promote_to_moderator": "Mianuj @{name} moderatorem", "admin.users.actions.promote_to_moderator_message": "Mianowano @{acct} moderatorem", - "admin.users.actions.remove_donor": "Remove @{name} as a donor", - "admin.users.actions.set_donor": "Set @{name} as a donor", + "admin.users.actions.remove_donor": "Usuń @{name} ze wspierających", + "admin.users.actions.set_donor": "Ustaw @{name} jako wspierającego", "admin.users.actions.suggest_user": "Polecaj @{name}", "admin.users.actions.unsuggest_user": "Przestań polecać @{name}", "admin.users.actions.unverify_user": "Cofnij weryfikację @{name}", "admin.users.actions.verify_user": "Weryfikuj @{name}", - "admin.users.remove_donor_message": "@{acct} was removed as a donor", - "admin.users.set_donor_message": "@{acct} was set as a donor", + "admin.users.remove_donor_message": "Usunięto @{acct} ze wspierających", + "admin.users.set_donor_message": "Ustawiono @{acct} jako wspierającego", "admin.users.user_deactivated_message": "Zdezaktywowano @{acct}", "admin.users.user_deleted_message": "Usunięto @{acct}", "admin.users.user_suggested_message": "Zaczęto polecać @{acct}", @@ -121,12 +121,12 @@ "admin_nav.awaiting_approval": "Oczekujące zgłoszenia", "admin_nav.dashboard": "Panel administracyjny", "admin_nav.reports": "Zgłoszenia", - "alert.unexpected.body": "We're sorry for the interruption. If the problem persists, please reach out to our support team. You may also try to {clearCookies} (this will log you out).", - "alert.unexpected.browser": "Browser", + "alert.unexpected.body": "Przepraszamy za niedogodności. Jeżeli problem nie ustanie, skontaktuj się z naszym wsparciem technicznym. Możesz też spróbować {clearCookies} (zostaniesz wylogowany(-a)).", + "alert.unexpected.browser": "Przeglądarka", "alert.unexpected.clear_cookies": "wyczyścić pliki cookies i dane przeglądarki", - "alert.unexpected.links.help": "Help Center", - "alert.unexpected.links.status": "Status", - "alert.unexpected.links.support": "Support", + "alert.unexpected.links.help": "Centrum pomocy", + "alert.unexpected.links.status": "Stan", + "alert.unexpected.links.support": "Wsparcie techniczne", "alert.unexpected.message": "Wystąpił nieoczekiwany błąd.", "alert.unexpected.return_home": "Wróć na stronę główną", "alert.unexpected.title": "O nie!", @@ -148,6 +148,7 @@ "app_create.scopes_placeholder": "np. „read write follow”", "app_create.submit": "Utwórz aplikację", "app_create.website_label": "Strona", + "auth_layout.register": "Utwórz konto", "auth.invalid_credentials": "Nieprawidłowa nazwa użytkownika lub hasło", "auth.logged_out": "Wylogowano.", "backups.actions.create": "Utwórz kopię zapasową", @@ -155,7 +156,8 @@ "backups.empty_message.action": "Chcesz utworzyć?", "backups.pending": "Oczekująca", "beta.also_available": "Dostępne w językach:", - "birthday_panel.title": "Birthdays", + "birthdays_modal.empty": "Żaden z Twoich znajomych nie ma dziś urodzin.", + "birthday_panel.title": "Urodziny", "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem", "bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.", "bundle_column_error.retry": "Spróbuj ponownie", @@ -362,7 +364,7 @@ "developers.navigation.app_create_label": "Utwórz aplikację", "developers.navigation.intentional_error_label": "Wywołaj błąd", "developers.navigation.leave_developers_label": "Opuść programistów", - "developers.navigation.network_error_label": "Network error", + "developers.navigation.network_error_label": "Błąd sieci", "developers.navigation.settings_store_label": "Settings store", "developers.navigation.test_timeline_label": "Testowa oś czasu", "developers.settings_store.hint": "Możesz tu bezpośrednio edytować swoje ustawienia. UWAŻAJ! Edytowanie tej sekcji może uszkodzić Twoje konto, co może zostać naprawione tylko przez API.", @@ -643,6 +645,7 @@ "login.fields.otp_code_hint": "Wprowadź kod uwierzytelniania dwuetapowego wygenerowany przez aplikację mobilną lub jeden z kodów zapasowych", "login.fields.otp_code_label": "Kod uwierzytelniania dwuetapowego:", "login.fields.password_placeholder": "Hasło", + "login.fields.username_label": "Adres e-mail lub nazwa użytkownika", "login.fields.username_placeholder": "Nazwa użytkownika", "login.log_in": "Zaloguj się", "login.otp_log_in": "Login OTP", @@ -686,6 +689,10 @@ "morefollows.followers_label": "…i {count} więcej {count, plural, one {obserwujący(-a)} few {obserwujących} many {obserwujących} other {obserwujących}} na zdalnych stronach.", "morefollows.following_label": "…i {count} więcej {count, plural, one {obserwowany(-a)} few {obserwowanych} many {obserwowanych} other {obserwowanych}} na zdalnych stronach.", "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?", + "navbar.login.action": "Zaloguj się", + "navbar.login.forgot_password": "Nie pamiętasz hasła?", + "navbar.login.password.label": "Hasło", + "navbar.login.username.placeholder": "Adres e-mail lub nazwa użytkownika", "navigation.chats": "Czaty", "navigation.compose": "Utwórz wpis", "navigation.dashboard": "Administracja", @@ -768,9 +775,9 @@ "onboarding.avatar.title": "Wybierz zdjęcie profilowe", "onboarding.display_name.subtitle": "Możesz ją zawsze zmienić później.", "onboarding.display_name.title": "Wybierz wyświetlaną nazwę", - "onboarding.done": "Done", + "onboarding.done": "Gotowe", "onboarding.finished.message": "Cieszymy się, że możemy powitać Cię w naszej społeczności! Naciśnij poniższy przycisk, aby rozpocząć.", - "onboarding.finished.title": "Onboarding complete", + "onboarding.finished.title": "Wprowadzenie ukończone", "onboarding.header.subtitle": "Będzie widoczny w górnej części Twojego profilu", "onboarding.header.title": "Wybierz obraz tła", "onboarding.next": "Dalej", @@ -781,11 +788,12 @@ "onboarding.suggestions.subtitle": "Oto kilka najbardziej popularnych kont, które mogą Ci się spodobać.", "onboarding.suggestions.title": "Proponowane konta", "onboarding.view_feed": "Pokaż strumień", + "password_reset.confirmation_hint": "Nie otrzymałeś(-aś) maila potwierdzającego?", "password_reset.confirmation": "Sprawdź swoją pocztę e-mail, aby potwierdzić.", "password_reset.fields.username_placeholder": "Adres e-mail lub nazwa użytkownika", "password_reset.reset": "Resetuj hasło", - "patron.donate": "Donate", - "patron.title": "Funding Goal", + "patron.donate": "Wesprzyj", + "patron.title": "Cel wsparcia", "pinned_accounts.title": "Polecani przez {name}", "pinned_statuses.none": "Brak przypięć do pokazania.", "poll.closed": "Zamknięte", @@ -822,7 +830,7 @@ "profile_dropdown.add_account": "Dodaj istniejące konto", "profile_dropdown.logout": "Wyloguj @{acct}", "profile_dropdown.theme": "Motyw", - "profile_fields_panel.title": "Profile fields", + "profile_fields_panel.title": "Pola profilu", "public.column_settings.title": "Ustawienia osi czasu Fediwersum", "reactions.all": "Wszystkie", "regeneration_indicator.label": "Ładuję…", @@ -942,10 +950,12 @@ "settings.configure_mfa": "Konfiguruj uwierzytelnianie wieloskładnikowe", "settings.delete_account": "Usuń konto", "settings.edit_profile": "Edytuj profil", + "settings.other": "Inne", "settings.preferences": "Preferencje", "settings.profile": "Profil", "settings.save.success": "Pomyślnie zapisano preferencje!", "settings.security": "Bezpieczeństwo", + "settings.sessions": "Aktywne sesje", "settings.settings": "Ustawienia", "shared.tos": "Zasady użytkowania", "signup_panel.subtitle": "Zarejestruj się, aby przyłączyć się do dyskusji.", @@ -1081,6 +1091,10 @@ "theme_toggle.dark": "Ciemny", "theme_toggle.light": "Jasny", "theme_toggle.system": "Systemowy", + "thread_login.login": "Zaloguj się", + "thread_login.message": "Dołącz do {siteTitle}, aby dowiedzieć się więcej.", + "thread_login.signup": "Zarejestruj się", + "thread_login.title": "Kontynuuj dyskusję", "time_remaining.days": "{number, plural, one {Pozostał # dzień} few {Pozostały # dni} many {Pozostało # dni} other {Pozostało # dni}}", "time_remaining.hours": "{number, plural, one {Pozostała # godzina} few {Pozostały # godziny} many {Pozostało # godzin} other {Pozostało # godzin}}", "time_remaining.minutes": "{number, plural, one {Pozostała # minuta} few {Pozostały # minuty} many {Pozostało # minut} other {Pozostało # minut}}", From 8c6308d52376b05bf79d5c90b1d3ef3ef04478d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 17 May 2022 23:21:48 +0200 Subject: [PATCH 21/38] Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../verification/steps/age-verification.js | 5 +++- app/soapbox/locales/pl.json | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/soapbox/features/verification/steps/age-verification.js b/app/soapbox/features/verification/steps/age-verification.js index 64d2297ce..ff1ba51f9 100644 --- a/app/soapbox/features/verification/steps/age-verification.js +++ b/app/soapbox/features/verification/steps/age-verification.js @@ -49,7 +49,10 @@ const AgeVerification = () => { snackbar.error( intl.formatMessage({ id: 'age_verification.fail', - defaultMessage: `You must be ${ageMinimum} years old or older.`, + defaultMessage: 'You must be {ageMinimum, plural, one {# year} other {# years}} old or older.', + values: { + ageMinimum, + }, }), ), ); diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index 7af8d9aba..f8340a195 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -14,6 +14,7 @@ "account.column_settings.title": "Account timeline settings", "account.deactivated": "Dezaktywowany(-a)", "account.direct": "Wyślij wiadomość bezpośrednią do @{name}", + "account.domain_blocked": "Wyciszono domenę", "account.edit_profile": "Edytuj profil", "account.endorse": "Polecaj na profilu", "account.follow": "Śledź", @@ -32,6 +33,7 @@ "account.mention": "Wspomnij", "account.moved_to": "{name} przeniósł(-osła) się do:", "account.mute": "Wycisz @{name}", + "account.muted": "Wyciszono", "account.never_active": "Nigdy", "account.posts": "Wpisy", "account.posts_with_replies": "Wpisy i odpowiedzi", @@ -121,6 +123,8 @@ "admin_nav.awaiting_approval": "Oczekujące zgłoszenia", "admin_nav.dashboard": "Panel administracyjny", "admin_nav.reports": "Zgłoszenia", + "age_verification.header": "Wprowadź datę urodzenia", + "age_verification.fail": "Musisz mieć przynajmniej {ageMinimum, plural, one {# rok} few {# lata} many {# lat} other {# lat}}.", "alert.unexpected.body": "Przepraszamy za niedogodności. Jeżeli problem nie ustanie, skontaktuj się z naszym wsparciem technicznym. Możesz też spróbować {clearCookies} (zostaniesz wylogowany(-a)).", "alert.unexpected.browser": "Przeglądarka", "alert.unexpected.clear_cookies": "wyczyścić pliki cookies i dane przeglądarki", @@ -375,6 +379,7 @@ "directory.recently_active": "Ostatnio aktywni", "donate": "Wesprzyj", "donate_crypto": "Przekaż kryptowalutę", + "edit_email.header": "Zmień e-mail", "edit_federation.followers_only": "Ukryj wpisy z wyjątkiem obserwowanych", "edit_federation.force_nsfw": "Wymuś oznaczanie załączników jako wrażliwe", "edit_federation.media_removal": "Wycinaj media", @@ -417,14 +422,22 @@ "edit_profile.hints.stranger_notifications": "Wyświetlaj tylko powiadomienia od osób, które obserwujesz", "edit_profile.save": "Zapisz", "edit_profile.success": "Zapisano profil!", + "email_confirmation.success": "Twój adres e-mail został potwierdzony!", "email_passthru.confirmed.body": "Zamknij tę kartę i kontynuuj rejestrację z {bold}, z którego został wysłany ten e-mail.", "email_passthru.confirmed.heading": "Potwierdzono adres e-mail!", + "email_passthru.fail.expired": "Token z maila wygasł.", + "email_passthru.fail.generic": "Nie udało się potwierdzić adresu e-mail", + "email_passthru.fail.not_found": "Token z maila jest nieprawidłowy.", "email_passthru.generic_fail.body": "Poproś o nowe potwierdzenie adresu e-mail.", "email_passthru.generic_fail.heading": "Coś poszło nie tak", + "email_passthru.success": "Twój adres e-mail został zweryfikowany", "email_passthru.token_expired.body": "Token e-mail wygasł. Poproś o nowe potwierdzenie adresu e-mail z {bold}, z którego został wysłany ten e-mail.", "email_passthru.token_expired.heading": "Token wygasł", "email_passthru.token_not_found.body": "Nie odnaleziono tokenu e-mail. Poproś o nowe potwierdzenie adresu e-mail z {bold}, z którego został wysłany ten e-mail.", "email_passthru.token_not_found.heading": "Nieprawidłowy token", + "email_verification.exists": "Ten adres e-mail już jest zajęty", + "email_verification.fail": "Nie udało się zażądać weryfikacji e-mail", + "email_verification.header": "Wprowadź adres e-mail", "embed.instructions": "Osadź ten wpis na swojej stronie wklejając poniższy kod.", "embed.preview": "Tak będzie to wyglądać:", "emoji_button.activity": "Aktywność", @@ -652,11 +665,14 @@ "login.otp_log_in.fail": "Nieprawidłowy kod, spróbuj ponownie później.", "login.reset_password_hint": "Problem z zalogowaniem?", "login.sign_in": "Zaloguj się", + "login_form.header": "Zaloguj się", "media_gallery.toggle_visible": "Przełącz widoczność", "media_panel.empty_message": "Nie znaleziono mediów.", "media_panel.title": "Media", "mfa.confirm.success_message": "Potwierdzono MFA", "mfa.disable.success_message": "Wyłączono MFA", + "mfa.disabled": "Wyłączone", + "mfa.enabled": "Włączone", "mfa.mfa_disable_enter_password": "Wprowadź obecne hasło, aby wyłączyć uwierzytelnianie dwuetapowe:", "mfa.mfa_setup.code_hint": "Wprowadź kod z aplikacji do uwierzytelniania dwuskładnikowego.", "mfa.mfa_setup.code_placeholder": "Kod", @@ -791,6 +807,7 @@ "password_reset.confirmation_hint": "Nie otrzymałeś(-aś) maila potwierdzającego?", "password_reset.confirmation": "Sprawdź swoją pocztę e-mail, aby potwierdzić.", "password_reset.fields.username_placeholder": "Adres e-mail lub nazwa użytkownika", + "password_reset.header": "Resetuj hasło", "password_reset.reset": "Resetuj hasło", "patron.donate": "Wesprzyj", "patron.title": "Cel wsparcia", @@ -837,6 +854,9 @@ "regeneration_indicator.sublabel": "Twoja oś czasu jest przygotowywana!", "register_invite.lead": "Wypełnij poniższy formularz, aby utworzyć konto.", "register_invite.title": "Otrzymałeś(-aś) zaproszenie na {siteTitle}!", + "registrations.error": "Nie udało się zarejestrować konta.", + "registrations.success": "Witamy na {siteTitle}!", + "registrations.unprocessable_entity": "Ta nazwa użytkownika jest już zajęta.", "registration.agreement": "Akceptuję {tos}.", "registration.captcha.hint": "Naciśnij na obrazek, aby uzyskać nową captchę", "registration.captcha.placeholder": "Wprowadź kod z obrazka", @@ -848,6 +868,7 @@ "registration.fields.password_placeholder": "Hasło", "registration.fields.username_hint": "Możesz używać tylko liter, cyfr i podkreślników.", "registration.fields.username_placeholder": "Nazwa użytkownika", + "registration.header": "Zarejestruj konto", "registration.newsletter": "Zasubskrybuj newsletter.", "registration.password_mismatch": "Hasła nie pasują do siebie.", "registration.reason": "Dlaczego chcesz dołączyć?", @@ -905,6 +926,7 @@ "report.reason.title": "Powód zgłoszenia", "report.submit": "Wyślij", "report.target": "Zgłaszanie {target}", + "reset_password.fail": "Token wygasł, spróbuj ponownie.", "reset_password.header": "Ustaw nowe hasło", "schedule.post_time": "Data/godzina publikacji", "schedule.remove": "Usuń zaplanowany wpis", @@ -961,6 +983,11 @@ "signup_panel.subtitle": "Zarejestruj się, aby przyłączyć się do dyskusji.", "signup_panel.title": "Nowi na {site_title}?", "site_preview.preview": "Podgląd", + "sms_verification.header": "Wprowadź numer telefonu", + "sms_verification.fail": "Nie udało się wysłać kodu weryfikującego na Twój numer telefonu.", + "sms_verification.invalid": "Wprowadź prawidłowy numer telefonu.", + "sms_verification.sent.header": "Kod weryfikujący", + "sms_verification.success": "Kod weryfikujący został wysłany na Twój numer telefonu.", "snackbar.view": "Wyświetl", "soapbox_config.authenticated_profile_hint": "Użytkownicy muszą być zalogowani, aby zobaczyć odpowiedzi i media na profilach użytkowników.", "soapbox_config.authenticated_profile_label": "Profile wymagają uwierzytelniania", From f33e491c0c8a1976c9f80419e008baee026fb510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 18 May 2022 14:49:35 +0200 Subject: [PATCH 22/38] Make 'Switch accounts' string translatable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/components/sidebar_menu.tsx | 4 +++- app/soapbox/locales/pl.json | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/soapbox/components/sidebar_menu.tsx b/app/soapbox/components/sidebar_menu.tsx index 5339247f5..e407bdef7 100644 --- a/app/soapbox/components/sidebar_menu.tsx +++ b/app/soapbox/components/sidebar_menu.tsx @@ -161,7 +161,9 @@ const SidebarMenu: React.FC = (): JSX.Element | null => { diff --git a/app/soapbox/containers/soapbox.tsx b/app/soapbox/containers/soapbox.tsx index c7f2f3c87..cde450e65 100644 --- a/app/soapbox/containers/soapbox.tsx +++ b/app/soapbox/containers/soapbox.tsx @@ -18,6 +18,7 @@ import AuthLayout from 'soapbox/features/auth_layout'; import OnboardingWizard from 'soapbox/features/onboarding/onboarding-wizard'; import PublicLayout from 'soapbox/features/public_layout'; import NotificationsContainer from 'soapbox/features/ui/containers/notifications_container'; +import { ModalContainer } from 'soapbox/features/ui/util/async-components'; import WaitlistPage from 'soapbox/features/verification/waitlist_page'; import { createGlobals } from 'soapbox/globals'; import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures, useSoapboxConfig, useSettings, useSystemTheme } from 'soapbox/hooks'; @@ -29,6 +30,7 @@ import { checkOnboardingStatus } from '../actions/onboarding'; import { preload } from '../actions/preload'; import ErrorBoundary from '../components/error_boundary'; import UI from '../features/ui'; +import BundleContainer from '../features/ui/containers/bundle_container'; import { store } from '../store'; /** Ensure the given locale exists in our codebase */ @@ -96,7 +98,7 @@ const SoapboxMount = () => { MESSAGES[locale]().then(messages => { setMessages(messages); setLocaleLoading(false); - }).catch(() => {}); + }).catch(() => { }); }, [locale]); // Load initial data from the API @@ -172,7 +174,13 @@ const SoapboxMount = () => { )} {waitlisted && ( - } /> + <> + } /> + + + {Component => } + + )} {!me && (singleUserMode diff --git a/app/soapbox/features/notifications/components/notification.tsx b/app/soapbox/features/notifications/components/notification.tsx index c4bd4add6..9dd8a8bd0 100644 --- a/app/soapbox/features/notifications/components/notification.tsx +++ b/app/soapbox/features/notifications/components/notification.tsx @@ -3,6 +3,8 @@ import { HotKeys } from 'react-hotkeys'; import { FormattedMessage, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; +import { useAppSelector } from 'soapbox/hooks'; + import Icon from '../../../components/icon'; import Permalink from '../../../components/permalink'; import { HStack, Text, Emoji } from '../../../components/ui'; @@ -50,6 +52,7 @@ const icons: Record = { move: require('@tabler/icons/icons/briefcase.svg'), 'pleroma:chat_mention': require('@tabler/icons/icons/messages.svg'), 'pleroma:emoji_reaction': require('@tabler/icons/icons/mood-happy.svg'), + user_approved: require('@tabler/icons/icons/user-plus.svg'), }; const messages: Record = { @@ -93,16 +96,20 @@ const messages: Record id: 'notification.pleroma:emoji_reaction', defaultMessage: '{name} reacted to your post', }, + user_approved: { + id: 'notification.user_approved', + defaultMessage: 'Welcome to {instance}!', + }, }; -const buildMessage = (type: NotificationType, account: Account, targetName?: string): JSX.Element => { +const buildMessage = (type: NotificationType, account: Account, targetName: string, instanceTitle: string): JSX.Element => { const link = buildLink(account); return ( ); }; @@ -128,6 +135,7 @@ const Notification: React.FC = (props) => { const history = useHistory(); const intl = useIntl(); + const instance = useAppSelector((state) => state.instance); const type = notification.type; const { account, status } = notification; @@ -216,6 +224,7 @@ const Notification: React.FC = (props) => { switch (type) { case 'follow': case 'follow_request': + case 'user_approved': return account && typeof account === 'object' ? ( = (props) => { case 'pleroma:emoji_reaction': return status && typeof status === 'object' ? (