From 329cdb8ebc2c7c4a9bfe21dfad5bc52f81cd0eb6 Mon Sep 17 00:00:00 2001 From: loischen68 Date: Fri, 8 Dec 2023 21:04:34 +0800 Subject: [PATCH] bugfix: add permission & adjust program hook --- src/components/audio/AudioPlayer.tsx | 7 +- src/components/audio/GlobalAudioPlayer.tsx | 156 +++++--- src/components/audio/PlaylistOverlay.tsx | 151 ++++--- src/hasura.d.ts | 438 ++++++++++++++++++++- src/hooks/program.ts | 83 +++- src/types/program.ts | 1 - 6 files changed, 715 insertions(+), 121 deletions(-) diff --git a/src/components/audio/AudioPlayer.tsx b/src/components/audio/AudioPlayer.tsx index 5dbed6bde..8ceb10d03 100644 --- a/src/components/audio/AudioPlayer.tsx +++ b/src/components/audio/AudioPlayer.tsx @@ -372,7 +372,12 @@ const AudioPlayer: React.VFC<{ audioUrl: string mode: AudioPlayerMode lastProgress: number | undefined - playList: (ProgramContent & { programId?: string; contentSectionTitle?: string; progress?: number })[] + playList: (ProgramContent & { + programId?: string + contentSectionTitle?: string + progress?: number + isLock: boolean + })[] currentIndex: number mimeType: string onPlay: (state: boolean) => void diff --git a/src/components/audio/GlobalAudioPlayer.tsx b/src/components/audio/GlobalAudioPlayer.tsx index 1bf3576ee..16e47f094 100644 --- a/src/components/audio/GlobalAudioPlayer.tsx +++ b/src/components/audio/GlobalAudioPlayer.tsx @@ -1,11 +1,13 @@ import axios from 'axios' +import dayjs from 'dayjs' import { useApp } from 'lodestar-app-element/src/contexts/AppContext' import { useAuth } from 'lodestar-app-element/src/contexts/AuthContext' import { useContext, useEffect, useRef, useState } from 'react' import { useHistory, useLocation } from 'react-router-dom' import AudioPlayerContext, { AudioPlayerMode } from '../../contexts/AudioPlayerContext' import { useInsertProgress } from '../../contexts/ProgressContext' -import { useProgram, useProgramContent } from '../../hooks/program' +import { useProgram, useProgramContent, useProgramContentEnrollment, useProgramProgress } from '../../hooks/program' +import { DisplayModeEnum } from '../../types/program' import AudioPlayer from './AudioPlayer' const GlobalAudioPlayer: React.VFC = () => { @@ -28,7 +30,7 @@ const GlobalAudioPlayer: React.VFC = () => { const endedAtRef = useRef(0) const history = useHistory() const location = useLocation() - const { authToken, currentMemberId } = useAuth() + const { authToken, currentMemberId, isAuthenticated } = useAuth() const { settings } = useApp() const [mode, setMode] = useState('sequential') @@ -38,10 +40,29 @@ const GlobalAudioPlayer: React.VFC = () => { const insertProgress = useInsertProgress(currentMemberId || '') const { program } = useProgram(programId) const { programContent, loadingProgramContent } = useProgramContent(programContentId) + const { programContentEnrollment } = useProgramContentEnrollment(programId) + const programContentIds = program.contentSections.map(section => section.contents.map(content => content.id)).flat() + const { programContentProgress } = useProgramProgress(programContentIds) const pathname = location.pathname - const playList = program.contentSections.map(p => p.contents).flat() - + const playList = program.contentSections + .map(section => + section.contents.map(content => { + const enrolled = programContentEnrollment?.find(enrollment => content.id === enrollment.contentId)?.displayMode + const lastProgress = programContentProgress?.find(progress => progress.contentId === content.id)?.lastProgress + return { + ...content, + isLock: + !!enrolled || + content.displayMode === DisplayModeEnum.trial || + (content.displayMode === DisplayModeEnum.loginToTrial && Boolean(currentMemberId && isAuthenticated)) + ? false + : true, + lastProgress, + } + }), + ) + .flat() const currentIndex = playList.findIndex(p => p.id === contentId) useEffect(() => { @@ -69,35 +90,40 @@ const GlobalAudioPlayer: React.VFC = () => { const contentId = pathname.split('/')[4] setProgramContentId(contentId) if (!loadingProgramContent && programContent) { - const programContentBodyType = programContent.programContentBody?.type - if (programContentBodyType === 'audio' && programContent.audios.length !== 0) { - setup?.({ - backgroundMode: isBackgroundMode, - title: programContent.title, - contentSectionTitle: programContent.contentSectionTitle, - programId: programId, - contentId, - contentType: programContent.contentType, - }) - } else if ( - programContent && - programContentBodyType === 'video' && - isBackgroundMode && - programContent.videos[0] && - programContent.videos[0].data?.source !== 'youtube' && - programContent.videos.length !== 0 - ) { - const programContentVideo = programContent.videos[0] - setup?.({ - backgroundMode: true, - title: programContent.title, - contentSectionTitle: programContent.contentSectionTitle, - programId: programId, - contentId: programContentId, - contentType: programContent.contentType, - videoId: programContentVideo.id, - source: programContentVideo.options?.cloudflare ? 'cloudflare' : programContentVideo.data?.source, - }) + const isPublish = dayjs().isSame(programContent.publishedAt) || dayjs().isAfter(programContent.publishedAt) + if (isPublish) { + const programContentBodyType = programContent.programContentBody?.type + if (programContentBodyType === 'audio' && programContent.audios.length !== 0) { + setup?.({ + backgroundMode: isBackgroundMode, + title: programContent.title, + contentSectionTitle: programContent.contentSectionTitle, + programId: programId, + contentId, + contentType: programContent.contentType, + }) + } else if ( + programContent && + programContentBodyType === 'video' && + isBackgroundMode && + programContent.videos[0] && + programContent.videos[0].data?.source !== 'youtube' && + programContent.videos.length !== 0 + ) { + const programContentVideo = programContent.videos[0] + setup?.({ + backgroundMode: true, + title: programContent.title, + contentSectionTitle: programContent.contentSectionTitle, + programId: programId, + contentId: programContentId, + contentType: programContent.contentType, + videoId: programContentVideo.id, + source: programContentVideo.options?.cloudflare ? 'cloudflare' : programContentVideo.data?.source, + }) + } else { + close?.() + } } else { close?.() } @@ -188,35 +214,43 @@ const GlobalAudioPlayer: React.VFC = () => { while (true) { nextIndex = (nextIndex + quantity + playList.length) % playList.length - const nextContentType = playList[nextIndex].contentType - const nextAudio = playList[nextIndex].audios + const { + id: contentId, + programId, + title, + contentSectionTitle, + contentType, + videos, + audios, + isLock, + publishedAt, + } = playList[nextIndex] + const isPublish = dayjs().isSame(publishedAt) || dayjs().isAfter(publishedAt) if (isBackgroundMode) { - const nextVideo = playList[nextIndex].videos - const nextContentVideoSource = nextVideo[0]?.data?.source - if ( - (nextContentType === 'video' && nextContentVideoSource !== 'youtube' && nextVideo.length !== 0) || - (nextContentType === 'audio' && nextAudio.length !== 0) - ) { - const { id: contentId, programId, title, contentSectionTitle, contentType, videos } = playList[nextIndex] - - setup?.({ - backgroundMode: true, - title: title || '', - contentSectionTitle: contentSectionTitle || '', - programId, - contentId, - contentType: contentType || '', - videoId: videos[0]?.id, - source: videos[0]?.options?.cloudflare ? 'cloudflare' : videos[0]?.data?.source, - }) - if (pathname.includes('contents') && documentVisible) { - history.push(`/programs/${programId}/contents/${contentId}`) + if (!isLock && isPublish) { + const contentVideoSource = videos[0]?.data?.source + if ( + (contentType === 'video' && contentVideoSource !== 'youtube' && videos.length !== 0) || + (contentType === 'audio' && audios.length !== 0) + ) { + setup?.({ + backgroundMode: true, + title: title || '', + contentSectionTitle: contentSectionTitle || '', + programId, + contentId, + contentType: contentType || '', + videoId: videos[0]?.id, + source: videos[0]?.options?.cloudflare ? 'cloudflare' : videos[0]?.data?.source, + }) + if (pathname.includes('contents') && documentVisible) { + history.push(`/programs/${programId}/contents/${contentId}`) + } + return } - - return } } else { - if (nextContentType === 'audio' && nextAudio.length !== 0) { + if (contentType === 'audio' && audios.length !== 0 && !isLock && isPublish) { const { id: contentId, programId, title, contentSectionTitle, contentType } = playList[nextIndex] setup?.({ @@ -244,11 +278,7 @@ const GlobalAudioPlayer: React.VFC = () => { contentSectionTitle={contentSectionTitle} playList={playList} isPlaying={isPlaying} - lastProgress={ - !loadingProgramContent && pathname.includes('contents') - ? programContent?.lastProgress - : playList[currentIndex]?.lastProgress - } + lastProgress={playList[currentIndex]?.lastProgress || 0} audioUrl={audioUrl} mimeType={mimeType} mode={mode} diff --git a/src/components/audio/PlaylistOverlay.tsx b/src/components/audio/PlaylistOverlay.tsx index 27da42625..d1a4060ca 100644 --- a/src/components/audio/PlaylistOverlay.tsx +++ b/src/components/audio/PlaylistOverlay.tsx @@ -1,6 +1,7 @@ import { CheckIcon } from '@chakra-ui/icons' -import { Icon } from '@chakra-ui/react' +import { HStack, Icon } from '@chakra-ui/react' import { List } from 'antd' +import dayjs from 'dayjs' import React, { useContext } from 'react' import { AiOutlineFileText, AiOutlineVideoCamera } from 'react-icons/ai' import { useIntl } from 'react-intl' @@ -8,8 +9,9 @@ import { useHistory, useLocation } from 'react-router-dom' import styled from 'styled-components' import AudioPlayerContext from '../../contexts/AudioPlayerContext' import { durationFormatter, rgba } from '../../helpers' -import { podcastMessages } from '../../helpers/translation' +import { commonMessages, podcastMessages } from '../../helpers/translation' import { MicrophoneIcon } from '../../images' +import { ReactComponent as LockIcon } from '../../images/icon-lock.svg' import { ReactComponent as PracticeIcon } from '../../images/practice-icon.svg' import { ProgramContent } from '../../types/program' import { BREAK_POINT } from '../common/Responsive' @@ -91,14 +93,15 @@ const StyledListItem = styled(List.Item)<{ variant?: 'playing' }>` const PlaylistOverlay: React.VFC<{ title?: string - playList: (ProgramContent & { programId?: string; contentSectionTitle?: string; progress?: number })[] + playList: (ProgramContent & { + programId?: string + contentSectionTitle?: string + progress?: number + isLock: boolean + })[] currentIndex: number }> = ({ title, playList, currentIndex }) => { - const history = useHistory() - const location = useLocation() - const pathname = location.pathname const { formatMessage } = useIntl() - const { setup, isBackgroundMode } = useContext(AudioPlayerContext) return ( @@ -118,45 +121,28 @@ const PlaylistOverlay: React.VFC<{ id: contentId, contentSectionTitle, contentType, + publishedAt, progress = 0, audios, + duration, videos, + isLock, } = item return ( { - const videoSource = videos[0]?.data?.source - if (pathname.includes('contents') && (contentType === 'audio' || contentType === 'video')) { - history.push(`/programs/${programId}/contents/${contentId}`) - } - if ( - !pathname.includes('contents') && - ((contentType !== 'audio' && contentType !== 'video') || - (contentType === 'audio' && audios?.length === 0) || - (!isBackgroundMode && contentType === 'video') || - (isBackgroundMode && - contentType === 'video' && - (videoSource === 'youtube' || videos?.length === 0))) - ) { - history.push(`/programs/${programId}/contents/${contentId}`) - } - setup?.({ - backgroundMode: isBackgroundMode, - title, - contentSectionTitle: contentSectionTitle || '', - source: videos[0]?.options?.cloudflare ? 'cloudflare' : videos[0]?.data?.source, - videoId: videos[0]?.id, - programId, - contentId, - contentType: contentType || '', - }) - }} /> ) })} @@ -171,32 +157,95 @@ const PlayListItem: React.VFC<{ progress: number contentType: string | null title: string - onClick: () => void -}> = ({ title, contentType, isPlaying, onClick, progress, duration }) => { + isLock: boolean + videos: ProgramContent['videos'] + audios: ProgramContent['audios'] + contentSectionTitle: string + contentId: string + programId: string + publishedAt: Date | null +}> = ({ + title, + contentId, + programId, + isLock, + contentType, + isPlaying, + contentSectionTitle, + progress, + duration, + publishedAt, + videos, + audios, +}) => { + const { formatMessage } = useIntl() + const { setup, isBackgroundMode } = useContext(AudioPlayerContext) + const history = useHistory() + const location = useLocation() + const pathname = location.pathname + const progressStatus = progress === 0 ? 'unread' : progress === 1 ? 'done' : 'half' + const videoSource = videos[0]?.data?.source + const isPublish = (publishedAt && dayjs().isSame(publishedAt)) || dayjs().isAfter(publishedAt) return ( { + if (pathname.includes('contents') && (contentType === 'audio' || contentType === 'video')) { + history.push(`/programs/${programId}/contents/${contentId}`) + } + if ( + !pathname.includes('contents') && + (!isPublish || + isLock || + (contentType !== 'audio' && contentType !== 'video') || + (contentType === 'audio' && audios?.length === 0) || + (!isBackgroundMode && contentType === 'video') || + (isBackgroundMode && contentType === 'video' && (videoSource === 'youtube' || videos?.length === 0))) + ) { + history.push(`/programs/${programId}/contents/${contentId}`) + } else { + setup?.({ + backgroundMode: isBackgroundMode, + title, + contentSectionTitle: contentSectionTitle || '', + source: videos[0]?.options?.cloudflare ? 'cloudflare' : videos[0]?.data?.source, + videoId: videos[0]?.id, + programId, + contentId, + contentType: contentType || '', + }) + } + }} >
{title} - {contentType === 'video' || contentType === 'audio' ? ( - <> - - {durationFormatter(duration)} - + {isLock ? ( + + + + {(contentType === 'video' || contentType === 'audio') && {durationFormatter(duration)}} + + + ) : contentType === 'video' || contentType === 'audio' ? ( + + + + {isPublish + ? `${durationFormatter(duration)}` + : publishedAt && + `${durationFormatter(duration)} (${dayjs(publishedAt).format('MM/DD')}) ${formatMessage( + commonMessages.text.publish, + )}`} + + ) : contentType === 'practice' ? ( - + ) : ( - + )} diff --git a/src/hasura.d.ts b/src/hasura.d.ts index cfa105a49..3201b6c1c 100644 --- a/src/hasura.d.ts +++ b/src/hasura.d.ts @@ -30862,6 +30862,8 @@ export type meet = { meet_members_aggregate: meet_member_aggregate; nbf_at?: Maybe; options: Scalars['jsonb']; + recording_type?: Maybe; + recording_url?: Maybe; service_id?: Maybe; started_at: Scalars['timestamptz']; /** appointment_plan_id, member_task_id */ @@ -30941,6 +30943,8 @@ export type meet_bool_exp = { meet_members_aggregate?: InputMaybe; nbf_at?: InputMaybe; options?: InputMaybe; + recording_type?: InputMaybe; + recording_url?: InputMaybe; service_id?: InputMaybe; started_at?: InputMaybe; target?: InputMaybe; @@ -30984,6 +30988,8 @@ export type meet_insert_input = { meet_members?: InputMaybe; nbf_at?: InputMaybe; options?: InputMaybe; + recording_type?: InputMaybe; + recording_url?: InputMaybe; service_id?: InputMaybe; started_at?: InputMaybe; /** appointment_plan_id, member_task_id */ @@ -31005,6 +31011,8 @@ export type meet_max_fields = { host_member_id?: Maybe; id?: Maybe; nbf_at?: Maybe; + recording_type?: Maybe; + recording_url?: Maybe; service_id?: Maybe; started_at?: Maybe; /** appointment_plan_id, member_task_id */ @@ -31260,6 +31268,8 @@ export type meet_min_fields = { host_member_id?: Maybe; id?: Maybe; nbf_at?: Maybe; + recording_type?: Maybe; + recording_url?: Maybe; service_id?: Maybe; started_at?: Maybe; /** appointment_plan_id, member_task_id */ @@ -31305,6 +31315,8 @@ export type meet_order_by = { meet_members_aggregate?: InputMaybe; nbf_at?: InputMaybe; options?: InputMaybe; + recording_type?: InputMaybe; + recording_url?: InputMaybe; service_id?: InputMaybe; started_at?: InputMaybe; target?: InputMaybe; @@ -31347,6 +31359,10 @@ export enum meet_select_column { /** column name */ options = 'options', /** column name */ + recording_type = 'recording_type', + /** column name */ + recording_url = 'recording_url', + /** column name */ service_id = 'service_id', /** column name */ started_at = 'started_at', @@ -31372,6 +31388,8 @@ export type meet_set_input = { id?: InputMaybe; nbf_at?: InputMaybe; options?: InputMaybe; + recording_type?: InputMaybe; + recording_url?: InputMaybe; service_id?: InputMaybe; started_at?: InputMaybe; /** appointment_plan_id, member_task_id */ @@ -31402,6 +31420,8 @@ export type meet_stream_cursor_value_input = { id?: InputMaybe; nbf_at?: InputMaybe; options?: InputMaybe; + recording_type?: InputMaybe; + recording_url?: InputMaybe; service_id?: InputMaybe; started_at?: InputMaybe; /** appointment_plan_id, member_task_id */ @@ -31435,6 +31455,10 @@ export enum meet_update_column { /** column name */ options = 'options', /** column name */ + recording_type = 'recording_type', + /** column name */ + recording_url = 'recording_url', + /** column name */ service_id = 'service_id', /** column name */ started_at = 'started_at', @@ -46250,6 +46274,10 @@ export type mutation_root = { delete_report?: Maybe; /** delete single row from the table: "report" */ delete_report_by_pk?: Maybe; + /** delete data from the table: "report_permission_group" */ + delete_report_permission_group?: Maybe; + /** delete single row from the table: "report_permission_group" */ + delete_report_permission_group_by_pk?: Maybe; /** delete data from the table: "review" */ delete_review?: Maybe; /** delete single row from the table: "review" */ @@ -47232,6 +47260,10 @@ export type mutation_root = { insert_report?: Maybe; /** insert a single row into the table: "report" */ insert_report_one?: Maybe; + /** insert data into the table: "report_permission_group" */ + insert_report_permission_group?: Maybe; + /** insert a single row into the table: "report_permission_group" */ + insert_report_permission_group_one?: Maybe; /** insert data into the table: "review" */ insert_review?: Maybe; /** insert a single row into the table: "review" */ @@ -48612,6 +48644,12 @@ export type mutation_root = { update_report_by_pk?: Maybe; /** update multiples rows of table: "report" */ update_report_many?: Maybe>>; + /** update data of the table: "report_permission_group" */ + update_report_permission_group?: Maybe; + /** update single row of the table: "report_permission_group" */ + update_report_permission_group_by_pk?: Maybe; + /** update multiples rows of table: "report_permission_group" */ + update_report_permission_group_many?: Maybe>>; /** update data of the table: "review" */ update_review?: Maybe; /** update single row of the table: "review" */ @@ -51258,6 +51296,18 @@ export type mutation_rootdelete_report_by_pkArgs = { }; +/** mutation root */ +export type mutation_rootdelete_report_permission_groupArgs = { + where: report_permission_group_bool_exp; +}; + + +/** mutation root */ +export type mutation_rootdelete_report_permission_group_by_pkArgs = { + id: Scalars['uuid']; +}; + + /** mutation root */ export type mutation_rootdelete_reviewArgs = { where: review_bool_exp; @@ -54592,6 +54642,20 @@ export type mutation_rootinsert_report_oneArgs = { }; +/** mutation root */ +export type mutation_rootinsert_report_permission_groupArgs = { + objects: Array; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type mutation_rootinsert_report_permission_group_oneArgs = { + object: report_permission_group_insert_input; + on_conflict?: InputMaybe; +}; + + /** mutation root */ export type mutation_rootinsert_reviewArgs = { objects: Array; @@ -60011,6 +60075,26 @@ export type mutation_rootupdate_report_manyArgs = { }; +/** mutation root */ +export type mutation_rootupdate_report_permission_groupArgs = { + _set?: InputMaybe; + where: report_permission_group_bool_exp; +}; + + +/** mutation root */ +export type mutation_rootupdate_report_permission_group_by_pkArgs = { + _set?: InputMaybe; + pk_columns: report_permission_group_pk_columns_input; +}; + + +/** mutation root */ +export type mutation_rootupdate_report_permission_group_manyArgs = { + updates: Array; +}; + + /** mutation root */ export type mutation_rootupdate_reviewArgs = { _inc?: InputMaybe; @@ -101580,6 +101664,12 @@ export type query_root = { report_aggregate: report_aggregate; /** fetch data from the table: "report" using primary key columns */ report_by_pk?: Maybe; + /** fetch data from the table: "report_permission_group" */ + report_permission_group: Array; + /** fetch aggregated fields from the table: "report_permission_group" */ + report_permission_group_aggregate: report_permission_group_aggregate; + /** fetch data from the table: "report_permission_group" using primary key columns */ + report_permission_group_by_pk?: Maybe; /** fetch data from the table: "resource" */ resource: Array; /** fetch aggregated fields from the table: "resource" */ @@ -107782,6 +107872,29 @@ export type query_rootreport_by_pkArgs = { }; +export type query_rootreport_permission_groupArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type query_rootreport_permission_group_aggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type query_rootreport_permission_group_by_pkArgs = { + id: Scalars['uuid']; +}; + + export type query_rootresourceArgs = { distinct_on?: InputMaybe>; limit?: InputMaybe; @@ -110727,6 +110840,10 @@ export type report = { app_id: Scalars['String']; id: Scalars['uuid']; options?: Maybe; + /** An array relationship */ + report_permission_groups: Array; + /** An aggregate relationship */ + report_permission_groups_aggregate: report_permission_group_aggregate; title: Scalars['String']; type?: Maybe; }; @@ -110737,6 +110854,26 @@ export type reportoptionsArgs = { path?: InputMaybe; }; + +/** columns and relationships of "report" */ +export type reportreport_permission_groupsArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +/** columns and relationships of "report" */ +export type reportreport_permission_groups_aggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + /** aggregated selection of "report" */ export type report_aggregate = { __typename?: 'report_aggregate'; @@ -110772,6 +110909,8 @@ export type report_bool_exp = { app_id?: InputMaybe; id?: InputMaybe; options?: InputMaybe; + report_permission_groups?: InputMaybe; + report_permission_groups_aggregate?: InputMaybe; title?: InputMaybe; type?: InputMaybe; }; @@ -110802,6 +110941,7 @@ export type report_insert_input = { app_id?: InputMaybe; id?: InputMaybe; options?: InputMaybe; + report_permission_groups?: InputMaybe; title?: InputMaybe; type?: InputMaybe; }; @@ -110833,6 +110973,13 @@ export type report_mutation_response = { returning: Array; }; +/** input type for inserting object relation for remote table "report" */ +export type report_obj_rel_insert_input = { + data: report_insert_input; + /** upsert condition */ + on_conflict?: InputMaybe; +}; + /** on_conflict condition type for table "report" */ export type report_on_conflict = { constraint: report_constraint; @@ -110845,10 +110992,239 @@ export type report_order_by = { app_id?: InputMaybe; id?: InputMaybe; options?: InputMaybe; + report_permission_groups_aggregate?: InputMaybe; title?: InputMaybe; type?: InputMaybe; }; +/** which permission group has the ability to view which data table */ +export type report_permission_group = { + __typename?: 'report_permission_group'; + created_at: Scalars['timestamptz']; + /** An object relationship */ + editor?: Maybe; + editor_id: Scalars['String']; + id: Scalars['uuid']; + /** An object relationship */ + permission_group?: Maybe; + permission_group_id: Scalars['uuid']; + /** An object relationship */ + report?: Maybe; + report_id: Scalars['uuid']; +}; + +/** aggregated selection of "report_permission_group" */ +export type report_permission_group_aggregate = { + __typename?: 'report_permission_group_aggregate'; + aggregate?: Maybe; + nodes: Array; +}; + +export type report_permission_group_aggregate_bool_exp = { + count?: InputMaybe; +}; + +export type report_permission_group_aggregate_bool_exp_count = { + arguments?: InputMaybe>; + distinct?: InputMaybe; + filter?: InputMaybe; + predicate: Int_comparison_exp; +}; + +/** aggregate fields of "report_permission_group" */ +export type report_permission_group_aggregate_fields = { + __typename?: 'report_permission_group_aggregate_fields'; + count: Scalars['Int']; + max?: Maybe; + min?: Maybe; +}; + + +/** aggregate fields of "report_permission_group" */ +export type report_permission_group_aggregate_fieldscountArgs = { + columns?: InputMaybe>; + distinct?: InputMaybe; +}; + +/** order by aggregate values of table "report_permission_group" */ +export type report_permission_group_aggregate_order_by = { + count?: InputMaybe; + max?: InputMaybe; + min?: InputMaybe; +}; + +/** input type for inserting array relation for remote table "report_permission_group" */ +export type report_permission_group_arr_rel_insert_input = { + data: Array; + /** upsert condition */ + on_conflict?: InputMaybe; +}; + +/** Boolean expression to filter rows from the table "report_permission_group". All fields are combined with a logical 'AND'. */ +export type report_permission_group_bool_exp = { + _and?: InputMaybe>; + _not?: InputMaybe; + _or?: InputMaybe>; + created_at?: InputMaybe; + editor?: InputMaybe; + editor_id?: InputMaybe; + id?: InputMaybe; + permission_group?: InputMaybe; + permission_group_id?: InputMaybe; + report?: InputMaybe; + report_id?: InputMaybe; +}; + +/** unique or primary key constraints on table "report_permission_group" */ +export enum report_permission_group_constraint { + /** unique or primary key constraint on columns "id" */ + report_permission_group_pkey = 'report_permission_group_pkey' +} + +/** input type for inserting data into table "report_permission_group" */ +export type report_permission_group_insert_input = { + created_at?: InputMaybe; + editor?: InputMaybe; + editor_id?: InputMaybe; + id?: InputMaybe; + permission_group?: InputMaybe; + permission_group_id?: InputMaybe; + report?: InputMaybe; + report_id?: InputMaybe; +}; + +/** aggregate max on columns */ +export type report_permission_group_max_fields = { + __typename?: 'report_permission_group_max_fields'; + created_at?: Maybe; + editor_id?: Maybe; + id?: Maybe; + permission_group_id?: Maybe; + report_id?: Maybe; +}; + +/** order by max() on columns of table "report_permission_group" */ +export type report_permission_group_max_order_by = { + created_at?: InputMaybe; + editor_id?: InputMaybe; + id?: InputMaybe; + permission_group_id?: InputMaybe; + report_id?: InputMaybe; +}; + +/** aggregate min on columns */ +export type report_permission_group_min_fields = { + __typename?: 'report_permission_group_min_fields'; + created_at?: Maybe; + editor_id?: Maybe; + id?: Maybe; + permission_group_id?: Maybe; + report_id?: Maybe; +}; + +/** order by min() on columns of table "report_permission_group" */ +export type report_permission_group_min_order_by = { + created_at?: InputMaybe; + editor_id?: InputMaybe; + id?: InputMaybe; + permission_group_id?: InputMaybe; + report_id?: InputMaybe; +}; + +/** response of any mutation on the table "report_permission_group" */ +export type report_permission_group_mutation_response = { + __typename?: 'report_permission_group_mutation_response'; + /** number of rows affected by the mutation */ + affected_rows: Scalars['Int']; + /** data from the rows affected by the mutation */ + returning: Array; +}; + +/** on_conflict condition type for table "report_permission_group" */ +export type report_permission_group_on_conflict = { + constraint: report_permission_group_constraint; + update_columns?: Array; + where?: InputMaybe; +}; + +/** Ordering options when selecting data from "report_permission_group". */ +export type report_permission_group_order_by = { + created_at?: InputMaybe; + editor?: InputMaybe; + editor_id?: InputMaybe; + id?: InputMaybe; + permission_group?: InputMaybe; + permission_group_id?: InputMaybe; + report?: InputMaybe; + report_id?: InputMaybe; +}; + +/** primary key columns input for table: report_permission_group */ +export type report_permission_group_pk_columns_input = { + id: Scalars['uuid']; +}; + +/** select columns of table "report_permission_group" */ +export enum report_permission_group_select_column { + /** column name */ + created_at = 'created_at', + /** column name */ + editor_id = 'editor_id', + /** column name */ + id = 'id', + /** column name */ + permission_group_id = 'permission_group_id', + /** column name */ + report_id = 'report_id' +} + +/** input type for updating data in table "report_permission_group" */ +export type report_permission_group_set_input = { + created_at?: InputMaybe; + editor_id?: InputMaybe; + id?: InputMaybe; + permission_group_id?: InputMaybe; + report_id?: InputMaybe; +}; + +/** Streaming cursor of the table "report_permission_group" */ +export type report_permission_group_stream_cursor_input = { + /** Stream column input with initial value */ + initial_value: report_permission_group_stream_cursor_value_input; + /** cursor ordering */ + ordering?: InputMaybe; +}; + +/** Initial value of the column from where the streaming should start */ +export type report_permission_group_stream_cursor_value_input = { + created_at?: InputMaybe; + editor_id?: InputMaybe; + id?: InputMaybe; + permission_group_id?: InputMaybe; + report_id?: InputMaybe; +}; + +/** update columns of table "report_permission_group" */ +export enum report_permission_group_update_column { + /** column name */ + created_at = 'created_at', + /** column name */ + editor_id = 'editor_id', + /** column name */ + id = 'id', + /** column name */ + permission_group_id = 'permission_group_id', + /** column name */ + report_id = 'report_id' +} + +export type report_permission_group_updates = { + /** sets the columns of the filtered rows to the given values */ + _set?: InputMaybe; + /** filter the rows which have to be updated */ + where: report_permission_group_bool_exp; +}; + /** primary key columns input for table: report */ export type report_pk_columns_input = { id: Scalars['uuid']; @@ -117425,6 +117801,14 @@ export type subscription_root = { report_aggregate: report_aggregate; /** fetch data from the table: "report" using primary key columns */ report_by_pk?: Maybe; + /** fetch data from the table: "report_permission_group" */ + report_permission_group: Array; + /** fetch aggregated fields from the table: "report_permission_group" */ + report_permission_group_aggregate: report_permission_group_aggregate; + /** fetch data from the table: "report_permission_group" using primary key columns */ + report_permission_group_by_pk?: Maybe; + /** fetch data from the table in a streaming manner: "report_permission_group" */ + report_permission_group_stream: Array; /** fetch data from the table in a streaming manner: "report" */ report_stream: Array; /** fetch data from the table: "resource" */ @@ -125655,6 +126039,36 @@ export type subscription_rootreport_by_pkArgs = { }; +export type subscription_rootreport_permission_groupArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type subscription_rootreport_permission_group_aggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type subscription_rootreport_permission_group_by_pkArgs = { + id: Scalars['uuid']; +}; + + +export type subscription_rootreport_permission_group_streamArgs = { + batch_size: Scalars['Int']; + cursor: Array>; + where?: InputMaybe; +}; + + export type subscription_rootreport_streamArgs = { batch_size: Scalars['Int']; cursor: Array>; @@ -143019,7 +143433,7 @@ export type GetProgramVariables = Exact<{ }>; -export type GetProgram = { __typename?: 'query_root', program_by_pk?: { __typename?: 'program', id: any, cover_url?: string | null, cover_mobile_url?: string | null, cover_thumbnail_url?: string | null, title: string, abstract?: string | null, published_at?: any | null, is_subscription: boolean, is_sold_out?: boolean | null, list_price?: any | null, sale_price?: any | null, sold_at?: any | null, description?: string | null, cover_video_url?: string | null, meta_tag?: any | null, is_issues_open: boolean, is_private: boolean, is_countdown_timer_visible: boolean, is_introduction_section_visible: boolean, is_enrolled_count_visible: boolean, editors: Array<{ __typename?: 'program_editor', member_id?: string | null }>, program_plans: Array<{ __typename?: 'program_plan', id: any }>, program_categories: Array<{ __typename?: 'program_category', id: any, category: { __typename?: 'category', id: string, name: string } }>, program_tags: Array<{ __typename?: 'program_tag', tag: { __typename?: 'tag', name: string } }>, program_roles: Array<{ __typename?: 'program_role', id: any, name: string, member_id: string }>, program_review_score?: { __typename?: 'program_review_score', score?: any | null } | null, program_duration?: { __typename?: 'program_duration', duration?: any | null } | null, program_content_sections: Array<{ __typename?: 'program_content_section', id: any, title: string, description?: string | null, program_contents: Array<{ __typename?: 'program_content', id: any, title: string, abstract?: string | null, metadata?: any | null, duration?: any | null, published_at?: any | null, display_mode: string, list_price?: any | null, sale_price?: any | null, sold_at?: any | null, content_body_id: any, program_content_progress: Array<{ __typename?: 'program_content_progress', last_progress: any }>, program_content_type?: { __typename?: 'program_content_type', id?: any | null, type?: string | null } | null, program_content_videos: Array<{ __typename?: 'program_content_video', attachment: { __typename?: 'attachment', id: any, size: any, options?: any | null, data?: any | null } }>, program_content_audios: Array<{ __typename?: 'program_content_audio', data: any }> }> }> } | null }; +export type GetProgram = { __typename?: 'query_root', program_by_pk?: { __typename?: 'program', id: any, cover_url?: string | null, cover_mobile_url?: string | null, cover_thumbnail_url?: string | null, title: string, abstract?: string | null, published_at?: any | null, is_subscription: boolean, is_sold_out?: boolean | null, list_price?: any | null, sale_price?: any | null, sold_at?: any | null, description?: string | null, cover_video_url?: string | null, meta_tag?: any | null, is_issues_open: boolean, is_private: boolean, is_countdown_timer_visible: boolean, is_introduction_section_visible: boolean, is_enrolled_count_visible: boolean, editors: Array<{ __typename?: 'program_editor', member_id?: string | null }>, program_plans: Array<{ __typename?: 'program_plan', id: any }>, program_categories: Array<{ __typename?: 'program_category', id: any, category: { __typename?: 'category', id: string, name: string } }>, program_tags: Array<{ __typename?: 'program_tag', tag: { __typename?: 'tag', name: string } }>, program_roles: Array<{ __typename?: 'program_role', id: any, name: string, member_id: string }>, program_review_score?: { __typename?: 'program_review_score', score?: any | null } | null, program_duration?: { __typename?: 'program_duration', duration?: any | null } | null, program_content_sections: Array<{ __typename?: 'program_content_section', id: any, title: string, description?: string | null, program_contents: Array<{ __typename?: 'program_content', id: any, title: string, abstract?: string | null, metadata?: any | null, duration?: any | null, published_at?: any | null, display_mode: string, list_price?: any | null, sale_price?: any | null, sold_at?: any | null, content_body_id: any, program_content_type?: { __typename?: 'program_content_type', id?: any | null, type?: string | null } | null, program_content_videos: Array<{ __typename?: 'program_content_video', attachment: { __typename?: 'attachment', id: any, size: any, options?: any | null, data?: any | null } }>, program_content_audios: Array<{ __typename?: 'program_content_audio', data: any }> }> }> } | null }; export type GetProgramPlansVariables = Exact<{ programId: Scalars['uuid']; @@ -143128,6 +143542,28 @@ export type GetRecentProgramContentVariables = Exact<{ export type GetRecentProgramContent = { __typename?: 'query_root', program_content_progress: Array<{ __typename?: 'program_content_progress', updated_at?: any | null, program_content_id: any, last_progress: any, program_content: { __typename?: 'program_content', program_content_body: { __typename?: 'program_content_body', type?: string | null }, program_content_audios: Array<{ __typename?: 'program_content_audio', id: any }>, program_content_videos: Array<{ __typename?: 'program_content_video', id: any, attachment: { __typename?: 'attachment', data?: any | null, options?: any | null } }> } }> }; +export type GetProgramContentEnrollmentIdsVariables = Exact<{ + programId: Scalars['uuid']; + currentMemberId: Scalars['String']; +}>; + + +export type GetProgramContentEnrollmentIds = { __typename?: 'query_root', program_content_enrollment: Array<{ __typename?: 'program_content_enrollment', program_content_id?: any | null }> }; + +export type GetProgramContentDisplayModeVariables = Exact<{ + contentIds?: InputMaybe | Scalars['uuid']>; +}>; + + +export type GetProgramContentDisplayMode = { __typename?: 'query_root', program_content: Array<{ __typename?: 'program_content', id: any, display_mode: string }> }; + +export type GetProgramProgressVariables = Exact<{ + programContentIds?: InputMaybe | Scalars['uuid']>; +}>; + + +export type GetProgramProgress = { __typename?: 'query_root', program_content_progress: Array<{ __typename?: 'program_content_progress', program_content_id: any, last_progress: any }> }; + export type GET_PROGRAM_PACKAGE_INTRODUCTIONVariables = Exact<{ programPackageId: Scalars['uuid']; }>; diff --git a/src/hooks/program.ts b/src/hooks/program.ts index a6ed2c2b6..a808fc5ac 100644 --- a/src/hooks/program.ts +++ b/src/hooks/program.ts @@ -303,9 +303,6 @@ export const useProgram = (programId: string) => { sale_price sold_at content_body_id - program_content_progress(order_by: { updated_at: desc }, limit: 1) { - last_progress - } program_content_type { id type @@ -452,7 +449,6 @@ export const useProgram = (programId: string) => { salePrice: programContent.sale_price, soldAt: programContent.sold_at && new Date(programContent.sold_at), contentBodyId: programContent.content_body_id, - lastProgress: programContent.program_content_progress[0]?.last_progress || 0, videos: programContent.program_content_videos.map(v => ({ id: v.attachment.id, size: v.attachment.size, @@ -1022,3 +1018,82 @@ export const useRecentProgramContent = (memberId: string) => { RefetchRecentProgramContent: refetch, } } + +export const useProgramContentEnrollment = (programId: string) => { + const { currentMemberId } = useAuth() + const { data: programContentEnrollmentData } = useQuery< + hasura.GetProgramContentEnrollmentIds, + hasura.GetProgramContentEnrollmentIdsVariables + >( + gql` + query GetProgramContentEnrollmentIds($programId: uuid!, $currentMemberId: String!) { + program_content_enrollment(where: { program_id: { _eq: $programId }, member_id: { _eq: $currentMemberId } }) { + program_content_id + } + } + `, + { + variables: { + programId, + currentMemberId: currentMemberId || '', + }, + }, + ) + const contentIds = programContentEnrollmentData?.program_content_enrollment.map(content => content.program_content_id) + + const { data: programContentData } = useQuery< + hasura.GetProgramContentDisplayMode, + hasura.GetProgramContentDisplayModeVariables + >( + gql` + query GetProgramContentDisplayMode($contentIds: [uuid!]) { + program_content(where: { id: { _in: $contentIds } }) { + id + display_mode + } + } + `, + { + variables: { + contentIds, + }, + }, + ) + + const programContentEnrollment = programContentEnrollmentData?.program_content_enrollment.map(enrollment => { + const displayMode = programContentData?.program_content.find( + content => enrollment.program_content_id === content.id, + )?.display_mode + + return { + contentId: enrollment.program_content_id, + displayMode, + } + }) + + return { + programContentEnrollment, + } +} + +export const useProgramProgress = (programContentIds: string[]) => { + const { data } = useQuery( + gql` + query GetProgramProgress($programContentIds: [uuid!]) { + program_content_progress( + where: { program_content_id: { _in: $programContentIds } } + order_by: { updated_at: desc } + ) { + program_content_id + last_progress + } + } + `, + { variables: { programContentIds } }, + ) + const programContentProgress = data?.program_content_progress.map(progress => ({ + contentId: progress.program_content_id, + lastProgress: progress.last_progress, + })) + return { programContentProgress } +} diff --git a/src/types/program.ts b/src/types/program.ts index 396015d94..29d11d06c 100644 --- a/src/types/program.ts +++ b/src/types/program.ts @@ -113,7 +113,6 @@ export type ProgramContent = { listPrice: number | null salePrice: number | null soldAt: Date | null - lastProgress: number // materials?: ProgramContentMaterialProps[] videos: { id: string; size: number; options: { cloudflare?: object }; data: { source?: string } }[] audios: { data: object }[]