Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions packages/shared/src/components/archive/ArchiveIndexPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ReactElement } from 'react';
import React from 'react';
import classNames from 'classnames';
import type { Archive } from '../../graphql/archive';
import { ArchiveScopeType } from '../../graphql/archive';
import type { ArchiveScopeInfo, ArchivesByYear } from '../../lib/archive';
import {
getArchiveUrlFromArchive,
Expand All @@ -13,6 +14,9 @@ import Link from '../utilities/Link';
import { ArrowIcon } from '../icons';
import { IconSize } from '../Icon';
import { ElementPlaceholder } from '../ElementPlaceholder';
import { CopyLinkButton } from '../share/CopyLinkButton';
import { LogEvent } from '../../lib/log';
import { ReferralCampaignKey } from '../../lib/referral';

interface ArchiveIndexPageProps {
scopeType: ArchiveScopeInfo['scopeType'];
Expand Down Expand Up @@ -159,13 +163,31 @@ export function ArchiveIndexPage({
className,
}: ArchiveIndexPageProps): ReactElement {
const groups = groupArchivesByYear(archives);
const isTagScope = scopeType === ArchiveScopeType.Tag;
const shareCampaign = isTagScope
? ReferralCampaignKey.ShareTag
: ReferralCampaignKey.ShareSource;
const shareEvent = isTagScope ? LogEvent.ShareTag : LogEvent.ShareSource;

return (
<div className={classNames('flex flex-col', className)}>
{/* Header */}
<h1 className="mx-4 font-bold typo-title2 tablet:typo-title1">
Best of {scopeName} &mdash; Archive
</h1>
<div className="mx-4 flex items-center gap-2">
<h1 className="flex-1 font-bold typo-title2 tablet:typo-title1">
Best of {scopeName} &mdash; Archive
</h1>
<CopyLinkButton
shareProps={{
text: `Check out the best of ${scopeName} on daily.dev`,
link: globalThis?.location?.href,
cid: shareCampaign,
logObject: () => ({
event_name: shareEvent,
target_id: scopeId,
}),
}}
/>
</div>

{/* Archive grid by year */}
<ArchiveGrid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import type { CommonLeaderboardProps } from './LeaderboardList';
import { LeaderboardList } from './LeaderboardList';
import { LeaderboardListItem } from './LeaderboardListItem';
import { UserHighlight, UserType } from '../../widgets/PostUsersHighlights';
import { CopyLinkButton } from '../../share/CopyLinkButton';
import { ButtonVariant } from '../../buttons/Button';
import { ReferralCampaignKey } from '../../../lib/referral';
import { LogEvent } from '../../../lib/log';

export function SourceTopList({
items,
Expand All @@ -16,7 +20,7 @@ export function SourceTopList({
<LeaderboardListItem
key={item.id}
index={i + 1}
className="flex w-full flex-row items-center rounded-8 px-2 hover:bg-accent-pepper-subtler"
className="group/source flex w-full flex-row items-center rounded-8 px-2 hover:bg-accent-pepper-subtler"
>
<UserHighlight
{...item}
Expand All @@ -30,6 +34,20 @@ export function SourceTopList({
}}
allowSubscribe={false}
/>
{/* Hover-revealed only where hover exists; always there on touch. */}
<CopyLinkButton
variant={ButtonVariant.Tertiary}
className="ml-auto shrink-0 laptop:opacity-0 laptop:group-focus-within/source:opacity-100 laptop:group-hover/source:opacity-100"
shareProps={{
text: `Check out ${item.handle} on daily.dev`,
link: item.permalink,
cid: ReferralCampaignKey.ShareSource,
logObject: () => ({
event_name: LogEvent.ShareSource,
target_id: item.id,
}),
}}
/>
</LeaderboardListItem>
))}
</LeaderboardList>
Expand Down
17 changes: 16 additions & 1 deletion packages/shared/src/components/cards/entity/SquadEntityCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import {
TypographyType,
} from '../../typography/Typography';
import type { Origin } from '../../../lib/log';
import { LogEvent } from '../../../lib/log';
import { largeNumberFormat } from '../../../lib';
import { SquadActionButton } from '../../squads/SquadActionButton';
import { SourceIcon } from '../../icons';
import { IconSize } from '../../Icon';
import { useSquad } from '../../../hooks';
import { ButtonSize } from '../../buttons/Button';
import { ButtonSize, ButtonVariant } from '../../buttons/Button';
import SquadHeaderMenu from '../../squads/SquadHeaderMenu';
import { CopyLinkButton } from '../../share/CopyLinkButton';
import { ReferralCampaignKey } from '../../../lib/referral';
import { Separator } from '../common/common';
import EntityDescription from './EntityDescription';
import EntityCard from './EntityCard';
Expand Down Expand Up @@ -59,6 +62,18 @@ const SquadEntityCard = ({
actionButtons={
!isLoading && (
<>
<CopyLinkButton
variant={ButtonVariant.Tertiary}
shareProps={{
text: `Check out the ${name} squad on daily.dev`,
link: permalink,
cid: ReferralCampaignKey.ShareSource,
logObject: () => ({
event_name: LogEvent.ShareSource,
target_id: squad.id,
}),
}}
/>
<SquadActionButton
className={{
button: 'order-6',
Expand Down
38 changes: 38 additions & 0 deletions packages/shared/src/components/share/CopyLinkButton.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Tooltip content={LABEL}>
<Button
aria-label={LABEL}
className={className}
icon={<LinkIcon />}
onClick={() => onShareOrCopyLink()}
size={size}
variant={variant}
/>
</Tooltip>
);
};
59 changes: 34 additions & 25 deletions packages/shared/src/components/sources/SourceActions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import SourceActionsNotify from './SourceActionsNotify';
import SourceActionsBlock from './SourceActionsBlock';
import SourceActionsFollow from './SourceActionsFollow';
import CustomFeedOptionsMenu from '../../CustomFeedOptionsMenu';
import { CopyLinkButton } from '../../share/CopyLinkButton';
import { LogEvent } from '../../../lib/log';
import { useContentPreference } from '../../../hooks/contentPreference/useContentPreference';
import { ContentPreferenceType } from '../../../graphql/contentPreference';
import type { ContentPreferenceMutation } from '../../../hooks/contentPreference/types';

interface SourceActionsButton {
className?: string;
Expand All @@ -25,6 +27,7 @@ export interface SourceActionsProps {
hideFollow?: boolean;
notifyProps?: SourceActionsButton;
hideNotify?: boolean;
showCopyLink?: boolean;
}

export const SourceActions = ({
Expand All @@ -35,6 +38,7 @@ export const SourceActions = ({
hideNotify = false,
source,
notifyProps,
showCopyLink = false,
}: SourceActionsProps): ReactElement => {
const {
isBlocked,
Expand All @@ -49,6 +53,32 @@ export const SourceActions = ({
const { follow, unfollow } = useContentPreference();
const router = useRouter();

const updateCustomFeed = (
mutate: ContentPreferenceMutation,
feedId: string,
) => {
if (!source.id) {
throw new Error('Cannot update a custom feed for a source without an id');
}

return mutate({
id: source.id,
entity: ContentPreferenceType.Source,
entityName: source.handle,
feedId,
});
};

const shareProps = {
text: `Check out ${source.handle} on daily.dev`,
link: source.permalink,
cid: ReferralCampaignKey.ShareSource,
logObject: () => ({
event_name: LogEvent.ShareSource,
target_id: source.id,
}),
};

return (
<div className="inline-flex flex-row gap-2">
{!hideFollow && !isBlocked && (
Expand All @@ -74,37 +104,16 @@ export const SourceActions = ({
{...blockProps}
/>
)}
{showCopyLink && <CopyLinkButton shareProps={shareProps} />}
<CustomFeedOptionsMenu
onCreateNewFeed={() =>
router.push(
`/feeds/new?entityId=${source.id}&entityType=${ContentPreferenceType.Source}`,
)
}
onAdd={(feedId) =>
follow({
id: source.id,
entity: ContentPreferenceType.Source,
entityName: source.handle,
feedId,
})
}
onUndo={(feedId) =>
unfollow({
id: source.id,
entity: ContentPreferenceType.Source,
entityName: source.handle,
feedId,
})
}
shareProps={{
text: `Check out ${source.handle} on daily.dev`,
link: source.permalink,
cid: ReferralCampaignKey.ShareSource,
logObject: () => ({
event_name: LogEvent.ShareSource,
target_id: source.id,
}),
}}
onAdd={(feedId) => updateCustomFeed(follow, feedId)}
onUndo={(feedId) => updateCustomFeed(unfollow, feedId)}
shareProps={shareProps}
/>
</div>
);
Expand Down
22 changes: 13 additions & 9 deletions packages/shared/src/components/tags/TagTopicPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { cloudinarySourceRoadmap } from '../../lib/image';
import { anchorDefaultRel, formatKeyword } from '../../lib/strings';
import Link from '../utilities/Link';
import CustomFeedOptionsMenu from '../CustomFeedOptionsMenu';
import { CopyLinkButton } from '../share/CopyLinkButton';
import { ArchiveEntryCard } from '../archive/ArchiveEntryCard';
import { ArchiveScopeType } from '../../graphql/archive';
import { useContentPreference } from '../../hooks/contentPreference/useContentPreference';
Expand Down Expand Up @@ -356,6 +357,16 @@ export const TagTopicPage = ({
},
});

const 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,
}),
};

const statParts: ReactNode[] = [];
if (typeof followers === 'number') {
statParts.push(
Expand Down Expand Up @@ -447,6 +458,7 @@ export const TagTopicPage = ({
{tagStatus === 'blocked' ? 'Unblock' : 'Block'}
</Button>
)}
<CopyLinkButton shareProps={shareProps} />
<CustomFeedOptionsMenu
onCreateNewFeed={() =>
push(
Expand All @@ -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}
/>
</div>
{/* SEO crawl paths preserved from the legacy tag page. */}
Expand Down
Loading
Loading