mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 03:00:33 +02:00
Merge commit '562ea656f495f0619e393b7d93bd07c5abd28e5a' into glitch-soc/merge-upstream
This commit is contained in:
@@ -50,9 +50,19 @@ const preview: Preview = {
|
||||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
description: 'Theme for the story',
|
||||
toolbar: {
|
||||
title: 'Theme',
|
||||
icon: 'circlehollow',
|
||||
items: [{ value: 'light' }, { value: 'dark' }],
|
||||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
initialGlobals: {
|
||||
locale: 'en',
|
||||
theme: 'light',
|
||||
},
|
||||
decorators: [
|
||||
(Story, { parameters, globals, args, argTypes }) => {
|
||||
@@ -135,6 +145,13 @@ const preview: Preview = {
|
||||
</IntlProvider>
|
||||
);
|
||||
},
|
||||
(Story, { globals }) => {
|
||||
const theme = (globals.theme as string) || 'light';
|
||||
useEffect(() => {
|
||||
document.body.setAttribute('data-color-scheme', theme);
|
||||
}, [theme]);
|
||||
return <Story />;
|
||||
},
|
||||
(Story) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
|
||||
@@ -74,6 +74,7 @@ class Api::V1Alpha::CollectionsController < Api::BaseController
|
||||
.order(created_at: :desc)
|
||||
.offset(offset_param)
|
||||
.limit(limit_param(DEFAULT_COLLECTIONS_LIMIT))
|
||||
@collections = @collections.discoverable unless @account == current_account
|
||||
end
|
||||
|
||||
def set_collection
|
||||
|
||||
10
app/javascript/images/icons/icon_admin.svg
Normal file
10
app/javascript/images/icons/icon_admin.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path fill="url(#badgeGradient)" d="M8 .666c.301 0 .595.094.839.268l.101.079.006.005c1.092.956 2.603 1.649 3.721 1.649A1.333 1.333 0 0 1 14 4v4.667c0 1.842-.652 3.259-1.702 4.336-1.032 1.058-2.417 1.76-3.852 2.26l-.005.002a1.334 1.334 0 0 1-.879-.01c-1.437-.496-2.825-1.195-3.858-2.253C2.654 11.926 2 10.509 2 8.667V4a1.334 1.334 0 0 1 1.334-1.333c1.118 0 2.634-.7 3.72-1.65l.007-.004C7.322.789 7.656.666 8 .666Z"/>
|
||||
<path stroke="#fff" fill="none" stroke-linecap="round" stroke-linejoin="round" d="M5 10.998A3.053 3.053 0 0 1 6.173 9.57a3.333 3.333 0 0 1 1.828-.54m0 0c.653 0 1.29.189 1.826.54.537.353.946.852 1.173 1.43M8 9.03c1.179 0 2.133-.903 2.133-2.015C10.133 5.902 9.178 5 8 5c-1.179 0-2.134.902-2.134 2.015 0 1.112.956 2.015 2.135 2.015Z"/>
|
||||
<defs>
|
||||
<linearGradient id="badgeGradient" x1=".5" x2="14" y1="2.5" y2="15" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E17100"/>
|
||||
<stop offset="1" stop-color="#F0B100"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
10
app/javascript/images/icons/icon_verified.svg
Normal file
10
app/javascript/images/icons/icon_verified.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
|
||||
<path fill="url(#VerifiedGradient)" d="M8 .837a3.168 3.168 0 0 1 2.47 1.187 3.166 3.166 0 0 1 2.601.906 3.168 3.168 0 0 1 .905 2.6A3.167 3.167 0 0 1 15.164 8a3.172 3.172 0 0 1-1.188 2.47 3.167 3.167 0 0 1-.903 2.597 3.168 3.168 0 0 1-2.596.909 3.167 3.167 0 0 1-4.95.001 3.166 3.166 0 0 1-3.397-2.258 3.169 3.169 0 0 1-.107-1.24A3.168 3.168 0 0 1 .826 8a3.17 3.17 0 0 1 1.197-2.479 3.168 3.168 0 0 1 .91-2.593 3.166 3.166 0 0 1 2.596-.905A3.169 3.169 0 0 1 8 .837Z"/>
|
||||
<path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.333" d="m6 8 1.333 1.333L10 6.667"/>
|
||||
<defs>
|
||||
<linearGradient id="VerifiedGradient" x1="-.966" x2="12.162" y1="2.629" y2="17.493" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".13" stop-color="#5638CC"/>
|
||||
<stop offset=".995" stop-color="#DC03F0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 930 B |
@@ -1,31 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import GroupsIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
||||
import SmartToyIcon from '@/material-icons/400-24px/smart_toy.svg?react';
|
||||
|
||||
|
||||
export const Badge = ({ icon = <PersonIcon />, label, domain, roleId }) => (
|
||||
<div className='account-role' data-account-role-id={roleId}>
|
||||
{icon}
|
||||
{label}
|
||||
{domain && <span className='account-role__domain'>{domain}</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
Badge.propTypes = {
|
||||
icon: PropTypes.node,
|
||||
label: PropTypes.node,
|
||||
domain: PropTypes.node,
|
||||
roleId: PropTypes.string
|
||||
};
|
||||
|
||||
export const GroupBadge = () => (
|
||||
<Badge icon={<GroupsIcon />} label={<FormattedMessage id='account.badges.group' defaultMessage='Group' />} />
|
||||
);
|
||||
|
||||
export const AutomatedBadge = () => (
|
||||
<Badge icon={<SmartToyIcon />} label={<FormattedMessage id='account.badges.bot' defaultMessage='Automated' />} />
|
||||
);
|
||||
46
app/javascript/mastodon/components/badge.tsx
Normal file
46
app/javascript/mastodon/components/badge.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { FC, ReactNode } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import GroupsIcon from '@/material-icons/400-24px/group.svg?react';
|
||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
||||
import SmartToyIcon from '@/material-icons/400-24px/smart_toy.svg?react';
|
||||
|
||||
export const Badge: FC<{
|
||||
label: ReactNode;
|
||||
icon?: ReactNode;
|
||||
className?: string;
|
||||
domain?: ReactNode;
|
||||
roleId?: string;
|
||||
}> = ({ icon = <PersonIcon />, label, className, domain, roleId }) => (
|
||||
<div
|
||||
className={classNames('account-role', className)}
|
||||
data-account-role-id={roleId}
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
{domain && <span className='account-role__domain'>{domain}</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const GroupBadge: FC<{ className?: string }> = ({ className }) => (
|
||||
<Badge
|
||||
icon={<GroupsIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.badges.group' defaultMessage='Group' />
|
||||
}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
|
||||
export const AutomatedBadge: FC<{ className?: string }> = ({ className }) => (
|
||||
<Badge
|
||||
icon={<SmartToyIcon />}
|
||||
label={
|
||||
<FormattedMessage id='account.badges.bot' defaultMessage='Automated' />
|
||||
}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
@@ -10,7 +10,9 @@ import type { MiniCardProps } from '.';
|
||||
import classes from './styles.module.css';
|
||||
|
||||
interface MiniCardListProps {
|
||||
cards?: (Pick<MiniCardProps, 'label' | 'value'> & { key?: Key })[];
|
||||
cards?: (Pick<MiniCardProps, 'label' | 'value' | 'className'> & {
|
||||
key?: Key;
|
||||
})[];
|
||||
className?: string;
|
||||
onOverflowClick?: MouseEventHandler;
|
||||
}
|
||||
@@ -42,6 +44,7 @@ export const MiniCardList: FC<MiniCardListProps> = ({
|
||||
label={card.label}
|
||||
value={card.value}
|
||||
hidden={hasOverflow && index >= hiddenIndex}
|
||||
className={card.className}
|
||||
/>
|
||||
))}
|
||||
</dl>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import type { FC } from 'react';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
|
||||
import IconAdmin from '@/images/icons/icon_admin.svg?react';
|
||||
import { AutomatedBadge, Badge, GroupBadge } from '@/mastodon/components/badge';
|
||||
import { Icon } from '@/mastodon/components/icon';
|
||||
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||
import type { AccountRole } from '@/mastodon/models/account';
|
||||
import { useAppSelector } from '@/mastodon/store';
|
||||
|
||||
import { isRedesignEnabled } from '../common';
|
||||
|
||||
import classes from './redesign.module.scss';
|
||||
|
||||
export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
||||
const account = useAccount(accountId);
|
||||
const localDomain = useAppSelector(
|
||||
@@ -15,22 +22,32 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const className = isRedesignEnabled() ? classes.badge : '';
|
||||
|
||||
if (account.bot) {
|
||||
badges.push(<AutomatedBadge key='bot-badge' />);
|
||||
badges.push(<AutomatedBadge key='bot-badge' className={className} />);
|
||||
} else if (account.group) {
|
||||
badges.push(<GroupBadge key='group-badge' />);
|
||||
badges.push(<GroupBadge key='group-badge' className={className} />);
|
||||
}
|
||||
|
||||
const domain = account.acct.includes('@')
|
||||
? account.acct.split('@')[1]
|
||||
: localDomain;
|
||||
account.roles.forEach((role) => {
|
||||
let icon: ReactNode = undefined;
|
||||
if (isAdminBadge(role)) {
|
||||
icon = (
|
||||
<Icon icon={IconAdmin} id='badge-admin' className={classes.badgeIcon} />
|
||||
);
|
||||
}
|
||||
badges.push(
|
||||
<Badge
|
||||
key={`role-badge-${role.get('id')}`}
|
||||
label={<span>{role.get('name')}</span>}
|
||||
domain={domain}
|
||||
roleId={role.get('id')}
|
||||
key={role.id}
|
||||
label={role.name}
|
||||
className={className}
|
||||
domain={isRedesignEnabled() ? `(${domain})` : domain}
|
||||
roleId={role.id}
|
||||
icon={icon}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
@@ -39,5 +56,10 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <div className='account__header__badges'>{badges}</div>;
|
||||
return <div className={'account__header__badges'}>{badges}</div>;
|
||||
};
|
||||
|
||||
function isAdminBadge(role: AccountRole) {
|
||||
const name = role.name.toLowerCase();
|
||||
return isRedesignEnabled() && (name === 'admin' || name === 'owner');
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ import type { FC } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import IconVerified from '@/images/icons/icon_verified.svg?react';
|
||||
import { openModal } from '@/mastodon/actions/modal';
|
||||
import { AccountFields } from '@/mastodon/components/account_fields';
|
||||
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||
import { FormattedDateWrapper } from '@/mastodon/components/formatted_date';
|
||||
import { Icon } from '@/mastodon/components/icon';
|
||||
import { MiniCardList } from '@/mastodon/components/mini_card/list';
|
||||
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
|
||||
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||
@@ -55,25 +59,40 @@ const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => {
|
||||
const htmlHandlers = useElementHandledLink();
|
||||
const cards = useMemo(
|
||||
() =>
|
||||
account.fields.toArray().map(({ value_emojified, name_emojified }) => ({
|
||||
label: (
|
||||
<EmojiHTML
|
||||
htmlString={name_emojified}
|
||||
extraEmojis={account.emojis}
|
||||
className='translate'
|
||||
as='span'
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
),
|
||||
value: (
|
||||
<EmojiHTML
|
||||
as='span'
|
||||
htmlString={value_emojified}
|
||||
extraEmojis={account.emojis}
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
),
|
||||
})),
|
||||
account.fields
|
||||
.toArray()
|
||||
.map(({ value_emojified, name_emojified, verified_at }) => ({
|
||||
label: (
|
||||
<>
|
||||
<EmojiHTML
|
||||
htmlString={name_emojified}
|
||||
extraEmojis={account.emojis}
|
||||
className='translate'
|
||||
as='span'
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
{!!verified_at && (
|
||||
<Icon
|
||||
id='verified'
|
||||
icon={IconVerified}
|
||||
className={classes.fieldIconVerified}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
value: (
|
||||
<EmojiHTML
|
||||
as='span'
|
||||
htmlString={value_emojified}
|
||||
extraEmojis={account.emojis}
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
),
|
||||
className: classNames(
|
||||
classes.fieldCard,
|
||||
!!verified_at && classes.fieldCardVerified,
|
||||
),
|
||||
})),
|
||||
[account.emojis, account.fields, htmlHandlers],
|
||||
);
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ import type { FC } from 'react';
|
||||
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import IconVerified from '@/images/icons/icon_verified.svg?react';
|
||||
import { DisplayName } from '@/mastodon/components/display_name';
|
||||
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
|
||||
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||
import { Icon } from '@/mastodon/components/icon';
|
||||
import { IconButton } from '@/mastodon/components/icon_button';
|
||||
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
|
||||
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
|
||||
@@ -56,7 +58,10 @@ export const AccountFieldsModal: FC<{
|
||||
<AnimateEmojiProvider>
|
||||
<dl className={classes.modalFieldsList}>
|
||||
{account.fields.map((field, index) => (
|
||||
<div key={index} className={classes.modalFieldItem}>
|
||||
<div
|
||||
key={index}
|
||||
className={`${classes.modalFieldItem} ${classes.fieldCard}`}
|
||||
>
|
||||
<EmojiHTML
|
||||
as='dt'
|
||||
htmlString={field.name_emojified}
|
||||
@@ -64,12 +69,21 @@ export const AccountFieldsModal: FC<{
|
||||
className='translate'
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
<EmojiHTML
|
||||
as='dd'
|
||||
htmlString={field.value_emojified}
|
||||
extraEmojis={account.emojis}
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
<dd>
|
||||
<EmojiHTML
|
||||
as='span'
|
||||
htmlString={field.value_emojified}
|
||||
extraEmojis={account.emojis}
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
{!!field.verified_at && (
|
||||
<Icon
|
||||
id='verified'
|
||||
icon={IconVerified}
|
||||
className={classes.fieldIconVerified}
|
||||
/>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
|
||||
@@ -39,10 +39,64 @@ h1.name > small {
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: none;
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 600;
|
||||
|
||||
> span {
|
||||
font-weight: unset;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
svg.badgeIcon {
|
||||
opacity: 1;
|
||||
fill: revert-layer;
|
||||
|
||||
path {
|
||||
fill: revert-layer;
|
||||
}
|
||||
}
|
||||
|
||||
.fieldList {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.fieldCard {
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
color: var(--color-text-brand);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fieldCardVerified {
|
||||
background-color: var(--color-bg-brand-softer);
|
||||
|
||||
dt {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.fieldIconVerified {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.fieldIconVerified {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
// Need to override .icon path.
|
||||
path {
|
||||
fill: revert-layer;
|
||||
}
|
||||
}
|
||||
|
||||
.fieldNumbersWrapper {
|
||||
a {
|
||||
font-weight: unset;
|
||||
@@ -85,4 +139,9 @@ h1.name > small {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.fieldIconVerified {
|
||||
vertical-align: middle;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"account.go_to_profile": "Ir al perfil",
|
||||
"account.hide_reblogs": "Ocultar impulsos de @{name}",
|
||||
"account.in_memoriam": "Cuenta conmemorativa.",
|
||||
"account.joined_long": "Se unió el {date}",
|
||||
"account.joined_short": "Se unió",
|
||||
"account.languages": "Cambiar idiomas suscritos",
|
||||
"account.link_verified_on": "La propiedad de este enlace fue verificada el {date}",
|
||||
@@ -90,6 +91,8 @@
|
||||
"account.unmute": "Dejar de silenciar a @{name}",
|
||||
"account.unmute_notifications_short": "Dejar de silenciar notificaciones",
|
||||
"account.unmute_short": "Dejar de silenciar",
|
||||
"account_fields_modal.close": "Cerrar",
|
||||
"account_fields_modal.title": "Información de {name}",
|
||||
"account_note.placeholder": "Haz clic para añadir nota",
|
||||
"admin.dashboard.daily_retention": "Tasa de retención de usuarios por día después del registro",
|
||||
"admin.dashboard.monthly_retention": "Tasa de retención de usuarios por mes después del registro",
|
||||
@@ -589,6 +592,7 @@
|
||||
"load_pending": "{count, plural, one {# nuevo elemento} other {# nuevos elementos}}",
|
||||
"loading_indicator.label": "Cargando…",
|
||||
"media_gallery.hide": "Ocultar",
|
||||
"minicard.more_items": "+{count}",
|
||||
"moved_to_account_banner.text": "Tu cuenta {disabledAccount} está actualmente deshabilitada porque te has mudado a {movedToAccount}.",
|
||||
"mute_modal.hide_from_notifications": "Ocultar de las notificaciones",
|
||||
"mute_modal.hide_options": "Ocultar opciones",
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
var(--border-strength-primary)
|
||||
)};
|
||||
--color-border-media: rgb(252 248 255 / 15%);
|
||||
--color-border-verified: rgb(220, 3, 240);
|
||||
--color-border-on-bg-secondary: #{utils.css-alpha(
|
||||
var(--color-indigo-200),
|
||||
calc(var(--border-strength-primary) / 1.5)
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
var(--color-grey-950) var(--border-strength-primary)
|
||||
);
|
||||
--color-border-media: rgb(252 248 255 / 15%);
|
||||
--color-border-verified: rgb(220, 3, 240);
|
||||
--color-border-on-bg-secondary: var(--color-grey-200);
|
||||
--color-border-on-bg-brand-softer: var(--color-indigo-200);
|
||||
--color-border-on-bg-error-softer: #{utils.css-alpha(
|
||||
|
||||
@@ -379,6 +379,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
def conversation_from_uri(uri)
|
||||
return nil if uri.nil?
|
||||
return Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if OStatus::TagManager.instance.local_id?(uri)
|
||||
return ActivityPub::TagManager.instance.uri_to_resource(uri, Conversation) if ActivityPub::TagManager.instance.local_uri?(uri)
|
||||
|
||||
begin
|
||||
Conversation.find_or_create_by!(uri: uri)
|
||||
|
||||
@@ -243,12 +243,6 @@ class ActivityPub::TagManager
|
||||
!host.nil? && (::TagManager.instance.local_domain?(host) || ::TagManager.instance.web_domain?(host))
|
||||
end
|
||||
|
||||
def uri_to_local_id(uri, param = :id)
|
||||
path_params = Rails.application.routes.recognize_path(uri)
|
||||
path_params[:username] = Rails.configuration.x.local_domain if path_params[:controller] == 'instance_actors'
|
||||
path_params[param]
|
||||
end
|
||||
|
||||
def uris_to_local_accounts(uris)
|
||||
usernames = []
|
||||
ids = []
|
||||
@@ -266,6 +260,14 @@ class ActivityPub::TagManager
|
||||
uri_to_resource(uri, Account)
|
||||
end
|
||||
|
||||
def uri_to_local_conversation(uri)
|
||||
path_params = Rails.application.routes.recognize_path(uri)
|
||||
return unless path_params[:controller] == 'activitypub/contexts'
|
||||
|
||||
account_id, conversation_id = path_params[:id].split('-')
|
||||
Conversation.find_by(parent_account_id: account_id, id: conversation_id)
|
||||
end
|
||||
|
||||
def uri_to_resource(uri, klass)
|
||||
return if uri.nil?
|
||||
|
||||
@@ -273,6 +275,8 @@ class ActivityPub::TagManager
|
||||
case klass.name
|
||||
when 'Account'
|
||||
uris_to_local_accounts([uri]).first
|
||||
when 'Conversation'
|
||||
uri_to_local_conversation(uri)
|
||||
else
|
||||
StatusFinder.new(uri).status
|
||||
end
|
||||
|
||||
@@ -11,16 +11,12 @@ class OStatus::TagManager
|
||||
def unique_tag_to_local_id(tag, expected_type)
|
||||
return nil unless local_id?(tag)
|
||||
|
||||
if ActivityPub::TagManager.instance.local_uri?(tag)
|
||||
ActivityPub::TagManager.instance.uri_to_local_id(tag)
|
||||
else
|
||||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag)
|
||||
matches[1] unless matches.nil?
|
||||
end
|
||||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag)
|
||||
matches[1] unless matches.nil?
|
||||
end
|
||||
|
||||
def local_id?(id)
|
||||
id.start_with?("tag:#{Rails.configuration.x.local_domain}") || ActivityPub::TagManager.instance.local_uri?(id)
|
||||
id.start_with?("tag:#{Rails.configuration.x.local_domain}")
|
||||
end
|
||||
|
||||
def uri_for(target)
|
||||
|
||||
@@ -18,7 +18,7 @@ class TagManager
|
||||
return if domain.nil?
|
||||
|
||||
uri = Addressable::URI.new
|
||||
uri.host = domain.delete_suffix('/')
|
||||
uri.host = domain.strip.delete_suffix('/')
|
||||
uri.normalized_host
|
||||
end
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ class Collection < ApplicationRecord
|
||||
|
||||
scope :with_items, -> { includes(:collection_items).merge(CollectionItem.with_accounts) }
|
||||
scope :with_tag, -> { includes(:tag) }
|
||||
scope :discoverable, -> { where(discoverable: true) }
|
||||
|
||||
def remote?
|
||||
!local?
|
||||
|
||||
@@ -156,7 +156,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def serialized_account(account)
|
||||
ActiveModelSerializers::SerializableResource.new(account, serializer: REST::AccountSerializer)
|
||||
ActiveModelSerializers::SerializableResource.new(account, serializer: REST::AccountSerializer, scope_name: :current_user, scope: object.current_account&.user)
|
||||
end
|
||||
|
||||
def instance_presenter
|
||||
|
||||
@@ -71,6 +71,9 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
||||
accounts: {
|
||||
max_featured_tags: FeaturedTag::LIMIT,
|
||||
max_pinned_statuses: StatusPinValidator::PIN_LIMIT,
|
||||
max_profile_fields: Account::DEFAULT_FIELDS_SIZE,
|
||||
profile_field_name_limit: Account::Field::MAX_CHARACTERS_LOCAL,
|
||||
profile_field_value_limit: Account::Field::MAX_CHARACTERS_LOCAL,
|
||||
},
|
||||
|
||||
statuses: {
|
||||
|
||||
@@ -2162,6 +2162,7 @@ ca:
|
||||
error: Hi ha hagut un problema al esborrar la teva clau de seguretat. Tornau-ho a provar.
|
||||
success: La teva clau de seguretat s'ha esborrat correctament.
|
||||
invalid_credential: Clau de seguretat invàlida
|
||||
nickname: Sobrenom
|
||||
nickname_hint: Introdueix el sobrenom de la teva clau de seguretat nova
|
||||
not_enabled: Encara no has activat WebAuthn
|
||||
not_supported: Aquest navegador no suporta claus de seguretat
|
||||
|
||||
@@ -175,7 +175,7 @@ de:
|
||||
labels:
|
||||
account:
|
||||
attribution_domains: Websites, die auf dich verweisen dürfen
|
||||
discoverable: Profil und Beiträge in Suchalgorithmen berücksichtigen
|
||||
discoverable: Profil und Beiträge in Empfehlungsalgorithmen berücksichtigen
|
||||
fields:
|
||||
name: Beschriftung
|
||||
value: Inhalt
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@csstools/stylelint-formatter-github": "^1.0.0",
|
||||
"@csstools/stylelint-formatter-github": "^2.0.0",
|
||||
"@dnd-kit/core": "^6.1.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
@@ -188,7 +188,7 @@
|
||||
"storybook": "^10.0.5",
|
||||
"stylelint": "^16.19.1",
|
||||
"stylelint-config-prettier-scss": "^1.0.0",
|
||||
"stylelint-config-standard-scss": "^16.0.0",
|
||||
"stylelint-config-standard-scss": "^17.0.0",
|
||||
"typescript": "~5.9.0",
|
||||
"typescript-eslint": "^8.45.0",
|
||||
"typescript-plugin-css-modules": "^5.2.0",
|
||||
|
||||
@@ -521,7 +521,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a reply' do
|
||||
context 'with a reply without explicitly setting a conversation' do
|
||||
let(:original_status) { Fabricate(:status) }
|
||||
|
||||
let(:object_json) do
|
||||
@@ -543,6 +543,30 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a reply explicitly setting a conversation' do
|
||||
let(:original_status) { Fabricate(:status) }
|
||||
|
||||
let(:object_json) do
|
||||
build_object(
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
conversation: ActivityPub::TagManager.instance.uri_for(original_status.conversation),
|
||||
context: ActivityPub::TagManager.instance.uri_for(original_status.conversation)
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
expect { subject.perform }.to change(sender.statuses, :count).by(1)
|
||||
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.thread).to eq original_status
|
||||
expect(status.reply?).to be true
|
||||
expect(status.in_reply_to_account).to eq original_status.account
|
||||
expect(status.conversation).to eq original_status.conversation
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mentions' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
|
||||
@@ -629,14 +629,6 @@ RSpec.describe ActivityPub::TagManager do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uri_to_local_id' do
|
||||
let(:account) { Fabricate(:account, id_scheme: :username_ap_id) }
|
||||
|
||||
it 'returns the local ID' do
|
||||
expect(subject.uri_to_local_id(subject.uri_for(account), :username)).to eq account.username
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uris_to_local_accounts' do
|
||||
it 'returns the expected local accounts' do
|
||||
account = Fabricate(:account)
|
||||
|
||||
@@ -54,12 +54,44 @@ RSpec.describe TagManager do
|
||||
end
|
||||
|
||||
describe '#normalize_domain' do
|
||||
it 'returns nil if the given parameter is nil' do
|
||||
expect(described_class.instance.normalize_domain(nil)).to be_nil
|
||||
subject { described_class.instance.normalize_domain(domain) }
|
||||
|
||||
context 'with a nil value' do
|
||||
let(:domain) { nil }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
it 'returns normalized domain' do
|
||||
expect(described_class.instance.normalize_domain('DoMaIn.Example.com/')).to eq 'domain.example.com'
|
||||
context 'with a blank value' do
|
||||
let(:domain) { '' }
|
||||
|
||||
it { is_expected.to be_blank }
|
||||
end
|
||||
|
||||
context 'with a mixed case string' do
|
||||
let(:domain) { 'DoMaIn.Example.com' }
|
||||
|
||||
it { is_expected.to eq('domain.example.com') }
|
||||
end
|
||||
|
||||
context 'with a trailing slash string' do
|
||||
let(:domain) { 'domain.example.com/' }
|
||||
|
||||
it { is_expected.to eq('domain.example.com') }
|
||||
end
|
||||
|
||||
context 'with a space padded string' do
|
||||
let(:domain) { ' domain.example.com ' }
|
||||
|
||||
it { is_expected.to eq('domain.example.com') }
|
||||
end
|
||||
|
||||
context 'with an invalid domain string' do
|
||||
let(:domain) { ' !@#$@#$@$@# ' }
|
||||
|
||||
it 'raises invalid uri error' do
|
||||
expect { subject }.to raise_error(Addressable::URI::InvalidURIError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ require 'rails_helper'
|
||||
RSpec.describe InstanceModerationNote do
|
||||
describe 'chronological' do
|
||||
it 'returns the instance notes sorted by oldest first' do
|
||||
instance = Instance.find_or_initialize_by(domain: TagManager.instance.normalize_domain('mastodon.example'))
|
||||
instance = Instance.find_or_initialize_by(domain: 'mastodon.example')
|
||||
|
||||
note1 = Fabricate(:instance_moderation_note, domain: instance.domain)
|
||||
note2 = Fabricate(:instance_moderation_note, domain: instance.domain)
|
||||
|
||||
@@ -55,6 +55,32 @@ RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when some collections are not discoverable' do
|
||||
before do
|
||||
Fabricate(:collection, account:, discoverable: false)
|
||||
end
|
||||
|
||||
context 'when requesting user is a third party' do
|
||||
it 'hides the collections that are not discoverable' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.parsed_body.size).to eq 3
|
||||
end
|
||||
end
|
||||
|
||||
context 'when requesting user owns the collection' do
|
||||
let(:account) { user.account }
|
||||
|
||||
it 'returns all collections, including the ones that are not discoverable' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.parsed_body.size).to eq 4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1_alpha/collections/:id' do
|
||||
|
||||
107
yarn.lock
107
yarn.lock
@@ -1926,12 +1926,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@csstools/stylelint-formatter-github@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@csstools/stylelint-formatter-github@npm:1.0.0"
|
||||
"@csstools/stylelint-formatter-github@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@csstools/stylelint-formatter-github@npm:2.0.0"
|
||||
peerDependencies:
|
||||
stylelint: ^16.6.0
|
||||
checksum: 10c0/2052c4e4d89656b2b4176a6d07508ef73278d33c24a7408a3555d07f26ec853f85da95525590c51751fb3150a2ebb5e3083d8200dc6597af2cd8e93198695269
|
||||
stylelint: ^17.0.0
|
||||
checksum: 10c0/1eddcb749eb93efff2e2d7edb4405459bf558ceaa6d90e792408802f30c55e3482a4cead9e69fd651f04a927e863782fc6cf813c37433da9ff1f068910080a06
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2861,7 +2861,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@mastodon/mastodon@workspace:."
|
||||
dependencies:
|
||||
"@csstools/stylelint-formatter-github": "npm:^1.0.0"
|
||||
"@csstools/stylelint-formatter-github": "npm:^2.0.0"
|
||||
"@dnd-kit/core": "npm:^6.1.0"
|
||||
"@dnd-kit/sortable": "npm:^10.0.0"
|
||||
"@dnd-kit/utilities": "npm:^3.2.2"
|
||||
@@ -2992,7 +2992,7 @@ __metadata:
|
||||
stringz: "npm:^2.1.0"
|
||||
stylelint: "npm:^16.19.1"
|
||||
stylelint-config-prettier-scss: "npm:^1.0.0"
|
||||
stylelint-config-standard-scss: "npm:^16.0.0"
|
||||
stylelint-config-standard-scss: "npm:^17.0.0"
|
||||
substring-trie: "npm:^1.0.2"
|
||||
tesseract.js: "npm:^7.0.0"
|
||||
tiny-queue: "npm:^0.2.1"
|
||||
@@ -9327,13 +9327,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"known-css-properties@npm:^0.36.0":
|
||||
version: 0.36.0
|
||||
resolution: "known-css-properties@npm:0.36.0"
|
||||
checksum: 10c0/098c8f956408a7ce26a639c2354e0184fb2bb2772bb7d1ba23192b6b6cf5818cbb8a0acfb4049705ea103d9916065703bc540fa084a6349fdb41bf745aada4bc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"known-css-properties@npm:^0.37.0":
|
||||
version: 0.37.0
|
||||
resolution: "known-css-properties@npm:0.37.0"
|
||||
@@ -9695,10 +9688,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdn-data@npm:^2.21.0":
|
||||
version: 2.21.0
|
||||
resolution: "mdn-data@npm:2.21.0"
|
||||
checksum: 10c0/cd26902551af2cc29f06f130893cb04bca9ee278939fce3ffbcb759497cc80d53a6f4abdef2ae2f3ed3c95ac8d651f53fc141defd580ebf4ae2f93aea325957b
|
||||
"mdn-data@npm:^2.25.0":
|
||||
version: 2.26.0
|
||||
resolution: "mdn-data@npm:2.26.0"
|
||||
checksum: 10c0/e5f17f4dac247f3e260c081761628d371e23659a7ff13413f83f5bd7fd0f2d8317e72277bb77f0e13115041334ff728a5363db64aabaf376c0e1b0b31016d0b8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10697,8 +10690,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"pino@npm:^10.0.0":
|
||||
version: 10.2.0
|
||||
resolution: "pino@npm:10.2.0"
|
||||
version: 10.2.1
|
||||
resolution: "pino@npm:10.2.1"
|
||||
dependencies:
|
||||
"@pinojs/redact": "npm:^0.4.0"
|
||||
atomic-sleep: "npm:^1.0.0"
|
||||
@@ -10713,7 +10706,7 @@ __metadata:
|
||||
thread-stream: "npm:^4.0.0"
|
||||
bin:
|
||||
pino: bin.js
|
||||
checksum: 10c0/8f88a2e205508d47ef04d2a6ec26ec450abb4b344d2d998d2e24b9e624e1a1ef7184f260ca5be06bc3733aa1ad76704657e373b359c7b71489a11709227e26da
|
||||
checksum: 10c0/2eaed48bb7fb8865e27ac6d6709383f5c117f1e59c818734c7cc22b362e9aa5846a0547e7fd9cde64088a3b48aa314e1dab07ee16da8dc3b87897970eb56843e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -13201,74 +13194,74 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylelint-config-recommended-scss@npm:^16.0.1":
|
||||
version: 16.0.2
|
||||
resolution: "stylelint-config-recommended-scss@npm:16.0.2"
|
||||
"stylelint-config-recommended-scss@npm:^17.0.0":
|
||||
version: 17.0.0
|
||||
resolution: "stylelint-config-recommended-scss@npm:17.0.0"
|
||||
dependencies:
|
||||
postcss-scss: "npm:^4.0.9"
|
||||
stylelint-config-recommended: "npm:^17.0.0"
|
||||
stylelint-scss: "npm:^6.12.1"
|
||||
stylelint-config-recommended: "npm:^18.0.0"
|
||||
stylelint-scss: "npm:^7.0.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.3.3
|
||||
stylelint: ^16.24.0
|
||||
stylelint: ^17.0.0
|
||||
peerDependenciesMeta:
|
||||
postcss:
|
||||
optional: true
|
||||
checksum: 10c0/d4e30a881e248d8b039347bf967526f6afe6d6a07f18e2747e14568de32273e819ba478be7a61a0dd63178931b4e891050a34e73d296ab533aa434209a7f3146
|
||||
checksum: 10c0/05b2e8d4316c2a8cc66eed0a2a8f01237e0ee8966a2e73d0b3c6706694f7630be165daa5a0cef511bc51f7e3fcb07a84c55d948c15fe6193a7e13cf9bb67c913
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylelint-config-recommended@npm:^17.0.0":
|
||||
"stylelint-config-recommended@npm:^18.0.0":
|
||||
version: 18.0.0
|
||||
resolution: "stylelint-config-recommended@npm:18.0.0"
|
||||
peerDependencies:
|
||||
stylelint: ^17.0.0
|
||||
checksum: 10c0/c7f8ff45c76ec23f4c8c0438894726976fd5e872c59d489f959b728d9879bba20dbf0040cd29ad3bbc00eb32befd95f5b6ca150002bb8aea74b0797bc42ccc17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylelint-config-standard-scss@npm:^17.0.0":
|
||||
version: 17.0.0
|
||||
resolution: "stylelint-config-recommended@npm:17.0.0"
|
||||
peerDependencies:
|
||||
stylelint: ^16.23.0
|
||||
checksum: 10c0/49e5d1c0f58197b2c5585b85fad814fed9bdec44c9870368c46a762664c5ff158c1145b6337456ae194409d692992b5b87421d62880422f71d8a3360417f5ad1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylelint-config-standard-scss@npm:^16.0.0":
|
||||
version: 16.0.0
|
||||
resolution: "stylelint-config-standard-scss@npm:16.0.0"
|
||||
resolution: "stylelint-config-standard-scss@npm:17.0.0"
|
||||
dependencies:
|
||||
stylelint-config-recommended-scss: "npm:^16.0.1"
|
||||
stylelint-config-standard: "npm:^39.0.0"
|
||||
stylelint-config-recommended-scss: "npm:^17.0.0"
|
||||
stylelint-config-standard: "npm:^40.0.0"
|
||||
peerDependencies:
|
||||
postcss: ^8.3.3
|
||||
stylelint: ^16.23.1
|
||||
stylelint: ^17.0.0
|
||||
peerDependenciesMeta:
|
||||
postcss:
|
||||
optional: true
|
||||
checksum: 10c0/eb77f23824c5d649b193cb71d7f9b538b32b8cc1769451b2993270361127243d4011baf891ec265711b8e34e69ce28acb57ab6c3947b51fa3713ac26f4276439
|
||||
checksum: 10c0/0506537ba896f3d5e0fb002608090fcb41aa8ba7b65f1de8533702ce7c70e3f92b275782788a8356b5b687c86c53468c223e082226dda62780294b1cba324a36
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylelint-config-standard@npm:^39.0.0":
|
||||
version: 39.0.1
|
||||
resolution: "stylelint-config-standard@npm:39.0.1"
|
||||
"stylelint-config-standard@npm:^40.0.0":
|
||||
version: 40.0.0
|
||||
resolution: "stylelint-config-standard@npm:40.0.0"
|
||||
dependencies:
|
||||
stylelint-config-recommended: "npm:^17.0.0"
|
||||
stylelint-config-recommended: "npm:^18.0.0"
|
||||
peerDependencies:
|
||||
stylelint: ^16.23.0
|
||||
checksum: 10c0/70a9862a2cedcc2a1807bd92fc91c40877270cf8a39576b91ae056d6de51d3b68104b26f71056ff22461b4319e9ec988d009abf10ead513b2ec15569d82e865a
|
||||
stylelint: ^17.0.0
|
||||
checksum: 10c0/d8942552d53a3afda59b64d0c49503bb626fe5cef39a9e8c9583fcd60869f21431125ef4480ff27a59f7f2cf0da8af810d377129ef1d670ddc5def4defe2880c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylelint-scss@npm:^6.12.1":
|
||||
version: 6.12.1
|
||||
resolution: "stylelint-scss@npm:6.12.1"
|
||||
"stylelint-scss@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "stylelint-scss@npm:7.0.0"
|
||||
dependencies:
|
||||
css-tree: "npm:^3.0.1"
|
||||
is-plain-object: "npm:^5.0.0"
|
||||
known-css-properties: "npm:^0.36.0"
|
||||
mdn-data: "npm:^2.21.0"
|
||||
known-css-properties: "npm:^0.37.0"
|
||||
mdn-data: "npm:^2.25.0"
|
||||
postcss-media-query-parser: "npm:^0.2.3"
|
||||
postcss-resolve-nested-selector: "npm:^0.1.6"
|
||||
postcss-selector-parser: "npm:^7.1.0"
|
||||
postcss-selector-parser: "npm:^7.1.1"
|
||||
postcss-value-parser: "npm:^4.2.0"
|
||||
peerDependencies:
|
||||
stylelint: ^16.0.2
|
||||
checksum: 10c0/9a0903d34be3c75a72bef32402899db5f6b94c0823c5944fdf1acb2c3dc61c1f70fbb322558f8cb7e42dd01ed5e0dec22ed298f03b7bacc9f467c28330acae71
|
||||
stylelint: ^16.8.2 || ^17.0.0
|
||||
checksum: 10c0/07d0f20c6bcb34b8b0b6bfb1d4367b4825b52a7eef7dde2adfbaec11ebc67242e6b99dccf70dfbef1eb0a9bf8712fe0ab49d183ff6e4cca9c7f89752f7e27027
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user