mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 03:00:33 +02:00
Add support for FEP-3b86 (Activity Intents) (#38120)
This commit is contained in:
@@ -39,18 +39,57 @@ const findLink = (rel: string, data: unknown): JRDLink | undefined => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const findTemplateLink = (data: unknown) =>
|
const intentParams = (intent: string) => {
|
||||||
findLink('http://ostatus.org/schema/1.0/subscribe', data)?.template;
|
switch (intent) {
|
||||||
|
case 'follow':
|
||||||
|
return ['https://w3id.org/fep/3b86/Follow', 'object'] as [string, string];
|
||||||
|
case 'reblog':
|
||||||
|
return ['https://w3id.org/fep/3b86/Announce', 'object'] as [
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
];
|
||||||
|
case 'favourite':
|
||||||
|
return ['https://w3id.org/fep/3b86/Like', 'object'] as [string, string];
|
||||||
|
case 'vote':
|
||||||
|
case 'reply':
|
||||||
|
return ['https://w3id.org/fep/3b86/Object', 'object'] as [string, string];
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const findTemplateLink = (data: unknown, intent: string) => {
|
||||||
|
const [needle, param] = intentParams(intent) ?? [
|
||||||
|
'http://ostatus.org/schema/1.0/subscribe',
|
||||||
|
'uri',
|
||||||
|
];
|
||||||
|
|
||||||
|
const match = findLink(needle, data);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return [match.template, param] as [string, string];
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallback = findLink('http://ostatus.org/schema/1.0/subscribe', data);
|
||||||
|
|
||||||
|
if (fallback) {
|
||||||
|
return [fallback.template, 'uri'] as [string, string];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [null, null];
|
||||||
|
};
|
||||||
|
|
||||||
const fetchInteractionURLSuccess = (
|
const fetchInteractionURLSuccess = (
|
||||||
uri_or_domain: string,
|
uri_or_domain: string,
|
||||||
template: string,
|
template: string,
|
||||||
|
param: string,
|
||||||
) => {
|
) => {
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: 'fetchInteractionURL-success',
|
type: 'fetchInteractionURL-success',
|
||||||
uri_or_domain,
|
uri_or_domain,
|
||||||
template,
|
template,
|
||||||
|
param,
|
||||||
},
|
},
|
||||||
window.origin,
|
window.origin,
|
||||||
);
|
);
|
||||||
@@ -74,7 +113,7 @@ const isValidDomain = (value: unknown) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Attempt to find a remote interaction URL from a domain
|
// Attempt to find a remote interaction URL from a domain
|
||||||
const fromDomain = (domain: string) => {
|
const fromDomain = (domain: string, intent: string) => {
|
||||||
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||||
|
|
||||||
axios
|
axios
|
||||||
@@ -82,17 +121,21 @@ const fromDomain = (domain: string) => {
|
|||||||
params: { resource: `https://${domain}` },
|
params: { resource: `https://${domain}` },
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
const template = findTemplateLink(data);
|
const [template, param] = findTemplateLink(data, intent);
|
||||||
fetchInteractionURLSuccess(domain, template ?? fallbackTemplate);
|
fetchInteractionURLSuccess(
|
||||||
|
domain,
|
||||||
|
template ?? fallbackTemplate,
|
||||||
|
param ?? 'uri',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
fetchInteractionURLSuccess(domain, fallbackTemplate);
|
fetchInteractionURLSuccess(domain, fallbackTemplate, 'uri');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attempt to find a remote interaction URL from an arbitrary URL
|
// Attempt to find a remote interaction URL from an arbitrary URL
|
||||||
const fromURL = (url: string) => {
|
const fromURL = (url: string, intent: string) => {
|
||||||
const domain = new URL(url).host;
|
const domain = new URL(url).host;
|
||||||
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||||
|
|
||||||
@@ -101,17 +144,21 @@ const fromURL = (url: string) => {
|
|||||||
params: { resource: url },
|
params: { resource: url },
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
const template = findTemplateLink(data);
|
const [template, param] = findTemplateLink(data, intent);
|
||||||
fetchInteractionURLSuccess(url, template ?? fallbackTemplate);
|
fetchInteractionURLSuccess(
|
||||||
|
url,
|
||||||
|
template ?? fallbackTemplate,
|
||||||
|
param ?? 'uri',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
fromDomain(domain);
|
fromDomain(domain, intent);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attempt to find a remote interaction URL from a `user@domain` string
|
// Attempt to find a remote interaction URL from a `user@domain` string
|
||||||
const fromAcct = (acct: string) => {
|
const fromAcct = (acct: string, intent: string) => {
|
||||||
acct = acct.replace(/^@/, '');
|
acct = acct.replace(/^@/, '');
|
||||||
|
|
||||||
const segments = acct.split('@');
|
const segments = acct.split('@');
|
||||||
@@ -134,25 +181,29 @@ const fromAcct = (acct: string) => {
|
|||||||
params: { resource: `acct:${acct}` },
|
params: { resource: `acct:${acct}` },
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
const template = findTemplateLink(data);
|
const [template, param] = findTemplateLink(data, intent);
|
||||||
fetchInteractionURLSuccess(acct, template ?? fallbackTemplate);
|
fetchInteractionURLSuccess(
|
||||||
|
acct,
|
||||||
|
template ?? fallbackTemplate,
|
||||||
|
param ?? 'uri',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// TODO: handle host-meta?
|
// TODO: handle host-meta?
|
||||||
fromDomain(domain);
|
fromDomain(domain, intent);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInteractionURL = (uri_or_domain: string) => {
|
const fetchInteractionURL = (uri_or_domain: string, intent: string) => {
|
||||||
if (uri_or_domain === '') {
|
if (uri_or_domain === '') {
|
||||||
fetchInteractionURLFailure();
|
fetchInteractionURLFailure();
|
||||||
} else if (/^https?:\/\//.test(uri_or_domain)) {
|
} else if (/^https?:\/\//.test(uri_or_domain)) {
|
||||||
fromURL(uri_or_domain);
|
fromURL(uri_or_domain, intent);
|
||||||
} else if (uri_or_domain.includes('@')) {
|
} else if (uri_or_domain.includes('@')) {
|
||||||
fromAcct(uri_or_domain);
|
fromAcct(uri_or_domain, intent);
|
||||||
} else {
|
} else {
|
||||||
fromDomain(uri_or_domain);
|
fromDomain(uri_or_domain, intent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -172,8 +223,10 @@ window.addEventListener('message', (event: MessageEvent<unknown>) => {
|
|||||||
'type' in event.data &&
|
'type' in event.data &&
|
||||||
event.data.type === 'fetchInteractionURL' &&
|
event.data.type === 'fetchInteractionURL' &&
|
||||||
'uri_or_domain' in event.data &&
|
'uri_or_domain' in event.data &&
|
||||||
typeof event.data.uri_or_domain === 'string'
|
typeof event.data.uri_or_domain === 'string' &&
|
||||||
|
'intent' in event.data &&
|
||||||
|
typeof event.data.intent === 'string'
|
||||||
) {
|
) {
|
||||||
fetchInteractionURL(event.data.uri_or_domain);
|
fetchInteractionURL(event.data.uri_or_domain, event.data.intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export const FollowButton: React.FC<{
|
|||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'follow',
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
url: account?.url,
|
url: account?.url,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ export const Poll: React.FC<PollProps> = ({ pollId, disabled, status }) => {
|
|||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'vote',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ const StandaloneBoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'reblog',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
@@ -120,6 +121,7 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'reblog',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
if (signedIn) {
|
if (signedIn) {
|
||||||
this.props.onReply(this.props.status);
|
this.props.onReply(this.props.status);
|
||||||
} else {
|
} else {
|
||||||
this.props.onInteractionModal(this.props.status);
|
this.props.onInteractionModal(this.props.status, 'reply');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
if (signedIn) {
|
if (signedIn) {
|
||||||
this.props.onFavourite(this.props.status);
|
this.props.onFavourite(this.props.status);
|
||||||
} else {
|
} else {
|
||||||
this.props.onInteractionModal(this.props.status);
|
this.props.onInteractionModal(this.props.status, 'favourite');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -382,7 +382,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
const bookmarkTitle = intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark);
|
const bookmarkTitle = intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark);
|
||||||
const favouriteTitle = intl.formatMessage(status.get('favourited') ? messages.removeFavourite : messages.favourite);
|
const favouriteTitle = intl.formatMessage(status.get('favourited') ? messages.removeFavourite : messages.favourite);
|
||||||
const isReply = status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
|
const isReply = status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
|
||||||
|
|
||||||
const shouldShowQuoteRemovalHint = isQuotingMe && contextType === 'notifications';
|
const shouldShowQuoteRemovalHint = isQuotingMe && contextType === 'notifications';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
|
|||||||
onReblog (status, e) {
|
onReblog (status, e) {
|
||||||
dispatch(toggleReblog(status.get('id'), e.shiftKey));
|
dispatch(toggleReblog(status.get('id'), e.shiftKey));
|
||||||
},
|
},
|
||||||
|
|
||||||
onQuote (status) {
|
onQuote (status) {
|
||||||
dispatch(quoteComposeById(status.get('id')));
|
dispatch(quoteComposeById(status.get('id')));
|
||||||
},
|
},
|
||||||
@@ -231,10 +231,11 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
|
|||||||
dispatch(deployPictureInPicture({statusId: status.get('id'), accountId: status.getIn(['account', 'id']), playerType: type, props: mediaProps}));
|
dispatch(deployPictureInPicture({statusId: status.get('id'), accountId: status.getIn(['account', 'id']), playerType: type, props: mediaProps}));
|
||||||
},
|
},
|
||||||
|
|
||||||
onInteractionModal (status) {
|
onInteractionModal (status, intent) {
|
||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent,
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type InteractionIntent = 'follow' | 'reblog' | 'favourite' | 'reply' | 'vote';
|
||||||
|
|
||||||
interface LoginFormMessage {
|
interface LoginFormMessage {
|
||||||
type:
|
type:
|
||||||
| 'fetchInteractionURL'
|
| 'fetchInteractionURL'
|
||||||
@@ -32,6 +34,8 @@ interface LoginFormMessage {
|
|||||||
| 'fetchInteractionURL-success';
|
| 'fetchInteractionURL-success';
|
||||||
uri_or_domain: string;
|
uri_or_domain: string;
|
||||||
template?: string;
|
template?: string;
|
||||||
|
param?: string;
|
||||||
|
intent?: InteractionIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PERSISTENCE_KEY = 'mastodon_home';
|
const PERSISTENCE_KEY = 'mastodon_home';
|
||||||
@@ -110,7 +114,11 @@ const isValueValid = (value: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendToFrame = (frame: HTMLIFrameElement | null, value: string): void => {
|
const sendToFrame = (
|
||||||
|
frame: HTMLIFrameElement | null,
|
||||||
|
value: string,
|
||||||
|
intent: string,
|
||||||
|
): void => {
|
||||||
if (valueToDomain(value.trim()) === localDomain) {
|
if (valueToDomain(value.trim()) === localDomain) {
|
||||||
window.location.href = '/auth/sign_in';
|
window.location.href = '/auth/sign_in';
|
||||||
return;
|
return;
|
||||||
@@ -120,6 +128,7 @@ const sendToFrame = (frame: HTMLIFrameElement | null, value: string): void => {
|
|||||||
{
|
{
|
||||||
type: 'fetchInteractionURL',
|
type: 'fetchInteractionURL',
|
||||||
uri_or_domain: value.trim(),
|
uri_or_domain: value.trim(),
|
||||||
|
intent,
|
||||||
},
|
},
|
||||||
window.origin,
|
window.origin,
|
||||||
);
|
);
|
||||||
@@ -127,7 +136,8 @@ const sendToFrame = (frame: HTMLIFrameElement | null, value: string): void => {
|
|||||||
|
|
||||||
const LoginForm: React.FC<{
|
const LoginForm: React.FC<{
|
||||||
resourceUrl: string;
|
resourceUrl: string;
|
||||||
}> = ({ resourceUrl }) => {
|
intent: string;
|
||||||
|
}> = ({ resourceUrl, intent }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [value, setValue] = useState(
|
const [value, setValue] = useState(
|
||||||
localStorage.getItem(PERSISTENCE_KEY) ?? '',
|
localStorage.getItem(PERSISTENCE_KEY) ?? '',
|
||||||
@@ -161,7 +171,7 @@ const LoginForm: React.FC<{
|
|||||||
try {
|
try {
|
||||||
const url = new URL(
|
const url = new URL(
|
||||||
event.data.template.replace(
|
event.data.template.replace(
|
||||||
'{uri}',
|
`{${event.data.param}}`,
|
||||||
encodeURIComponent(resourceUrl),
|
encodeURIComponent(resourceUrl),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -242,8 +252,8 @@ const LoginForm: React.FC<{
|
|||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
sendToFrame(iframeRef.current, value);
|
sendToFrame(iframeRef.current, value, intent);
|
||||||
}, [setIsSubmitting, value]);
|
}, [setIsSubmitting, value, intent]);
|
||||||
|
|
||||||
const handleFocus = useCallback(() => {
|
const handleFocus = useCallback(() => {
|
||||||
setExpanded(true);
|
setExpanded(true);
|
||||||
@@ -287,7 +297,7 @@ const LoginForm: React.FC<{
|
|||||||
setError(false);
|
setError(false);
|
||||||
setValue(selectedOptionValue);
|
setValue(selectedOptionValue);
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
sendToFrame(iframeRef.current, selectedOptionValue);
|
sendToFrame(iframeRef.current, selectedOptionValue, intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -300,6 +310,7 @@ const LoginForm: React.FC<{
|
|||||||
setValue,
|
setValue,
|
||||||
selectedOption,
|
selectedOption,
|
||||||
options,
|
options,
|
||||||
|
intent,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -318,9 +329,9 @@ const LoginForm: React.FC<{
|
|||||||
setValue(option);
|
setValue(option);
|
||||||
setError(false);
|
setError(false);
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
sendToFrame(iframeRef.current, option);
|
sendToFrame(iframeRef.current, option, intent);
|
||||||
},
|
},
|
||||||
[options, setSelectedOption, setValue, setError],
|
[options, setSelectedOption, setValue, setError, intent],
|
||||||
);
|
);
|
||||||
|
|
||||||
const domain = (valueToDomain(value) ?? '').trim();
|
const domain = (valueToDomain(value) ?? '').trim();
|
||||||
@@ -404,7 +415,8 @@ const LoginForm: React.FC<{
|
|||||||
const InteractionModal: React.FC<{
|
const InteractionModal: React.FC<{
|
||||||
accountId: string;
|
accountId: string;
|
||||||
url: string;
|
url: string;
|
||||||
}> = ({ accountId, url }) => {
|
intent: string;
|
||||||
|
}> = ({ accountId, url, intent }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const signupUrl = useAppSelector(
|
const signupUrl = useAppSelector(
|
||||||
(state) =>
|
(state) =>
|
||||||
@@ -479,7 +491,7 @@ const InteractionModal: React.FC<{
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LoginForm resourceUrl={url} />
|
<LoginForm resourceUrl={url} intent={intent} />
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export const Footer: React.FC<{
|
|||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'reply',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
@@ -106,6 +107,7 @@ export const Footer: React.FC<{
|
|||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'favourite',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'favourite',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
@@ -219,6 +220,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'reply',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
@@ -236,6 +238,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
|
intent: 'reblog',
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
@@ -274,13 +277,13 @@ class Status extends ImmutablePureComponent {
|
|||||||
// Error handling - could show error message
|
// Error handling - could show error message
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'CONFIRM_DELETE_STATUS',
|
modalType: 'CONFIRM_DELETE_STATUS',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
statusId: status.get('id'),
|
statusId: status.get('id'),
|
||||||
withRedraft,
|
withRedraft,
|
||||||
onDeleteSuccess: handleDeleteSuccess
|
onDeleteSuccess: handleDeleteSuccess
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -498,7 +501,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
// Only highlight replies after the initial load
|
// Only highlight replies after the initial load
|
||||||
if (prevProps.descendantsIds.length && isSameStatus) {
|
if (prevProps.descendantsIds.length && isSameStatus) {
|
||||||
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
||||||
|
|
||||||
if (newRepliesIds.length) {
|
if (newRepliesIds.length) {
|
||||||
this.setState({newRepliesIds});
|
this.setState({newRepliesIds});
|
||||||
}
|
}
|
||||||
@@ -631,7 +634,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
</Hotkeys>
|
</Hotkeys>
|
||||||
|
|
||||||
{descendants}
|
{descendants}
|
||||||
|
|
||||||
<RefreshController
|
<RefreshController
|
||||||
isLocal={isLocal}
|
isLocal={isLocal}
|
||||||
statusId={status.get('id')}
|
statusId={status.get('id')}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class WebfingerSerializer < ActiveModel::Serializer
|
|||||||
{ rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_page_href },
|
{ rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_page_href },
|
||||||
{ rel: 'self', type: 'application/activity+json', href: self_href },
|
{ rel: 'self', type: 'application/activity+json', href: self_href },
|
||||||
{ rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" },
|
{ rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" },
|
||||||
|
{ rel: 'https://w3id.org/fep/3b86/Create', template: "#{share_url}?text={content}" },
|
||||||
|
{ rel: 'https://w3id.org/fep/3b86/Object', template: "#{authorize_interaction_url}?uri={object}" },
|
||||||
].tap do |x|
|
].tap do |x|
|
||||||
x << { rel: 'http://webfinger.net/rel/avatar', type: object.avatar.content_type, href: full_asset_url(object.avatar_original_url) } if show_avatar?
|
x << { rel: 'http://webfinger.net/rel/avatar', type: object.avatar.content_type, href: full_asset_url(object.avatar_original_url) } if show_avatar?
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user