|
| 1 | +import { getErrorMessage } from '@common/utilities/error-handler' |
| 2 | +import { gql, pocketRequest } from '@common/utilities/pocket-request' |
| 3 | + |
| 4 | +import { getClaims, verifySession } from '../user-info/session' |
| 5 | + |
| 6 | +import { FRAGMENT_ITEM_PREVIEW } from '../_fragments/preview' |
| 7 | +import { FRAGMENT_ITEM_SAVED_INFO } from '../_fragments/saved-info' |
| 8 | + |
| 9 | +// Types |
| 10 | +import type { ResponseError } from '@common/types' |
| 11 | +import type { |
| 12 | + Item, |
| 13 | + PageInfo, |
| 14 | + PaginationInput, |
| 15 | + PocketMetadata, |
| 16 | + SavedItem, |
| 17 | + SavedItemConnection, |
| 18 | + SavedItemsFilter, |
| 19 | + SavedItemsPage, |
| 20 | + SavedItemsSort, |
| 21 | + User |
| 22 | +} from '@common/types/pocket' |
| 23 | + |
| 24 | +// Custom Types for this return |
| 25 | +export interface UserSavesQueryResponse { |
| 26 | + itemsById: Record<string, PocketMetadata> |
| 27 | + saveDataById: Record<string, SavedItem> |
| 28 | + savePageIds: string[] |
| 29 | + savePageInfo: PageInfo & { |
| 30 | + totalCount: number |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +interface UserSavesArguments { |
| 35 | + pagination: PaginationInput |
| 36 | + sort?: SavedItemsSort |
| 37 | + filter?: SavedItemsFilter |
| 38 | +} |
| 39 | + |
| 40 | +const getUserSavesQuery = gql` |
| 41 | + query UserSaves($pagination: PaginationInput, $sort: SavedItemsSort, $filter: SavedItemsFilter) { |
| 42 | + user { |
| 43 | + savedItems(pagination: $pagination, sort: $sort, filter: $filter) { |
| 44 | + totalCount |
| 45 | + pageInfo { |
| 46 | + hasNextPage |
| 47 | + endCursor |
| 48 | + hasPreviousPage |
| 49 | + startCursor |
| 50 | + } |
| 51 | + edges { |
| 52 | + node { |
| 53 | + item { |
| 54 | + ... on Item { |
| 55 | + itemId |
| 56 | + preview { |
| 57 | + ...ItemPreviewFragment |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + ...ItemSavedFragment |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + } |
| 67 | + ${FRAGMENT_ITEM_PREVIEW} |
| 68 | + ${FRAGMENT_ITEM_SAVED_INFO} |
| 69 | +` |
| 70 | + |
| 71 | +/** |
| 72 | + * getUserSaves |
| 73 | + * --- |
| 74 | + * Returns users saves |
| 75 | + */ |
| 76 | +export async function getUserSaves( |
| 77 | + variables: UserSavesArguments |
| 78 | +): Promise<UserSavesQueryResponse | ResponseError> { |
| 79 | + try { |
| 80 | + const authResponse = await verifySession() |
| 81 | + |
| 82 | + const { token, isAuthenticated } = authResponse |
| 83 | + |
| 84 | + if (!isAuthenticated) throw new PageSavesError('User not authenticated') |
| 85 | + |
| 86 | + const response = await pocketRequest<{ user: Pick<User, 'savedItems'> }>( |
| 87 | + { |
| 88 | + query: getUserSavesQuery, |
| 89 | + variables |
| 90 | + }, |
| 91 | + token |
| 92 | + ) |
| 93 | + |
| 94 | + const savedItems = response?.user?.savedItems |
| 95 | + if (!savedItems) throw new PageSavesError('Issue retrieving user saves ') |
| 96 | + |
| 97 | + const { edges, pageInfo, totalCount } = savedItems |
| 98 | + |
| 99 | + const convertedEdges = edges |
| 100 | + ?.map((edge) => { |
| 101 | + const { item, ...savedData } = edge?.node || {} |
| 102 | + |
| 103 | + // We are gonna filter pending items out |
| 104 | + // ?? NOTE: Pending item was a future pattern to separate parser response |
| 105 | + // ?? from metadata. As it stands, this is not fully implemented, but we |
| 106 | + // ?? will still handle this case for type safety |
| 107 | + if (item?.__typename === 'PendingItem') return false |
| 108 | + |
| 109 | + const { preview, itemId, shareId } = item as Item |
| 110 | + return { preview: { ...preview, itemId }, savedData } |
| 111 | + }) |
| 112 | + .filter(Boolean) |
| 113 | + |
| 114 | + const itemsById = convertedEdges.reduce((previous, current) => { |
| 115 | + const { preview, saveData } = current |
| 116 | + return { ...previous, [preview.itemId]: preview } |
| 117 | + }, {}) |
| 118 | + |
| 119 | + const saveDataById = convertedEdges.reduce((previous, current) => { |
| 120 | + const { preview, saveData } = current |
| 121 | + return { ...previous, [preview.itemId]: saveData } |
| 122 | + }, {}) |
| 123 | + |
| 124 | + const savePageIds = edges.map((edge) => edge?.node?.item?.itemId) |
| 125 | + |
| 126 | + return { |
| 127 | + itemsById, |
| 128 | + saveDataById, |
| 129 | + savePageIds, |
| 130 | + savePageInfo: { ...pageInfo, totalCount } |
| 131 | + } |
| 132 | + } catch (error) { |
| 133 | + return { responseError: getErrorMessage(error) } |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +/** |
| 138 | + * UserRequestError |
| 139 | + * --- |
| 140 | + * Generic UserRequestError to make visibility more useful |
| 141 | + */ |
| 142 | +class PageSavesError extends Error { |
| 143 | + constructor(message?: string) { |
| 144 | + super(message) |
| 145 | + this.name = 'PageSavesError' |
| 146 | + } |
| 147 | +} |
0 commit comments