From 1d278d455e5748321593489622f24bd6517230e1 Mon Sep 17 00:00:00 2001 From: tomeredlich Date: Wed, 2 Sep 2026 18:01:26 +0300 Subject: [PATCH 1/3] feat(share): add a reusable copy link button Wraps useShareOrCopyLink so a surface can offer a link directly instead of burying it in an overflow menu: the native sheet on mobile, a clipboard copy on desktop. The hook already raises a toast on copy, so the button stays static rather than swapping in a confirmation icon. Defaults to Float because it usually sits beside a Block or overflow button; surfaces whose neighbours are tertiary pass their own variant. --- .../src/components/share/CopyLinkButton.tsx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 packages/shared/src/components/share/CopyLinkButton.tsx diff --git a/packages/shared/src/components/share/CopyLinkButton.tsx b/packages/shared/src/components/share/CopyLinkButton.tsx new file mode 100644 index 0000000000..c81e483086 --- /dev/null +++ b/packages/shared/src/components/share/CopyLinkButton.tsx @@ -0,0 +1,38 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import { Button, ButtonSize, ButtonVariant } from '../buttons/Button'; +import { LinkIcon } from '../icons'; +import { Tooltip } from '../tooltip/Tooltip'; +import type { UseShareOrCopyLinkProps } from '../../hooks/useShareOrCopyLink'; +import { useShareOrCopyLink } from '../../hooks/useShareOrCopyLink'; + +interface CopyLinkButtonProps { + shareProps: UseShareOrCopyLinkProps; + className?: string; + size?: ButtonSize; + variant?: ButtonVariant; +} + +const LABEL = 'Copy link'; + +export const CopyLinkButton = ({ + shareProps, + className, + size = ButtonSize.Small, + variant = ButtonVariant.Float, +}: CopyLinkButtonProps): ReactElement => { + const [, onShareOrCopyLink] = useShareOrCopyLink(shareProps); + + return ( + + )} + push( @@ -469,15 +481,7 @@ export const TagTopicPage = ({ feedId, }) } - shareProps={{ - text: `Check out the ${tag} tag on daily.dev`, - link: globalThis?.location?.href, - cid: ReferralCampaignKey.ShareTag, - logObject: () => ({ - event_name: LogEvent.ShareTag, - target_id: tag, - }), - }} + shareProps={shareProps} /> {/* SEO crawl paths preserved from the legacy tag page. */} diff --git a/packages/webapp/pages/sources/[source].tsx b/packages/webapp/pages/sources/[source].tsx index baab4eadec..4a48fc4e6f 100644 --- a/packages/webapp/pages/sources/[source].tsx +++ b/packages/webapp/pages/sources/[source].tsx @@ -301,7 +301,7 @@ const SourcePage = ({

{source.name}

- +
{source?.description && (

{source?.description}

From 013296370356d12a709c215ad4c8e337f768a89d Mon Sep 17 00:00:00 2001 From: tomeredlich Date: Wed, 2 Sep 2026 18:01:41 +0300 Subject: [PATCH 3/3] docs(snapshot): document the topic and directory page share placements One story covering the four surfaces, each showing the placement that ships across desktop, tablet and mobile, so the layouts can be compared side by side without opening four pages. Mockup-to-eng-pass: 1 --- .../features/snapshot/surfaceChrome.tsx | 162 +++++++++ .../snapshot/surfaces/Directories.stories.tsx | 323 ++++++++++++++++++ 2 files changed, 485 insertions(+) create mode 100644 packages/storybook/stories/features/snapshot/surfaceChrome.tsx create mode 100644 packages/storybook/stories/features/snapshot/surfaces/Directories.stories.tsx diff --git a/packages/storybook/stories/features/snapshot/surfaceChrome.tsx b/packages/storybook/stories/features/snapshot/surfaceChrome.tsx new file mode 100644 index 0000000000..d4654bca9f --- /dev/null +++ b/packages/storybook/stories/features/snapshot/surfaceChrome.tsx @@ -0,0 +1,162 @@ +import React from 'react'; +import { + Button, + ButtonSize, + ButtonVariant, +} from '@dailydotdev/shared/src/components/buttons/Button'; +import { LinkIcon } from '@dailydotdev/shared/src/components/icons'; + +export const AVATAR = + 'https://res.cloudinary.com/daily-now/image/upload/s--O0TOmw4y--/f_auto/v1715772965/public/noProfile'; + + +/* ------------------------------------------------------------------ prose */ + +export const H1 = ({ children }: { children: React.ReactNode }) => ( +

{children}

+); + +export const P = ({ children }: { children: React.ReactNode }) => ( +

{children}

+); + +export const Note = ({ children }: { children: React.ReactNode }) => ( +

+ {children} +

+); + +/* ---------------------------------------------------------------- controls */ + +/** + * Inert on purpose: this page compares where a control sits inside a real + * screen. The working buttons and live capture are on Button placements. + */ +export const Control = () => ( + + + + + + + +); + +/* ---------------------------------------------------------------- sources */ + +/** The source page is left-aligned, and its actions sit below the title. */ +const SourceScreen = ({ device }: { device: DeviceName }) => ( + +
+ + Sources / XDA Developers + + +
+ +

+ XDA Developers +

+
+ +
+ +
+ +

+ News and reviews for developers, by developers. +

+ +
+ {['#android', '#hardware', '#reviews'].map((tag) => ( + + {tag} + + ))} +
+
+
+); + +/* ----------------------------------------------------------------- squads */ + +/** SquadEntityCard: w-80, image and actions on one row, body under it. */ +const SquadCard = () => ( +
+
+ +
+ + + {/* SquadHeaderMenu — `invisible group-hover/menu:visible`. */} + +
+
+
+ + Frontend Fans + +

+ Everything CSS, React and the browser. Ship it and show it. +

+ + 2.4K Members + · + 12K Upvotes + +
+
+); + +const SquadScreen = ({ device }: { device: DeviceName }) => ( + +
+

Squads

+
+ + {device === 'Desktop' && } +
+
+
+); + +/* ---------------------------------------------------------------- archive */ + +/** ArchiveIndexPage: a month grid, no posts and no feed controls. */ +const ArchiveScreen = ({ device }: { device: DeviceName }) => ( + +
+ + Sources / XDA Developers / Best of + +
+

+ Best of XDA Developers — Archive +

+ +
+ +
+ {['2026', '2025'].map((year) => ( +
+

+ {year} +

+
+ {['January', 'February', 'March', 'April'].map((month) => ( + + + {month} + + + 24 posts + + + ))} +
+
+ ))} +
+
+
+); + +/* -------------------------------------------------------------------- page */ + +const Rails = ({ + Screen, +}: { + Screen: React.ComponentType<{ device: DeviceName }>; +}) => ( + + + + + +); + +const Directories = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + +); + +const meta: Meta = { + title: 'Features/Snapshot/Surfaces/Topic & directory pages', + component: Directories, + parameters: { layout: 'fullscreen' }, +}; + +export default meta; + +export const Variations: StoryObj = {};