Merge commit '14f5932f49567ce0d63570b7204511f912ca6d22' into glitch-soc/merge-4.3

This commit is contained in:
Claire
2026-03-09 13:20:24 +01:00
7 changed files with 86 additions and 9 deletions

View File

@@ -182,15 +182,25 @@ function loaded() {
({ target }) => {
if (!(target instanceof HTMLInputElement)) return;
if (target.value && target.value.length > 0) {
const checkedUsername = target.value;
if (checkedUsername && checkedUsername.length > 0) {
axios
.get('/api/v1/accounts/lookup', { params: { acct: target.value } })
.get('/api/v1/accounts/lookup', {
params: { acct: checkedUsername },
})
.then(() => {
target.setCustomValidity(formatMessage(messages.usernameTaken));
// Only update the validity if the result is for the currently-typed username
if (checkedUsername === target.value) {
target.setCustomValidity(formatMessage(messages.usernameTaken));
}
return true;
})
.catch(() => {
target.setCustomValidity('');
// Only update the validity if the result is for the currently-typed username
if (checkedUsername === target.value) {
target.setCustomValidity('');
}
});
} else {
target.setCustomValidity('');

View File

@@ -23,6 +23,7 @@ export const HoverCardController: React.FC = () => {
const [open, setOpen] = useState(false);
const [accountId, setAccountId] = useState<string | undefined>();
const [anchor, setAnchor] = useState<HTMLElement | null>(null);
const isUsingTouchRef = useRef(false);
const cardRef = useRef<HTMLDivElement | null>(null);
const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout();
const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout();
@@ -60,6 +61,12 @@ export const HoverCardController: React.FC = () => {
setAccountId(undefined);
};
const handleTouchStart = () => {
// Keeping track of touch events to prevent the
// hover card from being displayed on touch devices
isUsingTouchRef.current = true;
};
const handleMouseEnter = (e: MouseEvent) => {
const { target } = e;
@@ -69,6 +76,11 @@ export const HoverCardController: React.FC = () => {
return;
}
// Bail out if a touch is active
if (isUsingTouchRef.current) {
return;
}
// We've entered an anchor
if (!isScrolling && isHoverCardAnchor(target)) {
cancelLeaveTimeout();
@@ -127,9 +139,16 @@ export const HoverCardController: React.FC = () => {
};
const handleMouseMove = () => {
if (isUsingTouchRef.current) {
isUsingTouchRef.current = false;
}
delayEnterTimeout(enterDelay);
};
document.body.addEventListener('touchstart', handleTouchStart, {
passive: true,
});
document.body.addEventListener('mouseenter', handleMouseEnter, {
passive: true,
capture: true,
@@ -151,6 +170,7 @@ export const HoverCardController: React.FC = () => {
});
return () => {
document.body.removeEventListener('touchstart', handleTouchStart);
document.body.removeEventListener('mouseenter', handleMouseEnter);
document.body.removeEventListener('mousemove', handleMouseMove);
document.body.removeEventListener('mouseleave', handleMouseLeave);

View File

@@ -326,6 +326,11 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
return unless poll.present? && poll.expires_at.present? && poll.votes.exists?
# If the poll had previously expired, notifications should have already been sent out (or scheduled),
# and re-scheduling them would cause duplicate notifications for people who had already dismissed them
# (see #37948)
return if @previous_expires_at&.past?
PollExpirationNotifyWorker.remove_from_scheduled(poll.id) if @previous_expires_at.present? && @previous_expires_at > poll.expires_at
PollExpirationNotifyWorker.perform_at(poll.expires_at + 5.minutes, poll.id)
end

View File

@@ -4,7 +4,7 @@ class ResolveURLService < BaseService
include JsonLdHelper
include Authorization
USERNAME_STATUS_RE = %r{/@(?<username>#{Account::USERNAME_RE})/(?<status_id>[0-9]+)\Z}
USERNAME_STATUS_RE = %r{/@(?<username>#{Account::USERNAME_RE})/(statuses/)?(?<status_id>[0-9a-zA-Z]+)\Z}
def call(url, on_behalf_of: nil)
@url = url

View File

@@ -1,7 +1,7 @@
<%= t 'devise.mailer.two_factor_enabled.title' %>
<%= t 'devise.mailer.webauthn_credential.added.title' %>
===
<%= t 'devise.mailer.two_factor_enabled.explanation' %>
<%= t 'devise.mailer.webauthn_credential.added.explanation' %>
=> <%= edit_user_registration_url %>

View File

@@ -1,7 +1,7 @@
<%= t 'devise.mailer.webauthn_credential.added.title' %>
<%= t 'devise.mailer.webauthn_enabled.title' %>
===
<%= t 'devise.mailer.webauthn_credential.added.explanation' %>
<%= t 'devise.mailer.webauthn_enabled.explanation' %>
=> <%= edit_user_registration_url %>

View File

@@ -136,6 +136,48 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
context 'with an implicit update of a poll that has already expired' do
let(:account) { Fabricate(:account, domain: 'example.com') }
let!(:expiration) { 10.days.ago.utc }
let!(:status) do
Fabricate(:status,
text: 'Hello world',
account: account,
poll_attributes: {
options: %w(Foo Bar),
account: account,
multiple: false,
hide_totals: false,
expires_at: expiration,
})
end
let(:payload) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'https://example.com/foo',
type: 'Question',
content: 'Hello world',
endTime: expiration.iso8601,
oneOf: [
poll_option_json('Foo', 4),
poll_option_json('Bar', 3),
],
}
end
before do
travel_to(expiration - 1.day) do
Fabricate(:poll_vote, poll: status.poll)
end
end
it 'does not re-trigger notifications' do
expect { subject.call(status, json, json) }
.to_not enqueue_sidekiq_job(PollExpirationNotifyWorker)
end
end
context 'when the status changes a poll despite being not explicitly marked as updated' do
let(:account) { Fabricate(:account, domain: 'example.com') }
let!(:expiration) { 10.days.from_now.utc }