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: add logic that lets users favorite a template #9713

Merged
merged 28 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e22cbbd
chore: update activity library custom tab empty state
nickoferrall Apr 23, 2024
6df1450
change var name to showResults instead of hideResults
nickoferrall Apr 23, 2024
d34002e
add favourite category
nickoferrall Apr 23, 2024
ad5ff86
add favorite icon
nickoferrall Apr 23, 2024
6cf0e57
can click on favorite icon
nickoferrall Apr 23, 2024
06152ae
only show favorite icon on activity grid
nickoferrall Apr 23, 2024
bdda429
show favourite icon in activity details
nickoferrall Apr 23, 2024
cd0f758
show tooltip on hover
nickoferrall Apr 23, 2024
b3334df
add favourite img
nickoferrall Apr 29, 2024
63d82ce
update favourite img
nickoferrall Apr 29, 2024
5f245f3
update favorite emoji color in al tabs
nickoferrall Apr 29, 2024
92d1d83
fix merge conflict
nickoferrall Apr 29, 2024
7e85cd1
implement addFavoriteTemplateIds migration
nickoferrall Apr 30, 2024
ac68c9a
update typeDef with favoriteTemplates
nickoferrall Apr 30, 2024
3aa7cb4
implement toggleFavoriteTemplate skeleton
nickoferrall Apr 30, 2024
86fe072
update favoriteTemplateids
nickoferrall May 2, 2024
630f8cf
favorite activities showing up
nickoferrall May 2, 2024
acbbdd8
update favs section after toggling
nickoferrall May 3, 2024
c6ebe25
implement favoriteTemplateIds dataloader
nickoferrall May 3, 2024
5ed46a9
bump font size of fav icon
nickoferrall May 3, 2024
4856285
update toggle success source type
nickoferrall May 3, 2024
7f1269b
use normalize in dataloader and adjust favorite icon position
nickoferrall May 7, 2024
70a055e
Merge branch 'master' into feat/9114/favorite-activities-ui
nickoferrall May 7, 2024
3e763da
fix merge conflict
nickoferrall May 7, 2024
d9cfbe0
update migration order
nickoferrall May 7, 2024
42a514e
remove vertical scroll from empty state
nickoferrall May 8, 2024
0b57e63
fix favorite icon height in safari
nickoferrall May 8, 2024
80310a9
fix merge conflicts
nickoferrall May 20, 2024
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
5 changes: 3 additions & 2 deletions codegen.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@
"ActionMeeting": "../../database/types/MeetingAction#default",
"ActionMeetingMember": "../../database/types/ActionMeetingMember#default as ActionMeetingMemberDB",
"AddApprovedOrganizationDomainsSuccess": "./types/AddApprovedOrganizationDomainsSuccess#AddApprovedOrganizationDomainsSuccessSource",
"AddPokerTemplateSuccess": "./types/AddPokerTemplateSuccess#AddPokerTemplateSuccessSource",
"AddReactjiToReactableSuccess": "./types/AddReactjiToReactableSuccess#AddReactjiToReactableSuccessSource",
"AddReflectTemplateSuccess": "./types/AddReflectTemplateSuccess#AddReflectTemplateSuccessSource",
"AddPokerTemplateSuccess": "./types/AddPokerTemplateSuccess#AddPokerTemplateSuccessSource",
"AddTranscriptionBotSuccess": "./types/AddTranscriptionBotSuccess#AddTranscriptionBotSuccessSource",
"AddedNotification": "./types/AddedNotification#AddedNotificationSource",
"AgendaItem": "../../database/types/AgendaItem#default as AgendaItemDB",
Expand Down Expand Up @@ -132,6 +132,7 @@
"TeamPromptResponse": "../../postgres/queries/getTeamPromptResponsesByIds#TeamPromptResponse",
"TemplateDimension": "../../database/types/TemplateDimension#default",
"TimelineEventTeamPromptComplete": "./types/TimelineEventTeamPromptComplete#TimelineEventTeamPromptCompleteSource",
"ToggleFavoriteTemplateSuccess": "./types/ToggleFavoriteTemplateSuccess#ToggleFavoriteTemplateSuccessSource",
"ToggleSummaryEmailSuccess": "./types/ToggleSummaryEmailSuccess#ToggleSummaryEmailSuccessSource",
"TopRetroTemplate": "./types/TopRetroTemplate#TopRetroTemplateSource",
"UpdateAutoJoinSuccess": "./types/UpdateAutoJoinSuccess#UpdateAutoJoinSuccessSource",
Expand All @@ -140,9 +141,9 @@
"UpdateFeatureFlagPayload": "./types/UpdateFeatureFlagPayload#UpdateFeatureFlagPayloadSource",
"UpdateGitLabDimensionFieldSuccess": "./types/UpdateGitLabDimensionFieldSuccess#UpdateGitLabDimensionFieldSuccessSource",
"UpdateMeetingPromptSuccess": "./types/UpdateMeetingPromptSuccess#UpdateMeetingPromptSuccessSource",
"UpdateMeetingTemplateSuccess": "./types/UpdateMeetingTemplateSuccess#UpdateMeetingTemplateSuccessSource",
"UpdateOrgPayload": "./types/UpdateOrgPayload#UpdateOrgPayloadSource",
"UpdateRecurrenceSettingsSuccess": "./types/UpdateRecurrenceSettingsSuccess#UpdateRecurrenceSettingsSuccessSource",
"UpdateMeetingTemplateSuccess": "./types/UpdateMeetingTemplateSuccess#UpdateMeetingTemplateSuccessSource",
"UpdateTaskPayload": "./types/UpdateTaskPayload#UpdateTaskPayloadSource",
"UpdateTemplateCategorySuccess": "./types/UpdateTemplateCategorySuccess#UpdateTemplateCategorySuccessSource",
"UpdateUserProfilePayload": "./types/UpdateUserProfilePayload#UpdateUserProfilePayloadSource",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
import {Favorite} from '@mui/icons-material'
import graphql from 'babel-plugin-relay/macro'
import clsx from 'clsx'
import React, {useState} from 'react'
import React from 'react'
import {useFragment} from 'react-relay'
import {ActivityCardFavorite_user$key} from '../../__generated__/ActivityCardFavorite_user.graphql'
import useAtmosphere from '../../hooks/useAtmosphere'
import useMutationProps from '../../hooks/useMutationProps'
import ToggleFavoriteTemplateMutation from '../../mutations/ToggleFavoriteTemplateMutation'
import {Tooltip} from '../../ui/Tooltip/Tooltip'
import {TooltipContent} from '../../ui/Tooltip/TooltipContent'
import {TooltipTrigger} from '../../ui/Tooltip/TooltipTrigger'

type Props = {
className?: string
templateId: string
viewerRef: ActivityCardFavorite_user$key
}

const ActivityCardFavorite = (props: Props) => {
const {className} = props
const [isSelected, setIsSelected] = useState(false)
const tooltipCopy = isSelected ? 'Remove from favorites' : 'Add to favorites'
const {className, templateId, viewerRef} = props
const atmosphere = useAtmosphere()
const {onError, onCompleted} = useMutationProps()

const viewer = useFragment(
graphql`
fragment ActivityCardFavorite_user on User {
favoriteTemplates {
id
}
}
`,
viewerRef
)
const favoriteTemplateIds = viewer.favoriteTemplates.map((template) => template.id)
const isFavorite = favoriteTemplateIds.includes(templateId)
const tooltipCopy = isFavorite ? 'Remove from favorites' : 'Add to favorites'

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
setIsSelected((prev) => !prev)
ToggleFavoriteTemplateMutation(atmosphere, {templateId}, {onError, onCompleted})
}

return (
Expand All @@ -32,7 +54,7 @@ const ActivityCardFavorite = (props: Props) => {
onClick={handleClick}
className='flex h-full w-full cursor-pointer items-center justify-center bg-transparent'
>
<Favorite className={isSelected ? 'text-rose-600' : 'text-slate-600'} />
<Favorite className={isFavorite ? 'text-rose-600' : 'text-slate-600'} />
</button>
</TooltipTrigger>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export const TemplateDetails = (props: Props) => {
const viewer = useFragment(
graphql`
fragment TemplateDetails_user on User {
...ActivityCardFavorite_user
preferredTeamId
teams {
...TeamPickerModal_teams
Expand Down Expand Up @@ -260,7 +261,11 @@ export const TemplateDetails = (props: Props) => {
<div className='flex items-center justify-between'>
<div className='py-2 text-sm font-semibold text-slate-600'>{description}</div>
<div className='flex items-center gap-2'>
<ActivityCardFavorite className='rounded-full border border-solid border-slate-400 hover:bg-slate-200' />
<ActivityCardFavorite
templateId={activityId}
viewerRef={viewer}
className='rounded-full border border-solid border-slate-400 hover:bg-slate-200'
/>
<div className='rounded-full border border-solid border-slate-400'>
<FlatButton
style={{padding: '8px 12px', border: '0'}}
Expand Down
23 changes: 21 additions & 2 deletions packages/client/components/ActivityLibrary/ActivityGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import graphql from 'babel-plugin-relay/macro'
import React from 'react'
import {useFragment} from 'react-relay'
import {Link} from 'react-router-dom'
import {ActivityGrid_user$key} from '../../__generated__/ActivityGrid_user.graphql'
import {ActivityBadge} from './ActivityBadge'
import {ActivityCard, ActivityCardImage} from './ActivityCard'
import ActivityCardFavorite from './ActivityCardFavorite'
Expand All @@ -10,9 +13,19 @@ import {CATEGORY_THEMES, CategoryID} from './Categories'
interface ActivityGridProps {
templates: Template[]
selectedCategory: string
viewerRef?: ActivityGrid_user$key
}

const ActivityGrid = ({templates, selectedCategory}: ActivityGridProps) => {
const ActivityGrid = (props: ActivityGridProps) => {
const {templates, selectedCategory, viewerRef} = props
const viewer = useFragment(
graphql`
fragment ActivityGrid_user on User {
...ActivityCardFavorite_user
}
`,
viewerRef ?? null
)
return (
<>
{templates.map((template) => {
Expand Down Expand Up @@ -43,7 +56,13 @@ const ActivityGrid = ({templates, selectedCategory}: ActivityGridProps) => {
src={template.illustrationUrl}
category={template.category as CategoryID}
/>
<ActivityCardFavorite className='absolute bottom-3 right-3' />
{viewer && (
<ActivityCardFavorite
templateId={template.id}
className='absolute bottom-2 right-2'
viewerRef={viewer}
/>
)}
<ActivityLibraryCardDescription
className='hidden group-hover/card:flex'
templateRef={template}
Expand Down
11 changes: 10 additions & 1 deletion packages/client/components/ActivityLibrary/ActivityLibrary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ graphql`
const query = graphql`
query ActivityLibraryQuery {
viewer {
...ActivityGrid_user
favoriteTemplates {
...ActivityLibrary_template @relay(mask: false)
}
availableTemplates(first: 2000) @connection(key: "ActivityLibrary_availableTemplates") {
edges {
node {
Expand Down Expand Up @@ -240,6 +244,9 @@ export const ActivityLibrary = (props: Props) => {
// If there's a search query, just use the search filter results
return filteredTemplates
}
if (categoryId === 'favorite') {
return viewer.favoriteTemplates
}

return filteredTemplates.filter((template) =>
categoryId === QUICK_START_CATEGORY_ID
Expand Down Expand Up @@ -344,7 +351,7 @@ export const ActivityLibrary = (props: Props) => {
style={{
color:
category === 'favorite'
? category === categoryId
? category === categoryId && searchQuery.length === 0
? 'white'
: 'red'
: undefined
Expand Down Expand Up @@ -388,6 +395,7 @@ export const ActivityLibrary = (props: Props) => {
<ActivityGrid
templates={subCategoryTemplates}
selectedCategory={categoryId}
viewerRef={viewer}
/>
</div>
</Fragment>
Expand All @@ -400,6 +408,7 @@ export const ActivityLibrary = (props: Props) => {
<ActivityGrid
templates={templatesToRender as Template[]}
selectedCategory={categoryId}
viewerRef={viewer}
/>
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ const ActivityLibraryEmptyState = (props: Props) => {

if (categoryId === 'favorite') {
return (
<div className='relative mx-auto flex p-2 text-slate-700'>
<div className='md:p-18 p-4 xl:p-24'>
<img className='w-82' src={favoriteImg} alt='Favorite placeholder' />
<div className='relative mx-auto flex justify-center p-2 align-middle text-slate-700'>
<div className='p-4 md:p-0 '>
<img
className='w-[500px] md:w-[700px] lg:w-[900px]'
src={favoriteImg}
alt='Favorite placeholder'
/>
<div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform'>
<div className='flex flex-col items-center'>
<FavoriteIcon
Expand Down
3 changes: 1 addition & 2 deletions packages/client/components/ActivityLibrary/Categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ export const CATEGORY_ID_TO_NAME: Record<AllCategoryID, string | JSX.Element> =
style={{
color: 'inherit',
display: 'flex',
alignItems: 'center',
fontSize: '18px'
fontSize: '22px'
}}
/>
),
Expand Down
41 changes: 41 additions & 0 deletions packages/client/mutations/ToggleFavoriteTemplateMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import graphql from 'babel-plugin-relay/macro'
import {commitMutation} from 'react-relay'
import {ToggleFavoriteTemplateMutation as TToggleFavoriteTemplateMutation} from '../__generated__/ToggleFavoriteTemplateMutation.graphql'
import {StandardMutation} from '../types/relayMutations'

graphql`
fragment ToggleFavoriteTemplateMutation_viewer on ToggleFavoriteTemplateSuccess {
user {
id
...ActivityCardFavorite_user
}
}
`

const mutation = graphql`
mutation ToggleFavoriteTemplateMutation($templateId: ID!) {
toggleFavoriteTemplate(templateId: $templateId) {
... on ErrorPayload {
error {
message
}
}
...ToggleFavoriteTemplateMutation_viewer @relay(mask: false)
}
}
`

const ToggleFavoriteTemplateMutation: StandardMutation<TToggleFavoriteTemplateMutation> = (
atmosphere,
variables,
{onError, onCompleted}
) => {
return commitMutation<TToggleFavoriteTemplateMutation>(atmosphere, {
mutation,
variables,
onCompleted,
onError
})
}

export default ToggleFavoriteTemplateMutation
4 changes: 4 additions & 0 deletions packages/server/database/types/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface Input {
id?: string
preferredName: string
email: string
favoriteTemplateIds?: string[]
featureFlags?: string[]
lastSeenAt?: Date
lastSeenAtURLs?: string[]
Expand All @@ -28,6 +29,7 @@ export default class User {
id: string
preferredName: string
email: string
favoriteTemplateIds: string[]
featureFlags: string[]
lastSeenAt: Date
lastSeenAtURLs: string[] | null
Expand Down Expand Up @@ -55,6 +57,7 @@ export default class User {
createdAt,
picture,
updatedAt,
favoriteTemplateIds,
featureFlags,
lastSeenAt,
lastSeenAtURLs,
Expand All @@ -73,6 +76,7 @@ export default class User {
this.createdAt = createdAt || now
this.picture = picture
this.updatedAt = updatedAt || now
this.favoriteTemplateIds = favoriteTemplateIds || []
this.featureFlags = featureFlags || []
this.identities = identities || []
this.inactive = inactive || false
Expand Down
21 changes: 21 additions & 0 deletions packages/server/dataloader/customLoaderMakers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,27 @@ export const isCompanyDomain = (parent: RootDataLoader) => {
)
}

export const favoriteTemplateIds = (parent: RootDataLoader) => {
return new DataLoader<string, string[], string>(
async (userIds) => {
const pg = getKysely()
const users = await pg
.selectFrom('User')
.select(['id', 'favoriteTemplateIds'])
.where('id', 'in', userIds)
.execute()

const userIdToFavoriteTemplateIds = new Map(
users.map((user) => [user.id, user.favoriteTemplateIds])
)
return userIds.map((userId) => userIdToFavoriteTemplateIds.get(userId) || [])
nickoferrall marked this conversation as resolved.
Show resolved Hide resolved
},
{
...parent.dataLoaderOptions
}
)
}

export const fileStoreAsset = (parent: RootDataLoader) => {
return new DataLoader<string, string, string>(
async (urls) => {
Expand Down
37 changes: 37 additions & 0 deletions packages/server/graphql/public/mutations/toggleFavoriteTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import getKysely from '../../../postgres/getKysely'
import {getUserId} from '../../../utils/authorization'
import {MutationResolvers} from '../resolverTypes'

const toggleFavoriteTemplate: MutationResolvers['toggleFavoriteTemplate'] = async (
_source,
{templateId},
{authToken, dataLoader}
) => {
const viewerId = getUserId(authToken)
const pg = getKysely()
const userId = getUserId(authToken)

const favoriteTemplateIds = await dataLoader.get('favoriteTemplateIds').load(viewerId)

let updatedFavoriteTemplateIds

const isCurrentlyFavorite = favoriteTemplateIds.includes(templateId)

if (isCurrentlyFavorite) {
updatedFavoriteTemplateIds = favoriteTemplateIds.filter((id) => id !== templateId)
} else {
updatedFavoriteTemplateIds = [...favoriteTemplateIds, templateId]
}

await pg
.updateTable('User')
.set({
favoriteTemplateIds: updatedFavoriteTemplateIds
})
.where('id', '=', userId)
.execute()

return true
}

export default toggleFavoriteTemplate
5 changes: 5 additions & 0 deletions packages/server/graphql/public/typeDefs/User.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ type User {
"""
email: Email!

"""
The user's favorite meeting templates
"""
favoriteTemplates: [MeetingTemplate!]!

"""
Any super power given to the user via a super user
"""
Expand Down
Loading
Loading