-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
43251b9
commit d503994
Showing
75 changed files
with
4,637 additions
and
203 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import type { FC } from "react"; | ||
import type { ButtonProps } from "@mantine/core"; | ||
import { | ||
ActivityCreateButtonActivityFragment, | ||
CreateActivityMutationDocument, | ||
} from "~/helpers/graphql"; | ||
|
||
export type ActivityCreateButtonProps = Omit< | ||
ButtonProps, | ||
"loading" | "onClick" | ||
> & { | ||
readonly googleEventId: string; | ||
readonly onCreate: (activity: ActivityCreateButtonActivityFragment) => void; | ||
}; | ||
|
||
const ActivityCreateButton: FC<ActivityCreateButtonProps> = ({ | ||
googleEventId, | ||
onCreate, | ||
children, | ||
...otherProps | ||
}) => { | ||
// == Mutation | ||
const onError = useApolloAlertCallback("Failed to create activity"); | ||
const [runMutation, { loading }] = useMutation( | ||
CreateActivityMutationDocument, | ||
{ | ||
onCompleted: ({ payload: { activity, errors } }) => { | ||
if (activity) { | ||
showNotice({ message: "Activity created successfully." }); | ||
onCreate(activity); | ||
} else { | ||
invariant(errors, "Missing input field errors"); | ||
const formErrors = parseFormErrors(errors); | ||
showFormErrorsAlert(formErrors, "Couldn't create activity"); | ||
} | ||
}, | ||
onError, | ||
}, | ||
); | ||
|
||
return ( | ||
<Button | ||
leftIcon={<AddIcon />} | ||
onClick={() => { | ||
runMutation({ | ||
variables: { | ||
input: { | ||
googleEventId, | ||
}, | ||
}, | ||
}); | ||
}} | ||
{...{ loading }} | ||
{...otherProps} | ||
> | ||
{children ?? "Create Activity"} | ||
</Button> | ||
); | ||
}; | ||
|
||
export default ActivityCreateButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import type { FC, JSXElementConstructor } from "react"; | ||
import Linkify from "linkify-react"; | ||
import humanizeDuration from "humanize-duration"; | ||
import RightArrowIcon from "~icons/heroicons/arrow-right-20-solid"; | ||
|
||
import { Text, MantineProvider } from "@mantine/core"; | ||
import type { BoxProps, TextProps } from "@mantine/core"; | ||
|
||
import type { | ||
ActivityCreateButtonActivityFragment, | ||
GoogleEventCardEventFragment, | ||
} from "~/helpers/graphql"; | ||
|
||
import ActivityCreateButton from "./ActivityCreateButton"; | ||
|
||
export type GoogleEventCardProps = Omit<BoxProps, "children"> & { | ||
readonly event: GoogleEventCardEventFragment; | ||
readonly onCreateActivity: ( | ||
activity: ActivityCreateButtonActivityFragment, | ||
) => void; | ||
}; | ||
|
||
const durationHumanizer = humanizeDuration.humanizer({ | ||
language: "shortEn", | ||
languages: { | ||
shortEn: { | ||
y: () => "y", | ||
mo: () => "mo", | ||
w: () => "w", | ||
d: () => "d", | ||
h: () => "h", | ||
m: () => "m", | ||
s: () => "s", | ||
ms: () => "ms", | ||
}, | ||
}, | ||
spacer: "", | ||
delimiter: " ", | ||
}); | ||
|
||
const GoogleEventCard: FC<GoogleEventCardProps> = ({ | ||
event: { | ||
id: eventId, | ||
title, | ||
description, | ||
start, | ||
durationSeconds, | ||
activity, | ||
viewerIsOrganizer, | ||
}, | ||
onCreateActivity, | ||
...otherProps | ||
}) => { | ||
return ( | ||
<Card radius="md" withBorder {...otherProps}> | ||
<Group align="start"> | ||
<Text weight={500} sx={{ flexGrow: 1 }}> | ||
{title} | ||
</Text> | ||
<MantineProvider | ||
inherit | ||
theme={{ | ||
components: { | ||
Text: { | ||
defaultProps: { | ||
size: "sm", | ||
color: "dimmed", | ||
lh: 1.4, | ||
}, | ||
}, | ||
}, | ||
}} | ||
> | ||
<Group spacing={6} noWrap> | ||
<Box> | ||
<Time format={{ month: "short", day: "numeric" }}>{start}</Time> | ||
</Box> | ||
<Box> | ||
<Time | ||
format={{ | ||
hour: "numeric", | ||
minute: "numeric", | ||
hour12: true, | ||
}} | ||
> | ||
{start} | ||
</Time> | ||
</Box> | ||
<Text | ||
span | ||
color="gray.4" | ||
sx={({ fontFamilyMonospace }) => ({ | ||
fontFamily: fontFamilyMonospace, | ||
})} | ||
> | ||
{" / "} | ||
</Text>{" "} | ||
<Text>{durationHumanizer(durationSeconds * 1000)}</Text> | ||
</Group> | ||
</MantineProvider> | ||
</Group> | ||
{!!description && ( | ||
<Linkify<TextProps, JSXElementConstructor<TextProps>> | ||
as={Text} | ||
options={{ | ||
render: ({ content, attributes }) => ( | ||
<Anchor | ||
target="_blank" | ||
rel="noopener noreferrer nofollow" | ||
{...attributes} | ||
> | ||
{content} | ||
</Anchor> | ||
), | ||
}} | ||
size="sm" | ||
color="dimmed" | ||
lineClamp={4} | ||
sx={{ whiteSpace: "pre-wrap" }} | ||
> | ||
{description} | ||
</Linkify> | ||
)} | ||
<Space h={6} /> | ||
{activity ? ( | ||
<Button | ||
component={Link} | ||
href={activity.url} | ||
leftIcon={<RightArrowIcon />} | ||
radius="md" | ||
> | ||
Go To Activity | ||
</Button> | ||
) : ( | ||
<Tooltip | ||
label="right now, you must be the event organizer to create an activity :(" | ||
withArrow | ||
disabled={viewerIsOrganizer} | ||
> | ||
<Box display="inline-block"> | ||
<ActivityCreateButton | ||
googleEventId={eventId} | ||
onCreate={onCreateActivity} | ||
disabled={!viewerIsOrganizer} | ||
radius="md" | ||
/> | ||
</Box> | ||
</Tooltip> | ||
)} | ||
</Card> | ||
); | ||
}; | ||
|
||
export default GoogleEventCard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import type { FC } from "react"; | ||
import type { BoxProps } from "@mantine/core"; | ||
|
||
import { GoogleEventsQueryDocument } from "~/helpers/graphql"; | ||
|
||
import GoogleEventCard, { GoogleEventCardProps } from "./GoogleEventCard"; | ||
|
||
export type GoogleEventsProps = Omit<BoxProps, "children"> & | ||
Pick<GoogleEventCardProps, "onCreateActivity">; | ||
|
||
const GoogleEvents: FC<GoogleEventsProps> = ({ | ||
onCreateActivity, | ||
...otherProps | ||
}) => { | ||
// == Search | ||
const [search, setSearch] = useState(""); | ||
const [debouncedSearch] = useDebouncedValue(search, 200); | ||
|
||
// == Query | ||
const onError = useApolloAlertCallback("Failed to load events"); | ||
const { data } = useQuery(GoogleEventsQueryDocument, { | ||
variables: { | ||
query: debouncedSearch, | ||
}, | ||
onError, | ||
}); | ||
const { googleEvents: events } = data?.viewer ?? {}; | ||
|
||
// == Markup | ||
return ( | ||
<Stack spacing={8} {...otherProps}> | ||
<Title order={2} size="h3"> | ||
Your Events | ||
</Title> | ||
<Stack spacing="xs"> | ||
<TextInput | ||
variant="filled" | ||
size="md" | ||
placeholder="Search events..." | ||
value={search} | ||
radius="md" | ||
onChange={({ target }) => setSearch(target.value)} | ||
/> | ||
{events ? ( | ||
!isEmpty(events) ? ( | ||
events.map(event => ( | ||
<GoogleEventCard | ||
key={event.id} | ||
{...{ event, onCreateActivity }} | ||
/> | ||
)) | ||
) : ( | ||
<EmptyCard itemLabel="events" radius="md" /> | ||
) | ||
) : ( | ||
[...new Array(3)].map((value, index) => ( | ||
<Skeleton key={index} radius="md" h={64} /> | ||
)) | ||
)} | ||
</Stack> | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default GoogleEvents; |
Oops, something went wrong.