Skip to content

Commit

Permalink
feat: habit info page
Browse files Browse the repository at this point in the history
  • Loading branch information
owengretzinger committed Jan 11, 2025
1 parent e6ff703 commit 55a6a59
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 12 deletions.
12 changes: 12 additions & 0 deletions src/api/habits/mock-habits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ export const mockHabits: { id: HabitIdT; data: DbHabitT }[] = [
displayName: 'John Doe',
username: 'john_doe',
lastActivity: new Date('2024-12-01T00:00:00'),
},
['6' as UserIdT]: {
displayName: 'Sarah Wilson',
username: 'sarah_wilson',
lastActivity: new Date('2024-12-15T00:00:00'),
isOwner: true,
},
},
Expand Down Expand Up @@ -161,6 +166,13 @@ export const mockHabitCompletions: Record<HabitIdT, AllCompletionsT> = {
'2024-12-06': { numberOfCompletions: 1 },
},
},
['6' as UserIdT]: {
entries: {
'2024-12-13': { numberOfCompletions: 1 },
'2024-12-14': { numberOfCompletions: 1 },
'2024-12-15': { numberOfCompletions: 1, note: 'Great book tonight!' },
},
},
},
};
export const setMockHabitCompletions = (
Expand Down
7 changes: 7 additions & 0 deletions src/api/users/mock-users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export const mockUsers: UserT[] = [
username: 'lorem_ipsum',
createdAt: new Date(),
},
{
id: '6' as UserIdT,
displayName: 'Sarah Wilson',
username: 'sarah_wilson',
createdAt: new Date(),
},
];

export const mockPictures: Record<UserIdT, string> = {
Expand All @@ -39,6 +45,7 @@ export const mockPictures: Record<UserIdT, string> = {
['3' as UserIdT]: 'https://randomuser.me/api/portraits/women/4.jpg',
['4' as UserIdT]: 'https://randomuser.me/api/portraits/men/5.jpg',
['5' as UserIdT]: 'https://randomuser.me/api/portraits/men/6.jpg',
['6' as UserIdT]: 'https://randomuser.me/api/portraits/women/7.jpg',
};

export const mockRelationships: Record<
Expand Down
108 changes: 101 additions & 7 deletions src/app/habits/view-habit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import {
type HabitT,
type ParticipantWithIdT,
useAllUsersHabitCompletions,
type UserIdT,
type UserT,
} from '@/api';
import { ErrorMessage } from '@/components/error-message';
import { HabitIcon, type habitIcons } from '@/components/habit-icon';
import ModifyHabitEntry from '@/components/modify-habit-entry';
import { DayNavigation } from '@/components/modify-habit-entry/day-navigation';
import { UserImageNameAndUsername } from '@/components/user-card';
import UserCard, { UserImageNameAndUsername } from '@/components/user-card';
import { getTranslucentColor } from '@/core/get-translucent-color';
import {
Button,
Expand Down Expand Up @@ -180,6 +181,21 @@ interface HabitHeaderProps {
const HabitHeader = ({ habit }: HabitHeaderProps) => {
const { colorScheme } = useColorScheme();
const modal = useModal();
const participants = Object.entries(habit.participants)
.filter(
(entry): entry is [string, NonNullable<(typeof entry)[1]>] =>
entry[1] !== undefined,
)
.map(([_, p]) => p);
const isOwner = habit.participants['1' as UserIdT]?.isOwner;

const handleTransferOwnership = (userId: string) => {
alert('TODO: Transfer ownership to ' + userId);
};

const handleKickUser = (userId: string) => {
alert('TODO: Kick user ' + userId);
};

return (
<>
Expand Down Expand Up @@ -214,23 +230,101 @@ const HabitHeader = ({ habit }: HabitHeaderProps) => {
colorScheme === 'dark' ? colors.neutral[800] : colors.white,
}}
>
<View className="flex-1 px-4">
<Header title={habit.title} />
<View className="flex flex-col gap-4">
<ScrollView className="flex-1 px-4">
<View className="flex flex-col gap-6">
<View
className="flex flex-col gap-2 rounded-xl p-4"
style={{
backgroundColor:
colorScheme === 'dark'
? getTranslucentColor(habit.color.base, 0.05)
: habit.color.light,
borderColor:
colorScheme === 'dark' ? habit.color.base : 'transparent',
borderWidth: colorScheme === 'dark' ? 1 : 0,
}}
>
<View className="flex flex-row items-center gap-2">
<HabitIcon
icon={habit.icon}
size={24}
color={colorScheme === 'dark' ? colors.white : colors.black}
strokeWidth={2}
/>
<Text className="text-xl font-semibold">{habit.title}</Text>
</View>
{habit.description && (
<Text className="text-base">{habit.description}</Text>
)}
<Text
className="text-sm"
style={{
color:
colorScheme === 'dark'
? colors.stone[400]
: habit.color.text,
}}
>
{habit.settings.allowMultipleCompletions
? 'Multiple completions allowed per day'
: 'One completion per day limit'}
</Text>
<Text
className="text-sm"
style={{
color:
colorScheme === 'dark'
? colors.stone[400]
: habit.color.text,
}}
>
Created{' '}
{new Date(habit.createdAt).toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</Text>
</View>

<Button
label="Invite Friends"
variant="outline"
icon={UserPlusIcon}
onPress={() => alert('todo')}
/>

{participants.length >= 2 && (
<View className="flex flex-col">
<Text className="font-medium">
{participants.length} participants:
</Text>
{participants.map((p) => (
<UserCard
key={p.id}
data={{
id: p.id,
displayName: p.displayName,
username: p.username,
createdAt: p.lastActivity,
}}
showOwnerBadge={p.isOwner}
showManageOptions={isOwner && p.id !== '1'}
onTransferOwnership={() => handleTransferOwnership(p.id)}
onKickUser={() => handleKickUser(p.id)}
onPress={modal.dismiss}
/>
))}
</View>
)}

<Button
label="Leave Habit"
variant="destructive"
icon={Trash2Icon}
onPress={() => alert('todo')}
/>
</View>
</View>
</ScrollView>
</Modal>
</>
);
Expand Down Expand Up @@ -328,7 +422,7 @@ const UserWithEntry = ({
{modifyEntryButton ||
// for other users, show nudge button if no completions and it's today
(entry.numberOfCompletions === 0 && isToday && (
<View className="flex flex-row">
<View className="flex flex-row justify-end">
<Button
variant="outline"
size="sm"
Expand Down
2 changes: 1 addition & 1 deletion src/components/modify-habit-entry/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function ModifyHabitEntry({

return (
<>
<View className="flex flex-row">
<View className="flex flex-row justify-end">
{showAsNormalButton ? (
<Button
variant="outline"
Expand Down
71 changes: 68 additions & 3 deletions src/components/user-card.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
import { Link } from 'expo-router';
import { EllipsisIcon } from 'lucide-react-native';
import { useColorScheme } from 'nativewind';

import { type UserT } from '@/api';
import { Pressable, Text, View } from '@/ui';
import { colors, Pressable, Text, View } from '@/ui';
import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemTitle,
DropdownMenuRoot,
DropdownMenuTrigger,
} from '@/ui/dropdown-menu';

import UserPicture from './picture';

export default function UserCard({ data }: { data: UserT }) {
interface UserCardProps {
data: UserT;
showOwnerBadge?: boolean;
showManageOptions?: boolean;
onTransferOwnership?: () => void;
onKickUser?: () => void;
onPress?: () => void;
}

export default function UserCard({
data,
showOwnerBadge,
showManageOptions,
onTransferOwnership,
onKickUser,
onPress,
}: UserCardProps) {
const { colorScheme } = useColorScheme();

return (
<Link
push
Expand All @@ -18,8 +45,46 @@ export default function UserCard({ data }: { data: UserT }) {
}}
asChild
>
<Pressable className="my-1 rounded-3xl border border-stone-200 bg-white px-4 py-[10px] dark:border-stone-700 dark:bg-transparent">
<Pressable
className="my-1 flex-row items-center justify-between rounded-3xl border border-stone-200 bg-white px-4 py-[10px] dark:border-stone-700 dark:bg-transparent"
onPress={() => onPress?.()}
>
<UserImageNameAndUsername data={data} />
{showOwnerBadge && (
<Text className="text-xs text-stone-400 dark:text-stone-500">
Admin
</Text>
)}
{showManageOptions && (
<DropdownMenuRoot>
<DropdownMenuTrigger>
<Pressable>
<EllipsisIcon
size={24}
color={colorScheme === 'dark' ? colors.white : colors.black}
/>
</Pressable>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
key={'transfer'}
onSelect={onTransferOwnership}
destructive
>
<DropdownMenuItemTitle>
Transfer ownership
</DropdownMenuItemTitle>
</DropdownMenuItem>
<DropdownMenuItem
key={'remove'}
onSelect={onKickUser}
destructive
>
<DropdownMenuItemTitle>Remove from habit</DropdownMenuItemTitle>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenuRoot>
)}
</Pressable>
</Link>
);
Expand Down
2 changes: 1 addition & 1 deletion src/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const button = tv({
},
destructive: {
container:
'border border-red-200 bg-white dark:border-red-500 dark:bg-transparent',
'border border-red-200 bg-white dark:border-red-500 dark:bg-red-500/5',
label: 'font-medium text-red-500',
indicator: 'text-red-500',
},
Expand Down

0 comments on commit 55a6a59

Please sign in to comment.