Skip to content

Commit

Permalink
free user wishlist flow
Browse files Browse the repository at this point in the history
  • Loading branch information
a-type committed Dec 8, 2024
1 parent 63dcd3f commit 4b9ec8c
Show file tree
Hide file tree
Showing 19 changed files with 1,448 additions and 1,335 deletions.
13 changes: 13 additions & 0 deletions apps/wish-wash/common/src/components/onboarding/questions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ export type OnboardingQuestion = {
question: string;
options?: string[];
suggestions: string[];
prompt: string;
};

export const onboardingQuestions: OnboardingQuestion[] = [
{
id: 'sweet-treat',
question: 'What is your favorite sweet treat?',
prompt: 'Favorite sweet treat',
suggestions: [
'Dark chocolate',
'Apple pie',
Expand All @@ -19,16 +21,19 @@ export const onboardingQuestions: OnboardingQuestion[] = [
{
id: 'coffee-order',
question: 'What is your go-to coffee order?',
prompt: 'Go-to coffee order',
suggestions: ['Black coffee', 'Latte', 'Cappuccino', 'Cold brew'],
},
{
id: 'favorite-color',
question: 'What are your favorite colors?',
prompt: 'Favorite color(s)',
suggestions: ['Blue', 'Red', 'Purple', 'Rainbow'],
},
{
id: 'cuisine',
question: 'What restaurants do you visit frequently?',
prompt: 'Favorite restaurants',
suggestions: [
'Sushi',
'Oakwood Pizza Box',
Expand All @@ -39,16 +44,19 @@ export const onboardingQuestions: OnboardingQuestion[] = [
{
id: 'hobbies',
question: 'What are your favorite hobbies?',
prompt: 'Favorite hobbies',
suggestions: ['Reading', 'Knitting', 'Gardening', 'Cooking'],
},
{
id: 'new-hobby',
question: 'What new hobby would you like to try?',
prompt: "New hobby I'd like to try",
suggestions: ['Painting', 'Pottery', 'Baking', 'Yoga'],
},
{
id: 'beverages',
question: 'Do you drink? If so, what do you like?',
prompt: 'Favorite drink(s)',
suggestions: [
'Nope',
'Cabernet Sauvignon',
Expand All @@ -59,26 +67,31 @@ export const onboardingQuestions: OnboardingQuestion[] = [
{
id: 'music',
question: 'What music do you like to listen to?',
prompt: 'Favorite music genre',
suggestions: ['Pop', 'Jazz', 'Classical', 'Metal'],
},
{
id: 'books',
question: 'What books do you like to read?',
prompt: 'Favorite books',
suggestions: ['Mystery', 'Fantasy', 'Science fiction', 'Biography'],
},
{
id: 'movies',
question: 'What movies do you like to watch?',
prompt: 'Favorite movie genre(s)',
suggestions: ['Action', 'Comedy', 'Romance', 'Documentary'],
},
{
id: 'games',
question: 'What games do you like to play?',
prompt: 'Favorite kinds of games',
suggestions: ['Board games', 'Video games', 'Card games', 'Puzzles'],
},
{
id: 'sports',
question: 'What sports do you play?',
prompt: 'Played sport(s)',
suggestions: ['Basketball', 'Soccer', 'Golf', 'Tennis'],
},
];
6 changes: 6 additions & 0 deletions apps/wish-wash/web/src/components/items/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const ListItem = forwardRef<HTMLDivElement, ListItemProps>(
priceMin,
priceMax,
remoteImageUrl,
prompt,
} = hooks.useWatch(item);
hooks.useWatch(links);
hooks.useWatch(imageFiles);
Expand Down Expand Up @@ -79,6 +80,11 @@ export const ListItem = forwardRef<HTMLDivElement, ListItemProps>(
<Card.Content unstyled className="pt-2 pl-1">
<ItemTypeChip item={item} />
</Card.Content>
{prompt && (
<Card.Content className="p-1" unstyled>
{prompt}
</Card.Content>
)}
<Card.Content
unstyled
className={clsx(
Expand Down
16 changes: 1 addition & 15 deletions apps/wish-wash/web/src/components/lists/CreateListOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { useHasServerAccess } from '@biscuits/client';
import { useNavigate } from '@verdant-web/react-router';
import { authorization, List, ListInit } from '@wish-wash.biscuits/verdant';
import { useState } from 'react';
import { upsellState } from '../promotion/upsellState.js';

export interface CreateListOptionsProps {
onCreated?: (list: List) => void;
Expand Down Expand Up @@ -110,15 +109,7 @@ export function CreateListOptions({
<Card className="h-full">
<Card.Main
onClick={() => {
if (!canSync) {
upsellState.show = true;
} else {
createList(
'My wish list',
'wishlist',
authorization.private,
);
}
createList('My wish list', 'wishlist', authorization.private);
}}
>
<Card.Title>
Expand All @@ -127,11 +118,6 @@ export function CreateListOptions({
<Card.Content>
Make a wish list to share with others.
</Card.Content>
{!canSync && (
<Card.Content unstyled>
<PremiumBadge>$10 / year</PremiumBadge>
</Card.Content>
)}
</Card.Main>
</Card>
<Card className="h-full">
Expand Down
36 changes: 19 additions & 17 deletions apps/wish-wash/web/src/components/lists/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
IconName,
P,
} from '@a-type/ui';
import { WishlistOnboarding } from '@wish-wash.biscuits/common';
import { List } from '@wish-wash.biscuits/verdant';
import { ReactNode, Suspense } from 'react';
import { WishlistOnboarding } from '../../../../common/src/components/onboarding/WishlistOnboarding.jsx';
import { useItemSize } from '../items/hooks.js';
import { ItemEditDialog } from '../items/ItemEditDialog.jsx';
import { ListItem } from '../items/ListItem.jsx';
Expand Down Expand Up @@ -72,22 +72,24 @@ export function ListView({ list, className }: ListViewProps) {
if (needsOnboarding) {
return (
<div className="flex flex-col items-stretch gap-4">
<H2>Welcome to your wishlist!</H2>
<P>Let's get your wish list started with some ideas.</P>
<WishlistOnboarding
onAnswer={(question, answer) => {
items.push({
description: answer,
prompt: question.question,
type: 'idea',
});
completedQuestions.push(question.id);
}}
answeredQuestions={completedQuestions.getSnapshot()}
onSkip={() => {
completedQuestions.push('skipped');
}}
/>
<div className="mx-auto bg-primary-wash p-4 rounded-lg max-w-800px w-full">
<H2>Welcome to your wishlist!</H2>
<P>Let's get your wish list started with some ideas.</P>
<WishlistOnboarding
answeredQuestions={completedQuestions.getSnapshot()}
onAnswers={(answers) => {
for (const [question, answer] of answers) {
items.push({
description: answer,
prompt: question.prompt,
type: 'idea',
});
completedQuestions.push(question.id);
}
}}
thanksText="That'll get your list started. Now add more ideas!"
/>
</div>
</div>
);
}
Expand Down
31 changes: 12 additions & 19 deletions apps/wish-wash/web/src/components/promotion/SubscriptionDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Button, Dialog, P } from '@a-type/ui';
import { CONFIG, useIsLoggedIn } from '@biscuits/client';
import { Link } from '@verdant-web/react-router';
import { Dialog, P } from '@a-type/ui';
import { LoginButton, useIsLoggedIn } from '@biscuits/client';
import { useSnapshot } from 'valtio';
import { upsellState } from './upsellState.js';

Expand All @@ -15,27 +14,21 @@ export function SubscriptionDialog({}: SubscriptionDialogProps) {
<Dialog.Content>
<Dialog.Title>Share your list for a year</Dialog.Title>
<P className="gutter-bottom">
For $10, share your list with friends and family all year. (That's
your next birthday <i>and</i> yearly holidays sorted!)
Sign up for free to share your list with friends and family!
</P>
<P>
You'll get a link for your list you can share with people. They can
browse and mark items as bought.
Free users get one published wishlist on the house to share with
others. Recipients can browse your list and mark items as purchased.
</P>
<Dialog.Actions>
<Dialog.Close>Nevermind</Dialog.Close>
<Button asChild color="primary">
<Link
onClick={() => (upsellState.show = false)}
to={
isSignedIn ? '/buy-yearly' : (
`${CONFIG.HOME_ORIGIN}/login?tab=signup&appReferrer=wish-wash&appReturnTo=${encodeURIComponent('/buy-yearly')}`
)
}
>
Subscribe
</Link>
</Button>
<LoginButton
tab="signup"
color="primary"
onClick={() => (upsellState.show = false)}
>
Get started
</LoginButton>
</Dialog.Actions>
</Dialog.Content>
</Dialog>
Expand Down
6 changes: 5 additions & 1 deletion packages/client/src/components/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ import { useMaybeAppId } from './Context.js';

export interface LoginButtonProps extends ButtonProps {
returnTo?: string;
tab?: 'login' | 'signup';
}

export function LoginButton({ children, ...props }: LoginButtonProps) {
export function LoginButton({ children, tab, ...props }: LoginButtonProps) {
const url = new URL(CONFIG.HOME_ORIGIN + '/login');

url.searchParams.set('returnTo', props.returnTo ?? window.location.href);
const appId = useMaybeAppId();
if (appId) {
url.searchParams.set('appReferrer', appId);
}
if (tab) {
url.searchParams.set('tab', tab);
}

return (
<Button asChild {...props}>
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/components/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ export function UserMenu({
</div>
)}
{isLoggedIn && !hasServerAccess && (
<div className="pl-8 pr-4 py-1 text-gray-7 text-sm max-w-300px color-gray-7">
Your plan does not include this app
<div className="bg-wash pl-8 pr-4 py-1 text-gray-7 text-sm max-w-300px color-gray-7">
Your plan does not include sync for this app
</div>
)}

Expand Down
12 changes: 10 additions & 2 deletions packages/client/src/components/subscription/SubscriptionSetup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Spinner } from '@a-type/ui';
import { P, Spinner } from '@a-type/ui';
import { graphql, useQuery } from '@biscuits/graphql';
import { useSearchParams } from '@verdant-web/react-router';
import { useEffect } from 'react';
Expand Down Expand Up @@ -105,5 +105,13 @@ export function SubscriptionSetup({ priceKeys }: SubscriptionSetupProps) {
}

// subscription inactive - show plan selection
return <SubscriptionSelect priceKeys={priceKeys} />;
return (
<div className="flex flex-col gap-3">
<P>
You don't have an active subscription. Subscribe to unlock all Biscuits
app features.
</P>
<SubscriptionSelect priceKeys={priceKeys} />
</div>
);
}
40 changes: 20 additions & 20 deletions packages/libraries/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
export type LibraryAccess = 'members' | 'user';

export function getLibraryName({
planId,
app,
access,
userId,
planId,
app,
access,
userId,
}: {
planId: string;
app: string;
access: LibraryAccess;
userId: string;
planId: string;
app: string;
access: LibraryAccess;
userId: string;
}) {
if (access === 'user') {
return `${planId}__${app}__${userId}`;
}
return `${planId}__${app}`;
if (access === 'user') {
return `${planId}__${app}__${userId}`;
}
return `${planId}__${app}`;
}

export function parseLibraryName(libraryName: string): {
planId: string;
app: string;
maybeUserId?: string;
planId: string;
app: string;
maybeUserId?: string;
} {
const [planId, app, maybeUserId] = libraryName.split('__');
return { planId, app, maybeUserId };
const [planId, app, maybeUserId] = libraryName.split('__');
return { planId, app, maybeUserId };
}

export type BiscuitsVerdantProfile = {
id: string;
name: string;
imageUrl: string | null;
id: string;
name: string;
imageUrl: string | null;
};
Loading

0 comments on commit 4b9ec8c

Please sign in to comment.