Skip to content

Commit

Permalink
refactor: restore tagging feature
Browse files Browse the repository at this point in the history
  • Loading branch information
viet nguyen committed Jun 11, 2023
1 parent 52a8fb4 commit bbe49be
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 71 deletions.
7 changes: 3 additions & 4 deletions src/components/media/AddTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ export default function AddTag ({ mediaWithTags, onCancel, label, openSearch = f
: (props as TypesenseAreaType).id

await tagPhotoCmd({
mediaUuid: mediaWithTags.mediaUrl,
mediaUrl: mediaWithTags.mediaUrl,
destinationId: linkedEntityId,
destType: props.type === EntityType.climb ? TagTargetType.climb : TagTargetType.area
mediaId: mediaWithTags.id,
entityId: linkedEntityId,
entityType: props.type === EntityType.climb ? TagTargetType.climb : TagTargetType.area
})
} catch (e) {
// TODO: Add friendly error message
Expand Down
13 changes: 8 additions & 5 deletions src/components/media/Tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import NetworkSquareIcon from '../../assets/icons/network-square-icon.svg'

import clx from 'classnames'
import { EntityTag, TagTargetType } from '../../js/types'
import { onDeleteCallback } from '../../js/hooks/useDeleteTagBackend'

interface PhotoTagProps {
mediaId: string
tag: EntityTag
onDelete: (tagId: string) => void
onDelete: onDeleteCallback
isAuthorized?: boolean
showDelete?: boolean
size?: 'md' | 'lg'
}

export default function Tag ({ tag, onDelete, size = 'md', showDelete = false, isAuthorized = false }: PhotoTagProps): JSX.Element | null {
export default function Tag ({ mediaId, tag, onDelete, size = 'md', showDelete = false, isAuthorized = false }: PhotoTagProps): JSX.Element | null {
const [url, name] = resolver(tag)
if (url == null || name == null) return null
const isArea = tag.type === TagTargetType.area
Expand All @@ -34,10 +36,11 @@ export default function Tag ({ tag, onDelete, size = 'md', showDelete = false, i
<div className='mt-0.5 whitespace-nowrap truncate text-sm'>{name}</div>
{isAuthorized && showDelete &&
<button
disabled
onClick={(e) => {
onDelete(tag.targetId)
onClick={async (e) => {
e.preventDefault()
await onDelete({ mediaId: mediaId, tagId: tag.id })
// e.stopPropagation()
// e.preventDefault()
}}
title='Delete tag'
>
Expand Down
2 changes: 2 additions & 0 deletions src/components/media/TagList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default function TagList ({ mediaWithTags, isAuthorized = false, isAuthen
{entityTags.map((tag: EntityTag) =>
<Tag
key={`${tag.targetId}`}
mediaId={mediaWithTags.id}
tag={tag}
onDelete={onDelete}
isAuthorized={isAuthorized}
Expand Down Expand Up @@ -81,6 +82,7 @@ export const MobilePopupTagList: React.FC<TagListProps> = ({ mediaWithTags, isAu
{entityTags.map(tag => (
<PrimitiveDropdownMenuItem key={`${tag.id}`} className='px-2 py-3'>
<Tag
mediaId={mediaWithTags.id}
tag={tag}
isAuthorized={isAuthorized}
onDelete={onDelete}
Expand Down
18 changes: 8 additions & 10 deletions src/components/media/UserGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useCallback, useState, useEffect } from 'react'
import { useRouter } from 'next/router'
import { basename } from 'path'
import clx from 'classnames'
import InfiniteScroll from 'react-infinite-scroll-component'

import UserMedia from './UserMedia'
import MobileMediaCard from './MobileMediaCard'
Expand All @@ -10,21 +11,17 @@ import UploadCTA from './UploadCTA'
import { actions } from '../../js/stores'
import SlideViewer from './slideshow/SlideViewer'
import { TinyProfile } from '../users/PublicProfile'
import { WithPermission, UserPublicPage } from '../../js/types/User'
import { UserPublicPage } from '../../js/types/User'
import { useResponsive } from '../../js/hooks'
import TagList from './TagList'
import InfiniteScroll from 'react-infinite-scroll-component'
import usePermissions from '../../js/hooks/auth/usePermissions'

export interface UserGalleryProps {
uid: string
userPublicPage: UserPublicPage
postId: string | null
}

const auth: WithPermission = {
isAuthenticated: false,
isAuthorized: false
}
/**
* Image gallery on user profile.
* Simplifying component Todos:
Expand All @@ -40,7 +37,8 @@ export default function UserGallery ({ uid, postId: initialPostId, userPublicPag

const { isMobile } = useResponsive()

const { isAuthorized } = auth
const authz = usePermissions({ currentUserUuid: userProfile?.userUuid })
const { isAuthorized } = authz

const baseUrl = `/u/${uid}`

Expand Down Expand Up @@ -152,7 +150,7 @@ export default function UserGallery ({ uid, postId: initialPostId, userPublicPag
key={key}
mediaWithTags={mediaWithTags}
showTagActions
{...auth}
{...authz}
/>
)
}
Expand All @@ -178,7 +176,7 @@ export default function UserGallery ({ uid, postId: initialPostId, userPublicPag
<TagList
key={key}
mediaWithTags={mediaWithTags}
{...auth}
{...authz}
showDelete
/>
</div>
Expand All @@ -199,7 +197,7 @@ export default function UserGallery ({ uid, postId: initialPostId, userPublicPag
userProfile={userProfile} onClick={slideViewerCloseHandler}
/>}
onClose={slideViewerCloseHandler}
auth={auth}
auth={authz}
baseUrl={baseUrl}
onNavigate={navigateHandler}
/>}
Expand Down
62 changes: 33 additions & 29 deletions src/js/graphql/gql/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,19 @@ export const FRAGMENT_AREA_TAG = gql`
}
}`

export const FRAGMENT_ENTITY_TAG = gql`
fragment EntityTagFields on EntityTag {
id
targetId
climbName
areaName
ancestors
type
}
`

export const FRAGMENT_MEDIA_WITH_TAGS = gql`
${FRAGMENT_ENTITY_TAG}
fragment MediaWithTagsFields on MediaWithTags {
id
username
Expand All @@ -47,49 +59,41 @@ export const FRAGMENT_MEDIA_WITH_TAGS = gql`
height
uploadTime
entityTags {
targetId
climbName
areaName
ancestors
type
... EntityTagFields
}
}`

export interface SetTagType {
mediaUuid: string
mediaUrl: string
destinationId: string
destType: TagTargetType
mediaId: string
entityId: string
entityType: TagTargetType
}
/**
* Create a media <--> climb (or area) association
* {mediaId: "645aa64261c73112fc19b4fd", entityId: "a5364f01-4d10-5e35-98eb-e58f2c613ce4", entityType: 0
*/
export const MUTATION_ADD_CLIMB_TAG_TO_MEDIA = gql`
${FRAGMENT_CLIMB_TAG}
${FRAGMENT_AREA_TAG}
mutation tagPhotoWithClimb($mediaUuid: ID!, $mediaUrl: String!, $destinationId: ID!, $destType: Int!) {
setTag(
export const MUTATION_ADD_ENTITY_TAG = gql`
${FRAGMENT_ENTITY_TAG}
mutation addEntityTag($mediaId: ID!, $entityId: ID!, $entityType: Int!) {
addEntityTag(
input: {
mediaUuid: $mediaUuid,
mediaUrl: $mediaUrl,
mediaType: 0,
destinationId: $destinationId,
destType: $destType
mediaId: $mediaId,
entityId: $entityId,
entityType: $entityType
}
) {
... ClimbTagFields
... AreaTagFields
... EntityTagFields
}
}`

export const MUTATION_REMOVE_MEDIA_TAG = gql`
mutation removeTag($tagId: ID!) {
removeTag(tagId: $tagId) {
id
mediaUuid
destinationId
destType
}
export const MUTATION_REMOVE_ENTITY_TAG = gql`
mutation removeEntityTag($mediaId: ID!, $tagId: ID!) {
removeEntityTag(
input: {
mediaId: $mediaId,
tagId: $tagId
}
)
}`

export const QUERY_TAGS_BY_MEDIA_ID = gql`
Expand Down
1 change: 0 additions & 1 deletion src/js/hooks/auth/usePermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useSession } from 'next-auth/react'
import { WithPermission } from '../../types/User'

interface PermissionsProps {
// ownerProfileOnPage: UserPublicProfile
currentUserUuid?: string
}

Expand Down
39 changes: 25 additions & 14 deletions src/js/hooks/useDeleteTagBackend.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { useSession } from 'next-auth/react'
import { useMutation } from '@apollo/client'

import { MUTATION_REMOVE_MEDIA_TAG } from '../graphql/gql/tags'
import { toast } from 'react-toastify'
import { MUTATION_REMOVE_ENTITY_TAG } from '../graphql/gql/tags'
import { graphqlClient } from '../graphql/Client'
import { actions } from '../../js/stores'

interface ReturnType {
onDelete: (tagId: string) => Promise<void>
onDelete: onDeleteCallback
}

export type onDeleteCallback = (props: RemoveTagMutationProps) => Promise<void>

interface RemoveTagMutationProps {
mediaId: string
tagId: string
}

Expand All @@ -20,7 +23,7 @@ export interface DeleteTagResult {
}

export interface GQLRemoveTagType {
removeTag: DeleteTagResult
removeTag: boolean
}

/**
Expand All @@ -29,25 +32,33 @@ export interface GQLRemoveTagType {
* @returns see type declaration
*/
export default function useDeleteTagBackend (): ReturnType {
const session = useSession({ required: true })
const onCompletedHandler = async (data: GQLRemoveTagType): Promise<void> => {
if (data?.removeTag == null) return
await actions.media.removeTag(data.removeTag)
if (!data.removeTag) return
toast.success('Tag removed')
// await actions.media.removeTag(data.removeTag)
}

// eslint-disable-next-line
const [removeTag] = useMutation<GQLRemoveTagType, RemoveTagMutationProps>(
MUTATION_REMOVE_MEDIA_TAG, {
MUTATION_REMOVE_ENTITY_TAG, {
client: graphqlClient,
onCompleted: onCompletedHandler
}
)

const onDelete = async (tagId: string): Promise<void> => {
// await removeTag({
// variables: {
// tagId
// }
// })
const onDelete: onDeleteCallback = async ({ mediaId, tagId }) => {
await removeTag({
variables: {
mediaId,
tagId
},
context: {
headers: {
authorization: `Bearer ${session.data?.accessToken ?? ''}`
}
}
})
}

return { onDelete }
Expand Down
26 changes: 18 additions & 8 deletions src/js/hooks/usePhotoTagCmd.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useMutation } from '@apollo/client'
import { useSession } from 'next-auth/react'
import { toast } from 'react-toastify'

import { graphqlClient } from '../graphql/Client'
import { MUTATION_ADD_CLIMB_TAG_TO_MEDIA, SetTagType } from '../graphql/gql/tags'
import { MUTATION_ADD_ENTITY_TAG, SetTagType } from '../graphql/gql/tags'
import { actions } from '../stores'

export interface UsePhotTagReturn {
Expand All @@ -13,23 +15,31 @@ export interface UsePhotTagReturn {
* Todo: Move `useDeleteTagBanckend()` here.
*/
export default function usePhotoTagCmd (): UsePhotTagReturn {
const session = useSession({ required: true })
const addTagToLocalStore = async (data: any): Promise<void> => await actions.media.addTag(data)

// eslint-disable-next-line
const [tagPhoto] = useMutation<any, SetTagType>(
MUTATION_ADD_CLIMB_TAG_TO_MEDIA, {
MUTATION_ADD_ENTITY_TAG, {
client: graphqlClient,
errorPolicy: 'none',
onError: error => console.log('Error adding tag: ', error.message), // Todo: send a Toast message
onCompleted: addTagToLocalStore // Todo: send a Toast message
onError: error => toast.error(error.message),
onCompleted: async (data) => {
await addTagToLocalStore(data)
toast.success('Tag added.')
}
}
)

const tagPhotoCmd = async (props: SetTagType): Promise<any> => {
console.log('Tagging temporarily disabled')
// await tagPhoto({
// variables: props
// })
await tagPhoto({
variables: props,
context: {
headers: {
authorization: `Bearer ${session.data?.accessToken ?? ''}`
}
}
})
}

return { tagPhotoCmd }
Expand Down

0 comments on commit bbe49be

Please sign in to comment.