From 1cfe37b3c9133d23323047bbede3ac476c070889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 7 Oct 2021 11:11:46 +0200 Subject: [PATCH 01/39] Translate a few strings from `next` to Polish 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 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index 981c15ffd..c37f6bb31 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -119,7 +119,7 @@ "auth.invalid_credentials": "Nieprawidłowa nazwa użytkownika lub hasło", "auth.logged_out": "Wylogowano.", "backups.actions.create": "Utwórz kopię zapasową", - "backups.empty_message": "Nie znaleziono kopii zapasaowych. {action}", + "backups.empty_message": "Nie znaleziono kopii zapasowych. {action}", "backups.empty_message.action": "Chcesz utworzyć?", "backups.pending": "Oczekująca", "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem", @@ -525,12 +525,18 @@ "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?", + "navigation.chats": "Czaty", + "navigation.direct_messages": "Wiadomości", + "navigation.home": "Strona główna", + "navigation.notifications": "Powiadomienia", + "navigation.search": "Szukaj", "navigation_bar.account_aliases": "Aliasy kont", "navigation_bar.admin_settings": "Ustawienia administracyjne", "navigation_bar.blocks": "Zablokowani użytkownicy", "navigation_bar.bookmarks": "Zakładki", "navigation_bar.compose": "Utwórz nowy wpis", "navigation_bar.domain_blocks": "Ukryte domeny", + "navigation_bar.export_data": "Eksportuj dane", "navigation_bar.favourites": "Ulubione", "navigation_bar.filters": "Wyciszone słowa", "navigation_bar.follow_requests": "Prośby o śledzenie", @@ -809,10 +815,13 @@ "status.unpin": "Odepnij z profilu", "status_list.queue_label": "Naciśnij aby zobaczyć {count} {count, plural, one {nowy wpis} few {nowe wpisy} many {nowych wpisów} other {nowe wpisy}}", "statuses.tombstone": "Jeden lub więcej z wpisów jest już niedostępny.", + "sub_navigation.back": "Wróć", "suggestions.dismiss": "Odrzuć sugestię", + "tabs_bar.all": "Wszystkie", "tabs_bar.apps": "Aplikacje", "tabs_bar.chats": "Rozmowy", "tabs_bar.dashboard": "Panel administracyjny", + "tabs_bar.fediverse": "Fediwersum", "tabs_bar.header": "Informacje o koncie", "tabs_bar.home": "Strona główna", "tabs_bar.news": "Nowości", From 079736e19928ef87b77e28a4641fd01b17c9ee08 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 7 Oct 2021 14:35:07 -0500 Subject: [PATCH 02/39] Profile: start refactoring findAccountByUsername logic --- app/soapbox/features/account_gallery/index.js | 5 ++--- app/soapbox/features/account_timeline/index.js | 5 ++--- app/soapbox/features/favourited_statuses/index.js | 4 ++-- app/soapbox/features/followers/index.js | 4 ++-- app/soapbox/features/following/index.js | 4 ++-- app/soapbox/pages/profile_page.js | 3 ++- app/soapbox/selectors/index.js | 8 ++++++++ 7 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/soapbox/features/account_gallery/index.js b/app/soapbox/features/account_gallery/index.js index d09182d09..843f83e4d 100644 --- a/app/soapbox/features/account_gallery/index.js +++ b/app/soapbox/features/account_gallery/index.js @@ -10,7 +10,7 @@ import { expandAccountMediaTimeline } from '../../actions/timelines'; import LoadingIndicator from 'soapbox/components/loading_indicator'; import Column from '../ui/components/column'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { getAccountGallery } from 'soapbox/selectors'; +import { getAccountGallery, findAccountByUsername } from 'soapbox/selectors'; import MediaItem from './components/media_item'; import LoadMore from 'soapbox/components/load_more'; import MissingIndicator from 'soapbox/components/missing_indicator'; @@ -21,7 +21,6 @@ import { FormattedMessage } from 'react-intl'; const mapStateToProps = (state, { params, withReplies = false }) => { const username = params.username || ''; const me = state.get('me'); - const accounts = state.getIn(['accounts']); const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase()); let accountId = -1; @@ -29,7 +28,7 @@ const mapStateToProps = (state, { params, withReplies = false }) => { if (accountFetchError) { accountId = null; } else { - const account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase()); + const account = findAccountByUsername(state, username); accountId = account ? account.getIn(['id'], null) : -1; accountUsername = account ? account.getIn(['acct'], '') : ''; } diff --git a/app/soapbox/features/account_timeline/index.js b/app/soapbox/features/account_timeline/index.js index 2e49ed9db..003ab8dda 100644 --- a/app/soapbox/features/account_timeline/index.js +++ b/app/soapbox/features/account_timeline/index.js @@ -18,7 +18,7 @@ import { NavLink } from 'react-router-dom'; import { fetchPatronAccount } from '../../actions/patron'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { getSettings } from 'soapbox/actions/settings'; -import { makeGetStatusIds } from 'soapbox/selectors'; +import { makeGetStatusIds, findAccountByUsername } from 'soapbox/selectors'; import classNames from 'classnames'; const makeMapStateToProps = () => { @@ -27,7 +27,6 @@ const makeMapStateToProps = () => { const mapStateToProps = (state, { params, withReplies = false }) => { const username = params.username || ''; const me = state.get('me'); - const accounts = state.getIn(['accounts']); const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase()); const soapboxConfig = getSoapboxConfig(state); @@ -37,7 +36,7 @@ const makeMapStateToProps = () => { if (accountFetchError) { accountId = null; } else { - const account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase()); + const account = findAccountByUsername(state, username); accountId = account ? account.getIn(['id'], null) : -1; accountUsername = account ? account.getIn(['acct'], '') : ''; accountApId = account ? account.get('url') : ''; diff --git a/app/soapbox/features/favourited_statuses/index.js b/app/soapbox/features/favourited_statuses/index.js index be87190fa..e10455f21 100644 --- a/app/soapbox/features/favourited_statuses/index.js +++ b/app/soapbox/features/favourited_statuses/index.js @@ -11,6 +11,7 @@ import { debounce } from 'lodash'; import MissingIndicator from 'soapbox/components/missing_indicator'; import { fetchAccount, fetchAccountByUsername } from '../../actions/accounts'; import LoadingIndicator from '../../components/loading_indicator'; +import { findAccountByUsername } from 'soapbox/selectors'; const mapStateToProps = (state, { params }) => { const username = params.username || ''; @@ -28,14 +29,13 @@ const mapStateToProps = (state, { params }) => { }; } - const accounts = state.getIn(['accounts']); const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase()); let accountId = -1; if (accountFetchError) { accountId = null; } else { - const account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase()); + const account = findAccountByUsername(state, username); accountId = account ? account.getIn(['id'], null) : -1; } diff --git a/app/soapbox/features/followers/index.js b/app/soapbox/features/followers/index.js index 25d91880e..a4d48e0d0 100644 --- a/app/soapbox/features/followers/index.js +++ b/app/soapbox/features/followers/index.js @@ -17,18 +17,18 @@ import Column from '../ui/components/column'; import ScrollableList from '../../components/scrollable_list'; import MissingIndicator from 'soapbox/components/missing_indicator'; import { getFollowDifference } from 'soapbox/utils/accounts'; +import { findAccountByUsername } from 'soapbox/selectors'; const mapStateToProps = (state, { params, withReplies = false }) => { const username = params.username || ''; const me = state.get('me'); - const accounts = state.getIn(['accounts']); const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase()); let accountId = -1; if (accountFetchError) { accountId = null; } else { - const account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase()); + const account = findAccountByUsername(state, username); accountId = account ? account.getIn(['id'], null) : -1; } diff --git a/app/soapbox/features/following/index.js b/app/soapbox/features/following/index.js index 7e480e1a9..c09a742b8 100644 --- a/app/soapbox/features/following/index.js +++ b/app/soapbox/features/following/index.js @@ -17,18 +17,18 @@ import Column from '../ui/components/column'; import ScrollableList from '../../components/scrollable_list'; import MissingIndicator from 'soapbox/components/missing_indicator'; import { getFollowDifference } from 'soapbox/utils/accounts'; +import { findAccountByUsername } from 'soapbox/selectors'; const mapStateToProps = (state, { params, withReplies = false }) => { const username = params.username || ''; const me = state.get('me'); - const accounts = state.getIn(['accounts']); const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase()); let accountId = -1; if (accountFetchError) { accountId = null; } else { - const account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase()); + const account = findAccountByUsername(state, username); accountId = account ? account.getIn(['id'], null) : -1; } diff --git a/app/soapbox/pages/profile_page.js b/app/soapbox/pages/profile_page.js index 576ac0e75..ae09b4c2c 100644 --- a/app/soapbox/pages/profile_page.js +++ b/app/soapbox/pages/profile_page.js @@ -19,6 +19,7 @@ import { displayFqn } from 'soapbox/utils/state'; import { getFeatures } from 'soapbox/utils/features'; import { makeGetAccount } from '../selectors'; import { Redirect } from 'react-router-dom'; +import { findAccountByUsername } from 'soapbox/selectors'; const mapStateToProps = (state, { params, withReplies = false }) => { const username = params.username || ''; @@ -32,7 +33,7 @@ const mapStateToProps = (state, { params, withReplies = false }) => { if (accountFetchError) { accountId = null; } else { - account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase()); + account = findAccountByUsername(state, username); accountId = account ? account.getIn(['id'], null) : -1; accountUsername = account ? account.getIn(['acct'], '') : ''; } diff --git a/app/soapbox/selectors/index.js b/app/soapbox/selectors/index.js index 60afa8bd8..4520ee40e 100644 --- a/app/soapbox/selectors/index.js +++ b/app/soapbox/selectors/index.js @@ -47,6 +47,14 @@ export const makeGetAccount = () => { }); }; +export const findAccountByUsername = (state, username) => { + const accounts = state.get('accounts'); + + return accounts.find(account => { + return username.toLowerCase() === account.getIn(['acct'], '').toLowerCase(); + }); +}; + const toServerSideType = columnType => { switch (columnType) { case 'home': From c3da48ebf4d58a1371b5c119815069576a2b017c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 7 Oct 2021 14:43:14 -0500 Subject: [PATCH 03/39] Compare hosts in findAccountByUsername, fixes #730 --- app/soapbox/selectors/index.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/soapbox/selectors/index.js b/app/soapbox/selectors/index.js index 4520ee40e..b382957ee 100644 --- a/app/soapbox/selectors/index.js +++ b/app/soapbox/selectors/index.js @@ -47,14 +47,36 @@ export const makeGetAccount = () => { }); }; -export const findAccountByUsername = (state, username) => { +const findAccountsByUsername = (state, username) => { const accounts = state.get('accounts'); - return accounts.find(account => { + return accounts.filter(account => { return username.toLowerCase() === account.getIn(['acct'], '').toLowerCase(); }); }; +export const findAccountByUsername = (state, username) => { + const accounts = findAccountsByUsername(state, username); + + if (accounts.size > 1) { + const me = state.get('me'); + const meURL = state.getIn(['accounts', me, 'url']); + + return accounts.find(account => { + try { + // If more than one account has the same username, try matching its host + const { host } = new URL(account.get('url')); + const { host: meHost } = new URL(meURL); + return host === meHost; + } catch { + return false; + } + }); + } else { + return accounts.first(); + } +}; + const toServerSideType = columnType => { switch (columnType) { case 'home': From 14ecc9a4cee0d5538f9e2e82824491a05f44ed0a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 7 Oct 2021 14:52:50 -0500 Subject: [PATCH 04/39] Reblogs: add ColumnHeader --- app/soapbox/features/reblogs/index.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/soapbox/features/reblogs/index.js b/app/soapbox/features/reblogs/index.js index bf60a10c2..52e54017c 100644 --- a/app/soapbox/features/reblogs/index.js +++ b/app/soapbox/features/reblogs/index.js @@ -7,12 +7,16 @@ import LoadingIndicator from '../../components/loading_indicator'; import MissingIndicator from '../../components/missing_indicator'; import { fetchReblogs } from '../../actions/interactions'; import { fetchStatus } from '../../actions/statuses'; -import { FormattedMessage } from 'react-intl'; +import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; import AccountContainer from '../../containers/account_container'; import Column from '../ui/components/column'; import ScrollableList from '../../components/scrollable_list'; import { makeGetStatus } from '../../selectors'; +const messages = defineMessages({ + heading: { id: 'column.reblogs', defaultMessage: 'Reposts' }, +}); + const mapStateToProps = (state, props) => { const getStatus = makeGetStatus(); const status = getStatus(state, { @@ -27,9 +31,11 @@ const mapStateToProps = (state, props) => { }; export default @connect(mapStateToProps) +@injectIntl class Reblogs extends ImmutablePureComponent { static propTypes = { + intl: PropTypes.object.isRequired, params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.orderedSet, @@ -50,7 +56,7 @@ class Reblogs extends ImmutablePureComponent { } render() { - const { accountIds, status } = this.props; + const { intl, accountIds, status } = this.props; if (!accountIds) { return ( @@ -71,7 +77,7 @@ class Reblogs extends ImmutablePureComponent { const emptyMessage = ; return ( - + Date: Thu, 7 Oct 2021 15:19:28 -0500 Subject: [PATCH 05/39] Seach: autosubmit from search page --- app/soapbox/features/admin/user_index.js | 1 - .../compose/containers/search_container.js | 41 ++++++++++++------- app/soapbox/features/search/index.js | 2 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/app/soapbox/features/admin/user_index.js b/app/soapbox/features/admin/user_index.js index 3468e3036..4406a0aad 100644 --- a/app/soapbox/features/admin/user_index.js +++ b/app/soapbox/features/admin/user_index.js @@ -103,7 +103,6 @@ class UserIndex extends ImmutablePureComponent { diff --git a/app/soapbox/features/compose/containers/search_container.js b/app/soapbox/features/compose/containers/search_container.js index 3a45b4794..e6484216c 100644 --- a/app/soapbox/features/compose/containers/search_container.js +++ b/app/soapbox/features/compose/containers/search_container.js @@ -6,30 +6,41 @@ import { showSearch, } from '../../../actions/search'; import Search from '../components/search'; +import { debounce } from 'lodash'; const mapStateToProps = state => ({ value: state.getIn(['search', 'value']), submitted: state.getIn(['search', 'submitted']), }); -const mapDispatchToProps = dispatch => ({ +const mapDispatchToProps = (dispatch, { autoSubmit }) => { - onChange(value) { - dispatch(changeSearch(value)); - }, - - onClear() { - dispatch(clearSearch()); - }, - - onSubmit() { + const debouncedSubmit = debounce(() => { dispatch(submitSearch()); - }, + }, 900); - onShow() { - dispatch(showSearch()); - }, + return { + onChange(value) { + dispatch(changeSearch(value)); -}); + if (autoSubmit) { + debouncedSubmit(); + } + }, + + onClear() { + dispatch(clearSearch()); + }, + + onSubmit() { + dispatch(submitSearch()); + }, + + onShow() { + dispatch(showSearch()); + }, + + }; +}; export default connect(mapStateToProps, mapDispatchToProps)(Search); diff --git a/app/soapbox/features/search/index.js b/app/soapbox/features/search/index.js index 9e6da120d..12c177790 100644 --- a/app/soapbox/features/search/index.js +++ b/app/soapbox/features/search/index.js @@ -12,7 +12,7 @@ const messages = defineMessages({ const Search = ({ intl }) => (
- +
From e16a5ac9f9682a6ee16ff9023d4844a929e2b91d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 7 Oct 2021 15:45:41 -0500 Subject: [PATCH 06/39] ComposeForm: add back original character counter, commented out for now --- .../compose/components/compose_form.js | 10 ++++-- .../components/text_character_counter.js | 25 ++++++++++++++ ...counter.js => visual_character_counter.js} | 20 ++++++----- app/styles/components/compose-form.scss | 33 ++++++++++++++----- 4 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 app/soapbox/features/compose/components/text_character_counter.js rename app/soapbox/features/compose/components/{character_counter.js => visual_character_counter.js} (66%) diff --git a/app/soapbox/features/compose/components/compose_form.js b/app/soapbox/features/compose/components/compose_form.js index 0f34cc24d..0cb9eb98c 100644 --- a/app/soapbox/features/compose/components/compose_form.js +++ b/app/soapbox/features/compose/components/compose_form.js @@ -1,5 +1,6 @@ import React from 'react'; -import CharacterCounter from './character_counter'; +// import TextCharacterCounter from './text_character_counter'; +import VisualCharacterCounter from './visual_character_counter'; import Button from '../../../components/button'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; @@ -351,7 +352,12 @@ export default class ComposeForm extends ImmutablePureComponent {
- {maxTootChars &&
} + {maxTootChars && ( +
+ {/* */} + +
+ )}
diff --git a/app/soapbox/features/compose/components/text_character_counter.js b/app/soapbox/features/compose/components/text_character_counter.js new file mode 100644 index 000000000..99a96d1ca --- /dev/null +++ b/app/soapbox/features/compose/components/text_character_counter.js @@ -0,0 +1,25 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { length } from 'stringz'; + +export default class TextCharacterCounter extends React.PureComponent { + + static propTypes = { + text: PropTypes.string.isRequired, + max: PropTypes.number.isRequired, + }; + + checkRemainingText(diff) { + if (diff < 0) { + return {diff}; + } + + return {diff}; + } + + render() { + const diff = this.props.max - length(this.props.text); + return this.checkRemainingText(diff); + } + +} diff --git a/app/soapbox/features/compose/components/character_counter.js b/app/soapbox/features/compose/components/visual_character_counter.js similarity index 66% rename from app/soapbox/features/compose/components/character_counter.js rename to app/soapbox/features/compose/components/visual_character_counter.js index a3daa3867..f7873e82c 100644 --- a/app/soapbox/features/compose/components/character_counter.js +++ b/app/soapbox/features/compose/components/visual_character_counter.js @@ -13,7 +13,7 @@ const messages = defineMessages({ * @param {string} props.text - text to use to measure * @param {number} props.max - max text allowed */ -class CharacterCounter extends React.PureComponent { +class VisualCharacterCounter extends React.PureComponent { render() { const { intl, text, max } = this.props; @@ -22,21 +22,23 @@ class CharacterCounter extends React.PureComponent { const progress = textLength / max; return ( - +
+ +
); } } -CharacterCounter.propTypes = { +VisualCharacterCounter.propTypes = { intl: PropTypes.object.isRequired, text: PropTypes.string.isRequired, max: PropTypes.number.isRequired, }; -export default injectIntl(CharacterCounter); +export default injectIntl(VisualCharacterCounter); diff --git a/app/styles/components/compose-form.scss b/app/styles/components/compose-form.scss index 4345c77ec..cd9e91ec4 100644 --- a/app/styles/components/compose-form.scss +++ b/app/styles/components/compose-form.scss @@ -27,7 +27,7 @@ } } - .compose-form__warning { + &__warning { color: var(--primary-text-color); margin-bottom: 10px; background: var(--brand-color--faint); @@ -66,7 +66,7 @@ right: 5px; } - .compose-form__autosuggest-wrapper { + &__autosuggest-wrapper { position: relative; } @@ -195,7 +195,7 @@ color: var(--primary-text-color--faint); } - .compose-form__modifiers { + &__modifiers { color: var(--primary-text-color); font-family: inherit; font-size: 14px; @@ -327,7 +327,7 @@ } } // end .compose-form .compose-form__modifiers - .compose-form__buttons-wrapper { + &__buttons-wrapper { padding: 10px; background: var(--background-color); display: flex; @@ -380,13 +380,23 @@ } } - .character-counter__wrapper { - align-self: center; - margin: 0 10px 0 auto; + .character-counter { + display: block; + cursor: default; + font-family: var(--font-sans-serif), sans-serif; + font-size: 14px; + font-weight: 600; + color: var(--primary-text-color--faint); + &.character-counter--over { color: $warning-red; } + } + + .character-counter, + .visual-character-counter { + margin-right: 10px; } } - .compose-form__publish { + &__publish { display: flex; justify-content: flex-end; min-width: 0; @@ -396,6 +406,13 @@ overflow: hidden; } } + + &__counter { + display: flex; + align-items: center; + align-self: center; + margin-left: auto; + } } // end .compose-form // Icon tweaks From 82dd1d16c840b787e0fe13a66dbbed323e0af3e1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 7 Oct 2021 15:52:30 -0500 Subject: [PATCH 07/39] ScrollableList: fix border-radius on mobile --- app/styles/components/columns.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index 81233865d..cc2e09607 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -882,6 +882,10 @@ .column--transparent { .slist__append { @include standard-panel; + + @media screen and (max-width: 580px) { + border-radius: 0; + } } .sub-navigation ~ .slist .slist__append { From 891a65b66ff0fb6e8207a1411027323632213ebf Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 7 Oct 2021 18:21:28 -0500 Subject: [PATCH 08/39] Favourites: display favourites for Mastodon (if exposableReactions is manually toggled) --- app/soapbox/components/status_action_bar.js | 8 ++- app/soapbox/features/favourites/index.js | 12 +++-- .../components/status_interaction_bar.js | 49 +++++++++++++++---- app/soapbox/features/ui/index.js | 3 +- app/styles/components/detailed-status.scss | 1 - app/styles/components/emoji-reacts.scss | 15 +++++- 6 files changed, 71 insertions(+), 17 deletions(-) diff --git a/app/soapbox/components/status_action_bar.js b/app/soapbox/components/status_action_bar.js index 6c25d1104..4ae7a8539 100644 --- a/app/soapbox/components/status_action_bar.js +++ b/app/soapbox/components/status_action_bar.js @@ -458,7 +458,13 @@ class StatusActionBar extends ImmutablePureComponent { emoji={meEmojiReact} onClick={this.handleLikeButtonClick} /> - {emojiReactCount !== 0 && {emojiReactCount}} + {emojiReactCount !== 0 && ( + (features.exposableReactions && !features.emojiReacts) ? ( + {emojiReactCount} + ) : ( + {emojiReactCount} + ) + )}
{shareButton} diff --git a/app/soapbox/features/favourites/index.js b/app/soapbox/features/favourites/index.js index 1b91a0c55..7bf1852d9 100644 --- a/app/soapbox/features/favourites/index.js +++ b/app/soapbox/features/favourites/index.js @@ -5,19 +5,25 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import LoadingIndicator from '../../components/loading_indicator'; import { fetchFavourites } from '../../actions/interactions'; -import { FormattedMessage } from 'react-intl'; +import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; import AccountContainer from '../../containers/account_container'; import Column from '../ui/components/column'; import ScrollableList from '../../components/scrollable_list'; +const messages = defineMessages({ + heading: { id: 'column.favourites', defaultMessage: 'Likes' }, +}); + const mapStateToProps = (state, props) => ({ accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), }); export default @connect(mapStateToProps) +@injectIntl class Favourites extends ImmutablePureComponent { static propTypes = { + intl: PropTypes.object.isRequired, params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, accountIds: ImmutablePropTypes.orderedSet, @@ -37,7 +43,7 @@ class Favourites extends ImmutablePureComponent { } render() { - const { accountIds } = this.props; + const { intl, accountIds } = this.props; if (!accountIds) { return ( @@ -50,7 +56,7 @@ class Favourites extends ImmutablePureComponent { const emptyMessage = ; return ( - + { const instance = state.get('instance'); - const features = getFeatures(instance); return { allowedEmoji: getSoapboxConfig(state).get('allowedEmoji'), - reactionList: features.exposableReactions, + features: getFeatures(instance), }; }; @@ -29,7 +28,7 @@ class StatusInteractionBar extends ImmutablePureComponent { status: ImmutablePropTypes.map, me: SoapboxPropTypes.me, allowedEmoji: ImmutablePropTypes.list, - reactionList: PropTypes.bool, + features: PropTypes.object.isRequired, } getNormalizedReacts = () => { @@ -42,7 +41,7 @@ class StatusInteractionBar extends ImmutablePureComponent { ).reverse(); } - getRepost = () => { + getReposts = () => { const { status } = this.props; if (status.get('reblogs_count')) { return ( @@ -58,8 +57,39 @@ class StatusInteractionBar extends ImmutablePureComponent { return ''; } + getFavourites = () => { + const { features, status } = this.props; + + if (status.get('favourites_count')) { + const favourites = ( + <> + + + + + + ); + + if (features.exposableReactions) { + return ( + + {favourites} + + ); + } else { + return ( +
+ {favourites} +
+ ); + } + } + + return ''; + } + getEmojiReacts = () => { - const { status, reactionList } = this.props; + const { status, features } = this.props; const emojiReacts = this.getNormalizedReacts(); const count = emojiReacts.reduce((acc, cur) => ( @@ -81,7 +111,7 @@ class StatusInteractionBar extends ImmutablePureComponent { ); - if (reactionList) { + if (features.exposableReactions) { return {emojiReact}; } @@ -99,13 +129,12 @@ class StatusInteractionBar extends ImmutablePureComponent { }; render() { - const emojiReacts = this.getEmojiReacts(); - const repost = this.getRepost(); + const { features } = this.props; return (
- {emojiReacts} - {repost} + {features.emojiReacts ? this.getEmojiReacts() : this.getFavourites()} + {this.getReposts()}
); } diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index 27e7d13fd..022d56338 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -56,7 +56,7 @@ import { Following, Reblogs, Reactions, - // Favourites, + Favourites, DirectTimeline, Conversations, HashtagTimeline, @@ -288,6 +288,7 @@ class SwitchingColumnsArea extends React.PureComponent { + diff --git a/app/styles/components/detailed-status.scss b/app/styles/components/detailed-status.scss index c83c359e7..0debad833 100644 --- a/app/styles/components/detailed-status.scss +++ b/app/styles/components/detailed-status.scss @@ -71,7 +71,6 @@ .detailed-status__link { color: var(--primary-text-color--faint); - cursor: pointer; text-decoration: none; font-size: 13px; } diff --git a/app/styles/components/emoji-reacts.scss b/app/styles/components/emoji-reacts.scss index c912d21e3..9af8b33ca 100644 --- a/app/styles/components/emoji-reacts.scss +++ b/app/styles/components/emoji-reacts.scss @@ -21,12 +21,19 @@ } } -.emoji-react--reblogs { +.emoji-react--reblogs, +.emoji-react--favourites { vertical-align: middle; display: inline-flex; + margin-right: 10px; .svg-icon { margin-right: 0.2em; + } +} + +.emoji-react--reblogs { + .svg-icon { color: var(--highlight-text-color); svg { @@ -35,6 +42,12 @@ } } +.emoji-react--favourites { + .svg-icon { + color: $gold-star; + } +} + .emoji-reacts { display: inline-flex; flex-direction: row-reverse; From 943ee54c98d3aed936c87ab528bc080b87f92b57 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 7 Oct 2021 21:39:03 -0500 Subject: [PATCH 09/39] Profile: restore "extra" links on desktop --- .../features/account/components/header.js | 39 ++++++++++++++++++- app/styles/components/account-header.scss | 2 + 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index 2b8110d95..4dc7fad41 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -17,6 +17,8 @@ import { } from 'soapbox/utils/accounts'; import classNames from 'classnames'; import Avatar from 'soapbox/components/avatar'; +import { shortNumberFormat } from 'soapbox/utils/numbers'; +import { NavLink } from 'react-router-dom'; import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container'; import BundleContainer from 'soapbox/features/ui/containers/bundle_container'; import { ProfileInfoPanel } from 'soapbox/features/ui/util/async-components'; @@ -295,7 +297,7 @@ class Header extends ImmutablePureComponent { } render() { - const { account, username, me, features } = this.props; + const { account, intl, username, me, features } = this.props; const { isSmallScreen } = this.state; if (!account) { @@ -318,6 +320,7 @@ class Header extends ImmutablePureComponent { ); } + const ownAccount = account.get('id') === me; const info = this.makeInfo(); const menu = this.makeMenu(); @@ -349,6 +352,40 @@ class Header extends ImmutablePureComponent { +
+ + + {shortNumberFormat(account.get('statuses_count'))} + + + + {(ownAccount || !account.getIn(['pleroma', 'hide_follows'], false)) && + {account.getIn(['pleroma', 'hide_follows_count'], false) ? : {shortNumberFormat(account.get('following_count'))}} + + } + + {(ownAccount || !account.getIn(['pleroma', 'hide_followers'], false)) && + {account.getIn(['pleroma', 'hide_followers_count'], false) ? : {shortNumberFormat(account.get('followers_count'))}} + + } + + {(ownAccount || !account.getIn(['pleroma', 'hide_favorites'], true)) && + { /* : TODO : shortNumberFormat(account.get('favourite_count')) */ } + + + } + + {ownAccount && + + { /* : TODO : shortNumberFormat(account.get('pinned_count')) */ } + + + + } +
+ {isSmallScreen && (
diff --git a/app/styles/components/account-header.scss b/app/styles/components/account-header.scss index 12ca0146c..e84ea8915 100644 --- a/app/styles/components/account-header.scss +++ b/app/styles/components/account-header.scss @@ -219,9 +219,11 @@ display: flex; font-size: 14px; color: var(--primary-text-color--faint); + @media screen and (max-width: 895px) { justify-content: center; flex-wrap: wrap; + display: none; } a { From 3a37635040859aaf08cf66064f26f3a306e875e3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 10:43:29 -0500 Subject: [PATCH 10/39] Get rid of unused DrawerLoading component --- .../features/ui/components/drawer_loading.js | 11 --- app/styles/application.scss | 1 - app/styles/components/columns.scss | 35 ++++++-- app/styles/components/drawer.scss | 80 ------------------- app/styles/rtl.scss | 6 +- app/styles/ui.scss | 1 - 6 files changed, 31 insertions(+), 103 deletions(-) delete mode 100644 app/soapbox/features/ui/components/drawer_loading.js delete mode 100644 app/styles/components/drawer.scss diff --git a/app/soapbox/features/ui/components/drawer_loading.js b/app/soapbox/features/ui/components/drawer_loading.js deleted file mode 100644 index 08b0d2347..000000000 --- a/app/soapbox/features/ui/components/drawer_loading.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -const DrawerLoading = () => ( -
-
-
-
-
-); - -export default DrawerLoading; diff --git a/app/styles/application.scss b/app/styles/application.scss index 84d8978ee..1e0be7267 100644 --- a/app/styles/application.scss +++ b/app/styles/application.scss @@ -58,7 +58,6 @@ @import 'components/react-toggle'; @import 'components/getting-started'; @import 'components/promo-panel'; -@import 'components/drawer'; @import 'components/still-image'; @import 'components/timeline-queue-header'; @import 'components/badge'; diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index cc2e09607..0e0672381 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -61,6 +61,7 @@ box-sizing: border-box; display: flex; flex-direction: column; + flex: 1 1 100%; } @media screen and (min-width: 631px) { @@ -68,8 +69,7 @@ padding: 0; } - .column, - .drawer { + .column { flex: 0 0 auto; padding: 10px; padding-left: 5px; @@ -85,8 +85,7 @@ } .columns-area > div { - .column, - .drawer { + .column { padding-left: 5px; padding-right: 5px; } @@ -100,8 +99,7 @@ margin: 0 auto; padding: 15px 0; - .column, - .drawer { + .column { width: 100%; height: 100%; padding: 0; @@ -893,3 +891,28 @@ border-top-right-radius: 0; } } + +// TODO: Get rid of whatever a "drawer" is +// Probably rename to column-something +.drawer__pager { + box-sizing: border-box; + padding: 0; + flex-grow: 1; + position: relative; + overflow: hidden; + display: flex; +} + +.drawer__inner { + top: 0; + left: 0; + background: var(--foreground-color); + box-sizing: border-box; + padding: 0; + display: flex; + flex-direction: column; + overflow: hidden; + overflow-y: auto; + width: 100%; + height: 100%; +} diff --git a/app/styles/components/drawer.scss b/app/styles/components/drawer.scss deleted file mode 100644 index fe50fb50a..000000000 --- a/app/styles/components/drawer.scss +++ /dev/null @@ -1,80 +0,0 @@ -.drawer { - width: 300px; - box-sizing: border-box; - display: flex; - flex-direction: column; - overflow-y: hidden; -} - -.drawer__tab { - display: block; - flex: 1 1 auto; - padding: 15px 5px 13px; - color: var(--primary-text-color--faint); - text-decoration: none; - text-align: center; - font-size: 16px; - border-bottom: 2px solid transparent; -} - -.column, -.drawer { - flex: 1 1 100%; -} - -.drawer__pager { - box-sizing: border-box; - padding: 0; - flex-grow: 1; - position: relative; - overflow: hidden; - display: flex; -} - -.drawer__inner { - top: 0; - left: 0; - background: var(--foreground-color); - box-sizing: border-box; - padding: 0; - display: flex; - flex-direction: column; - overflow: hidden; - overflow-y: auto; - width: 100%; - height: 100%; -} - -.pseudo-drawer { - background: var(--background-color); - font-size: 13px; - text-align: left; -} - -.drawer__header { - flex: 0 0 auto; - font-size: 16px; - background: var(--brand-color--med); - margin-bottom: 10px; - display: flex; - flex-direction: row; - - a { - transition: background 100ms ease-in; - - &:hover { - background: var(--background-color); - transition: background 200ms ease-out; - } - } -} - -.drawer__backdrop { - cursor: pointer; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba($base-overlay-background, 0.5); -} diff --git a/app/styles/rtl.scss b/app/styles/rtl.scss index 051d0e82d..483ff5449 100644 --- a/app/styles/rtl.scss +++ b/app/styles/rtl.scss @@ -228,8 +228,7 @@ body.rtl { } @media screen and (min-width: 631px) { - .column, - .drawer { + .column { padding-left: 5px; padding-right: 5px; @@ -240,8 +239,7 @@ body.rtl { } .columns-area > div { - .column, - .drawer { + .column { padding-left: 5px; padding-right: 5px; } diff --git a/app/styles/ui.scss b/app/styles/ui.scss index 6f3a379bb..2f68412bf 100644 --- a/app/styles/ui.scss +++ b/app/styles/ui.scss @@ -318,7 +318,6 @@ .react-swipeable-view-container { &, .columns-area, - .drawer, .column { height: 100%; } From 683da333fd2c3259598789cc35545096101ff610 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 11:08:47 -0500 Subject: [PATCH 11/39] Entirely remove unused "drawer" concept --- app/soapbox/features/search/index.js | 6 +----- app/styles/components/columns.scss | 25 ------------------------- app/styles/components/list-forms.scss | 10 ---------- app/styles/components/search.scss | 12 ------------ 4 files changed, 1 insertion(+), 52 deletions(-) diff --git a/app/soapbox/features/search/index.js b/app/soapbox/features/search/index.js index 12c177790..2e7348769 100644 --- a/app/soapbox/features/search/index.js +++ b/app/soapbox/features/search/index.js @@ -13,11 +13,7 @@ const Search = ({ intl }) => (
-
-
- -
-
+
); diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index 0e0672381..19dcea779 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -891,28 +891,3 @@ border-top-right-radius: 0; } } - -// TODO: Get rid of whatever a "drawer" is -// Probably rename to column-something -.drawer__pager { - box-sizing: border-box; - padding: 0; - flex-grow: 1; - position: relative; - overflow: hidden; - display: flex; -} - -.drawer__inner { - top: 0; - left: 0; - background: var(--foreground-color); - box-sizing: border-box; - padding: 0; - display: flex; - flex-direction: column; - overflow: hidden; - overflow-y: auto; - width: 100%; - height: 100%; -} diff --git a/app/styles/components/list-forms.scss b/app/styles/components/list-forms.scss index 0679ac6e1..0212ebd1f 100644 --- a/app/styles/components/list-forms.scss +++ b/app/styles/components/list-forms.scss @@ -14,16 +14,6 @@ border-radius: 8px 8px 0 0; } - .drawer__inner { - border-radius: 0 0 8px 8px; - - &.backdrop { - width: calc(100% - 60px); - box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); - border-radius: 0 0 0 8px; - } - } - &__accounts { background: var(--background-color); overflow-y: auto; diff --git a/app/styles/components/search.scss b/app/styles/components/search.scss index d452e122c..ad6cdd239 100644 --- a/app/styles/components/search.scss +++ b/app/styles/components/search.scss @@ -166,10 +166,6 @@ } .search-page { - .drawer__inner:not(:empty) { - min-height: 48px; - } - .search { padding: 10px 15px; border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2); @@ -184,14 +180,6 @@ .search__icon .svg-icon { right: 24px; } - - .drawer__pager { - border-radius: 0 0 10px 10px; - - @media screen and (max-width: 450px) { - border-radius: 0; - } - } } .search-results { From a83caeccf794622e82ac07cc51d3cfd1b0c2c3ab Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 11:11:33 -0500 Subject: [PATCH 12/39] Column: let columns only fill their needed height --- app/styles/components/columns.scss | 1 - app/styles/components/search.scss | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index 19dcea779..d25792888 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -101,7 +101,6 @@ .column { width: 100%; - height: 100%; padding: 0; } diff --git a/app/styles/components/search.scss b/app/styles/components/search.scss index ad6cdd239..be71491a5 100644 --- a/app/styles/components/search.scss +++ b/app/styles/components/search.scss @@ -166,6 +166,8 @@ } .search-page { + height: 100%; + .search { padding: 10px 15px; border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2); From a5e5d2fc3f328f55068c7a43dc689903d981c661 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 11:16:14 -0500 Subject: [PATCH 13/39] Column: fix border-radius of .empty-column-indicator --- app/styles/components/columns.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index d25792888..bfae65e52 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -707,6 +707,7 @@ align-items: center; justify-content: center; min-height: 160px; + border-radius: 0 0 10px 10px; @supports (display: grid) { // hack to fix Chrome <57 contain: strict; @@ -724,21 +725,20 @@ text-decoration: underline; } } + + @media screen and (max-width: 580px) { + border-radius: 0; + } } .error-column { flex-direction: column; - border-radius: 0 0 10px 10px; .svg-icon { width: 70px; height: 70px; margin-bottom: 30px; } - - @media screen and (max-width: 580px) { - border-radius: 0; - } } .column-link--transparent .icon-with-badge__badge { From 43c86ff85c6f12b74f5cb6d42e5fb63725d6f755 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 11:20:41 -0500 Subject: [PATCH 14/39] BetterColumn: remove ColumnBackButton, fix border-bottom --- app/soapbox/features/ui/components/better_column.js | 2 -- app/styles/components/columns.scss | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/soapbox/features/ui/components/better_column.js b/app/soapbox/features/ui/components/better_column.js index 9bcbf2783..6d7e6e609 100644 --- a/app/soapbox/features/ui/components/better_column.js +++ b/app/soapbox/features/ui/components/better_column.js @@ -2,7 +2,6 @@ import React from 'react'; import ColumnHeader from './column_header'; import PropTypes from 'prop-types'; import Column from 'soapbox/components/column'; -import ColumnBackButton from '../../../components/column_back_button_slim'; import DropdownMenu from 'soapbox/containers/dropdown_menu_container'; // Yes, there are 3 types of columns at this point, but this one is better, I swear @@ -29,7 +28,6 @@ export default class BetterColumn extends React.PureComponent {
)} -
{children}
diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index bfae65e52..2748bab91 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -772,6 +772,11 @@ .column__top { display: flex; align-items: center; + border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2); + + .sub-navigation { + border-bottom: 0; + } } .column-header { From b47e7a3197623920ba6d16312cb6ea5702dad781 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 11:53:15 -0500 Subject: [PATCH 15/39] Column: remove ColumnBackButton from most places, fix DetailedStatus border-bottom, improve HashtagTimeline --- app/soapbox/features/hashtag_timeline/index.js | 12 +++++++----- app/soapbox/features/ui/index.js | 2 +- app/styles/chats.scss | 2 +- app/styles/components/detailed-status.scss | 5 +++++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/soapbox/features/hashtag_timeline/index.js b/app/soapbox/features/hashtag_timeline/index.js index ea9b30e84..a489c669e 100644 --- a/app/soapbox/features/hashtag_timeline/index.js +++ b/app/soapbox/features/hashtag_timeline/index.js @@ -8,7 +8,6 @@ import { expandHashtagTimeline, clearTimeline } from '../../actions/timelines'; import { FormattedMessage } from 'react-intl'; import { connectHashtagStream } from '../../actions/streaming'; import { isEqual } from 'lodash'; -import ColumnBackButton from '../../components/column_back_button'; const mapStateToProps = (state, props) => ({ hasUnread: state.getIn(['timelines', `hashtag:${props.params.id}`, 'unread']) > 0, @@ -26,8 +25,10 @@ class HashtagTimeline extends React.PureComponent { }; title = () => { - const title = [this.props.params.id]; + const title = [`#${this.props.params.id}`]; + // TODO: wtf is all this? + // It exists in Mastodon's codebase, but undocumented if (this.additionalFor('any')) { title.push(' ', ); } @@ -43,6 +44,8 @@ class HashtagTimeline extends React.PureComponent { return title; } + // TODO: wtf is this? + // It exists in Mastodon's codebase, but undocumented additionalFor = (mode) => { const { tags } = this.props.params; @@ -108,9 +111,8 @@ class HashtagTimeline extends React.PureComponent { const { id } = this.props.params; return ( - - - + + - + diff --git a/app/styles/chats.scss b/app/styles/chats.scss index b093d7386..f21411ea9 100644 --- a/app/styles/chats.scss +++ b/app/styles/chats.scss @@ -332,7 +332,7 @@ .ui--chatroom { padding-bottom: 0; - .columns-area__panels__main .columns-area { + .columns-area__panels__main .columns-area .column { height: calc(100vh - 100px); box-sizing: border-box; overflow: hidden; diff --git a/app/styles/components/detailed-status.scss b/app/styles/components/detailed-status.scss index 0debad833..e65e3fff2 100644 --- a/app/styles/components/detailed-status.scss +++ b/app/styles/components/detailed-status.scss @@ -160,6 +160,11 @@ .thread { &__status { position: relative; + + // Only display line if posts are below + &:last-child .detailed-status__action-bar { + border-bottom: 0; + } } &__connector { From 4e18d7e505665f02b739f4db33bc8556bed2b16c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 11:59:59 -0500 Subject: [PATCH 16/39] FollowRecommendations: fix transparent background --- app/soapbox/features/home_timeline/index.js | 5 +++-- app/styles/components/columns.scss | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/soapbox/features/home_timeline/index.js b/app/soapbox/features/home_timeline/index.js index 69b8e81b4..d746e9991 100644 --- a/app/soapbox/features/home_timeline/index.js +++ b/app/soapbox/features/home_timeline/index.js @@ -96,10 +96,11 @@ class HomeTimeline extends React.PureComponent { render() { const { intl, siteTitle, isLoading, isEmpty, features } = this.props; const { done } = this.state; + const showSuggestions = features.suggestions && isEmpty && !isLoading && !done; return ( - - {(features.suggestions && isEmpty && !isLoading && !done) ? ( + + {showSuggestions ? ( {Component => } diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index 2748bab91..88bfab6c6 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -858,6 +858,10 @@ .column-list { position: relative; + + &__empty-message { + padding: 0 20px; + } } .column-loading { From 78529df4c13afd09b7b92e48fa35d69e45112025 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 12:13:47 -0500 Subject: [PATCH 17/39] Chats: fix action button icons --- app/soapbox/features/chats/components/chat_box.js | 3 +-- app/styles/chats.scss | 12 +++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/soapbox/features/chats/components/chat_box.js b/app/soapbox/features/chats/components/chat_box.js index 2a0b72e89..e1403bccb 100644 --- a/app/soapbox/features/chats/components/chat_box.js +++ b/app/soapbox/features/chats/components/chat_box.js @@ -171,9 +171,8 @@ class ChatBox extends ImmutablePureComponent { return this.canSubmit() ? (
diff --git a/app/styles/chats.scss b/app/styles/chats.scss index f21411ea9..d241c63da 100644 --- a/app/styles/chats.scss +++ b/app/styles/chats.scss @@ -296,20 +296,22 @@ position: relative; .icon-button { - color: var(--primary-text-color--faint); + color: var(--primary-text-color); position: absolute; right: 10px; - top: calc(50% - 13px); + top: 50%; + transform: translateY(-50%); width: auto; height: auto; background: transparent !important; border: 0; padding: 0; margin: 0; - } - .chat-box__send .icon-button { - top: calc(50% - 9px); + .svg-icon { + width: 18px; + height: 18px; + } } textarea { From e0acd4c1ecf63d77e6f22ea1f91528f71d3dd6d9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 12:19:23 -0500 Subject: [PATCH 18/39] LinkFooter: pad bottom to make room for Chats --- app/styles/components/getting-started.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/styles/components/getting-started.scss b/app/styles/components/getting-started.scss index 0aef52bad..4bb228234 100644 --- a/app/styles/components/getting-started.scss +++ b/app/styles/components/getting-started.scss @@ -17,8 +17,7 @@ &__footer { flex: 0 0 auto; - padding: 10px; - padding-top: 20px; + padding: 20px 10px 40px; ul { margin-bottom: 10px; From 92fc853642ee419d6a8820fa1f271e77d876f382 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 13:12:38 -0500 Subject: [PATCH 19/39] AttachmentThumbs: display compact media thumbs instead of AttachmentList --- app/soapbox/components/attachment_thumbs.js | 43 +++++++++++++++++++ app/soapbox/components/media_gallery.js | 5 ++- app/soapbox/components/status.js | 7 +-- .../compose/components/reply_indicator.js | 4 +- .../components/scheduled_status.js | 4 +- .../features/ui/components/boost_modal.js | 4 +- app/styles/components/media-gallery.scss | 30 +++++++++++++ app/styles/components/modal.scss | 2 +- 8 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 app/soapbox/components/attachment_thumbs.js diff --git a/app/soapbox/components/attachment_thumbs.js b/app/soapbox/components/attachment_thumbs.js new file mode 100644 index 000000000..60532e00e --- /dev/null +++ b/app/soapbox/components/attachment_thumbs.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { MediaGallery } from 'soapbox/features/ui/util/async-components'; +import { openModal } from 'soapbox/actions/modal'; +import Bundle from 'soapbox/features/ui/components/bundle'; + +export default @connect() +class AttachmentThumbs extends ImmutablePureComponent { + + static propTypes = { + dispatch: PropTypes.func.isRequired, + media: ImmutablePropTypes.list.isRequired, + }; + + renderLoading() { + return
; + } + + onOpenMedia = (media, index) => { + this.props.dispatch(openModal('MEDIA', { media, index })); + } + + render() { + const { media } = this.props; + + return ( + + {Component => ( + + )} + + ); + } + +} diff --git a/app/soapbox/components/media_gallery.js b/app/soapbox/components/media_gallery.js index b89dbffdd..7a6b77eea 100644 --- a/app/soapbox/components/media_gallery.js +++ b/app/soapbox/components/media_gallery.js @@ -279,6 +279,7 @@ class MediaGallery extends React.PureComponent { visible: PropTypes.bool, onToggleVisibility: PropTypes.func, displayMedia: PropTypes.string, + compact: PropTypes.bool, }; static defaultProps = { @@ -560,7 +561,7 @@ class MediaGallery extends React.PureComponent { } render() { - const { media, intl, sensitive } = this.props; + const { media, intl, sensitive, compact } = this.props; const { visible } = this.state; const sizeData = this.getSizeData(media.size); @@ -592,7 +593,7 @@ class MediaGallery extends React.PureComponent { } return ( -
+
{spoilerButton}
diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index 5a4aa572d..2b4c43bc3 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -8,7 +8,7 @@ import RelativeTimestamp from './relative_timestamp'; import DisplayName from './display_name'; import StatusContent from './status_content'; import StatusActionBar from './status_action_bar'; -import AttachmentList from './attachment_list'; +import AttachmentThumbs from './attachment_thumbs'; import Card from '../features/status/components/card'; import { injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; @@ -384,10 +384,7 @@ class Status extends ImmutablePureComponent { if (size > 0) { if (this.props.muted) { media = ( - + ); } else if (size === 1 && status.getIn(['media_attachments', 0, 'type']) === 'video') { const video = status.getIn(['media_attachments', 0]); diff --git a/app/soapbox/features/compose/components/reply_indicator.js b/app/soapbox/features/compose/components/reply_indicator.js index c37cff67a..1dac73a20 100644 --- a/app/soapbox/features/compose/components/reply_indicator.js +++ b/app/soapbox/features/compose/components/reply_indicator.js @@ -7,7 +7,7 @@ import DisplayName from '../../../components/display_name'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { isRtl } from '../../../rtl'; -import AttachmentList from 'soapbox/components/attachment_list'; +import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; import { NavLink } from 'react-router-dom'; const messages = defineMessages({ @@ -57,7 +57,7 @@ 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 4dcc387ab..ecdc5f598 100644 --- a/app/soapbox/features/scheduled_statuses/components/scheduled_status.js +++ b/app/soapbox/features/scheduled_statuses/components/scheduled_status.js @@ -10,7 +10,7 @@ import { Link, NavLink } from 'react-router-dom'; import { getDomain } from 'soapbox/utils/accounts'; import Avatar from 'soapbox/components/avatar'; import DisplayName from 'soapbox/components/display_name'; -import AttachmentList from 'soapbox/components/attachment_list'; +import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; import PollPreview from './poll_preview'; import ScheduledStatusActionBar from './scheduled_status_action_bar'; @@ -67,7 +67,7 @@ class ScheduledStatus extends ImmutablePureComponent { collapsable /> - diff --git a/app/soapbox/features/ui/components/boost_modal.js b/app/soapbox/features/ui/components/boost_modal.js index 89f5d5d99..97c9e0631 100644 --- a/app/soapbox/features/ui/components/boost_modal.js +++ b/app/soapbox/features/ui/components/boost_modal.js @@ -9,7 +9,7 @@ import RelativeTimestamp from '../../../components/relative_timestamp'; import DisplayName from '../../../components/display_name'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Icon from 'soapbox/components/icon'; -import AttachmentList from 'soapbox/components/attachment_list'; +import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; const messages = defineMessages({ cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Un-repost' }, @@ -88,7 +88,7 @@ class BoostModal extends ImmutablePureComponent { {status.get('media_attachments').size > 0 && ( - diff --git a/app/styles/components/media-gallery.scss b/app/styles/components/media-gallery.scss index 31d407e3e..d4209b596 100644 --- a/app/styles/components/media-gallery.scss +++ b/app/styles/components/media-gallery.scss @@ -192,3 +192,33 @@ } } } + +$media-compact-size: 50px; + +.media-gallery--compact { + height: $media-compact-size !important; + background: transparent; + + .spoiler-button { + display: none; + } + + .media-gallery__item { + width: $media-compact-size !important; + height: $media-compact-size !important; + inset: auto !important; + margin-right: 5px; + + &-overflow { + font-size: 20px; + } + + &__icons { + font-size: 30px; + } + } + + .media-gallery__file-extension__label { + display: none; + } +} diff --git a/app/styles/components/modal.scss b/app/styles/components/modal.scss index d19081ca4..1f853929d 100644 --- a/app/styles/components/modal.scss +++ b/app/styles/components/modal.scss @@ -180,7 +180,7 @@ } .media-modal__button { - background-color: var(--primary-text-color); + background-color: #fff; height: 12px; width: 12px; border-radius: 6px; From bba3564ef3e404ae65d1feb0e5a31c056d8a42f1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 13:49:59 -0500 Subject: [PATCH 20/39] ProfilePreview: link to internal profile, improve styles --- .../edit_profile/components/profile_preview.js | 5 +++-- app/styles/accounts.scss | 16 ++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/app/soapbox/features/edit_profile/components/profile_preview.js b/app/soapbox/features/edit_profile/components/profile_preview.js index dbb6881d5..69be6a365 100644 --- a/app/soapbox/features/edit_profile/components/profile_preview.js +++ b/app/soapbox/features/edit_profile/components/profile_preview.js @@ -2,6 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import { Link } from 'react-router-dom'; import { getAcct, isVerified } from 'soapbox/utils/accounts'; import StillImage from 'soapbox/components/still_image'; import VerificationBadge from 'soapbox/components/verification_badge'; @@ -13,7 +14,7 @@ const mapStateToProps = state => ({ const ProfilePreview = ({ account, displayFqn }) => (
- +
); diff --git a/app/styles/accounts.scss b/app/styles/accounts.scss index 50e87e600..5c61002f6 100644 --- a/app/styles/accounts.scss +++ b/app/styles/accounts.scss @@ -3,7 +3,9 @@ display: block; text-decoration: none; color: inherit; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.3); + border-radius: 4px; + overflow: hidden; @media screen and (max-width: $no-gap-breakpoint) { box-shadow: none; @@ -13,7 +15,7 @@ &:active, &:focus { .card__bar { - background: var(--foreground-color); + background-color: var(--brand-color--faint); } } } @@ -22,7 +24,6 @@ height: 130px; position: relative; background: var(--background-color); - border-radius: 4px 4px 0 0; .still-image { display: block; @@ -30,7 +31,6 @@ height: 100%; margin: 0; object-fit: cover; - border-radius: 4px 4px 0 0; } @media screen and (max-width: 600px) { @@ -48,12 +48,8 @@ display: flex; justify-content: flex-start; align-items: center; - background: var(--brand-color--faint); - border-radius: 0 0 4px 4px; - - @media screen and (max-width: $no-gap-breakpoint) { - border-radius: 0; - } + background: var(--background-color); + transition: 0.2s; .avatar { flex: 0 0 auto; From 4840a3c7519e35b0802cd15623d47a79a94e5a54 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 14:34:23 -0500 Subject: [PATCH 21/39] TimelineQueueButtonHeader: display only when scrolled down --- .../timeline_queue_button_header.js | 52 +++++++++++++++++-- app/styles/components/remote-timeline.scss | 4 +- .../components/timeline-queue-header.scss | 32 +++++++----- 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/app/soapbox/components/timeline_queue_button_header.js b/app/soapbox/components/timeline_queue_button_header.js index 6a85e2130..bb02b0193 100644 --- a/app/soapbox/components/timeline_queue_button_header.js +++ b/app/soapbox/components/timeline_queue_button_header.js @@ -1,8 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl } from 'react-intl'; +import { throttle } from 'lodash'; import classNames from 'classnames'; -import SvgIcon from 'soapbox/components/svg_icon'; +import Icon from 'soapbox/components/icon'; export default @injectIntl class TimelineQueueButtonHeader extends React.PureComponent { @@ -11,25 +12,68 @@ class TimelineQueueButtonHeader extends React.PureComponent { onClick: PropTypes.func.isRequired, count: PropTypes.number, message: PropTypes.object.isRequired, + threshold: PropTypes.number, intl: PropTypes.object.isRequired, }; static defaultProps = { count: 0, + threshold: 400, }; + state = { + scrolled: false, + } + + componentDidMount() { + this.window = window; + this.documentElement = document.scrollingElement || document.documentElement; + + this.attachScrollListener(); + } + + componentWillUnmount() { + this.detachScrollListener(); + } + + attachScrollListener() { + this.window.addEventListener('scroll', this.handleScroll); + } + + detachScrollListener() { + this.window.removeEventListener('scroll', this.handleScroll); + } + + handleScroll = throttle(() => { + const { scrollTop } = (document.scrollingElement || document.documentElement); + const { threshold } = this.props; + + if (scrollTop > threshold) { + this.setState({ scrolled: true }); + } else { + this.setState({ scrolled: false }); + } + }, 150, { trailing: true }); + render() { const { count, message, onClick, intl } = this.props; + const { scrolled } = this.state; + + const visible = count > 0 && scrolled; const classes = classNames('timeline-queue-header', { - 'hidden': (count <= 0), + 'hidden': !visible, }); return ( ); diff --git a/app/styles/components/remote-timeline.scss b/app/styles/components/remote-timeline.scss index e059d7af7..803c315e8 100644 --- a/app/styles/components/remote-timeline.scss +++ b/app/styles/components/remote-timeline.scss @@ -29,9 +29,11 @@ } .pinned-hosts-picker { - margin-left: 10px; + padding: 10px 0 0 10px; display: inline-flex; flex-wrap: wrap; + background: var(--foreground-color); + border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2); .pinned-host { margin-right: 10px; diff --git a/app/styles/components/timeline-queue-header.scss b/app/styles/components/timeline-queue-header.scss index 405c7c5d8..93cf73d6f 100644 --- a/app/styles/components/timeline-queue-header.scss +++ b/app/styles/components/timeline-queue-header.scss @@ -1,35 +1,35 @@ .timeline-queue-header { display: flex; + align-self: center; align-items: center; justify-content: space-evenly; - max-height: 30px; - position: sticky; + height: 30px; + position: fixed; top: 60px; margin: 0 auto; - margin-bottom: 8px; background-color: var(--brand-color); color: #fff; - border-bottom: 1px solid; - border-top: 1px solid; - border-color: var(--brand-color--faint); border-radius: 100px; - transition: max-height 150ms ease; + transition: 150ms ease; overflow: hidden; - opacity: 1; - left: 0; - right: 0; padding: 0 10px; z-index: 500; + .sub-navigation ~ & { + top: calc(60px + 41px); + } + .svg-icon { margin-right: 5px; } &.hidden { - max-height: 0; - opacity: 0; - margin: 0; - border: 0; + transform: scaleY(0); + pointer-events: none; + + .timeline-queue-header__label { + opacity: 0; + } } &__btn { @@ -46,4 +46,8 @@ height: 46px; } } + + &__label { + transition: 150ms ease; + } } From 38b41ced760d0c9cab94df7e8b78cb9cef20644f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 14:51:54 -0500 Subject: [PATCH 22/39] TimelineQueueButtonHeader: always scroll to top --- app/soapbox/components/status_list.js | 1 - app/soapbox/components/timeline_queue_button_header.js | 9 +++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/soapbox/components/status_list.js b/app/soapbox/components/status_list.js index f96ec779f..c843a4b64 100644 --- a/app/soapbox/components/status_list.js +++ b/app/soapbox/components/status_list.js @@ -82,7 +82,6 @@ export default class StatusList extends ImmutablePureComponent { } handleDequeueTimeline = () => { - window.scrollTo({ top: 0, behavior: 'smooth' }); const { onDequeueTimeline, timelineId } = this.props; if (!onDequeueTimeline || !timelineId) return; onDequeueTimeline(timelineId); diff --git a/app/soapbox/components/timeline_queue_button_header.js b/app/soapbox/components/timeline_queue_button_header.js index bb02b0193..25a219336 100644 --- a/app/soapbox/components/timeline_queue_button_header.js +++ b/app/soapbox/components/timeline_queue_button_header.js @@ -55,8 +55,13 @@ class TimelineQueueButtonHeader extends React.PureComponent { } }, 150, { trailing: true }); + handleClick = e => { + window.scrollTo({ top: 0, behavior: 'smooth' }); + this.props.onClick(e); + } + render() { - const { count, message, onClick, intl } = this.props; + const { count, message, intl } = this.props; const { scrolled } = this.state; const visible = count > 0 && scrolled; @@ -67,7 +72,7 @@ class TimelineQueueButtonHeader extends React.PureComponent { return (
- + {(count > 0) && (
From 75a496c8069ab314d1e422fbc411154fa0a4425d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 14:53:21 -0500 Subject: [PATCH 23/39] TimelineQueueButtonHeader: update snapshots --- .../timeline_queue_button_header-test.js.snap | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/soapbox/components/__tests__/__snapshots__/timeline_queue_button_header-test.js.snap b/app/soapbox/components/__tests__/__snapshots__/timeline_queue_button_header-test.js.snap index ddc783a55..d63691a71 100644 --- a/app/soapbox/components/__tests__/__snapshots__/timeline_queue_button_header-test.js.snap +++ b/app/soapbox/components/__tests__/__snapshots__/timeline_queue_button_header-test.js.snap @@ -25,7 +25,7 @@ exports[` renders correctly 1`] = ` exports[` renders correctly 2`] = ` - Click to see 1 new post +
+ Click to see 1 new post +
`; exports[` renders correctly 3`] = ` - Click to see 9999999 new posts +
+ Click to see 9999999 new posts +
`; From 81921578e004178c16b7a6ab686b2e66395b16cf Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 15:20:08 -0500 Subject: [PATCH 24/39] TimelineQueueButtonHeader: automatically load new items when scrolled to the top of the page --- .../timeline_queue_button_header.js | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/app/soapbox/components/timeline_queue_button_header.js b/app/soapbox/components/timeline_queue_button_header.js index 25a219336..2dc194312 100644 --- a/app/soapbox/components/timeline_queue_button_header.js +++ b/app/soapbox/components/timeline_queue_button_header.js @@ -14,11 +14,15 @@ class TimelineQueueButtonHeader extends React.PureComponent { message: PropTypes.object.isRequired, threshold: PropTypes.number, intl: PropTypes.object.isRequired, + autoload: PropTypes.bool, + autoloadThreshold: PropTypes.number, }; static defaultProps = { count: 0, threshold: 400, + autoload: true, + autoloadThreshold: 50, }; state = { @@ -26,9 +30,6 @@ class TimelineQueueButtonHeader extends React.PureComponent { } componentDidMount() { - this.window = window; - this.documentElement = document.scrollingElement || document.documentElement; - this.attachScrollListener(); } @@ -36,17 +37,30 @@ class TimelineQueueButtonHeader extends React.PureComponent { this.detachScrollListener(); } + componentDidUpdate(prevProps, prevState) { + const { scrollTop } = (document.scrollingElement || document.documentElement); + const { count, onClick, autoload, autoloadThreshold } = this.props; + + if (autoload && scrollTop <= autoloadThreshold && count !== prevProps.count) { + onClick(); + } + } + attachScrollListener() { - this.window.addEventListener('scroll', this.handleScroll); + window.addEventListener('scroll', this.handleScroll); } detachScrollListener() { - this.window.removeEventListener('scroll', this.handleScroll); + window.removeEventListener('scroll', this.handleScroll); } handleScroll = throttle(() => { const { scrollTop } = (document.scrollingElement || document.documentElement); - const { threshold } = this.props; + const { threshold, onClick, autoload, autoloadThreshold } = this.props; + + if (autoload && scrollTop <= autoloadThreshold) { + onClick(); + } if (scrollTop > threshold) { this.setState({ scrolled: true }); From 6f35933badf2a6d1493c4f04274a1ebaa7bc3f07 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 15:42:29 -0500 Subject: [PATCH 25/39] Modal: increase border-radius to 10px --- app/styles/components/modal.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/styles/components/modal.scss b/app/styles/components/modal.scss index 1f853929d..de591da55 100644 --- a/app/styles/components/modal.scss +++ b/app/styles/components/modal.scss @@ -337,7 +337,7 @@ overflow: hidden; width: 480px; max-width: 90vw; - border-radius: 4px; + border-radius: 10px; border: 1px solid var(--primary-text-color--faint); color: var(--primary-text-color--faint); background: var(--foreground-color); From 080cef309dfb7b331c7d24a413b192c072aebad7 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 15:46:34 -0500 Subject: [PATCH 26/39] Status: add color to repost icon --- app/styles/components/status.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/styles/components/status.scss b/app/styles/components/status.scss index 5c7c08ce0..37a60f5f0 100644 --- a/app/styles/components/status.scss +++ b/app/styles/components/status.scss @@ -143,6 +143,10 @@ .status__prepend-icon-wrapper { left: -26px; position: absolute; + + svg.feather-repeat { + color: var(--highlight-text-color); + } } .status { From 6fa875e2611391804a99db98a0dcd9011590ef3d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 16:28:05 -0500 Subject: [PATCH 27/39] RemoteTimeline: use SubNavigation --- app/soapbox/features/remote_timeline/index.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/soapbox/features/remote_timeline/index.js b/app/soapbox/features/remote_timeline/index.js index 2f6aac0dd..337081bb6 100644 --- a/app/soapbox/features/remote_timeline/index.js +++ b/app/soapbox/features/remote_timeline/index.js @@ -3,8 +3,7 @@ import { connect } from 'react-redux'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import StatusListContainer from '../ui/containers/status_list_container'; -import Column from '../../components/column'; -import HomeColumnHeader from '../../components/home_column_header'; +import Column from 'soapbox/features/ui/components/column'; import PinnedHostsPicker from './components/pinned_hosts_picker'; import IconButton from 'soapbox/components/icon_button'; import { expandRemoteTimeline } from '../../actions/timelines'; @@ -82,11 +81,10 @@ class RemoteTimeline extends React.PureComponent { } render() { - const { intl, hasUnread, onlyMedia, timelineId, instance, pinned } = this.props; + const { intl, onlyMedia, timelineId, instance, pinned } = this.props; return ( - - + {!pinned &&
From b029225236f715841c935680e20c278f403b628a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 16:32:49 -0500 Subject: [PATCH 28/39] ListTimeline: use SubNavigation, nuke HomeColumnHeader once and for all --- app/soapbox/components/home_column_header.js | 185 ------------------- app/soapbox/features/list_timeline/index.js | 17 +- 2 files changed, 7 insertions(+), 195 deletions(-) delete mode 100644 app/soapbox/components/home_column_header.js diff --git a/app/soapbox/components/home_column_header.js b/app/soapbox/components/home_column_header.js deleted file mode 100644 index 9ed7fea2a..000000000 --- a/app/soapbox/components/home_column_header.js +++ /dev/null @@ -1,185 +0,0 @@ -'use strict'; - -import React from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import classNames from 'classnames'; -import { injectIntl, defineMessages } from 'react-intl'; -import { Link } from 'react-router-dom'; -import Icon from 'soapbox/components/icon'; -import { fetchLists } from 'soapbox/actions/lists'; -import { createSelector } from 'reselect'; -import { getFeatures } from 'soapbox/utils/features'; - -const messages = defineMessages({ - show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, - hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' }, - homeTitle: { id: 'home_column_header.home', defaultMessage: 'Home' }, - allTitle: { id: 'home_column_header.all', defaultMessage: 'All' }, - fediverseTitle: { id: 'home_column_header.fediverse', defaultMessage: 'Fediverse' }, - listTitle: { id: 'home_column.lists', defaultMessage: 'Lists' }, -}); - -const getOrderedLists = createSelector([state => state.get('lists')], lists => { - if (!lists) { - return lists; - } - - return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))); -}); - -const mapStateToProps = state => { - const instance = state.get('instance'); - const features = getFeatures(instance); - - return { - lists: getOrderedLists(state), - siteTitle: state.getIn(['instance', 'title']), - federating: features.federating, - }; -}; - -class ColumnHeader extends React.PureComponent { - - static contextTypes = { - router: PropTypes.object, - }; - - static propTypes = { - intl: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - active: PropTypes.bool, - children: PropTypes.node, - activeItem: PropTypes.string, - activeSubItem: PropTypes.string, - lists: ImmutablePropTypes.list, - siteTitle: PropTypes.string, - federating: PropTypes.bool, - }; - - state = { - collapsed: true, - animating: false, - expandedFor: null, //lists, groups, etc. - }; - - componentDidMount() { - this.props.dispatch(fetchLists()); - } - - handleToggleClick = (e) => { - e.stopPropagation(); - this.setState({ collapsed: !this.state.collapsed, animating: true }); - } - - handleTransitionEnd = () => { - this.setState({ animating: false }); - } - - expandLists = () => { - this.setState({ - expandedFor: 'lists', - }); - } - - render() { - const { active, children, intl: { formatMessage }, activeItem, activeSubItem, lists, siteTitle, federating } = this.props; - const { collapsed, animating, expandedFor } = this.state; - - const wrapperClassName = classNames('column-header__wrapper', { - 'active': active, - }); - - const buttonClassName = classNames('column-header', { - 'active': active, - }); - - const collapsibleClassName = classNames('column-header__collapsible', { - 'collapsed': collapsed, - 'animating': animating, - }); - - const collapsibleButtonClassName = classNames('column-header__button', { - 'active': !collapsed, - }); - - const expansionClassName = classNames('column-header column-header__expansion', { - 'open': expandedFor, - }); - - let extraContent, collapseButton; - - if (children) { - extraContent = ( -
- {children} -
- ); - - collapseButton = ; - } - - const collapsedContent = [ - extraContent, - ]; - - let expandedContent = null; - if ((expandedFor === 'lists' || activeItem === 'lists') && lists) { - expandedContent = lists.map(list => - ( - {list.get('title')} - ), - ); - } - - return ( -
-

- - - {formatMessage(messages.homeTitle)} - - - - - {federating ? siteTitle : formatMessage(messages.allTitle)} - - - {federating && - - {formatMessage(messages.fediverseTitle)} - } - -
- {collapseButton} -
-

- - { - expandedContent && -

- {expandedContent} -

- } - -
-
- {(!collapsed || animating) && collapsedContent} -
-
-
- ); - } - -} - -export default injectIntl(connect(mapStateToProps)(ColumnHeader)); diff --git a/app/soapbox/features/list_timeline/index.js b/app/soapbox/features/list_timeline/index.js index c14df6b23..e45bcbcd6 100644 --- a/app/soapbox/features/list_timeline/index.js +++ b/app/soapbox/features/list_timeline/index.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import StatusListContainer from '../ui/containers/status_list_container'; -import Column from '../../components/column'; +import Column from 'soapbox/features/ui/components/column'; import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import { connectListStream } from '../../actions/streaming'; import { expandListTimeline } from '../../actions/timelines'; @@ -11,9 +11,6 @@ import { fetchList, deleteList } from '../../actions/lists'; import { openModal } from '../../actions/modal'; import MissingIndicator from '../../components/missing_indicator'; import LoadingIndicator from '../../components/loading_indicator'; -import Icon from 'soapbox/components/icon'; -import HomeColumnHeader from '../../components/home_column_header'; -import { Link } from 'react-router-dom'; import Button from 'soapbox/components/button'; const messages = defineMessages({ @@ -23,7 +20,7 @@ const messages = defineMessages({ const mapStateToProps = (state, props) => ({ list: state.getIn(['lists', props.params.id]), - hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0, + // hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0, }); export default @connect(mapStateToProps) @@ -37,7 +34,7 @@ class ListTimeline extends React.PureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - hasUnread: PropTypes.bool, + // hasUnread: PropTypes.bool, list: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]), intl: PropTypes.object.isRequired, }; @@ -97,7 +94,7 @@ class ListTimeline extends React.PureComponent { } render() { - const { hasUnread, list } = this.props; + const { list } = this.props; const { id } = this.props.params; const title = list ? list.get('title') : id; @@ -126,8 +123,8 @@ class ListTimeline extends React.PureComponent { ); return ( - - + + {/*
-
+
*/} Date: Fri, 8 Oct 2021 16:35:47 -0500 Subject: [PATCH 29/39] RemoteInstancePage: use standard navigation like other pages --- app/soapbox/pages/remote_instance_page.js | 39 +++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/app/soapbox/pages/remote_instance_page.js b/app/soapbox/pages/remote_instance_page.js index d5c807a12..7805c6daa 100644 --- a/app/soapbox/pages/remote_instance_page.js +++ b/app/soapbox/pages/remote_instance_page.js @@ -1,7 +1,9 @@ import React from 'react'; import { connect } from 'react-redux'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import Sticky from 'react-stickynode'; import BundleContainer from 'soapbox/features/ui/containers/bundle_container'; +import PrimaryNavigation from 'soapbox/components/primary_navigation'; import { PromoPanel, FeaturesPanel, @@ -36,14 +38,9 @@ class RemoteInstancePage extends ImmutablePureComponent {
- - {Component => } - - {(disclosed || isAdmin) && ( - - {Component => } - - )} + + +
@@ -55,15 +52,25 @@ class RemoteInstancePage extends ImmutablePureComponent {
- {me && ( - - {Component => } + + {me && ( + + {Component => } + + )} + + {Component => } - )} - - {Component => } - - + + {Component => } + + {(disclosed || isAdmin) && ( + + {Component => } + + )} + +
From e9b5a9cf2e37dfd6c0ebc9a90edde91bbcdc11fb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 16:44:24 -0500 Subject: [PATCH 30/39] MediaModal: use Tabler next/prev arrows --- app/soapbox/features/ui/components/media_modal.js | 13 +++++++++++-- app/styles/components/modal.scss | 5 +++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/soapbox/features/ui/components/media_modal.js b/app/soapbox/features/ui/components/media_modal.js index 5cdfabd3b..b93e23edf 100644 --- a/app/soapbox/features/ui/components/media_modal.js +++ b/app/soapbox/features/ui/components/media_modal.js @@ -138,8 +138,17 @@ class MediaModal extends ImmutablePureComponent { const index = this.getIndex(); let pagination = []; - const leftNav = media.size > 1 && ; - const rightNav = media.size > 1 && ; + const leftNav = media.size > 1 && ( + + ); + + const rightNav = media.size > 1 && ( + + ); if (media.size > 1) { pagination = media.map((item, i) => { diff --git a/app/styles/components/modal.scss b/app/styles/components/modal.scss index de591da55..ca1bb1698 100644 --- a/app/styles/components/modal.scss +++ b/app/styles/components/modal.scss @@ -128,8 +128,9 @@ @media screen and (max-width: 600px) { padding: 30px 2px; } - .fa { - margin-right: 0; + .svg-icon { + width: 24px; + height: 24px; } } From 2dec47e1792fc86f83ed950cc8c732c5696d0701 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 17:09:55 -0500 Subject: [PATCH 31/39] MaterialStatus: fix focused state --- app/soapbox/components/material_status.js | 6 +++--- app/soapbox/components/status.js | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/soapbox/components/material_status.js b/app/soapbox/components/material_status.js index 25fd24e05..6454497c5 100644 --- a/app/soapbox/components/material_status.js +++ b/app/soapbox/components/material_status.js @@ -19,9 +19,9 @@ export default class MaterialStatus extends React.Component { } return ( -
-
- +
+
+
); diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index 2b4c43bc3..a3efe15b8 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -93,6 +93,11 @@ class Status extends ImmutablePureComponent { group: ImmutablePropTypes.map, displayMedia: PropTypes.string, allowedEmoji: ImmutablePropTypes.list, + focusable: PropTypes.bool, + }; + + static defaultProps = { + focusable: true, }; // Avoid checking props that are functions (and whose equality will always @@ -336,7 +341,7 @@ class Status extends ImmutablePureComponent { return ( -
+
@@ -490,7 +495,7 @@ class Status extends ImmutablePureComponent { return ( -
+
{prepend}
From 53ce30e472e8891b623545959195100ade9e881b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 17:11:18 -0500 Subject: [PATCH 32/39] Add react-content-loader --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index f6544915c..eccf8594c 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "qrcode.react": "^1.0.0", "react": "^16.13.1", "react-color": "^2.18.1", + "react-content-loader": "^6.0.3", "react-datepicker": "^4.1.1", "react-dom": "^16.13.1", "react-helmet": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index 62d92d092..3d75cd47c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7757,6 +7757,11 @@ react-color@^2.18.1: reactcss "^1.2.0" tinycolor2 "^1.4.1" +react-content-loader@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/react-content-loader/-/react-content-loader-6.0.3.tgz#32e28ca7120e0a2552fc26655d0d4448cc1fc0c5" + integrity sha512-CIRgTHze+ls+jGDIfCitw27YkW2XcaMpsYORTUdBxsMFiKuUYMnlvY76dZE4Lsaa9vFXVw+41ieBEK7SJt0nug== + react-datepicker@^4.1.1: version "4.2.1" resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.2.1.tgz#72caf5055bc7c4eb0279c1f6d7624ded053edc4c" From 14d800c845da794d0eca67edc4073e6162bddcc8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 18:53:12 -0500 Subject: [PATCH 33/39] VideoPlayer: use accent color for volume/seek progress bars --- app/styles/components/video-player.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/styles/components/video-player.scss b/app/styles/components/video-player.scss index 3122849a4..57061a2ff 100644 --- a/app/styles/components/video-player.scss +++ b/app/styles/components/video-player.scss @@ -297,7 +297,7 @@ left: 0; top: 50%; transform: translate(0, -50%); - background: var(--brand-color); + background: var(--accent-color); } &__handle { @@ -310,7 +310,7 @@ left: 0; margin-left: -6px; transform: translate(0, -50%); - background: var(--brand-color); + background: var(--accent-color); box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2); opacity: 0; @@ -364,7 +364,7 @@ height: 4px; border-radius: 4px; top: 14px; - background: var(--brand-color); + background: var(--accent-color); } &__buffer { @@ -380,7 +380,7 @@ height: 12px; top: 10px; margin-left: -6px; - background: var(--brand-color); + background: var(--accent-color); box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2); .no-reduce-motion & { From c264a5fb475e0ce805d0aeeb355521e79081407d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 8 Oct 2021 19:09:34 -0500 Subject: [PATCH 34/39] SubNavigation: border-radius 0 when scrolled --- app/soapbox/components/sub_navigation.js | 41 +++++++++++++++++++++++- app/styles/navigation.scss | 4 +++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/app/soapbox/components/sub_navigation.js b/app/soapbox/components/sub_navigation.js index 6edf355a4..627dffff4 100644 --- a/app/soapbox/components/sub_navigation.js +++ b/app/soapbox/components/sub_navigation.js @@ -1,7 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; +import { throttle } from 'lodash'; import Icon from 'soapbox/components/icon'; +import classNames from 'classnames'; export default class SubNavigation extends React.PureComponent { @@ -13,6 +15,10 @@ export default class SubNavigation extends React.PureComponent { router: PropTypes.object.isRequired, } + state = { + scrolled: false, + } + handleBackClick = () => { if (window.history && window.history.length === 1) { this.context.router.history.push('/'); @@ -27,11 +33,44 @@ export default class SubNavigation extends React.PureComponent { } } + componentDidMount() { + this.attachScrollListener(); + } + + componentWillUnmount() { + this.detachScrollListener(); + } + + attachScrollListener() { + window.addEventListener('scroll', this.handleScroll); + } + + detachScrollListener() { + window.removeEventListener('scroll', this.handleScroll); + } + + handleScroll = throttle(() => { + if (this.node) { + const { top } = this.node.getBoundingClientRect(); + + if (top <= 50) { + this.setState({ scrolled: true }); + } else { + this.setState({ scrolled: false }); + } + } + }, 150, { trailing: true }); + + setRef = c => { + this.node = c; + } + render() { const { message } = this.props; + const { scrolled } = this.state; return ( -
+