diff --git a/Gemfile.lock b/Gemfile.lock index e92684c50d..e8dbaad831 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -864,7 +864,7 @@ GEM unicode-display_width (>= 1.1.1, < 4) terrapin (1.1.1) climate_control - test-prof (1.6.0) + test-prof (1.6.1) thor (1.5.0) tilt (2.7.0) timeout (0.6.1) diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index 0590ea4027..2aa83717c3 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -24,12 +24,7 @@ class MediaController < ApplicationController private def set_media_attachment - id = params[:id] || params[:medium_id] - return if id.nil? - - scope = MediaAttachment.local.attached - # If id is 19 characters long, it's a shortcode, otherwise it's an identifier - @media_attachment = id.size == 19 ? scope.find_by!(shortcode: id) : scope.find(id) + @media_attachment = MediaAttachment.local.attached.identified(params[:id]) end def verify_permitted_status! diff --git a/app/javascript/mastodon/features/account_edit/components/tag_search.tsx b/app/javascript/mastodon/features/account_edit/components/tag_search.tsx index 3b423c0735..2dd17c2a72 100644 --- a/app/javascript/mastodon/features/account_edit/components/tag_search.tsx +++ b/app/javascript/mastodon/features/account_edit/components/tag_search.tsx @@ -65,7 +65,7 @@ export const AccountEditTagSearch: FC = () => { value={query} onChange={handleSearchChange} placeholder={inputLabel} - items={suggestedTags as TagSearchResult[]} + items={suggestedTags} isLoading={isLoading} renderItem={renderItem} onSelectItem={handleSelect} diff --git a/app/javascript/mastodon/features/account_edit/index.tsx b/app/javascript/mastodon/features/account_edit/index.tsx index 2e56a1b878..a119e8e61d 100644 --- a/app/javascript/mastodon/features/account_edit/index.tsx +++ b/app/javascript/mastodon/features/account_edit/index.tsx @@ -13,11 +13,15 @@ import { Button } from '@/mastodon/components/button'; import { DismissibleCallout } from '@/mastodon/components/callout/dismissible'; import { CustomEmojiProvider } from '@/mastodon/components/emoji/context'; import { EmojiHTML } from '@/mastodon/components/emoji/html'; +import { ToggleField } from '@/mastodon/components/form_fields'; import { useElementHandledLink } from '@/mastodon/components/status/handled_link'; import { useAccount } from '@/mastodon/hooks/useAccount'; import { useCurrentAccountId } from '@/mastodon/hooks/useAccountId'; import { autoPlayGif } from '@/mastodon/initial_state'; -import { fetchProfile } from '@/mastodon/reducers/slices/profile_edit'; +import { + fetchProfile, + patchProfile, +} from '@/mastodon/reducers/slices/profile_edit'; import { useAppDispatch, useAppSelector } from '@/mastodon/store'; import { AccountEditColumn, AccountEditEmptyColumn } from './components/column'; @@ -108,6 +112,10 @@ export const messages = defineMessages({ id: 'account_edit.profile_tab.subtitle', defaultMessage: 'Customize the tabs on your profile and what they display.', }, + advancedSettingsTitle: { + id: 'account_edit.advanced_settings.title', + defaultMessage: 'Advanced settings', + }, }); export const AccountEdit: FC = () => { @@ -117,7 +125,7 @@ export const AccountEdit: FC = () => { const dispatch = useAppDispatch(); - const { profile } = useAppSelector((state) => state.profileEdit); + const { profile, isPending } = useAppSelector((state) => state.profileEdit); useEffect(() => { void dispatch(fetchProfile()); }, [dispatch]); @@ -162,6 +170,10 @@ export const AccountEdit: FC = () => { history.push('/profile/featured_tags'); }, [history]); + const handleBotToggle = useCallback(() => { + void dispatch(patchProfile({ bot: !profile?.bot })); + }, [dispatch, profile?.bot]); + // Normally we would use the account emoji, but we want all custom emojis to be available to render after editing. const emojis = useAppSelector((state) => state.custom_emojis); const htmlHandlers = useElementHandledLink({ @@ -327,6 +339,26 @@ export const AccountEdit: FC = () => { } /> + + + + } + hint={ + + } + /> + ); diff --git a/app/javascript/mastodon/features/account_timeline/components/account_name.tsx b/app/javascript/mastodon/features/account_timeline/components/account_name.tsx index c90f5012de..a0296b5ed9 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_name.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_name.tsx @@ -8,13 +8,16 @@ import classNames from 'classnames'; import Overlay from 'react-overlays/esm/Overlay'; import FollowerIcon from '@/images/icons/icon_follower.svg?react'; +import { showAlert } from '@/mastodon/actions/alerts'; import { Badge } from '@/mastodon/components/badge'; +import { Button } from '@/mastodon/components/button'; import { DisplayName } from '@/mastodon/components/display_name'; import { Icon } from '@/mastodon/components/icon'; import { useAccount } from '@/mastodon/hooks/useAccount'; import { useRelationship } from '@/mastodon/hooks/useRelationship'; -import { useAppSelector } from '@/mastodon/store'; +import { useAppDispatch, useAppSelector } from '@/mastodon/store'; import AtIcon from '@/material-icons/400-24px/alternate_email.svg?react'; +import ContentCopyIcon from '@/material-icons/400-24px/content_copy.svg?react'; import HelpIcon from '@/material-icons/400-24px/help.svg?react'; import DomainIcon from '@/material-icons/400-24px/language.svg?react'; @@ -30,6 +33,10 @@ const messages = defineMessages({ id: 'account.name_info', defaultMessage: 'What does this mean?', }, + copied: { + id: 'copy_icon_button.copied', + defaultMessage: 'Copied to clipboard', + }, }); export const AccountName: FC<{ accountId: string }> = ({ accountId }) => { @@ -64,14 +71,12 @@ export const AccountName: FC<{ accountId: string }> = ({ accountId }) => { /> )} -

- @{username}@{domain} - -

+ + ); }; @@ -90,6 +95,19 @@ const AccountNameHelp: FC<{ setOpen((prev) => !prev); }, []); + const handle = `@${username}@${domain}`; + + const dispatch = useAppDispatch(); + const [copied, setCopied] = useState(false); + const handleCopy = useCallback(() => { + void navigator.clipboard.writeText(handle); + setCopied(true); + dispatch(showAlert({ message: messages.copied })); + setTimeout(() => { + setCopied(false); + }, 700); + }, [handle, dispatch]); + return ( <> )} diff --git a/app/javascript/mastodon/features/account_timeline/components/account_subscription_form.tsx b/app/javascript/mastodon/features/account_timeline/components/account_subscription_form.tsx index d183aa1cb3..0013a4bc14 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_subscription_form.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_subscription_form.tsx @@ -12,13 +12,10 @@ import type { ValidationErrorResponse, ValidationError, } from 'mastodon/api_types/errors'; -import { A11yLiveRegion } from 'mastodon/components/a11y_live_region'; import { Button } from 'mastodon/components/button'; -import { CalloutInline } from 'mastodon/components/callout_inline'; import { DisplayName } from 'mastodon/components/display_name'; import type { FieldStatus } from 'mastodon/components/form_fields'; -import formFieldClasses from 'mastodon/components/form_fields/form_field_wrapper.module.scss'; -import { TextInput } from 'mastodon/components/form_fields/text_input_field'; +import { TextInputField } from 'mastodon/components/form_fields/text_input_field'; import { useAppSelector } from 'mastodon/store'; import classes from './redesign.module.scss'; @@ -34,7 +31,7 @@ const messages = defineMessages({ }, email: { id: 'email_subscriptions.email', - defaultMessage: 'Email address', + defaultMessage: 'Email', }, }); @@ -159,33 +156,19 @@ export const AccountSubscriptionForm: React.FC<{ accountId: string }> = ({ }} /> -
-
- - - - {errors.email && ( - - )} - -
+