Merge pull request #3093 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to d887790e86124493df850e9bc52e1e059207b523
This commit is contained in:
commit
87e72fb8e1
1
.github/workflows/chromatic.yml
vendored
1
.github/workflows/chromatic.yml
vendored
@ -20,6 +20,7 @@ jobs:
|
|||||||
chromatic:
|
chromatic:
|
||||||
name: Run Chromatic
|
name: Run Chromatic
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'mastodon/mastodon'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
@ -1,181 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
|
|
||||||
import { is } from 'immutable';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
|
|
||||||
import Textarea from 'react-textarea-autosize';
|
|
||||||
|
|
||||||
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
placeholder: { id: 'account_note.placeholder', defaultMessage: 'Click to add a note' },
|
|
||||||
});
|
|
||||||
|
|
||||||
class InlineAlert extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
show: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
mountMessage: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static TRANSITION_DELAY = 200;
|
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
|
||||||
if (!this.props.show && nextProps.show) {
|
|
||||||
this.setState({ mountMessage: true });
|
|
||||||
} else if (this.props.show && !nextProps.show) {
|
|
||||||
setTimeout(() => this.setState({ mountMessage: false }), InlineAlert.TRANSITION_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { show } = this.props;
|
|
||||||
const { mountMessage } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span aria-live='polite' role='status' className='inline-alert' style={{ opacity: show ? 1 : 0 }}>
|
|
||||||
{mountMessage && <FormattedMessage id='generic.saved' defaultMessage='Saved' />}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class AccountNote extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
accountId: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.string,
|
|
||||||
onSave: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
value: null,
|
|
||||||
saving: false,
|
|
||||||
saved: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
UNSAFE_componentWillMount () {
|
|
||||||
this._reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
|
||||||
const accountWillChange = !is(this.props.accountId, nextProps.accountId);
|
|
||||||
const newState = {};
|
|
||||||
|
|
||||||
if (accountWillChange && this._isDirty()) {
|
|
||||||
this._save(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountWillChange || nextProps.value === this.state.value) {
|
|
||||||
newState.saving = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.value !== nextProps.value) {
|
|
||||||
newState.value = nextProps.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
if (this._isDirty()) {
|
|
||||||
this._save(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTextareaRef = c => {
|
|
||||||
this.textarea = c;
|
|
||||||
};
|
|
||||||
|
|
||||||
handleChange = e => {
|
|
||||||
this.setState({ value: e.target.value, saving: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleKeyDown = e => {
|
|
||||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (this.textarea) {
|
|
||||||
this.textarea.blur();
|
|
||||||
} else {
|
|
||||||
this._save();
|
|
||||||
}
|
|
||||||
} else if (e.keyCode === 27) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
this._reset(() => {
|
|
||||||
if (this.textarea) {
|
|
||||||
this.textarea.blur();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleBlur = () => {
|
|
||||||
if (this._isDirty()) {
|
|
||||||
this._save();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_save (showMessage = true) {
|
|
||||||
this.setState({ saving: true }, () => this.props.onSave(this.state.value));
|
|
||||||
|
|
||||||
if (showMessage) {
|
|
||||||
this.setState({ saved: true }, () => setTimeout(() => this.setState({ saved: false }), 2000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_reset (callback) {
|
|
||||||
this.setState({ value: this.props.value }, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
_isDirty () {
|
|
||||||
return !this.state.saving && this.props.value !== null && this.state.value !== null && this.state.value !== this.props.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { accountId, intl } = this.props;
|
|
||||||
const { value, saved } = this.state;
|
|
||||||
|
|
||||||
if (!accountId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='account__header__account-note'>
|
|
||||||
<label htmlFor={`account-note-${accountId}`}>
|
|
||||||
<FormattedMessage id='account.account_note_header' defaultMessage='Personal note' /> <InlineAlert show={saved} />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{this.props.value === undefined ? (
|
|
||||||
<div className='account__header__account-note__loading-indicator-wrapper'>
|
|
||||||
<LoadingIndicator />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Textarea
|
|
||||||
id={`account-note-${accountId}`}
|
|
||||||
className='account__header__account-note__content'
|
|
||||||
disabled={value === null}
|
|
||||||
placeholder={intl.formatMessage(messages.placeholder)}
|
|
||||||
value={value || ''}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
onKeyDown={this.handleKeyDown}
|
|
||||||
onBlur={this.handleBlur}
|
|
||||||
ref={this.setTextareaRef}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default injectIntl(AccountNote);
|
|
||||||
@ -0,0 +1,131 @@
|
|||||||
|
import type { ChangeEventHandler, KeyboardEventHandler } from 'react';
|
||||||
|
import { useState, useRef, useCallback, useId } from 'react';
|
||||||
|
|
||||||
|
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import Textarea from 'react-textarea-autosize';
|
||||||
|
|
||||||
|
import { submitAccountNote } from '@/flavours/glitch/actions/account_notes';
|
||||||
|
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
|
||||||
|
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
placeholder: {
|
||||||
|
id: 'account_note.placeholder',
|
||||||
|
defaultMessage: 'Click to add a note',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const AccountNoteUI: React.FC<{
|
||||||
|
initialValue: string | undefined;
|
||||||
|
onSubmit: (newNote: string) => void;
|
||||||
|
wasSaved: boolean;
|
||||||
|
}> = ({ initialValue, onSubmit, wasSaved }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const uniqueId = useId();
|
||||||
|
const [value, setValue] = useState(initialValue ?? '');
|
||||||
|
const isLoading = initialValue === undefined;
|
||||||
|
const canSubmitOnBlurRef = useRef(true);
|
||||||
|
|
||||||
|
const handleChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>(
|
||||||
|
(e) => {
|
||||||
|
setValue(e.target.value);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleKeyDown = useCallback<KeyboardEventHandler<HTMLTextAreaElement>>(
|
||||||
|
(e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
setValue(initialValue ?? '');
|
||||||
|
|
||||||
|
canSubmitOnBlurRef.current = false;
|
||||||
|
e.currentTarget.blur();
|
||||||
|
} else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
onSubmit(value);
|
||||||
|
|
||||||
|
canSubmitOnBlurRef.current = false;
|
||||||
|
e.currentTarget.blur();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[initialValue, onSubmit, value],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleBlur = useCallback(() => {
|
||||||
|
if (initialValue !== value && canSubmitOnBlurRef.current) {
|
||||||
|
onSubmit(value);
|
||||||
|
}
|
||||||
|
canSubmitOnBlurRef.current = true;
|
||||||
|
}, [initialValue, onSubmit, value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='account__header__account-note'>
|
||||||
|
<label htmlFor={`account-note-${uniqueId}`}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.account_note_header'
|
||||||
|
defaultMessage='Personal note'
|
||||||
|
/>{' '}
|
||||||
|
<span
|
||||||
|
aria-live='polite'
|
||||||
|
role='status'
|
||||||
|
className='inline-alert'
|
||||||
|
style={{ opacity: wasSaved ? 1 : 0 }}
|
||||||
|
>
|
||||||
|
{wasSaved && (
|
||||||
|
<FormattedMessage id='generic.saved' defaultMessage='Saved' />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
{isLoading ? (
|
||||||
|
<div className='account__header__account-note__loading-indicator-wrapper'>
|
||||||
|
<LoadingIndicator />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Textarea
|
||||||
|
id={`account-note-${uniqueId}`}
|
||||||
|
className='account__header__account-note__content'
|
||||||
|
placeholder={intl.formatMessage(messages.placeholder)}
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AccountNote: React.FC<{
|
||||||
|
accountId: string;
|
||||||
|
}> = ({ accountId }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const initialValue = useAppSelector((state) =>
|
||||||
|
state.relationships.get(accountId)?.get('note'),
|
||||||
|
);
|
||||||
|
const [wasSaved, setWasSaved] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(
|
||||||
|
(note: string) => {
|
||||||
|
setWasSaved(true);
|
||||||
|
void dispatch(submitAccountNote({ accountId, note }));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setWasSaved(false);
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
[dispatch, accountId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccountNoteUI
|
||||||
|
key={`${accountId}-${initialValue}`}
|
||||||
|
initialValue={initialValue}
|
||||||
|
wasSaved={wasSaved}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,19 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { submitAccountNote } from 'flavours/glitch/actions/account_notes';
|
|
||||||
|
|
||||||
import AccountNote from '../components/account_note';
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { accountId }) => ({
|
|
||||||
value: state.relationships.getIn([accountId, 'note']),
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { accountId }) => ({
|
|
||||||
|
|
||||||
onSave (value) {
|
|
||||||
dispatch(submitAccountNote({ accountId: accountId, note: value }));
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountNote);
|
|
||||||
@ -42,8 +42,8 @@ import { FollowButton } from 'flavours/glitch/components/follow_button';
|
|||||||
import { FormattedDateWrapper } from 'flavours/glitch/components/formatted_date';
|
import { FormattedDateWrapper } from 'flavours/glitch/components/formatted_date';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
|
import { AccountNote } from 'flavours/glitch/features/account/components/account_note';
|
||||||
import { DomainPill } from 'flavours/glitch/features/account/components/domain_pill';
|
import { DomainPill } from 'flavours/glitch/features/account/components/domain_pill';
|
||||||
import AccountNoteContainer from 'flavours/glitch/features/account/containers/account_note_container';
|
|
||||||
import FollowRequestNoteContainer from 'flavours/glitch/features/account/containers/follow_request_note_container';
|
import FollowRequestNoteContainer from 'flavours/glitch/features/account/containers/follow_request_note_container';
|
||||||
import { useLinks } from 'flavours/glitch/hooks/useLinks';
|
import { useLinks } from 'flavours/glitch/hooks/useLinks';
|
||||||
import { useIdentity } from 'flavours/glitch/identity_context';
|
import { useIdentity } from 'flavours/glitch/identity_context';
|
||||||
@ -927,7 +927,7 @@ export const AccountHeader: React.FC<{
|
|||||||
onClickCapture={handleLinkClick}
|
onClickCapture={handleLinkClick}
|
||||||
>
|
>
|
||||||
{account.id !== me && signedIn && (
|
{account.id !== me && signedIn && (
|
||||||
<AccountNoteContainer accountId={accountId} />
|
<AccountNote accountId={accountId} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{account.note.length > 0 && account.note !== '<p></p>' && (
|
{account.note.length > 0 && account.note !== '<p></p>' && (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useState, useRef } from 'react';
|
import { useCallback, useState, useRef, useEffect } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineMessages,
|
defineMessages,
|
||||||
@ -72,6 +72,10 @@ export const Search: React.FC<{
|
|||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const [selectedOption, setSelectedOption] = useState(-1);
|
const [selectedOption, setSelectedOption] = useState(-1);
|
||||||
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(initialValue ?? '');
|
||||||
|
setQuickActions([]);
|
||||||
|
}, [initialValue]);
|
||||||
const searchOptions: SearchOption[] = [];
|
const searchOptions: SearchOption[] = [];
|
||||||
|
|
||||||
if (searchEnabled) {
|
if (searchEnabled) {
|
||||||
|
|||||||
@ -1948,16 +1948,18 @@ body > [data-popper-placement] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status__quote {
|
.status__quote {
|
||||||
|
--quote-margin: 36px;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-block-start: 16px;
|
margin-block-start: 16px;
|
||||||
margin-inline-start: 36px;
|
margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px));
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--nested-card-text);
|
color: var(--nested-card-text);
|
||||||
background: var(--nested-card-background);
|
background: var(--nested-card-background);
|
||||||
border: var(--nested-card-border);
|
border: var(--nested-card-border);
|
||||||
|
|
||||||
@media screen and (min-width: $mobile-breakpoint) {
|
@container (width > 460px) {
|
||||||
margin-inline-start: 56px;
|
--quote-margin: 56px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2017,7 +2019,7 @@ body > [data-popper-placement] {
|
|||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $mobile-breakpoint) {
|
@container (width > 460px) {
|
||||||
inset-inline-start: -50px;
|
inset-inline-start: -50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2943,6 +2945,7 @@ a.account__display-name {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
contain: inline-size layout paint style;
|
contain: inline-size layout paint style;
|
||||||
|
container: column / inline-size;
|
||||||
|
|
||||||
@media screen and (min-width: $no-gap-breakpoint) {
|
@media screen and (min-width: $no-gap-breakpoint) {
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useState, useRef } from 'react';
|
import { useCallback, useState, useRef, useEffect } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineMessages,
|
defineMessages,
|
||||||
@ -72,6 +72,10 @@ export const Search: React.FC<{
|
|||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const [selectedOption, setSelectedOption] = useState(-1);
|
const [selectedOption, setSelectedOption] = useState(-1);
|
||||||
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
const [quickActions, setQuickActions] = useState<SearchOption[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(initialValue ?? '');
|
||||||
|
setQuickActions([]);
|
||||||
|
}, [initialValue]);
|
||||||
const searchOptions: SearchOption[] = [];
|
const searchOptions: SearchOption[] = [];
|
||||||
|
|
||||||
if (searchEnabled) {
|
if (searchEnabled) {
|
||||||
|
|||||||
@ -1882,16 +1882,18 @@ body > [data-popper-placement] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status__quote {
|
.status__quote {
|
||||||
|
--quote-margin: 36px;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-block-start: 16px;
|
margin-block-start: 16px;
|
||||||
margin-inline-start: 36px;
|
margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px));
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--nested-card-text);
|
color: var(--nested-card-text);
|
||||||
background: var(--nested-card-background);
|
background: var(--nested-card-background);
|
||||||
border: var(--nested-card-border);
|
border: var(--nested-card-border);
|
||||||
|
|
||||||
@media screen and (min-width: $mobile-breakpoint) {
|
@container (width > 460px) {
|
||||||
margin-inline-start: 56px;
|
--quote-margin: 56px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1951,7 +1953,7 @@ body > [data-popper-placement] {
|
|||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $mobile-breakpoint) {
|
@container (width > 460px) {
|
||||||
inset-inline-start: -50px;
|
inset-inline-start: -50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2878,6 +2880,7 @@ a.account__display-name {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
contain: inline-size layout paint style;
|
contain: inline-size layout paint style;
|
||||||
|
container: column / inline-size;
|
||||||
|
|
||||||
@media screen and (min-width: $no-gap-breakpoint) {
|
@media screen and (min-width: $no-gap-breakpoint) {
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ class Admin::SystemCheck::SidekiqProcessCheck < Admin::SystemCheck::BaseCheck
|
|||||||
pull
|
pull
|
||||||
scheduler
|
scheduler
|
||||||
ingress
|
ingress
|
||||||
|
fasp
|
||||||
).freeze
|
).freeze
|
||||||
|
|
||||||
def skip?
|
def skip?
|
||||||
|
|||||||
@ -16,7 +16,7 @@ module Status::SafeReblogInsert
|
|||||||
# The code is kept similar to ActiveRecord::Persistence code and calls it
|
# The code is kept similar to ActiveRecord::Persistence code and calls it
|
||||||
# directly when we are not handling a reblog.
|
# directly when we are not handling a reblog.
|
||||||
#
|
#
|
||||||
# https://github.com/rails/rails/blob/v7.2.1.1/activerecord/lib/active_record/persistence.rb#L238-L263
|
# https://github.com/rails/rails/blob/v8.0.2/activerecord/lib/active_record/persistence.rb#L238-L261
|
||||||
def _insert_record(connection, values, returning)
|
def _insert_record(connection, values, returning)
|
||||||
return super unless values.is_a?(Hash) && values['reblog_of_id']&.value.present?
|
return super unless values.is_a?(Hash) && values['reblog_of_id']&.value.present?
|
||||||
|
|
||||||
@ -36,15 +36,13 @@ module Status::SafeReblogInsert
|
|||||||
# Instead, we use a custom builder when a reblog is happening:
|
# Instead, we use a custom builder when a reblog is happening:
|
||||||
im = _compile_reblog_insert(values)
|
im = _compile_reblog_insert(values)
|
||||||
|
|
||||||
with_connection do |_c|
|
connection.insert(
|
||||||
connection.insert(
|
im, "#{self} Create", primary_key || false, primary_key_value,
|
||||||
im, "#{self} Create", primary_key || false, primary_key_value,
|
returning: returning
|
||||||
returning: returning
|
).tap do |result|
|
||||||
).tap do |result|
|
# Since we are using SELECT instead of VALUES, a non-error `nil` return is possible.
|
||||||
# Since we are using SELECT instead of VALUES, a non-error `nil` return is possible.
|
# For our purposes, it's equivalent to a foreign key constraint violation
|
||||||
# For our purposes, it's equivalent to a foreign key constraint violation
|
raise ActiveRecord::InvalidForeignKey, "(reblog_of_id)=(#{values['reblog_of_id'].value}) is not present in table \"statuses\"" if result.nil?
|
||||||
raise ActiveRecord::InvalidForeignKey, "(reblog_of_id)=(#{values['reblog_of_id'].value}) is not present in table \"statuses\"" if result.nil?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -35,11 +35,11 @@ RSpec.describe Admin::SystemCheck::SidekiqProcessCheck do
|
|||||||
|
|
||||||
describe 'message' do
|
describe 'message' do
|
||||||
it 'sends values to message instance' do
|
it 'sends values to message instance' do
|
||||||
allow(Admin::SystemCheck::Message).to receive(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress')
|
allow(Admin::SystemCheck::Message).to receive(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress, fasp')
|
||||||
|
|
||||||
check.message
|
check.message
|
||||||
|
|
||||||
expect(Admin::SystemCheck::Message).to have_received(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress')
|
expect(Admin::SystemCheck::Message).to have_received(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress, fasp')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user