From 101f8616feb845f70ef89fa0d0b3ebc37c472930 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 Jul 2021 19:16:06 +0200 Subject: [Glitch] Fix pop-in player display when poster has long username or handle Port 1381e0e1d9f27bd108d8b9349896f10ffe996cb2 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/components/status.scss | 1 + 1 file changed, 1 insertion(+) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index e906a7261..e9d30544f 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -1095,6 +1095,7 @@ a.status-card.compact:hover { &__account { display: flex; text-decoration: none; + overflow: hidden; } .account__avatar { -- cgit From a85eb7d930c0b1acfef9a25c102d902f63f3d379 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 12 Jul 2021 08:07:04 +0200 Subject: Fix follow suggestions scrolling on mobile view Also simplify the CSS a bit and bring it closer to upstream. --- .../features/compose/components/search_results.js | 16 ++--- .../flavours/glitch/styles/components/drawer.scss | 76 ++++++++-------------- .../flavours/glitch/styles/components/search.scss | 11 +++- 3 files changed, 41 insertions(+), 62 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/features/compose/components/search_results.js b/app/javascript/flavours/glitch/features/compose/components/search_results.js index a0f86a06a..9a76e5418 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search_results.js +++ b/app/javascript/flavours/glitch/features/compose/components/search_results.js @@ -71,7 +71,7 @@ class SearchResults extends ImmutablePureComponent { ); } else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) { statuses = ( -
+
@@ -87,7 +87,7 @@ class SearchResults extends ImmutablePureComponent { if (results.get('accounts') && results.get('accounts').size > 0) { count += results.get('accounts').size; accounts = ( -
+
{results.get('accounts').map(accountId => )} @@ -100,7 +100,7 @@ class SearchResults extends ImmutablePureComponent { if (results.get('statuses') && results.get('statuses').size > 0) { count += results.get('statuses').size; statuses = ( -
+
{results.get('statuses').map(statusId => )} @@ -113,7 +113,7 @@ class SearchResults extends ImmutablePureComponent { if (results.get('hashtags') && results.get('hashtags').size > 0) { count += results.get('hashtags').size; hashtags = ( -
+
{results.get('hashtags').map(hashtag => )} @@ -131,11 +131,9 @@ class SearchResults extends ImmutablePureComponent { -
- {accounts} - {statuses} - {hashtags} -
+ {accounts} + {statuses} + {hashtags}
); }; diff --git a/app/javascript/flavours/glitch/styles/components/drawer.scss b/app/javascript/flavours/glitch/styles/components/drawer.scss index b6d06f53a..61969abee 100644 --- a/app/javascript/flavours/glitch/styles/components/drawer.scss +++ b/app/javascript/flavours/glitch/styles/components/drawer.scss @@ -120,20 +120,22 @@ } .drawer--results { - background: $ui-base-color; - overflow: hidden; - display: flex; - flex-direction: column; - flex: 1 1 auto; + overflow-x: hidden; + overflow-y: scroll; +} - & > header { - color: $dark-text-color; - background: lighten($ui-base-color, 2%); +.search-results__section { + margin-bottom: 5px; + + h5 { + background: darken($ui-base-color, 4%); + border-bottom: 1px solid lighten($ui-base-color, 8%); + cursor: default; + display: flex; padding: 15px; font-weight: 500; font-size: 16px; - cursor: default; - flex: 0 0 auto; + color: $dark-text-color; .fa { display: inline-block; @@ -141,48 +143,22 @@ } } - & > .search-results__contents { - overflow-x: hidden; - overflow-y: scroll; - flex: 1 1 auto; - - & > section { - margin-bottom: 5px; - - h5 { - background: darken($ui-base-color, 4%); - border-bottom: 1px solid lighten($ui-base-color, 8%); - cursor: default; - display: flex; - padding: 15px; - font-weight: 500; - font-size: 16px; - color: $dark-text-color; - - .fa { - display: inline-block; - margin-right: 5px; - } - } + .account:last-child, + & > div:last-child .status { + border-bottom: 0; + } - .account:last-child, - & > div:last-child .status { - border-bottom: 0; - } + & > .hashtag { + display: block; + padding: 10px; + color: $secondary-text-color; + text-decoration: none; - & > .hashtag { - display: block; - padding: 10px; - color: $secondary-text-color; - text-decoration: none; - - &:hover, - &:active, - &:focus { - color: lighten($secondary-text-color, 4%); - text-decoration: underline; - } - } + &:hover, + &:active, + &:focus { + color: lighten($secondary-text-color, 4%); + text-decoration: underline; } } } diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss index eec2e64d6..929769130 100644 --- a/app/javascript/flavours/glitch/styles/components/search.scss +++ b/app/javascript/flavours/glitch/styles/components/search.scss @@ -94,10 +94,15 @@ .search-results__header { color: $dark-text-color; background: lighten($ui-base-color, 2%); - border-bottom: 1px solid darken($ui-base-color, 4%); - padding: 15px 10px; - font-size: 14px; + padding: 15px; font-weight: 500; + font-size: 16px; + cursor: default; + + .fa { + display: inline-block; + margin-right: 5px; + } } .search-results__info { -- cgit From ddf3f4cf85f0be6f221e68bda5944dd8a034ff91 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 12 Jul 2021 14:44:35 +0200 Subject: Fix clicking on the pop-up player placeholder expanding the clicked toot --- app/javascript/flavours/glitch/components/status_content.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 34ff97305..faa1302c4 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -224,8 +224,8 @@ export default class StatusContent extends React.PureComponent { const [ deltaX, deltaY ] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)]; let element = e.target; - while (element) { - if (['button', 'video', 'a', 'label', 'canvas'].includes(element.localName)) { + while (element !== e.currentTarget) { + if (['button', 'video', 'a', 'label', 'canvas'].includes(element.localName) || element.getAttribute('role') === 'button') { return; } element = element.parentNode; -- cgit From c5b4e6b7084e8257979adec87f97d4800b7bec57 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 12 Jul 2021 17:00:14 +0200 Subject: Add modal stack to allow better boost modal and media modal interaction. --- .../glitch/features/ui/containers/modal_container.js | 4 ++-- app/javascript/flavours/glitch/reducers/modal.js | 14 +++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/features/ui/containers/modal_container.js b/app/javascript/flavours/glitch/features/ui/containers/modal_container.js index f074002e4..e13e745e6 100644 --- a/app/javascript/flavours/glitch/features/ui/containers/modal_container.js +++ b/app/javascript/flavours/glitch/features/ui/containers/modal_container.js @@ -3,8 +3,8 @@ import { closeModal } from 'flavours/glitch/actions/modal'; import ModalRoot from '../components/modal_root'; const mapStateToProps = state => ({ - type: state.get('modal').modalType, - props: state.get('modal').modalProps, + type: state.getIn(['modal', 0, 'modalType'], null), + props: state.getIn(['modal', 0, 'modalProps'], {}), }); const mapDispatchToProps = dispatch => ({ diff --git a/app/javascript/flavours/glitch/reducers/modal.js b/app/javascript/flavours/glitch/reducers/modal.js index 52b05d69b..f8fdc2995 100644 --- a/app/javascript/flavours/glitch/reducers/modal.js +++ b/app/javascript/flavours/glitch/reducers/modal.js @@ -1,19 +1,15 @@ import { MODAL_OPEN, MODAL_CLOSE } from 'flavours/glitch/actions/modal'; import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines'; +import { Stack as ImmutableStack, Map as ImmutableMap } from 'immutable'; -const initialState = { - modalType: null, - modalProps: {}, -}; - -export default function modal(state = initialState, action) { +export default function modal(state = ImmutableStack(), action) { switch(action.type) { case MODAL_OPEN: - return { modalType: action.modalType, modalProps: action.modalProps }; + return state.unshift(ImmutableMap({ modalType: action.modalType, modalProps: action.modalProps })); case MODAL_CLOSE: - return (action.modalType === undefined || action.modalType === state.modalType) ? initialState : state; + return (action.modalType === undefined || action.modalType === state.getIn([0, 'modalType'])) ? state.shift() : state; case TIMELINE_DELETE: - return (state.modalProps.statusId === action.id) ? initialState : state; + return state.filterNot((modal) => modal.get('modalProps').statusId === action.id); default: return state; } -- cgit From 6e3d5cbca26995e99d6ca8b1e3191edc539d87eb Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 12 Jul 2021 17:55:40 +0200 Subject: Fix and simplify browser history handling in relation to modals This simplifies the logic to: - when the last modal gets closed and we're in our history buffer state, go back - whenever a modal is open, ensure we're in a history buffer state by potentially pushing one --- .../flavours/glitch/components/modal_root.js | 34 ++++++++++++++-------- .../glitch/features/ui/components/modal_root.js | 8 ++--- 2 files changed, 24 insertions(+), 18 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/modal_root.js b/app/javascript/flavours/glitch/components/modal_root.js index 913234d32..7b5a630e5 100644 --- a/app/javascript/flavours/glitch/components/modal_root.js +++ b/app/javascript/flavours/glitch/components/modal_root.js @@ -76,10 +76,13 @@ export default class ModalRoot extends React.PureComponent { this.activeElement = null; }).catch(console.error); - this.handleModalClose(); + this._handleModalClose(); } if (this.props.children && !prevProps.children) { - this.handleModalOpen(); + this._handleModalOpen(); + } + if (this.props.children) { + this._ensureHistoryBuffer(); } } @@ -88,22 +91,29 @@ export default class ModalRoot extends React.PureComponent { window.removeEventListener('keydown', this.handleKeyDown); } - handleModalClose () { + _handleModalOpen () { + this._modalHistoryKey = Date.now(); + this.unlistenHistory = this.history.listen((_, action) => { + if (action === 'POP') { + this.props.onClose(); + } + }); + } + + _handleModalClose () { this.unlistenHistory(); - const state = this.history.location.state; - if (state && state.mastodonModalOpen) { + const { state } = this.history.location; + if (state && state.mastodonModalKey === this._modalHistoryKey) { this.history.goBack(); } } - handleModalOpen () { - const history = this.history; - const state = {...history.location.state, mastodonModalOpen: true}; - history.push(history.location.pathname, state); - this.unlistenHistory = history.listen(() => { - this.props.onClose(); - }); + _ensureHistoryBuffer () { + const { pathname, state } = this.history.location; + if (!state || state.mastodonModalKey !== this._modalHistoryKey) { + this.history.push(pathname, { ...state, mastodonModalKey: this._modalHistoryKey }); + } } getSiblings = () => { diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js index 0fd70de34..2636e79f5 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -59,12 +59,8 @@ export default class ModalRoot extends React.PureComponent { backgroundColor: null, }; - getSnapshotBeforeUpdate () { - return { visible: !!this.props.type }; - } - - componentDidUpdate (prevProps, prevState, { visible }) { - if (visible) { + componentDidUpdate () { + if (!!this.props.type) { document.body.classList.add('with-modals--active'); document.documentElement.style.marginRight = `${getScrollbarWidth()}px`; } else { -- cgit From 99f28c17dea35d0eec90a74c5fe95f60b6ad2f9e Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Jul 2021 11:07:16 +0200 Subject: Fix scroll handling with modals --- app/javascript/flavours/glitch/components/scrollable_list.js | 3 +-- app/javascript/flavours/glitch/components/status_action_bar.js | 2 +- app/javascript/flavours/glitch/containers/mastodon.js | 2 +- app/javascript/flavours/glitch/features/account_gallery/index.js | 3 +-- app/javascript/flavours/glitch/features/status/index.js | 3 +-- 5 files changed, 5 insertions(+), 8 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.js index cc8d9f1f3..5d0a06561 100644 --- a/app/javascript/flavours/glitch/components/scrollable_list.js +++ b/app/javascript/flavours/glitch/components/scrollable_list.js @@ -265,8 +265,7 @@ class ScrollableList extends PureComponent { } defaultShouldUpdateScroll = (prevRouterProps, { location }) => { - if ((((prevRouterProps || {}).location || {}).state || {}).mastodonModalOpen) return false; - return !(location.state && location.state.mastodonModalOpen); + return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); } handleLoadPending = e => { diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 74bfd948e..206ae74c8 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -147,7 +147,7 @@ class StatusActionBar extends ImmutablePureComponent { handleOpen = () => { let state = {...this.context.router.history.location.state}; - if (state.mastodonModalOpen) { + if (state.mastodonModalKey) { this.context.router.history.replace(`/statuses/${this.props.status.get('id')}`, { mastodonBackSteps: (state.mastodonBackSteps || 0) + 1 }); } else { state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1; diff --git a/app/javascript/flavours/glitch/containers/mastodon.js b/app/javascript/flavours/glitch/containers/mastodon.js index bcdd9b54e..131303fd3 100644 --- a/app/javascript/flavours/glitch/containers/mastodon.js +++ b/app/javascript/flavours/glitch/containers/mastodon.js @@ -41,7 +41,7 @@ export default class Mastodon extends React.PureComponent { } shouldUpdateScroll (_, { location }) { - return !(location.state && location.state.mastodonModalOpen); + return !(location.state?.mastodonModalKey); } render () { diff --git a/app/javascript/flavours/glitch/features/account_gallery/index.js b/app/javascript/flavours/glitch/features/account_gallery/index.js index 2a43d1ed2..83d623356 100644 --- a/app/javascript/flavours/glitch/features/account_gallery/index.js +++ b/app/javascript/flavours/glitch/features/account_gallery/index.js @@ -105,8 +105,7 @@ class AccountGallery extends ImmutablePureComponent { } shouldUpdateScroll = (prevRouterProps, { location }) => { - if ((((prevRouterProps || {}).location || {}).state || {}).mastodonModalOpen) return false; - return !(location.state && location.state.mastodonModalOpen); + return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); } setColumnRef = c => { diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 513a6227f..230966f2a 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -508,8 +508,7 @@ class Status extends ImmutablePureComponent { } shouldUpdateScroll = (prevRouterProps, { location }) => { - if ((((prevRouterProps || {}).location || {}).state || {}).mastodonModalOpen) return false; - return !(location.state && location.state.mastodonModalOpen); + return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); } render () { -- cgit From 84fbe4d030e3176fffaf49ac8eec0c0602b1ba87 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Jul 2021 12:40:15 +0200 Subject: Refactor shouldUpdateScroll stuff --- .../flavours/glitch/components/scrollable_list.js | 11 +++-------- app/javascript/flavours/glitch/components/status_list.js | 1 - .../flavours/glitch/containers/scroll_container.js | 15 +++++++++++++++ .../flavours/glitch/features/account_gallery/index.js | 8 ++------ .../flavours/glitch/features/directory/index.js | 7 +++---- .../flavours/glitch/features/notifications/index.js | 4 +--- app/javascript/flavours/glitch/features/status/index.js | 8 ++------ app/javascript/flavours/glitch/features/ui/index.js | 2 +- 8 files changed, 27 insertions(+), 29 deletions(-) create mode 100644 app/javascript/flavours/glitch/containers/scroll_container.js (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.js index 5d0a06561..16f13afa4 100644 --- a/app/javascript/flavours/glitch/components/scrollable_list.js +++ b/app/javascript/flavours/glitch/components/scrollable_list.js @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'flavours/glitch/containers/scroll_container'; import PropTypes from 'prop-types'; import IntersectionObserverArticleContainer from 'flavours/glitch/containers/intersection_observer_article_container'; import LoadMore from './load_more'; @@ -34,7 +34,6 @@ class ScrollableList extends PureComponent { onScrollToTop: PropTypes.func, onScroll: PropTypes.func, trackScroll: PropTypes.bool, - shouldUpdateScroll: PropTypes.func, isLoading: PropTypes.bool, showLoading: PropTypes.bool, hasMore: PropTypes.bool, @@ -264,10 +263,6 @@ class ScrollableList extends PureComponent { this.props.onLoadMore(); } - defaultShouldUpdateScroll = (prevRouterProps, { location }) => { - return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); - } - handleLoadPending = e => { e.preventDefault(); this.props.onLoadPending(); @@ -281,7 +276,7 @@ class ScrollableList extends PureComponent { } render () { - const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props; + const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props; const { fullscreen } = this.state; const childrenCount = React.Children.count(children); @@ -347,7 +342,7 @@ class ScrollableList extends PureComponent { if (trackScroll) { return ( - + {scrollableArea} ); diff --git a/app/javascript/flavours/glitch/components/status_list.js b/app/javascript/flavours/glitch/components/status_list.js index 60cc23f4b..9095e087e 100644 --- a/app/javascript/flavours/glitch/components/status_list.js +++ b/app/javascript/flavours/glitch/components/status_list.js @@ -18,7 +18,6 @@ export default class StatusList extends ImmutablePureComponent { onScrollToTop: PropTypes.func, onScroll: PropTypes.func, trackScroll: PropTypes.bool, - shouldUpdateScroll: PropTypes.func, isLoading: PropTypes.bool, isPartial: PropTypes.bool, hasMore: PropTypes.bool, diff --git a/app/javascript/flavours/glitch/containers/scroll_container.js b/app/javascript/flavours/glitch/containers/scroll_container.js new file mode 100644 index 000000000..595f3155f --- /dev/null +++ b/app/javascript/flavours/glitch/containers/scroll_container.js @@ -0,0 +1,15 @@ +import { ScrollContainer as OriginalScrollContainer } from 'react-router-scroll-4'; + +// ScrollContainer is used to automatically scroll to the top when pushing a +// new history state and remembering the scroll position when going back. +// There are a few things we need to do differently, though. +const defaultShouldUpdateScroll = (prevRouterProps, { location }) => { + return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); +} + +export default +class ScrollContainer extends OriginalScrollContainer { + static defaultProps = { + shouldUpdateScroll: defaultShouldUpdateScroll, + }; +} diff --git a/app/javascript/flavours/glitch/features/account_gallery/index.js b/app/javascript/flavours/glitch/features/account_gallery/index.js index 83d623356..434a47dfc 100644 --- a/app/javascript/flavours/glitch/features/account_gallery/index.js +++ b/app/javascript/flavours/glitch/features/account_gallery/index.js @@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { getAccountGallery } from 'flavours/glitch/selectors'; import MediaItem from './components/media_item'; import HeaderContainer from 'flavours/glitch/features/account_timeline/containers/header_container'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'flavours/glitch/containers/scroll_container'; import LoadMore from 'flavours/glitch/components/load_more'; import MissingIndicator from 'flavours/glitch/components/missing_indicator'; import { openModal } from 'flavours/glitch/actions/modal'; @@ -104,10 +104,6 @@ class AccountGallery extends ImmutablePureComponent { this.handleScrollToBottom(); } - shouldUpdateScroll = (prevRouterProps, { location }) => { - return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); - } - setColumnRef = c => { this.column = c; } @@ -164,7 +160,7 @@ class AccountGallery extends ImmutablePureComponent { - +
diff --git a/app/javascript/flavours/glitch/features/directory/index.js b/app/javascript/flavours/glitch/features/directory/index.js index 858a8fa55..cde5926e0 100644 --- a/app/javascript/flavours/glitch/features/directory/index.js +++ b/app/javascript/flavours/glitch/features/directory/index.js @@ -12,7 +12,7 @@ import AccountCard from './components/account_card'; import RadioButton from 'flavours/glitch/components/radio_button'; import classNames from 'classnames'; import LoadMore from 'flavours/glitch/components/load_more'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'flavours/glitch/containers/scroll_container'; const messages = defineMessages({ title: { id: 'column.directory', defaultMessage: 'Browse profiles' }, @@ -40,7 +40,6 @@ class Directory extends React.PureComponent { isLoading: PropTypes.bool, accountIds: ImmutablePropTypes.list.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, columnId: PropTypes.string, intl: PropTypes.object.isRequired, multiColumn: PropTypes.bool, @@ -125,7 +124,7 @@ class Directory extends React.PureComponent { } render () { - const { isLoading, accountIds, intl, columnId, multiColumn, domain, shouldUpdateScroll } = this.props; + const { isLoading, accountIds, intl, columnId, multiColumn, domain } = this.props; const { order, local } = this.getParams(this.props, this.state); const pinned = !!columnId; @@ -163,7 +162,7 @@ class Directory extends React.PureComponent { multiColumn={multiColumn} /> - {multiColumn && !pinned ? {scrollableArea} : scrollableArea} + {multiColumn && !pinned ? {scrollableArea} : scrollableArea} ); } diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js index 6fc951e37..075e729b1 100644 --- a/app/javascript/flavours/glitch/features/notifications/index.js +++ b/app/javascript/flavours/glitch/features/notifications/index.js @@ -99,7 +99,6 @@ class Notifications extends React.PureComponent { notifications: ImmutablePropTypes.list.isRequired, showFilterBar: PropTypes.bool.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, intl: PropTypes.object.isRequired, isLoading: PropTypes.bool, isUnread: PropTypes.bool, @@ -220,7 +219,7 @@ class Notifications extends React.PureComponent { } render () { - const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props; + const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props; const { notifCleaning, notifCleaningActive } = this.props; const { animatingNCD } = this.state; const pinned = !!columnId; @@ -273,7 +272,6 @@ class Notifications extends React.PureComponent { onLoadPending={this.handleLoadPending} onScrollToTop={this.handleScrollToTop} onScroll={this.handleScroll} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} > {scrollableContent} diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 230966f2a..9dbba4772 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -32,7 +32,7 @@ import { initBlockModal } from 'flavours/glitch/actions/blocks'; import { initReport } from 'flavours/glitch/actions/reports'; import { initBoostModal } from 'flavours/glitch/actions/boosts'; import { makeGetStatus } from 'flavours/glitch/selectors'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'flavours/glitch/containers/scroll_container'; import ColumnBackButton from 'flavours/glitch/components/column_back_button'; import ColumnHeader from '../../components/column_header'; import StatusContainer from 'flavours/glitch/containers/status_container'; @@ -507,10 +507,6 @@ class Status extends ImmutablePureComponent { this.setState({ fullscreen: isFullscreen() }); } - shouldUpdateScroll = (prevRouterProps, { location }) => { - return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); - } - render () { let ancestors, descendants; const { setExpansion } = this; @@ -561,7 +557,7 @@ class Status extends ImmutablePureComponent { )} /> - +
{ancestors} diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index 1149eb14e..ad063f01b 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -212,7 +212,7 @@ class SwitchingColumnsArea extends React.PureComponent { - + -- cgit From 19ea6618b18ac5c11d4b0ac4eb8acb2276db6a89 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Jul 2021 12:57:07 +0200 Subject: Small scroll/history behavior fixup to take weird browser patterns into account --- app/javascript/flavours/glitch/containers/scroll_container.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/containers/scroll_container.js b/app/javascript/flavours/glitch/containers/scroll_container.js index 595f3155f..740e266cb 100644 --- a/app/javascript/flavours/glitch/containers/scroll_container.js +++ b/app/javascript/flavours/glitch/containers/scroll_container.js @@ -4,7 +4,8 @@ import { ScrollContainer as OriginalScrollContainer } from 'react-router-scroll- // new history state and remembering the scroll position when going back. // There are a few things we need to do differently, though. const defaultShouldUpdateScroll = (prevRouterProps, { location }) => { - return !(prevRouterProps?.location?.state?.mastodonModalKey || location.state?.mastodonModalKey); + // If the change is caused by opening a modal, do not scroll to top + return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey); } export default -- cgit From e4270cb55a133f4cb93290ff88b3fd2a3aa9f536 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Jul 2021 13:49:40 +0200 Subject: Please CodeClimate --- app/javascript/flavours/glitch/containers/scroll_container.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/containers/scroll_container.js b/app/javascript/flavours/glitch/containers/scroll_container.js index 740e266cb..d21ff6368 100644 --- a/app/javascript/flavours/glitch/containers/scroll_container.js +++ b/app/javascript/flavours/glitch/containers/scroll_container.js @@ -6,11 +6,13 @@ import { ScrollContainer as OriginalScrollContainer } from 'react-router-scroll- const defaultShouldUpdateScroll = (prevRouterProps, { location }) => { // If the change is caused by opening a modal, do not scroll to top return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey); -} +}; export default class ScrollContainer extends OriginalScrollContainer { + static defaultProps = { shouldUpdateScroll: defaultShouldUpdateScroll, }; + } -- cgit From 08139d3cd72f28b7c4199f1123ab149651800f2c Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 25 Jul 2021 01:13:46 +0200 Subject: [Glitch] Fix “open” link of media modal not closing modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port af08229ff43cf61b6b3eb386ca9d6205d05dc295 to glitch-soc Signed-off-by: Claire --- .../glitch/features/picture_in_picture/components/footer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js index fcb2df527..98d1f40b2 100644 --- a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js +++ b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js @@ -116,7 +116,11 @@ class Footer extends ImmutablePureComponent { return; } - const { status } = this.props; + const { status, onClose } = this.props; + + if (onClose) { + onClose(); + } router.history.push(`/statuses/${status.get('id')}`); } -- cgit From 34a573ac27fe9d5fb324d8910759cd2233911646 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 25 Jul 2021 01:14:43 +0200 Subject: [Glitch] Add confirmation modal when closing media edit modal with unsaved changes Port a8a7066e977cb0aa1988d340ef8b7c542f179b14 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/actions/compose.js | 32 ++++++ .../compose/containers/upload_container.js | 5 +- .../features/ui/components/focal_point_modal.js | 108 +++++++++------------ .../glitch/features/ui/components/modal_root.js | 23 ++++- .../features/ui/containers/modal_container.js | 16 ++- app/javascript/flavours/glitch/reducers/compose.js | 24 +++++ app/javascript/flavours/glitch/reducers/modal.js | 3 + 7 files changed, 142 insertions(+), 69 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index f83738093..eebe98626 100644 --- a/app/javascript/flavours/glitch/actions/compose.js +++ b/app/javascript/flavours/glitch/actions/compose.js @@ -10,6 +10,7 @@ import { importFetchedAccounts } from './importer'; import { updateTimeline } from './timelines'; import { showAlertForError } from './alerts'; import { showAlert } from './alerts'; +import { openModal } from './modal'; import { defineMessages } from 'react-intl'; let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags; @@ -68,6 +69,11 @@ export const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE'; export const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE'; export const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE'; +export const INIT_MEDIA_EDIT_MODAL = 'INIT_MEDIA_EDIT_MODAL'; + +export const COMPOSE_CHANGE_MEDIA_DESCRIPTION = 'COMPOSE_CHANGE_MEDIA_DESCRIPTION'; +export const COMPOSE_CHANGE_MEDIA_FOCUS = 'COMPOSE_CHANGE_MEDIA_FOCUS'; + const messages = defineMessages({ uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' }, uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, @@ -339,6 +345,32 @@ export const uploadThumbnailFail = error => ({ skipLoading: true, }); +export function initMediaEditModal(id) { + return dispatch => { + dispatch({ + type: INIT_MEDIA_EDIT_MODAL, + id, + }); + + dispatch(openModal('FOCAL_POINT', { id })); + }; +}; + +export function onChangeMediaDescription(description) { + return { + type: COMPOSE_CHANGE_MEDIA_DESCRIPTION, + description, + }; +}; + +export function onChangeMediaFocus(focusX, focusY) { + return { + type: COMPOSE_CHANGE_MEDIA_FOCUS, + focusX, + focusY, + }; +}; + export function changeUploadCompose(id, params) { return (dispatch, getState) => { dispatch(changeUploadComposeRequest()); diff --git a/app/javascript/flavours/glitch/features/compose/containers/upload_container.js b/app/javascript/flavours/glitch/features/compose/containers/upload_container.js index f687fae99..f3ca4ce7b 100644 --- a/app/javascript/flavours/glitch/features/compose/containers/upload_container.js +++ b/app/javascript/flavours/glitch/features/compose/containers/upload_container.js @@ -1,7 +1,6 @@ import { connect } from 'react-redux'; import Upload from '../components/upload'; -import { undoUploadCompose } from 'flavours/glitch/actions/compose'; -import { openModal } from 'flavours/glitch/actions/modal'; +import { undoUploadCompose, initMediaEditModal } from 'flavours/glitch/actions/compose'; import { submitCompose } from 'flavours/glitch/actions/compose'; const mapStateToProps = (state, { id }) => ({ @@ -15,7 +14,7 @@ const mapDispatchToProps = dispatch => ({ }, onOpenFocalPoint: id => { - dispatch(openModal('FOCAL_POINT', { id })); + dispatch(initMediaEditModal(id)); }, onSubmit (router) { diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js index b7ec63333..5a4baa5a1 100644 --- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; import classNames from 'classnames'; -import { changeUploadCompose, uploadThumbnail } from 'flavours/glitch/actions/compose'; +import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from 'flavours/glitch/actions/compose'; import { getPointerPosition } from 'flavours/glitch/features/video'; import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import IconButton from 'flavours/glitch/components/icon_button'; @@ -27,14 +27,22 @@ import { assetHost } from 'flavours/glitch/util/config'; const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, apply: { id: 'upload_modal.apply', defaultMessage: 'Apply' }, + applying: { id: 'upload_modal.applying', defaultMessage: 'Applying…' }, placeholder: { id: 'upload_modal.description_placeholder', defaultMessage: 'A quick brown fox jumps over the lazy dog' }, chooseImage: { id: 'upload_modal.choose_image', defaultMessage: 'Choose image' }, + discardMessage: { id: 'confirmations.discard_edit_media.message', defaultMessage: 'You have unsaved changes to the media description or preview, discard them anyway?' }, + discardConfirm: { id: 'confirmations.discard_edit_media.confirm', defaultMessage: 'Discard' }, }); const mapStateToProps = (state, { id }) => ({ media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id), account: state.getIn(['accounts', me]), isUploadingThumbnail: state.getIn(['compose', 'isUploadingThumbnail']), + description: state.getIn(['compose', 'media_modal', 'description']), + focusX: state.getIn(['compose', 'media_modal', 'focusX']), + focusY: state.getIn(['compose', 'media_modal', 'focusY']), + dirty: state.getIn(['compose', 'media_modal', 'dirty']), + is_changing_upload: state.getIn(['compose', 'is_changing_upload']), }); const mapDispatchToProps = (dispatch, { id }) => ({ @@ -43,6 +51,14 @@ const mapDispatchToProps = (dispatch, { id }) => ({ dispatch(changeUploadCompose(id, { description, focus: `${x.toFixed(2)},${y.toFixed(2)}` })); }, + onChangeDescription: (description) => { + dispatch(onChangeMediaDescription(description)); + }, + + onChangeFocus: (focusX, focusY) => { + dispatch(onChangeMediaFocus(focusX, focusY)); + }, + onSelectThumbnail: files => { dispatch(uploadThumbnail(id, files[0])); }, @@ -83,8 +99,8 @@ class ImageLoader extends React.PureComponent { } -export default @connect(mapStateToProps, mapDispatchToProps) -@injectIntl +export default @connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true }) +@(component => injectIntl(component, { withRef: true })) class FocalPointModal extends ImmutablePureComponent { static propTypes = { @@ -92,34 +108,21 @@ class FocalPointModal extends ImmutablePureComponent { account: ImmutablePropTypes.map.isRequired, isUploadingThumbnail: PropTypes.bool, onSave: PropTypes.func.isRequired, + onChangeDescription: PropTypes.func.isRequired, + onChangeFocus: PropTypes.func.isRequired, onSelectThumbnail: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, }; state = { - x: 0, - y: 0, - focusX: 0, - focusY: 0, dragging: false, - description: '', dirty: false, progress: 0, loading: true, ocrStatus: '', }; - componentWillMount () { - this.updatePositionFromMedia(this.props.media); - } - - componentWillReceiveProps (nextProps) { - if (this.props.media.get('id') !== nextProps.media.get('id')) { - this.updatePositionFromMedia(nextProps.media); - } - } - componentWillUnmount () { document.removeEventListener('mousemove', this.handleMouseMove); document.removeEventListener('mouseup', this.handleMouseUp); @@ -164,54 +167,37 @@ class FocalPointModal extends ImmutablePureComponent { const focusX = (x - .5) * 2; const focusY = (y - .5) * -2; - this.setState({ x, y, focusX, focusY, dirty: true }); - } - - updatePositionFromMedia = media => { - const focusX = media.getIn(['meta', 'focus', 'x']); - const focusY = media.getIn(['meta', 'focus', 'y']); - const description = media.get('description') || ''; - - if (focusX && focusY) { - const x = (focusX / 2) + .5; - const y = (focusY / -2) + .5; - - this.setState({ - x, - y, - focusX, - focusY, - description, - dirty: false, - }); - } else { - this.setState({ - x: 0.5, - y: 0.5, - focusX: 0, - focusY: 0, - description, - dirty: false, - }); - } + this.props.onChangeFocus(focusX, focusY); } handleChange = e => { - this.setState({ description: e.target.value, dirty: true }); + this.props.onChangeDescription(e.target.value); } handleKeyDown = (e) => { if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { e.preventDefault(); e.stopPropagation(); - this.setState({ description: e.target.value, dirty: true }); + this.props.onChangeDescription(e.target.value); this.handleSubmit(); } } handleSubmit = () => { - this.props.onSave(this.state.description, this.state.focusX, this.state.focusY); - this.props.onClose(); + this.props.onSave(this.props.description, this.props.focusX, this.props.focusY); + } + + getCloseConfirmationMessage = () => { + const { intl, dirty } = this.props; + + if (dirty) { + return { + message: intl.formatMessage(messages.discardMessage), + confirm: intl.formatMessage(messages.discardConfirm), + }; + } else { + return null; + } } setRef = c => { @@ -257,7 +243,8 @@ class FocalPointModal extends ImmutablePureComponent { await worker.loadLanguage('eng'); await worker.initialize('eng'); const { data: { text } } = await worker.recognize(media_url); - this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false }); + this.setState({ detecting: false }); + this.props.onChangeDescription(removeExtraLineBreaks(text)); await worker.terminate(); })().catch((e) => { if (refreshCache) { @@ -274,7 +261,6 @@ class FocalPointModal extends ImmutablePureComponent { handleThumbnailChange = e => { if (e.target.files.length > 0) { - this.setState({ dirty: true }); this.props.onSelectThumbnail(e.target.files); } } @@ -288,8 +274,10 @@ class FocalPointModal extends ImmutablePureComponent { } render () { - const { media, intl, account, onClose, isUploadingThumbnail } = this.props; - const { x, y, dragging, description, dirty, detecting, progress, ocrStatus } = this.state; + const { media, intl, account, onClose, isUploadingThumbnail, description, focusX, focusY, dirty, is_changing_upload } = this.props; + const { dragging, detecting, progress, ocrStatus } = this.state; + const x = (focusX / 2) + .5; + const y = (focusY / -2) + .5; const width = media.getIn(['meta', 'original', 'width']) || null; const height = media.getIn(['meta', 'original', 'height']) || null; @@ -344,7 +332,7 @@ class FocalPointModal extends ImmutablePureComponent { accept='image/png,image/jpeg' onChange={this.handleThumbnailChange} style={{ display: 'none' }} - disabled={isUploadingThumbnail} + disabled={isUploadingThumbnail || is_changing_upload} /> @@ -363,7 +351,7 @@ class FocalPointModal extends ImmutablePureComponent { value={detecting ? '…' : description} onChange={this.handleChange} onKeyDown={this.handleKeyDown} - disabled={detecting} + disabled={detecting || is_changing_upload} autoFocus /> @@ -373,11 +361,11 @@ class FocalPointModal extends ImmutablePureComponent {
- +
-
diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js index 2636e79f5..62bb167a0 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -83,16 +83,33 @@ export default class ModalRoot extends React.PureComponent { return ; } + handleClose = () => { + const { onClose } = this.props; + let message = null; + try { + message = this._modal?.getWrappedInstance?.().getCloseConfirmationMessage?.(); + } catch (_) { + // injectIntl defines `getWrappedInstance` but errors out if `withRef` + // isn't set. + // This would be much smoother with react-intl 3+ and `forwardRef`. + } + onClose(message); + } + + setModalRef = (c) => { + this._modal = c; + } + render () { - const { type, props, onClose } = this.props; + const { type, props } = this.props; const { backgroundColor } = this.state; const visible = !!type; return ( - + {visible && ( - {(SpecificComponent) => } + {(SpecificComponent) => } )} diff --git a/app/javascript/flavours/glitch/features/ui/containers/modal_container.js b/app/javascript/flavours/glitch/features/ui/containers/modal_container.js index e13e745e6..039aabd8a 100644 --- a/app/javascript/flavours/glitch/features/ui/containers/modal_container.js +++ b/app/javascript/flavours/glitch/features/ui/containers/modal_container.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { closeModal } from 'flavours/glitch/actions/modal'; +import { openModal, closeModal } from 'flavours/glitch/actions/modal'; import ModalRoot from '../components/modal_root'; const mapStateToProps = state => ({ @@ -8,8 +8,18 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ - onClose () { - dispatch(closeModal()); + onClose (confirmationMessage) { + if (confirmationMessage) { + dispatch( + openModal('CONFIRM', { + message: confirmationMessage.message, + confirm: confirmationMessage.confirm, + onConfirm: () => dispatch(closeModal()), + }), + ); + } else { + dispatch(closeModal()); + } }, }); diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js index e989401d8..1735cfb4d 100644 --- a/app/javascript/flavours/glitch/reducers/compose.js +++ b/app/javascript/flavours/glitch/reducers/compose.js @@ -42,6 +42,9 @@ import { COMPOSE_POLL_OPTION_CHANGE, COMPOSE_POLL_OPTION_REMOVE, COMPOSE_POLL_SETTINGS_CHANGE, + INIT_MEDIA_EDIT_MODAL, + COMPOSE_CHANGE_MEDIA_DESCRIPTION, + COMPOSE_CHANGE_MEDIA_FOCUS, } from 'flavours/glitch/actions/compose'; import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines'; import { STORE_HYDRATE } from 'flavours/glitch/actions/store'; @@ -97,6 +100,13 @@ const initialState = ImmutableMap({ resetFileKey: Math.floor((Math.random() * 0x10000)), idempotencyKey: null, tagHistory: ImmutableList(), + media_modal: ImmutableMap({ + id: null, + description: '', + focusX: 0, + focusY: 0, + dirty: false, + }), doodle: ImmutableMap({ fg: 'rgb( 0, 0, 0)', bg: 'rgb(255, 255, 255)', @@ -455,6 +465,19 @@ export default function compose(state = initialState, action) { return item; })); + case INIT_MEDIA_EDIT_MODAL: + const media = state.get('media_attachments').find(item => item.get('id') === action.id); + return state.set('media_modal', ImmutableMap({ + id: action.id, + description: media.get('description') || '', + focusX: media.getIn(['meta', 'focus', 'x'], 0), + focusY: media.getIn(['meta', 'focus', 'y'], 0), + dirty: false, + })); + case COMPOSE_CHANGE_MEDIA_DESCRIPTION: + return state.setIn(['media_modal', 'description'], action.description).setIn(['media_modal', 'dirty'], true); + case COMPOSE_CHANGE_MEDIA_FOCUS: + return state.setIn(['media_modal', 'focusX'], action.focusX).setIn(['media_modal', 'focusY'], action.focusY).setIn(['media_modal', 'dirty'], true); case COMPOSE_MENTION: return state.withMutations(map => { map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' ')); @@ -491,6 +514,7 @@ export default function compose(state = initialState, action) { case COMPOSE_UPLOAD_CHANGE_SUCCESS: return state .set('is_changing_upload', false) + .setIn(['media_modal', 'dirty'], false) .update('media_attachments', list => list.map(item => { if (item.get('id') === action.media.id) { return fromJS(action.media); diff --git a/app/javascript/flavours/glitch/reducers/modal.js b/app/javascript/flavours/glitch/reducers/modal.js index f8fdc2995..ae205c6d5 100644 --- a/app/javascript/flavours/glitch/reducers/modal.js +++ b/app/javascript/flavours/glitch/reducers/modal.js @@ -1,5 +1,6 @@ import { MODAL_OPEN, MODAL_CLOSE } from 'flavours/glitch/actions/modal'; import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines'; +import { COMPOSE_UPLOAD_CHANGE_SUCCESS } from 'flavours/glitch/actions/compose'; import { Stack as ImmutableStack, Map as ImmutableMap } from 'immutable'; export default function modal(state = ImmutableStack(), action) { @@ -8,6 +9,8 @@ export default function modal(state = ImmutableStack(), action) { return state.unshift(ImmutableMap({ modalType: action.modalType, modalProps: action.modalProps })); case MODAL_CLOSE: return (action.modalType === undefined || action.modalType === state.getIn([0, 'modalType'])) ? state.shift() : state; + case COMPOSE_UPLOAD_CHANGE_SUCCESS: + return state.getIn([0, 'modalType']) === 'FOCAL_POINT' ? state.shift() : state; case TIMELINE_DELETE: return state.filterNot((modal) => modal.get('modalProps').statusId === action.id); default: -- cgit From 2f6ff141984e7df12f7bcc64b2c8754542151a70 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 23 Jul 2021 02:53:17 +0200 Subject: [Glitch] Fix crashes with Microsoft Translate on Microsoft Edge Port e9659ae0312d0bf0acf2b55eed5dc688929cae04 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/status.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 782fd918e..d3944b0c3 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -543,9 +543,8 @@ class Status extends ImmutablePureComponent { return (
- {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} - {' '} - {status.get('content')} + {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} + {status.get('content')}
); -- cgit From 69b5139d2ac1070791819fd6f8ac18f46fe8cdc8 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Tue, 3 Aug 2021 18:53:02 +0900 Subject: Add Korean translations --- app/javascript/flavours/glitch/locales/ko.js | 62 +++++++++++++++++++++++++++- config/locales-glitch/ko.yml | 25 +++++++++++ config/locales-glitch/simple_form.ko.yml | 20 +++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 config/locales-glitch/ko.yml create mode 100644 config/locales-glitch/simple_form.ko.yml (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/locales/ko.js b/app/javascript/flavours/glitch/locales/ko.js index 3b55f89b9..e439b07b0 100644 --- a/app/javascript/flavours/glitch/locales/ko.js +++ b/app/javascript/flavours/glitch/locales/ko.js @@ -1,7 +1,67 @@ import inherited from 'mastodon/locales/ko.json'; const messages = { - // No translations available. + 'getting_started.open_source_notice': '글리치는 {Mastodon}의 자유 오픈소스 포크버전입니다. {github}에서 문제를 리포팅 하거나 기여를 할 수 있습니다.', + 'layout.auto': '자동', + 'layout.current_is': '현재 레이아웃:', + 'layout.desktop': '데스크탑', + 'layout.mobile': '모바일', + 'navigation_bar.app_settings': '앱 설정', + 'getting_started.onboarding': '둘러보기', + 'onboarding.page_one.federation': '{domain}은 마스토돈의 \'인스턴스\'입니다. 마스토돈은 하나의 거대한 소셜 네트워크를 만들기 위해 참여한 서버들의 네트워크입니다. 우린 이 서버들을 인스턴스라고 부릅니다.', + 'onboarding.page_one.welcome': '{domain}에 오신 것을 환영합니다!', + 'onboarding.page_six.github': '{domain}은 글리치를 통해 구동 됩니다. 글리치는 {Mastodon}의 {fork}입니다, 그리고 어떤 마스토돈 인스턴스나 앱과도 호환 됩니다. 글리치는 완전한 자유 오픈소스입니다. {github}에서 버그를 리포팅 하거나, 기능을 제안하거나, 코드를 기여할 수 있습니다.', + 'settings.auto_collapse': '자동으로 접기', + 'settings.auto_collapse_all': '모두', + 'settings.auto_collapse_lengthy': '긴 글', + 'settings.auto_collapse_media': '미디어 포함 글', + 'settings.auto_collapse_notifications': '알림', + 'settings.auto_collapse_reblogs': '부스트', + 'settings.auto_collapse_replies': '답글', + 'settings.show_action_bar': '접힌 글에 액션 버튼들 보이기', + 'settings.close': 'Close', + 'settings.collapsed_statuses': '접힌 글', + 'settings.enable_collapsed': '접힌 글 활성화', + 'settings.general': '일반', + 'settings.image_backgrounds': '이미지 배경', + 'settings.image_backgrounds_media': '접힌 글의 미디어 미리보기', + 'settings.image_backgrounds_users': '접힌 글에 이미지 배경 주기', + 'settings.media': '미디어', + 'settings.media_letterbox': '레터박스 미디어', + 'settings.media_fullwidth': '최대폭 미디어 미리보기', + 'settings.preferences': '사용자 설정', + 'settings.wide_view': '넓은 뷰 (데스크탑 모드 전용)', + 'settings.navbar_under': '내비바를 하단에 (모바일 전용)', + 'status.collapse': '접기', + 'status.uncollapse': '펼치기', + + 'media_gallery.sensitive': '민감함', + + 'favourite_modal.combo': '다음엔 {combo}를 눌러 건너뛸 수 있습니다', + + 'home.column_settings.show_direct': 'DM 보여주기', + + 'notification.markForDeletion': '삭제 마크', + 'notifications.clear': '내 알림 모두 지우기', + 'notifications.marked_clear_confirmation': '정말로 선택된 알림들을 영구적으로 삭제할까요?', + 'notifications.marked_clear': '선택된 알림 모두 삭제', + + 'notification_purge.btn_all': '전체선택', + 'notification_purge.btn_none': '전체선택해제', + 'notification_purge.btn_invert': '선택반전', + 'notification_purge.btn_apply': '선택된 알림 삭제', + + 'compose.attach.upload': '파일 업로드', + 'compose.attach.doodle': '뭔가 그려보세요', + 'compose.attach': '첨부…', + + 'advanced_options.local-only.short': '로컬 전용', + 'advanced_options.local-only.long': '다른 인스턴스에 게시하지 않기', + 'advanced_options.local-only.tooltip': '이 글은 로컬 전용입니다', + 'advanced_options.icon_title': '고급 옵션', + 'advanced_options.threaded_mode.short': '글타래 모드', + 'advanced_options.threaded_mode.long': '글을 작성하고 자동으로 답글 열기', + 'advanced_options.threaded_mode.tooltip': '글타래 모드 활성화됨', }; export default Object.assign({}, inherited, messages); diff --git a/config/locales-glitch/ko.yml b/config/locales-glitch/ko.yml new file mode 100644 index 000000000..ae7f091bb --- /dev/null +++ b/config/locales-glitch/ko.yml @@ -0,0 +1,25 @@ +--- +ko: + admin: + dashboard: + keybase: 키베이스 연동 + settings: + enable_keybase: + desc_html: 사용자들이 키베이스를 통해 개인 신원을 증명할 수 있도록 허용 + title: 키베이스 연동 활성화 + outgoing_spoilers: + desc_html: 게시물들을 연합할 때, 열람주의가 달려있지 않다면 이 열람주의를 추가합니다. 다른 서버들이 열람주의를 하길 원하는 콘텐츠들에 특화된 서버에서 유용합니다. 미디어 또한 민감함으로 설정 됩니다. + title: 나가는 게시물에 대한 열람주의 + hide_followers_count: + desc_html: 사용자 프로필에 팔로워 수를 표시하지 않습니다 + title: 팔로워 수 숨기기 + show_reblogs_in_public_timelines: + desc_html: 공개글의 공개적인 부스트를 로컬과 공개 타임라인에 표시합니다. + title: 부스트를 공개 타임라인에 표시 + show_replies_in_public_timelines: + desc_html: 자기자신에 대한 답글(글타래)와 마찬가지로, 공개적인 답글을 로컬과 공개 타임라인에 표시합니다. + title: 답글을 공개 타임라인에 표시 + generic: + use_this: 사용하기 + settings: + flavours: 풍미 diff --git a/config/locales-glitch/simple_form.ko.yml b/config/locales-glitch/simple_form.ko.yml new file mode 100644 index 000000000..474692195 --- /dev/null +++ b/config/locales-glitch/simple_form.ko.yml @@ -0,0 +1,20 @@ +--- +en: + simple_form: + hints: + defaults: + setting_default_content_type_html: 게시물을 작성할 때, 형식을 지정하지 않았다면, 생 HTML이라고 가정합니다 + setting_default_content_type_markdown: 게시물을 작성할 때, 형식을 지정하지 않았다면, 마크다운이라고 가정합니다 + setting_default_content_type_plain: 게시물을 작성할 때, 형식을 지정하지 않았다면, 일반적인 텍스트라고 가정합니다. (마스토돈의 기본 동작) + setting_default_language: 작성하는 게시물의 언어는 자동으로 설정될 수 있습니다, 하지만 언제나 정확하지는 않습니다 + setting_skin: 선택한 마스토돈 풍미의 스킨을 바꿉니다 + labels: + defaults: + setting_default_content_type: 게시물의 기본 포맷 + setting_default_content_type_html: HTML + setting_default_content_type_markdown: 마크다운 + setting_default_content_type_plain: 일반 텍스트 + setting_favourite_modal: 관심글을 지정할 때 확인 창을 띄웁니다(글리치 풍미에만 적용됨) + setting_hide_followers_count: 내 팔로워 수 숨기기 + setting_skin: 스킨 + setting_system_emoji_font: 에모지에 시스템 기본 폰트 적용하기 (글리치 풍미에만 적용됨) -- cgit From a61645ed30b854a1b4935026801eb5b287954095 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Tue, 3 Aug 2021 23:04:03 +0900 Subject: Add missing Korean translations --- app/javascript/flavours/glitch/locales/ko.js | 202 ++++++++++++++++++++++----- 1 file changed, 168 insertions(+), 34 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/locales/ko.js b/app/javascript/flavours/glitch/locales/ko.js index e439b07b0..b67fec187 100644 --- a/app/javascript/flavours/glitch/locales/ko.js +++ b/app/javascript/flavours/glitch/locales/ko.js @@ -1,16 +1,108 @@ import inherited from 'mastodon/locales/ko.json'; const messages = { + 'account.add_account_note': '@{name} 님에 대한 메모 추가', + 'account.disclaimer_full': '아래에 있는 정보들은 사용자의 프로필을 완벽하게 나타내지 못하고 있을 수도 있습니다.', + 'account.follows': '팔로우', + 'account.suspended_disclaimer_full': '이 사용자는 중재자에 의해 정지되었습니다.', + 'account.view_full_profile': '전체 프로필 보기', + 'account_note.cancel': '취소', + 'account_note.edit': '편집', + 'account_note.glitch_placeholder': '코멘트가 없습니다', + 'account_note.save': '저장', + 'advanced_options.icon_title': '고급 옵션', + 'advanced_options.local-only.long': '다른 서버에 게시하지 않기', + 'advanced_options.local-only.short': '로컬 전용', + 'advanced_options.local-only.tooltip': '이 게시물은 로컬 전용입니다', + 'advanced_options.threaded_mode.long': '글을 작성하고 자동으로 답글 열기', + 'advanced_options.threaded_mode.short': '글타래 모드', + 'advanced_options.threaded_mode.tooltip': '글타래 모드 활성화됨', + 'boost_modal.missing_description': '이 게시물은 설명이 없는 미디어를 포함하고 있습니다', + 'column.favourited_by': '즐겨찾기 한 사람', + 'column.heading': '기타', + 'column.reblogged_by': '부스트 한 사람', + 'column.subheading': '다양한 옵션', + 'column.toot': '게시물과 답글', + 'column_header.profile': '프로필', + 'column_subheading.lists': '리스트', + 'column_subheading.navigation': '탐색', + 'community.column_settings.allow_local_only': '로컬 전용 글 보기', + 'compose.attach': '첨부…', + 'compose.attach.doodle': '뭔가 그려보세요', + 'compose.attach.upload': '파일 업로드', + 'compose.content-type.html': 'HTML', + 'compose.content-type.markdown': '마크다운', + 'compose.content-type.plain': '일반 텍스트', + 'compose_form.poll.multiple_choices': '여러 개 선택 가능', + 'compose_form.poll.single_choice': '하나만 선택 가능', + 'compose_form.spoiler': '경고 메시지로 숨기기', + 'confirmation_modal.do_not_ask_again': '다음부터 확인창을 띄우지 않기', + 'confirmations.discard_edit_media.confirm': '취소', + 'confirmations.discard_edit_media.message': '저장하지 않은 미디어 설명이나 미리보기가 있습니다, 그냥 닫을까요?', + 'confirmations.missing_media_description.confirm': '그냥 보내기', + 'confirmations.missing_media_description.edit': '미디어 편집', + 'confirmations.missing_media_description.message': '하나 이상의 미디어에 대해 설명을 작성하지 않았습니다. 시각장애인을 위해 모든 미디어에 설명을 추가하는 것을 고려해주세요.', + 'confirmations.unfilter': '이 필터링 된 글에 대한 정보', + 'confirmations.unfilter.author': '작성자', + 'confirmations.unfilter.confirm': '보기', + 'confirmations.unfilter.edit_filter': '필터 편집', + 'confirmations.unfilter.filters': '적용된 {count, plural, one {필터} other {필터들}}', + 'content-type.change': '콘텐트 타입', + 'direct.conversations_mode': '대화', + 'direct.timeline_mode': '타임라인', + 'endorsed_accounts_editor.endorsed_accounts': '추천하는 계정들', + 'favourite_modal.combo': '다음엔 {combo}를 눌러 건너뛸 수 있습니다', + 'getting_started.onboarding': '둘러보기', 'getting_started.open_source_notice': '글리치는 {Mastodon}의 자유 오픈소스 포크버전입니다. {github}에서 문제를 리포팅 하거나 기여를 할 수 있습니다.', + 'home.column_settings.advanced': '고급', + 'home.column_settings.filter_regex': '정규표현식으로 필터', + 'home.column_settings.show_direct': 'DM 보여주기', + 'home.settings': '컬럼 설정', + 'keyboard_shortcuts.bookmark': '북마크', + 'keyboard_shortcuts.secondary_toot': '보조 프라이버시 설정으로 글 보내기', + 'keyboard_shortcuts.toggle_collapse': '글 접거나 펼치기', 'layout.auto': '자동', 'layout.current_is': '현재 레이아웃:', 'layout.desktop': '데스크탑', - 'layout.mobile': '모바일', + 'layout.hint.auto': '“고급 웹 인터페이스 활성화” 설정과 화면 크기에 따라 자동으로 레이아웃을 고릅니다.', + 'layout.hint.desktop': '“고급 웹 인터페이스 활성화” 설정이나 화면 크기에 관계 없이 멀티 컬럼 레이아웃을 사용합니다.', + 'layout.hint.single': '“고급 웹 인터페이스 활성화” 설정이나 화면 크기에 관계 없이 싱글 컬럼 레이아웃을 사용합니다.', + 'layout.single': '모바일', + 'media_gallery.sensitive': '민감함', + 'moved_to_warning': '이 계정은 {moved_to_link}로 이동한 것으로 표시되었고, 새 팔로우를 받지 않는 것 같습니다.', 'navigation_bar.app_settings': '앱 설정', - 'getting_started.onboarding': '둘러보기', + 'navigation_bar.featured_users': '추천된 계정들', + 'navigation_bar.misc': '다양한 옵션들', + 'notification.markForDeletion': '삭제하기 위해 표시', + 'notification_purge.btn_all': '전체선택', + 'notification_purge.btn_apply': '선택된 알림 삭제', + 'notification_purge.btn_invert': '선택반전', + 'notification_purge.btn_none': '전체선택해제', + 'notification_purge.start': '알림 삭제모드로 들어가기', + 'notifications.clear': '내 알림 모두 지우기', + 'notifications.marked_clear': '선택된 알림 모두 삭제', + 'notifications.marked_clear_confirmation': '정말로 선택된 알림들을 영구적으로 삭제할까요?', + 'onboarding.done': '완료', + 'onboarding.next': '다음', + 'onboarding.page_five.public_timelines': '로컬 타임라인은 {domain}에 있는 모든 사람의 공개글을 보여줍니다. 연합 타임라인은 {domain}에 있는 사람들이 팔로우 하는 모든 사람의 공개글을 보여줍니다. 이것들은 공개 타임라인이라고 불리며, 새로운 사람들을 발견할 수 있는 좋은 방법입니다.', + 'onboarding.page_four.home': '홈 타임라인은 당신이 팔로우 한 사람들의 글을 보여줍니다.', + 'onboarding.page_four.notifications': '알림 컬럼은 누군가가 당신과 상호작용한 것들을 보여줍니다.', 'onboarding.page_one.federation': '{domain}은 마스토돈의 \'인스턴스\'입니다. 마스토돈은 하나의 거대한 소셜 네트워크를 만들기 위해 참여한 서버들의 네트워크입니다. 우린 이 서버들을 인스턴스라고 부릅니다.', + 'onboarding.page_one.handle': '당신은 {domain}에 속해 있으며, 전체 핸들은 {handle} 입니다.', 'onboarding.page_one.welcome': '{domain}에 오신 것을 환영합니다!', + 'onboarding.page_six.admin': '우리 서버의 관리자는 {admin} 님입니다.', + 'onboarding.page_six.almost_done': '거의 다 되었습니다…', + 'onboarding.page_six.appetoot': '본 아페툿!', + 'onboarding.page_six.apps_available': 'iOS, 안드로이드, 그리고 다른 플랫폼들을 위한 {apps}이 존재합니다.', 'onboarding.page_six.github': '{domain}은 글리치를 통해 구동 됩니다. 글리치는 {Mastodon}의 {fork}입니다, 그리고 어떤 마스토돈 인스턴스나 앱과도 호환 됩니다. 글리치는 완전한 자유 오픈소스입니다. {github}에서 버그를 리포팅 하거나, 기능을 제안하거나, 코드를 기여할 수 있습니다.', + 'onboarding.page_six.guidelines': '커뮤니티 가이드라인', + 'onboarding.page_six.read_guidelines': '{domain}의 {guidelines}을 읽어주세요!', + 'onboarding.page_six.various_app': '모바일 앱', + 'onboarding.page_three.profile': '프로필을 수정해 아바타, 바이오, 표시되는 이름을 설정하세요. 거기에서 다른 설정들도 찾을 수 있습니다.', + 'onboarding.page_three.search': '검색창을 사용해 사람들과 해시태그를 찾아보세요. 예를 들면 {illustration}이라든지 {introcustions} 같은 것으로요. 이 인스턴스에 있지 않은 사람을 찾으려면, 전체 핸들을 사용하세요.', + 'onboarding.page_two.compose': '작성 컬럼에서 게시물을 작성하세요. 그림을 업로드 할 수 있고, 공개설정을 바꿀 수도 있으며, 아래 아이콘을 통해 열람주의 텍스트를 설정할 수 있습니다.', + 'onboarding.skip': '건너뛰기', + 'settings.always_show_spoilers_field': '열람주의 항목을 언제나 활성화', 'settings.auto_collapse': '자동으로 접기', 'settings.auto_collapse_all': '모두', 'settings.auto_collapse_lengthy': '긴 글', @@ -18,50 +110,92 @@ const messages = { 'settings.auto_collapse_notifications': '알림', 'settings.auto_collapse_reblogs': '부스트', 'settings.auto_collapse_replies': '답글', - 'settings.show_action_bar': '접힌 글에 액션 버튼들 보이기', - 'settings.close': 'Close', + 'settings.close': '닫기', 'settings.collapsed_statuses': '접힌 글', + 'settings.compose_box_opts': '작성 상자', + 'settings.confirm_before_clearing_draft': '작성 중인 메시지를 덮어씌우기 전에 확인창을 보여주기', + 'settings.confirm_boost_missing_media_description': '미디어 설명이 없는 글을 부스트하려 할 때 확인창을 보여주기', + 'settings.confirm_missing_media_description': '미디어 설명이 없는 글을 작성하려 할 때 확인창을 보여주기', + 'settings.content_warnings': '열람주의', + 'settings.content_warnings.regexp': '정규표현식', + 'settings.content_warnings_filter': '자동으로 펼치지 않을 열람주의 문구:', 'settings.enable_collapsed': '접힌 글 활성화', + 'settings.enable_content_warnings_auto_unfold': '자동으로 열람주의 펼치기', + 'settings.filtering_behavior': '필터링 동작', + 'settings.filtering_behavior.cw': '게시물을 보여주되, 필터된 단어를 열람주의에 추가합니다', + 'settings.filtering_behavior.drop': '완전히 숨깁니다', + 'settings.filtering_behavior.hide': '\'필터됨\'이라고 표시하고 이유를 표시하는 버튼을 추가합니다', + 'settings.filtering_behavior.upstream': '\'필터됨\'이라고 일반 마스토돈처럼 표시합니다', + 'settings.filters': '필터', 'settings.general': '일반', + 'settings.hicolor_privacy_icons': '높은 채도의 공개설정 아이콘', + 'settings.hicolor_privacy_icons.hint': '공개설정 아이콘들을 밝고 구분하기 쉬운 색으로 표시합니다', 'settings.image_backgrounds': '이미지 배경', 'settings.image_backgrounds_media': '접힌 글의 미디어 미리보기', 'settings.image_backgrounds_users': '접힌 글에 이미지 배경 주기', + 'settings.inline_preview_cards': '외부 링크에 대한 미리보기 카드를 같이 표시', + 'settings.layout': '레이아웃:', + 'settings.layout_opts': '레이아웃 옵션', 'settings.media': '미디어', - 'settings.media_letterbox': '레터박스 미디어', 'settings.media_fullwidth': '최대폭 미디어 미리보기', + 'settings.media_letterbox': '레터박스 미디어', + 'settings.media_letterbox_hint': '확대하고 자르는 대신 축소하고 레터박스에 넣어 이미지를 보여줍니다', + 'settings.media_reveal_behind_cw': '열람주의로 가려진 미디어를 기본으로 펼쳐 둡니다', + 'settings.navbar_under': '내비바를 하단에 (모바일 전용)', + 'settings.notifications.favicon_badge': '읽지 않은 알림 파비콘 배지', + 'settings.notifications.favicon_badge.hint': '읽지 않은 알림 배지를 파비콘에 추가합니다', + 'settings.notifications.tab_badge': '읽지 않은 알림 배지', + 'settings.notifications.tab_badge.hint': '알림 컬럼이 열려 있지 않을 때 알림 컬럼에 알림이 있다는 배지를 표시합니다', + 'settings.notifications_opts': '알림 옵션', + 'settings.pop_in_left': '왼쪽', + 'settings.pop_in_player': '떠있는 재생기 활성화', + 'settings.pop_in_position': '떠있는 재생기 위치:', + 'settings.pop_in_right': '오른쪽', 'settings.preferences': '사용자 설정', + 'settings.prepend_cw_re': '열람주의가 달린 글에 답장을 할 때 열람주의 문구 앞에 “re: ”를 추가합니다', + 'settings.preselect_on_reply': '답글 달 때 사용자명 미리 선택', + 'settings.preselect_on_reply_hint': '답글을 달 때 이미 멘션 된 사람의 사용자명을 미리 블럭으로 설정해 놓습니다', + 'settings.rewrite_mentions': '표시되는 게시물의 멘션 표시 바꾸기', + 'settings.rewrite_mentions_acct': '사용자명과 도메인으로 바꾸기(계정이 원격일 때)', + 'settings.rewrite_mentions_no': '멘션을 그대로 두기', + 'settings.rewrite_mentions_username': '사용자명으로 바꾸기', + 'settings.show_action_bar': '접힌 글에 액션 버튼들 보이기', + 'settings.show_content_type_choice': '글을 작성할 때 콘텐트 타입을 고를 수 있도록 합니다', + 'settings.show_reply_counter': '대략적인 답글 개수를 표시합니다', + 'settings.side_arm': '보조 작성 버튼:', + 'settings.side_arm.none': '없음', + 'settings.side_arm_reply_mode': '답글을 작성할 때:', + 'settings.side_arm_reply_mode.copy': '답글을 달려는 글의 공개설정을 복사합니다', + 'settings.side_arm_reply_mode.keep': '보조 작성 버튼의 공개설정을 유지합니다', + 'settings.side_arm_reply_mode.restrict': '답글을 달려는 글의 공개설정에 맞게 제한합니다', + 'settings.swipe_to_change_columns': '스와이프하여 컬럼간 전환을 허용합니다 (모바일 전용)', + 'settings.tag_misleading_links': '오해의 소지가 있는 링크를 표시합니다', + 'settings.tag_misleading_links.hint': '링크에 명시적으로 주소가 없는 경우엔 대상 호스트를 보이도록 표시합니다', 'settings.wide_view': '넓은 뷰 (데스크탑 모드 전용)', - 'settings.navbar_under': '내비바를 하단에 (모바일 전용)', + 'settings.wide_view_hint': '컬럼들을 늘려서 활용 가능한 공간을 사용합니다.', 'status.collapse': '접기', + 'status.has_audio': '소리 파일이 첨부되어 있습니다', + 'status.has_pictures': '그림 파일이 첨부되어 있습니다', + 'status.has_preview_card': '미리보기 카드가 첨부되어 있습니다', + 'status.has_video': '영상이 첨부되어 있습니다', + 'status.hide': '글 가리기', + 'status.in_reply_to': '이 글은 답글입니다', + 'status.is_poll': '이 글은 설문입니다', + 'status.local_only': '당신의 서버에서만 보입니다', + 'status.sensitive_toggle': '클릭해서 보기', + 'status.show_filter_reason': '(이유 보기)', 'status.uncollapse': '펼치기', - - 'media_gallery.sensitive': '민감함', - - 'favourite_modal.combo': '다음엔 {combo}를 눌러 건너뛸 수 있습니다', - - 'home.column_settings.show_direct': 'DM 보여주기', - - 'notification.markForDeletion': '삭제 마크', - 'notifications.clear': '내 알림 모두 지우기', - 'notifications.marked_clear_confirmation': '정말로 선택된 알림들을 영구적으로 삭제할까요?', - 'notifications.marked_clear': '선택된 알림 모두 삭제', - - 'notification_purge.btn_all': '전체선택', - 'notification_purge.btn_none': '전체선택해제', - 'notification_purge.btn_invert': '선택반전', - 'notification_purge.btn_apply': '선택된 알림 삭제', - - 'compose.attach.upload': '파일 업로드', - 'compose.attach.doodle': '뭔가 그려보세요', - 'compose.attach': '첨부…', - - 'advanced_options.local-only.short': '로컬 전용', - 'advanced_options.local-only.long': '다른 인스턴스에 게시하지 않기', - 'advanced_options.local-only.tooltip': '이 글은 로컬 전용입니다', - 'advanced_options.icon_title': '고급 옵션', - 'advanced_options.threaded_mode.short': '글타래 모드', - 'advanced_options.threaded_mode.long': '글을 작성하고 자동으로 답글 열기', - 'advanced_options.threaded_mode.tooltip': '글타래 모드 활성화됨', + 'upload_modal.applying': '적용중…', + 'web_app_crash.change_your_settings': '{settings}을 바꾸세요', + 'web_app_crash.content': '이것들을 시도해 볼 수 있습니다:', + 'web_app_crash.debug_info': '디버그 정보', + 'web_app_crash.disable_addons': '브라우저 애드온이나 기본 번역 도구를 비활성화 합니다', + 'web_app_crash.issue_tracker': '이슈 트래커', + 'web_app_crash.reload': '새로고침', + 'web_app_crash.reload_page': '이 페이지를 {reload}', + 'web_app_crash.report_issue': '{issuetracker}에 버그 제보', + 'web_app_crash.settings': '설정', + 'web_app_crash.title': '죄송합니다, 하지만 마스토돈 앱이 뭔가 잘못되었습니다.', }; export default Object.assign({}, inherited, messages); -- cgit From 8d55cb7d711723fe2a35377ee2443cb11de11cc3 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Thu, 5 Aug 2021 20:05:32 +0900 Subject: [Glitch] Fix trends layout Port 6e0ab6814f4d3906c035e10a9cedbc41ae5967e9 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/components/index.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 24f750e1d..04bd8805a 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -977,13 +977,13 @@ } @media screen and (max-height: 810px) { - .trends__item:nth-child(3) { + .trends__item:nth-of-type(3) { display: none; } } @media screen and (max-height: 720px) { - .trends__item:nth-child(2) { + .trends__item:nth-of-type(2) { display: none; } } -- cgit From 8681ef85d0728b9d34dc62785fab6fcec30ba95c Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Fri, 6 Aug 2021 19:14:13 +0900 Subject: [Glitch] Fix logout link not working in safari Port b2875b1864d5bd72e6902ffc842d1be6818c210e to glitch-soc Signed-off-by: Claire --- .../glitch/features/compose/containers/header_container.js | 1 + .../flavours/glitch/features/ui/components/confirmation_modal.js | 9 ++++++++- .../flavours/glitch/features/ui/components/link_footer.js | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/features/compose/containers/header_container.js b/app/javascript/flavours/glitch/features/compose/containers/header_container.js index b4dcb4d56..2f0da48c8 100644 --- a/app/javascript/flavours/glitch/features/compose/containers/header_container.js +++ b/app/javascript/flavours/glitch/features/compose/containers/header_container.js @@ -27,6 +27,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); }, diff --git a/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js b/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js index 47a49c0c7..a665b9fb1 100644 --- a/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js @@ -13,16 +13,23 @@ class ConfirmationModal extends React.PureComponent { onConfirm: PropTypes.func.isRequired, secondary: PropTypes.string, onSecondary: PropTypes.func, + closeWhenConfirm: PropTypes.bool, onDoNotAsk: PropTypes.func, intl: PropTypes.object.isRequired, }; + static defaultProps = { + closeWhenConfirm: true, + }; + componentDidMount() { this.button.focus(); } handleClick = () => { - this.props.onClose(); + if (this.props.closeWhenConfirm) { + this.props.onClose(); + } this.props.onConfirm(); if (this.props.onDoNotAsk && this.doNotAskCheckbox.checked) { this.props.onDoNotAsk(); diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js index 40ef506cc..722b47140 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js @@ -18,6 +18,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); }, -- cgit From 0e62c38b029c834363580868f7d5d486e565ad93 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 11 Aug 2021 17:48:55 +0200 Subject: [Glitch] Fix download button color in audio player Port aaf24d3093d565461b0051d2238d8b74db63a041 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/components/media.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/styles/components/media.scss b/app/javascript/flavours/glitch/styles/components/media.scss index 855cd07a9..8a551be73 100644 --- a/app/javascript/flavours/glitch/styles/components/media.scss +++ b/app/javascript/flavours/glitch/styles/components/media.scss @@ -400,7 +400,8 @@ opacity: 0.2; } - .video-player__buttons button { + .video-player__buttons button, + .video-player__buttons a { color: currentColor; opacity: 0.75; -- cgit From 4f074b68ba4b9b91a7989f3f7d64ab29cf42214c Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 11 Aug 2021 17:49:10 +0200 Subject: [Glitch] Fix crash if a notification contains an unprocessed media attachment Port 0c24c865b785a557f43125c976090e271247a2b1 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/components/attachment_list.js | 36 ++++++++-------------- 1 file changed, 13 insertions(+), 23 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/attachment_list.js b/app/javascript/flavours/glitch/components/attachment_list.js index 68d8d29c7..68b80b19f 100644 --- a/app/javascript/flavours/glitch/components/attachment_list.js +++ b/app/javascript/flavours/glitch/components/attachment_list.js @@ -2,6 +2,8 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; import Icon from 'flavours/glitch/components/icon'; const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; @@ -16,29 +18,13 @@ export default class AttachmentList extends ImmutablePureComponent { render () { const { media, compact } = this.props; - if (compact) { - return ( -
-
    - {media.map(attachment => { - const displayUrl = attachment.get('remote_url') || attachment.get('url'); - - return ( -
  • - {filename(displayUrl)} -
  • - ); - })} -
-
- ); - } - return ( -
-
- -
+
+ {!compact && ( +
+ +
+ )}
    {media.map(attachment => { @@ -46,7 +32,11 @@ export default class AttachmentList extends ImmutablePureComponent { return (
  • - {filename(displayUrl)} + + {compact && } + {compact && ' ' } + {displayUrl ? filename(displayUrl) : } +
  • ); })} -- cgit From 39193be1c464675ce9f51fc838a0e41264f23604 Mon Sep 17 00:00:00 2001 From: matildepark Date: Wed, 25 Aug 2021 11:46:29 -0400 Subject: [Glitch] Fix follow request count to dynamically update Port 79341d0f5f3eb2d90f5ea954f4037120f7189cec to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/actions/notifications.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js index bd3a34e5d..4b00ea632 100644 --- a/app/javascript/flavours/glitch/actions/notifications.js +++ b/app/javascript/flavours/glitch/actions/notifications.js @@ -1,6 +1,6 @@ import api, { getLinks } from 'flavours/glitch/util/api'; import IntlMessageFormat from 'intl-messageformat'; -import { fetchRelationships } from './accounts'; +import { fetchFollowRequests, fetchRelationships } from './accounts'; import { importFetchedAccount, importFetchedAccounts, @@ -90,6 +90,10 @@ export function updateNotifications(notification, intlMessages, intlLocale) { filtered = regex && regex.test(searchIndex); } + if (['follow_request'].includes(notification.type)) { + dispatch(fetchFollowRequests()); + } + dispatch(submitMarkers()); if (showInColumn) { -- cgit From 6bbcd99f1407bf0a34fb7367d4fae42f058c8ac7 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Sep 2021 14:39:14 +0200 Subject: Fix media attachments not being displayed on polls Fixes #1595 --- .../flavours/glitch/components/status.js | 48 ++++++++++---------- .../flavours/glitch/components/status_content.js | 51 ++++++++++++---------- .../flavours/glitch/components/status_icons.js | 31 +++++++------ .../features/status/components/detailed_status.js | 41 ++++++++--------- 4 files changed, 93 insertions(+), 78 deletions(-) (limited to 'app/javascript/flavours') diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index d3944b0c3..b72c2417c 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -516,8 +516,8 @@ class Status extends ImmutablePureComponent { const { isExpanded, isCollapsed, forceFilter } = this.state; let background = null; let attachments = null; - let media = null; - let mediaIcon = null; + let media = []; + let mediaIcons = []; if (status === null) { return null; @@ -586,25 +586,27 @@ class Status extends ImmutablePureComponent { // After we have generated our appropriate media element and stored it in // `media`, we snatch the thumbnail to use as our `background` if media // backgrounds for collapsed statuses are enabled. + attachments = status.get('media_attachments'); if (status.get('poll')) { - media = ; - mediaIcon = 'tasks'; - } else if (usingPiP) { - media = ; - mediaIcon = 'video-camera'; + media.push(); + mediaIcons.push('tasks'); + } + if (usingPiP) { + media.push(); + mediaIcons.push('video-camera'); } else if (attachments.size > 0) { if (muted || attachments.some(item => item.get('type') === 'unknown')) { - media = ( + media.push( + />, ); } else if (attachments.getIn([0, 'type']) === 'audio') { const attachment = status.getIn(['media_attachments', 0]); - media = ( + media.push( {Component => ( )} - + , ); - mediaIcon = 'music'; + mediaIcons.push('music'); } else if (attachments.getIn([0, 'type']) === 'video') { const attachment = status.getIn(['media_attachments', 0]); - media = ( + media.push( {Component => ()} - + , ); - mediaIcon = 'video-camera'; + mediaIcons.push('video-camera'); } else { // Media type is 'image' or 'gifv' - media = ( + media.push( {Component => ( )} - + , ); - mediaIcon = 'picture-o'; + mediaIcons.push('picture-o'); } if (!status.get('sensitive') && !(status.get('spoiler_text').length > 0) && settings.getIn(['collapsed', 'backgrounds', 'preview_images'])) { background = attachments.getIn([0, 'preview_url']); } } else if (status.get('card') && settings.get('inline_preview_cards')) { - media = ( + media.push( + />, ); - mediaIcon = 'link'; + mediaIcons.push('link'); } // Here we prepare extra data-* attributes for CSS selectors. @@ -753,7 +755,7 @@ class Status extends ImmutablePureComponent { )).reduce((aggregate, item) => [...aggregate, item, ' '], []); - const toggleText = hidden ? [ - , - mediaIcon ? ( -