Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: credit system #4203

Draft
wants to merge 39 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1b3f687
Credit system
rebelchris Feb 19, 2025
2cc58a9
Merge branch 'main' into feat-credit-system
rebelchris Feb 20, 2025
d2a7a86
fix: show award modal on comments (#4210)
rebelchris Feb 20, 2025
1ee5f47
Merge branch 'main' into feat-credit-system
rebelchris Feb 20, 2025
a13884b
Merge branch 'main' into feat-credit-system
rebelchris Feb 21, 2025
2c40ab8
feat: give reward modal (#4218)
rebelchris Feb 24, 2025
2301ab4
Merge branch 'main' into feat-credit-system
rebelchris Feb 24, 2025
7af05df
fix: add cores as plus bonus (#4231)
rebelchris Feb 26, 2025
cd66a8f
Merge branch 'main' into feat-credit-system
rebelchris Feb 26, 2025
7d30e4c
Merge branch 'main' into feat-credit-system
rebelchris Feb 27, 2025
ec52965
feat: earnings (#4243)
rebelchris Feb 27, 2025
7bee8e4
fix: show award on profile (#4230)
rebelchris Feb 27, 2025
6362445
Merge branch 'main' into feat-credit-system
rebelchris Feb 27, 2025
cce728f
Merge branch 'main' into feat-credit-system
rebelchris Feb 28, 2025
4dfad0a
Merge branch 'main' into feat-credit-system
rebelchris Feb 28, 2025
6fa141d
Merge branch 'main' into feat-credit-system
rebelchris Feb 28, 2025
55c6dc5
fix: small issues with modal changes
rebelchris Feb 28, 2025
6737520
Merge branch 'main' into feat-credit-system
rebelchris Mar 3, 2025
6403bd2
Merge branch 'main' into feat-credit-system
rebelchris Mar 4, 2025
8f241b1
feat: buy credit mock (not functional) (#4253)
rebelchris Mar 4, 2025
08b9f6b
Merge branch 'main' of github.com:dailydotdev/apps into feat-credit-s…
rebelchris Mar 5, 2025
193c109
Merge branch 'main' into feat-credit-system
capJavert Mar 6, 2025
faa4524
feat: buy cores flow (#4272)
rebelchris Mar 10, 2025
92ad726
feat: award post/user (#4273)
capJavert Mar 10, 2025
967498e
Merge branch 'main' into feat-credit-system
rebelchris Mar 11, 2025
5afc614
feat: award comment (#4279)
capJavert Mar 12, 2025
da9588e
Merge branch 'main' into feat-credit-system
capJavert Mar 14, 2025
17f5a76
fix: import
capJavert Mar 14, 2025
62df13a
feat: add core button to header (#4293)
rebelchris Mar 14, 2025
8ba2b95
Merge branch 'main' into feat-credit-system
rebelchris Mar 14, 2025
c3d0ef0
feat: show real balance from boot
capJavert Mar 17, 2025
95dba71
Merge branch 'main' into feat-credit-system
capJavert Mar 17, 2025
cdb8065
feat: analytics events for credits/awards (#4291)
rebelchris Mar 18, 2025
0f1bd8e
Merge branch 'main' into feat-credit-system
capJavert Mar 18, 2025
f4198ca
feat: purchase cores (#4294)
capJavert Mar 18, 2025
62e7f92
fix: pass post to award modal
capJavert Mar 18, 2025
45760ec
fix: cores page url
capJavert Mar 18, 2025
4b34095
feat: move logic to core page
capJavert Mar 19, 2025
24e24a5
feat: amount needed support for cores page
capJavert Mar 19, 2025
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
2 changes: 1 addition & 1 deletion packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"@growthbook/growthbook": "https://gitpkg.now.sh/dailydotdev/growthbook/packages/sdk-js?e354fcf41b2b3f67590294a0e2cdfb56044d7a1e",
"@growthbook/growthbook-react": "^0.17.0",
"@marsidev/react-turnstile": "^1.1.0",
"@paddle/paddle-js": "^1.3.2",
"@paddle/paddle-js": "1.4.0",
"@tippyjs/react": "^4.2.6",
"check-password-strength": "^2.0.10",
"fetch-event-stream": "^0.1.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/ProfilePicture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export enum ProfileImageSize {
}

type ProfileImageRoundSize = ProfileImageSize | 'full';
type UserImageProps = Pick<PublicProfile, 'image'> &
export type UserImageProps = Pick<PublicProfile, 'image'> &
Partial<Pick<PublicProfile, 'id' | 'username' | 'name'>>;

export interface ProfilePictureProps
Expand Down
89 changes: 89 additions & 0 deletions packages/shared/src/components/award/AwardButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { ReactElement } from 'react';
import React from 'react';
import classNames from 'classnames';
import type { ButtonProps } from '../buttons/Button';
import {
Button,
ButtonColor,
ButtonSize,
ButtonVariant,
} from '../buttons/Button';
import { MedalBadgeIcon } from '../icons';
import { SimpleTooltip } from '../tooltips';
import { AuthTriggers } from '../../lib/auth';
import { LazyModal } from '../modals/common/types';
import { useAuthContext } from '../../contexts/AuthContext';
import { useLazyModal } from '../../hooks/useLazyModal';
import type {
AwardEntity,
AwardTypes,
} from '../../contexts/GiveAwardModalContext';
import { useRequestProtocol } from '../../hooks/useRequestProtocol';
import { getCompanionWrapper } from '../../lib/extension';
import type { Post } from '../../graphql/posts';

type AwardButtonProps = {
appendTo?: 'parent' | Element | ((ref: Element) => Element);
type: AwardTypes;
className?: string;
entity: AwardEntity;
post?: Post;
} & Pick<ButtonProps<'button'>, 'pressed' | 'variant'>;
export const AwardButton = ({
appendTo: appendToProps,
type,
className,
entity,
pressed,
variant = ButtonVariant.Tertiary,
post,
}: AwardButtonProps): ReactElement => {
const { isCompanion } = useRequestProtocol();
const { user, showLogin } = useAuthContext();
const { openModal } = useLazyModal();

const openGiveAwardModal = () => {
if (!user) {
return showLogin({ trigger: AuthTriggers.GiveAward });
}

return openModal({
type: LazyModal.GiveAward,
props: {
type,
entity,
post,
},
});
};

if (user && entity.receiver?.id === user.id) {
return null;
}

const defaultAppendTo = isCompanion ? getCompanionWrapper : 'parent';
const appendTo = appendToProps || defaultAppendTo;

return (
<SimpleTooltip
content={
pressed
? `You already awarded this ${type.toLowerCase()}!`
: 'Award this user'
}
appendTo={appendTo}
>
<div>
<Button
pressed={pressed}
size={ButtonSize.Small}
icon={<MedalBadgeIcon secondary />}
className={classNames(className, pressed && 'pointer-events-none')}
variant={variant}
color={ButtonColor.Cabbage}
onClick={openGiveAwardModal}
/>
</div>
</SimpleTooltip>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ import { ContentPreferenceType } from '../../graphql/contentPreference';
import { isFollowingContent } from '../../hooks/contentPreference/types';
import { useIsSpecialUser } from '../../hooks/auth/useIsSpecialUser';
import { GiftIcon } from '../icons/gift';
import { AwardButton } from '../award/AwardButton';
import {
Typography,
TypographyColor,
TypographyType,
} from '../typography/Typography';

export interface CommentActionProps {
onComment: (comment: Comment, parentId: string | null) => void;
Expand Down Expand Up @@ -350,6 +356,29 @@ export default function CommentActionButtons({
color={ButtonColor.BlueCheese}
/>
</SimpleTooltip>
<AwardButton
appendTo={appendTo}
type="COMMENT"
entity={{
id: comment.id,
receiver: comment.author,
numAwards: comment.numAwards,
}}
pressed={!!comment.userState?.awarded}
post={post}
/>
{!!comment.numAwards && (
<Typography
className="mr-3"
type={TypographyType.Callout}
color={TypographyColor.Tertiary}
bold
>
{/* TODO feat/transactions show most expensive award image next to count */}
{largeNumberFormat(comment.numAwards)} Award
{comment.numAwards > 1 ? 's' : ''}
</Typography>
)}
<SimpleTooltip content="Share comment" appendTo={appendTo}>
<Button
size={ButtonSize.Small}
Expand Down
11 changes: 11 additions & 0 deletions packages/shared/src/components/comments/CommentContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { VerifiedCompanyUserBadge } from '../VerifiedCompanyUserBadge';
import { Separator } from '../cards/common/common';
import { PlusUserBadge } from '../PlusUserBadge';
import { ProfileImageSize } from '../ProfilePicture';
import { Image } from '../image/Image';

interface ClassName extends CommentClassName {
content?: string;
Expand Down Expand Up @@ -68,6 +69,7 @@ export default function CommentContainer({
ref={isCommentReferenced ? commentRef : null}
className={classNames(
'flex flex-col rounded-16 p-4 hover:bg-surface-hover focus:outline',
comment.userState?.awarded && 'bg-overlay-float-onion',
isCommentReferenced
? 'border border-accent-cabbage-default'
: 'border-border-subtlest-tertiary',
Expand Down Expand Up @@ -157,6 +159,15 @@ export default function CommentContainer({
/>
{actions}
</div>
{!!comment.award && (
<div className="absolute right-3 top-3 flex size-7 items-center justify-center rounded-10 bg-surface-float">
<Image
src={comment.award.image}
alt={comment.award.name}
className="size-4"
/>
</div>
)}
</article>
);
}
52 changes: 52 additions & 0 deletions packages/shared/src/components/cores/CoreAmountNeeded.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { ReactElement } from 'react';
import React from 'react';
import {
Typography,
TypographyColor,
TypographyTag,
TypographyType,
} from '../typography/Typography';
import { useBuyCoresContext } from '../../contexts/BuyCoresContext';

export const CoreAmountNeeded = (): ReactElement => {
const { amountNeeded, selectedProduct } = useBuyCoresContext();

if (!amountNeeded || !selectedProduct) {
return (
<Typography
className="w-full flex-1"
type={TypographyType.Callout}
color={TypographyColor.Secondary}
>
Choose how many Cores to buy.
</Typography>
);
}

return (
<>
<Typography
className="w-full flex-1"
type={TypographyType.Callout}
color={TypographyColor.Secondary}
>
<Typography
tag={TypographyTag.Span}
color={TypographyColor.Primary}
bold
>
{amountNeeded} Cores
</Typography>{' '}
will be used to give the Award. The remaining{' '}
<Typography
tag={TypographyTag.Span}
color={TypographyColor.Primary}
bold
>
{selectedProduct.value - amountNeeded} Cores
</Typography>{' '}
will be added to your balance.
</Typography>
</>
);
};
83 changes: 83 additions & 0 deletions packages/shared/src/components/cores/CoreFAQ.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { ReactElement, ReactNode } from 'react';
import React, { useId } from 'react';
import {
Typography,
TypographyColor,
TypographyTag,
TypographyType,
} from '../typography/Typography';
import { Accordion } from '../accordion';
import { anchorDefaultRel } from '../../lib/strings';
import { feedback } from '../../lib/constants';
import { plusFAQItems } from '../plus/common';

interface FAQ {
question: string;
answer: ReactNode;
}

const FAQItem = ({ item }: { item: FAQ }): ReactElement => (
<div className="rounded-10 bg-surface-float px-6 py-4">
<Accordion
title={
<Typography
bold
color={TypographyColor.Primary}
tag={TypographyTag.Span}
>
{item.question}
</Typography>
}
>
<div className="text-text-tertiary typo-callout">{item.answer}</div>
</Accordion>
</div>
);

export const CoreFAQ = (): ReactElement => {
const id = useId();
const titleId = `${id}-title`;
const items = plusFAQItems;
return (
<section aria-labelledby={titleId} className="my-10">
<Typography
bold
className="mb-10 text-center"
id={titleId}
tag={TypographyTag.H2}
type={TypographyType.Title3}
>
Frequently asked questions
</Typography>
<div className="mx-auto flex max-w-3xl flex-col gap-4">
{items.map((item) => (
<FAQItem key={item.question} item={item} />
))}
</div>
<Typography
className="mt-10 text-center"
color={TypographyColor.Tertiary}
type={TypographyType.Callout}
>
For technical or product related questions{' '}
<a
className="underline"
href={feedback}
target="_blank"
rel={anchorDefaultRel}
>
click here
</a>{' '}
or email us at{' '}
<a
className="underline"
href="mailto:[email protected]?subject=I have a question about Plus membership"
target="_blank"
rel={anchorDefaultRel}
>
[email protected]
</a>
</Typography>
</section>
);
};
Loading