Skip to content

Commit 1bd4a5a

Browse files
committed
Merge branch 'main' into temp3_for-taruntarun
# Conflicts: # app/javascript/mastodon/initial_state.ts # app/models/user_settings.rb # app/serializers/initial_state_serializer.rb # config/locales/simple_form.an.yml # config/locales/simple_form.ar.yml # config/locales/simple_form.ast.yml # config/locales/simple_form.be.yml # config/locales/simple_form.bg.yml # config/locales/simple_form.ca.yml # config/locales/simple_form.ckb.yml # config/locales/simple_form.co.yml # config/locales/simple_form.cs.yml # config/locales/simple_form.cy.yml # config/locales/simple_form.da.yml # config/locales/simple_form.de.yml # config/locales/simple_form.el.yml # config/locales/simple_form.en-GB.yml # config/locales/simple_form.en.yml # config/locales/simple_form.eo.yml # config/locales/simple_form.es-AR.yml # config/locales/simple_form.es-MX.yml # config/locales/simple_form.es.yml # config/locales/simple_form.et.yml # config/locales/simple_form.eu.yml # config/locales/simple_form.fa.yml # config/locales/simple_form.fi.yml # config/locales/simple_form.fo.yml # config/locales/simple_form.fr-CA.yml # config/locales/simple_form.fr.yml # config/locales/simple_form.fy.yml # config/locales/simple_form.gd.yml # config/locales/simple_form.gl.yml # config/locales/simple_form.he.yml # config/locales/simple_form.hu.yml # config/locales/simple_form.hy.yml # config/locales/simple_form.id.yml # config/locales/simple_form.is.yml # config/locales/simple_form.it.yml # config/locales/simple_form.ja.yml # config/locales/simple_form.kk.yml # config/locales/simple_form.ko.yml # config/locales/simple_form.ku.yml # config/locales/simple_form.lv.yml # config/locales/simple_form.my.yml # config/locales/simple_form.nl.yml # config/locales/simple_form.nn.yml # config/locales/simple_form.no.yml # config/locales/simple_form.oc.yml # config/locales/simple_form.pl.yml # config/locales/simple_form.pt-BR.yml # config/locales/simple_form.pt-PT.yml # config/locales/simple_form.ro.yml # config/locales/simple_form.sc.yml # config/locales/simple_form.sco.yml # config/locales/simple_form.si.yml # config/locales/simple_form.sk.yml # config/locales/simple_form.sl.yml # config/locales/simple_form.sq.yml # config/locales/simple_form.sr-Latn.yml # config/locales/simple_form.sr.yml # config/locales/simple_form.sv.yml # config/locales/simple_form.th.yml # config/locales/simple_form.tr.yml # config/locales/simple_form.uk.yml # config/locales/simple_form.vi.yml # config/locales/simple_form.zh-CN.yml # config/locales/simple_form.zh-HK.yml # config/locales/simple_form.zh-TW.yml
2 parents 7356494 + 612771d commit 1bd4a5a

365 files changed

Lines changed: 879 additions & 662 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ GEM
621621
raabro (1.4.0)
622622
racc (1.8.1)
623623
rack (3.2.3)
624-
rack-attack (6.7.0)
624+
rack-attack (6.8.0)
625625
rack (>= 1.0, < 4)
626626
rack-cors (3.0.0)
627627
logger
@@ -792,7 +792,7 @@ GEM
792792
ruby-vips (2.2.5)
793793
ffi (~> 1.12)
794794
logger
795-
rubyzip (3.1.1)
795+
rubyzip (3.2.0)
796796
rufus-scheduler (3.9.2)
797797
fugit (~> 1.1, >= 1.11.1)
798798
safety_net_attestation (0.5.0)

app/helpers/application_helper.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def conditional_link_to(condition, name, options = {}, html_options = {}, &block
113113
end
114114

115115
def material_symbol(icon, attributes = {})
116+
whitespace = attributes.delete(:whitespace) { true }
116117
safe_join(
117118
[
118119
inline_svg_tag(
@@ -121,7 +122,7 @@ def material_symbol(icon, attributes = {})
121122
role: :img,
122123
data: attributes[:data]
123124
),
124-
' ',
125+
whitespace ? ' ' : '',
125126
]
126127
)
127128
end

app/helpers/statuses_helper.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ def poll_summary(status)
4646
status.preloadable_poll.options.map { |o| "[ ] #{o}" }.join("\n")
4747
end
4848

49+
def status_classnames(status, is_quote)
50+
if is_quote
51+
'status--is-quote'
52+
elsif status.quote.present?
53+
'status--has-quote'
54+
end
55+
end
56+
4957
def status_description(status)
5058
components = [[media_summary(status), status_text_summary(status)].compact_blank.join(' · ')]
5159

app/javascript/mastodon/components/emoji/context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const AnimateEmojiProvider = polymorphicForwardRef<
6363

6464
// If there's a parent context or GIFs autoplay, we don't need handlers.
6565
const parentContext = useContext(AnimateEmojiContext);
66-
if (parentContext !== null || autoPlayGif === true) {
66+
if (parentContext !== null) {
6767
return (
6868
<Wrapper
6969
{...props}

app/javascript/mastodon/components/hotkeys/hotkeys.stories.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,24 +139,24 @@ export const Default = {
139139
Last pressed hotkey: <output>{matchedHotkey ?? 'None'}</output>
140140
</p>
141141
<p>
142-
Click within the dashed border and press the &quot;<kbd>n</kbd>
143-
&quot; or &quot;<kbd>/</kbd>&quot; key. Press &quot;
144-
<kbd>Backspace</kbd>&quot; to clear the displayed hotkey.
142+
Click within the dashed border and press the <kbd>n</kbd>
143+
or <kbd>/</kbd> key. Press
144+
<kbd>Backspace</kbd> to clear the displayed hotkey.
145145
</p>
146146
<p>
147-
Try typing a sequence, like &quot;<kbd>g</kbd>&quot; shortly
148-
followed by &quot;<kbd>h</kbd>&quot;, &quot;<kbd>n</kbd>&quot;, or
149-
&quot;<kbd>f</kbd>&quot;
147+
Try typing a sequence, like <kbd>g</kbd> shortly followed by{' '}
148+
<kbd>h</kbd>, <kbd>n</kbd>, or
149+
<kbd>f</kbd>
150150
</p>
151151
<p>
152152
Note that this playground doesn&apos;t support all hotkeys we use in
153153
the app.
154154
</p>
155155
<p>
156-
When a <button>Button</button> is focused, &quot;
156+
When a <button>Button</button> is focused,
157157
<kbd>Enter</kbd>
158-
&quot; should not trigger &quot;open&quot;, but &quot;<kbd>o</kbd>
159-
&quot; should.
158+
should not trigger open, but <kbd>o</kbd>
159+
should.
160160
</p>
161161
<p>
162162
When an input element is focused, hotkeys should not interfere with

app/javascript/mastodon/components/status/boost_button.tsx

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback, useMemo } from 'react';
2-
import type { FC, KeyboardEvent, MouseEvent } from 'react';
2+
import type { FC, KeyboardEvent, MouseEvent, MouseEventHandler } from 'react';
33

44
import { useIntl } from 'react-intl';
55

@@ -8,6 +8,7 @@ import classNames from 'classnames';
88
import { quoteComposeById } from '@/mastodon/actions/compose_typed';
99
import { toggleReblog } from '@/mastodon/actions/interactions';
1010
import { openModal } from '@/mastodon/actions/modal';
11+
import { quickBoosting } from '@/mastodon/initial_state';
1112
import type { ActionMenuItem } from '@/mastodon/models/dropdown_menu';
1213
import type { Status } from '@/mastodon/models/status';
1314
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
@@ -24,6 +25,55 @@ import {
2425
selectStatusState,
2526
} from './boost_button_utils';
2627

28+
const StandaloneBoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
29+
const intl = useIntl();
30+
const dispatch = useAppDispatch();
31+
32+
const statusState = useAppSelector((state) =>
33+
selectStatusState(state, status),
34+
);
35+
const { title, meta, iconComponent, disabled } = useMemo(
36+
() => boostItemState(statusState),
37+
[statusState],
38+
);
39+
40+
const handleClick: MouseEventHandler = useCallback(
41+
(event) => {
42+
if (statusState.isLoggedIn) {
43+
dispatch(toggleReblog(status.get('id') as string, event.shiftKey));
44+
} else {
45+
dispatch(
46+
openModal({
47+
modalType: 'INTERACTION',
48+
modalProps: {
49+
accountId: status.getIn(['account', 'id']),
50+
url: status.get('uri'),
51+
},
52+
}),
53+
);
54+
}
55+
},
56+
[dispatch, status, statusState.isLoggedIn],
57+
);
58+
59+
return (
60+
<IconButton
61+
disabled={disabled}
62+
active={!!status.get('reblogged')}
63+
title={intl.formatMessage(meta ?? title)}
64+
icon='retweet'
65+
iconComponent={iconComponent}
66+
onClick={!disabled ? handleClick : undefined}
67+
counter={
68+
counters
69+
? (status.get('reblogs_count') as number) +
70+
(status.get('quotes_count') as number)
71+
: undefined
72+
}
73+
/>
74+
);
75+
};
76+
2777
const renderMenuItem: RenderItemFn<ActionMenuItem> = (
2878
item,
2979
index,
@@ -46,7 +96,7 @@ interface ReblogButtonProps {
4696

4797
type ActionMenuItemWithIcon = SomeRequired<ActionMenuItem, 'icon'>;
4898

49-
export const BoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
99+
const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
50100
const intl = useIntl();
51101
const dispatch = useAppDispatch();
52102
const statusState = useAppSelector((state) =>
@@ -188,3 +238,9 @@ const ReblogMenuItem: FC<ReblogMenuItemProps> = ({
188238
</li>
189239
);
190240
};
241+
242+
// Switch between the standalone boost button or the
243+
// "Boost or quote" menu based on the quickBoosting preference
244+
export const BoostButton = quickBoosting
245+
? StandaloneBoostButton
246+
: BoostOrQuoteMenu;

app/javascript/mastodon/components/status_action_bar/index.jsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/
2121
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
2222

2323
import { Dropdown } from 'mastodon/components/dropdown_menu';
24-
import { me } from '../../initial_state';
24+
import { me, quickBoosting } from '../../initial_state';
2525

2626
import { IconButton } from '../icon_button';
2727
import { BoostButton } from '../status/boost_button';
2828
import { RemoveQuoteHint } from './remove_quote_hint';
29+
import { quoteItemState, selectStatusState } from '../status/boost_button_utils';
2930

3031
const messages = defineMessages({
3132
delete: { id: 'status.delete', defaultMessage: 'Delete' },
@@ -71,6 +72,7 @@ const mapStateToProps = (state, { status }) => {
7172
return ({
7273
relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
7374
quotedAccountId: quotedStatusId ? state.getIn(['statuses', quotedStatusId, 'account']) : null,
75+
statusQuoteState: selectStatusState(state, status),
7476
});
7577
};
7678

@@ -79,6 +81,7 @@ class StatusActionBar extends ImmutablePureComponent {
7981
identity: identityContextPropShape,
8082
status: ImmutablePropTypes.map.isRequired,
8183
relationship: ImmutablePropTypes.record,
84+
statusQuoteState: PropTypes.object,
8285
quotedAccountId: PropTypes.string,
8386
contextType: PropTypes.string,
8487
onReply: PropTypes.func,
@@ -128,6 +131,10 @@ class StatusActionBar extends ImmutablePureComponent {
128131
}
129132
};
130133

134+
handleQuoteClick = () => {
135+
this.props.onQuote(this.props.status);
136+
};
137+
131138
handleShareClick = () => {
132139
navigator.share({
133140
url: this.props.status.get('url'),
@@ -244,7 +251,7 @@ class StatusActionBar extends ImmutablePureComponent {
244251
};
245252

246253
render () {
247-
const { status, relationship, quotedAccountId, contextType, intl, withDismiss, withCounters, scrollKey } = this.props;
254+
const { status, relationship, statusQuoteState, quotedAccountId, contextType, intl, withDismiss, withCounters, scrollKey } = this.props;
248255
const { signedIn, permissions } = this.props.identity;
249256

250257
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
@@ -271,6 +278,20 @@ class StatusActionBar extends ImmutablePureComponent {
271278
if (publicStatus && 'share' in navigator) {
272279
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
273280
}
281+
282+
if (quickBoosting && signedIn) {
283+
const quoteItem = quoteItemState(statusQuoteState);
284+
menu.push(null);
285+
menu.push({
286+
text: intl.formatMessage(quoteItem.title),
287+
description: quoteItem.meta
288+
? intl.formatMessage(quoteItem.meta)
289+
: undefined,
290+
disabled: quoteItem.disabled,
291+
action: this.handleQuoteClick,
292+
});
293+
menu.push(null);
294+
}
274295

275296
if (publicStatus && !isRemote) {
276297
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });

app/javascript/mastodon/components/status_quoted.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { Status } from 'mastodon/models/status';
1212
import type { RootState } from 'mastodon/store';
1313
import { useAppDispatch, useAppSelector } from 'mastodon/store';
1414

15+
import { fetchRelationships } from '../actions/accounts';
1516
import { revealAccount } from '../actions/accounts_typed';
1617
import { fetchStatus } from '../actions/statuses';
1718
import { makeGetStatusWithExtraInfo } from '../selectors';
@@ -148,6 +149,10 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
148149
}
149150
}, [shouldFetchQuote, quotedStatusId, parentQuotePostId, dispatch]);
150151

152+
useEffect(() => {
153+
if (accountId && hiddenAccount) dispatch(fetchRelationships([accountId]));
154+
}, [accountId, hiddenAccount, dispatch]);
155+
151156
const isFilteredAndHidden = loadingState === 'filtered';
152157

153158
let quoteError: React.ReactNode = null;

app/javascript/mastodon/features/emoji/emoji_data.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

app/javascript/mastodon/features/emoji/emoji_map.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)