diff --git a/app/javascript/flavours/glitch/components/status/intercept_status_clicks.tsx b/app/javascript/flavours/glitch/components/status/intercept_status_clicks.tsx new file mode 100644 index 0000000000..b0dbc3c693 --- /dev/null +++ b/app/javascript/flavours/glitch/components/status/intercept_status_clicks.tsx @@ -0,0 +1,45 @@ +import { useCallback, useRef } from 'react'; + +export const InterceptStatusClicks: React.FC<{ + onPreventedClick: ( + clickedArea: 'account' | 'post', + event: React.MouseEvent, + ) => void; + children: React.ReactNode; +}> = ({ onPreventedClick, children }) => { + const wrapperRef = useRef(null); + + const handleClick = useCallback( + (e: React.MouseEvent) => { + const clickTarget = e.target as Element; + const allowedElementsSelector = + '.video-player, .audio-player, .media-gallery, .content-warning'; + const allowedElements = wrapperRef.current?.querySelectorAll( + allowedElementsSelector, + ); + const isTargetClickAllowed = + allowedElements && + Array.from(allowedElements).some((element) => { + return clickTarget === element || element.contains(clickTarget); + }); + + if (!isTargetClickAllowed) { + e.preventDefault(); + e.stopPropagation(); + + const wasAccountAreaClicked = !!clickTarget.closest( + 'a.status__display-name', + ); + + onPreventedClick(wasAccountAreaClicked ? 'account' : 'post', e); + } + }, + [onPreventedClick], + ); + + return ( +
+ {children} +
+ ); +}; diff --git a/app/javascript/flavours/glitch/features/annual_report/highlighted_post.tsx b/app/javascript/flavours/glitch/features/annual_report/highlighted_post.tsx index 9ca64d40ba..9e03c7e327 100644 --- a/app/javascript/flavours/glitch/features/annual_report/highlighted_post.tsx +++ b/app/javascript/flavours/glitch/features/annual_report/highlighted_post.tsx @@ -4,10 +4,14 @@ @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */ +import type { ComponentPropsWithoutRef } from 'react'; +import { useCallback } from 'react'; + import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; +import { InterceptStatusClicks } from 'flavours/glitch/components/status/intercept_status_clicks'; import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted'; import type { TopStatuses } from 'flavours/glitch/models/annual_report'; import { makeGetStatus } from 'flavours/glitch/selectors'; @@ -29,6 +33,24 @@ export const HighlightedPost: React.FC<{ statusId ? getStatus(state, { id: statusId }) : undefined, ); + const handleClick = useCallback< + ComponentPropsWithoutRef['onPreventedClick'] + >( + (clickedArea) => { + const link: string = + clickedArea === 'account' + ? status.getIn(['account', 'url']) + : status.get('url'); + + if (context === 'standalone') { + window.location.href = link; + } else { + window.open(link, '_blank'); + } + }, + [status, context], + ); + if (!status) { return
; } @@ -72,7 +94,9 @@ export const HighlightedPost: React.FC<{ {context === 'modal' &&

{label}

}
- + + + ); }; diff --git a/app/javascript/flavours/glitch/features/annual_report/index.module.scss b/app/javascript/flavours/glitch/features/annual_report/index.module.scss index 95ebb72729..7471e6282a 100644 --- a/app/javascript/flavours/glitch/features/annual_report/index.module.scss +++ b/app/javascript/flavours/glitch/features/annual_report/index.module.scss @@ -21,7 +21,8 @@ $mobile-breakpoint: 540px; scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary); @media (width < $mobile-breakpoint) { - padding-inline: 10px; + padding-top: 0; + padding-inline: 0; } .loading-indicator .circular-progress { @@ -50,37 +51,51 @@ $mobile-breakpoint: 540px; } .wrapper { + --gradient-strength: 0.4; + + box-sizing: border-box; position: relative; max-width: 600px; padding: 24px; + padding-top: 40px; contain: layout; flex: 0 0 auto; - pointer-events: auto; + pointer-events: all; color: var(--color-text-primary); background: var(--color-bg-primary); background: - radial-gradient(at 40% 87%, #240c9a99 0, transparent 50%), - radial-gradient(at 19% 10%, #6b0c9a99 0, transparent 50%), - radial-gradient(at 90% 27%, #9a0c8299 0, transparent 50%), - radial-gradient(at 16% 95%, #1e948299 0, transparent 50%), - radial-gradient(at 80% 91%, #16dae499 0, transparent 50%) + radial-gradient( + at 10% 27%, + rgba(83, 12, 154, var(--gradient-strength)) 0, + transparent 50% + ), + radial-gradient( + at 91% 10%, + rgba(30, 24, 223, var(--gradient-strength)) 0, + transparent 25% + ), + radial-gradient( + at 10% 91%, + rgba(22, 218, 228, var(--gradient-strength)) 0, + transparent 40% + ), + radial-gradient( + at 75% 87%, + rgba(37, 31, 217, var(--gradient-strength)) 0, + transparent 20% + ), + radial-gradient( + at 84% 60%, + rgba(95, 30, 148, var(--gradient-strength)) 0, + transparent 40% + ) var(--color-bg-primary); border-radius: 40px; @media (width < $mobile-breakpoint) { padding-inline: 12px; padding-bottom: 12px; - border-radius: 28px; - } - - &::after { - content: ''; - position: absolute; - inset: 0; - z-index: -1; - background: inherit; - border-radius: inherit; - filter: blur(20px); + border-radius: 0; } } @@ -92,7 +107,7 @@ $mobile-breakpoint: 540px; font-family: silkscreen-wrapstodon, monospace; font-size: 28px; line-height: 1; - margin-bottom: 8px; + margin-bottom: 4px; padding-inline: 40px; // Prevent overlap with close button @media (width < $mobile-breakpoint) { @@ -116,7 +131,7 @@ $mobile-breakpoint: 540px; .box { position: relative; - padding: 16px; + padding: 24px; border-radius: 16px; background: rgb(from var(--color-bg-primary) r g b / 60%); box-shadow: inset 0 0 0 1px rgb(from var(--color-text-primary) r g b / 40%); @@ -150,7 +165,6 @@ $mobile-breakpoint: 540px; flex-direction: column; justify-content: center; gap: 8px; - padding: 16px; font-size: 14px; text-align: center; text-wrap: balance; @@ -164,6 +178,10 @@ $mobile-breakpoint: 540px; text-transform: uppercase; color: #c2c8ff; font-weight: 500; + + &:last-child { + margin-bottom: -3px; + } } .statLarge { @@ -185,7 +203,7 @@ $mobile-breakpoint: 540px; .mostBoostedPost { padding: 0; - padding-top: 8px; + padding-top: 24px; overflow: hidden; } @@ -260,7 +278,7 @@ $mobile-breakpoint: 540px; display: flex; flex-direction: column; align-items: center; - gap: 12px; + gap: 16px; p { max-width: 460px; diff --git a/app/javascript/flavours/glitch/features/annual_report/index.tsx b/app/javascript/flavours/glitch/features/annual_report/index.tsx index d5133b3cb3..b1d7fc5585 100644 --- a/app/javascript/flavours/glitch/features/annual_report/index.tsx +++ b/app/javascript/flavours/glitch/features/annual_report/index.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import type { FC } from 'react'; -import { defineMessage, useIntl } from 'react-intl'; +import { useIntl } from 'react-intl'; import { useLocation } from 'react-router'; @@ -23,11 +23,6 @@ import { NewPosts } from './new_posts'; const moduleClassNames = classNames.bind(styles); -export const shareMessage = defineMessage({ - id: 'annual_report.summary.share_message', - defaultMessage: 'I got the {archetype} archetype!', -}); - export const AnnualReport: FC<{ context?: 'modal' | 'standalone' }> = ({ context = 'standalone', }) => { diff --git a/app/javascript/flavours/glitch/features/annual_report/share_button.tsx b/app/javascript/flavours/glitch/features/annual_report/share_button.tsx index 497d15d1e8..652c8af913 100644 --- a/app/javascript/flavours/glitch/features/annual_report/share_button.tsx +++ b/app/javascript/flavours/glitch/features/annual_report/share_button.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import type { FC } from 'react'; -import { useIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; import { resetCompose, focusCompose } from '@/flavours/glitch/actions/compose'; import { closeModal } from '@/flavours/glitch/actions/modal'; @@ -9,9 +9,19 @@ import { Button } from '@/flavours/glitch/components/button'; import type { AnnualReport as AnnualReportData } from '@/flavours/glitch/models/annual_report'; import { useAppDispatch } from '@/flavours/glitch/store'; -import { shareMessage } from '.'; import { archetypeNames } from './archetype'; +const messages = defineMessages({ + share_message: { + id: 'annual_report.summary.share_message', + defaultMessage: 'I got the {archetype} archetype!', + }, + share_on_mastodon: { + id: 'annual_report.summary.share_on_mastodon', + defaultMessage: 'Share on Mastodon', + }, +}); + export const ShareButton: FC<{ report: AnnualReportData }> = ({ report }) => { const intl = useIntl(); const dispatch = useAppDispatch(); @@ -21,7 +31,7 @@ export const ShareButton: FC<{ report: AnnualReportData }> = ({ report }) => { archetypeNames[report.data.archetype], ); const shareLines = [ - intl.formatMessage(shareMessage, { + intl.formatMessage(messages.share_message, { archetype: archetypeName, }), ]; @@ -37,5 +47,10 @@ export const ShareButton: FC<{ report: AnnualReportData }> = ({ report }) => { dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false })); }, [report, intl, dispatch]); - return