From 3638656743ada4e2417736ed10da4f51114eeee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Camblor?= Date: Thu, 31 Oct 2024 01:15:52 +0100 Subject: [PATCH] #74 implemented speaker detail page --- mobile/src/i18n/en/index.ts | 4 +- mobile/src/i18n/i18n-types.ts | 16 +++++ mobile/src/state/useEventSpeakers.ts | 32 +++++++++- mobile/src/views/SpeakerDetailsPage.vue | 62 ++++++------------- .../src/views/event/SpeakersDirectoryPage.vue | 5 -- 5 files changed, 70 insertions(+), 49 deletions(-) diff --git a/mobile/src/i18n/en/index.ts b/mobile/src/i18n/en/index.ts index fb378a85..fb4c79c8 100644 --- a/mobile/src/i18n/en/index.ts +++ b/mobile/src/i18n/en/index.ts @@ -149,7 +149,9 @@ const en = { Talk_Recording: "Talk record", Double_tap_on_the_map_to_zoom_in : "Double-tap on the map to zoom in", My_events: "My Events", - Talk_additional_speakers: "+{count:number} Speaker{{count:s}}" + Talk_additional_speakers: "+{count:number} Speaker{{count:s}}", + Speaker_talks: "Talks", + Speaker_infos: "Infos", } satisfies BaseTranslation export default en diff --git a/mobile/src/i18n/i18n-types.ts b/mobile/src/i18n/i18n-types.ts index 3cdc0eee..9b85d465 100644 --- a/mobile/src/i18n/i18n-types.ts +++ b/mobile/src/i18n/i18n-types.ts @@ -616,6 +616,14 @@ type RootTranslation = { * @param {number} count */ Talk_additional_speakers: RequiredParams<'count'> + /** + * T​a​l​k​s + */ + Speaker_talks: string + /** + * I​n​f​o​s + */ + Speaker_infos: string } export type TranslationFunctions = { @@ -1215,6 +1223,14 @@ export type TranslationFunctions = { * +{count} Speaker{{s}} */ Talk_additional_speakers: (arg: { count: number }) => LocalizedString + /** + * Talks + */ + Speaker_talks: () => LocalizedString + /** + * Infos + */ + Speaker_infos: () => LocalizedString } export type Formatters = { diff --git a/mobile/src/state/useEventSpeakers.ts b/mobile/src/state/useEventSpeakers.ts index 0953d860..f3685b04 100644 --- a/mobile/src/state/useEventSpeakers.ts +++ b/mobile/src/state/useEventSpeakers.ts @@ -5,7 +5,7 @@ import {collection, CollectionReference, doc, DocumentReference} from "firebase/ import {db} from "@/state/firebase"; import {resolvedEventFirestorePath} from "../../../shared/utilities/event-utils"; import {LineupSpeaker} from "../../../shared/event-lineup.firestore"; -import {createVoxxrinSpeakerFromFirestore, speakerMatchesSearchTerms} from "@/models/VoxxrinSpeaker"; +import {createVoxxrinSpeakerFromFirestore, SpeakerId, speakerMatchesSearchTerms} from "@/models/VoxxrinSpeaker"; import {match} from "ts-pattern"; import {sortBy} from "@/models/utils"; @@ -45,6 +45,26 @@ export function useLineupSpeakers(eventDescriptorRef: Ref, speakerIdRef: Ref) { + + const firestoreSpeakerRef = deferredVuefireUseDocument([eventDescriptorRef, speakerIdRef], + ([eventDescriptor, speakerId]) => eventLineupSpeakerDocument(eventDescriptor, speakerId)); + + return { + speaker: computed(() => { + const firestoreSpeaker = toValue(firestoreSpeakerRef), + eventDescriptor = toValue(eventDescriptorRef); + + if(!firestoreSpeaker || !eventDescriptor) { + return undefined; + } + + const speaker = createVoxxrinSpeakerFromFirestore(eventDescriptor, firestoreSpeaker); + return speaker; + }) + } +} + export function eventLineupSpeakersCollections(eventDescriptor: VoxxrinConferenceDescriptor|undefined) { if(!eventDescriptor || !eventDescriptor.id || !eventDescriptor.id.value) { return []; @@ -56,3 +76,13 @@ export function eventLineupSpeakersCollections(eventDescriptor: VoxxrinConferenc ) as CollectionReference ]; } + +export function eventLineupSpeakerDocument(eventDescriptor: VoxxrinConferenceDescriptor|undefined, speakerId: SpeakerId|undefined) { + if(!eventDescriptor || !eventDescriptor.id || !eventDescriptor.id.value || !speakerId || !speakerId.value) { + return undefined; + } + + return doc(db, + `${resolvedEventFirestorePath(eventDescriptor.id.value, eventDescriptor.spaceToken?.value)}/speakers/${speakerId.value}` + ) as DocumentReference; +} diff --git a/mobile/src/views/SpeakerDetailsPage.vue b/mobile/src/views/SpeakerDetailsPage.vue index 3b6f5c29..269eb5ac 100644 --- a/mobile/src/views/SpeakerDetailsPage.vue +++ b/mobile/src/views/SpeakerDetailsPage.vue @@ -6,7 +6,7 @@ @ionScrollStart="handleScrollStart()" @ionScroll="handleScroll($event)" @ionScrollEnd="handleScrollEnd()" - v-themed-event-styles="confDescriptor" v-if="confDescriptor && detailedSpeaker"> + v-themed-event-styles="confDescriptor" v-if="confDescriptor && speaker">
- + - {{detailedSpeaker.fullName}} + {{speaker.fullName}}
@@ -25,45 +25,35 @@
- Total talks - -
- +
- Total favorites - -
- {{detailedSpeaker.fullName}} + {{speaker.fullName}} - {{detailedSpeaker.companyName}} + {{speaker.companyName}}
- - - Talks - - - Infos - - -
+
{{LL.Speaker_bio()}} - - {{detailedSpeaker.bio}} - + +
+
+ {{LL.Speaker_talks()}} +
-
+
{{ LL.Social_media() }}
    -
  • +
@@ -87,39 +77,27 @@ import {Logger} from "@/services/Logger"; import {SpeakerId, VoxxrinDetailedSpeaker} from "@/models/VoxxrinSpeaker"; import {businessSharp} from "ionicons/icons"; import VoxDivider from "@/components/ui/VoxDivider.vue"; -import {useCurrentSpaceEventIdRef} from "@/services/Spaces"; +import {getResolvedEventRootPathFromSpacedEventIdRef, useCurrentSpaceEventIdRef} from "@/services/Spaces"; import SpeakerThumbnail from "@/components/speaker/SpeakerThumbnail.vue"; import SocialMediaIcon from "@/components/ui/SocialMediaIcon.vue"; +import {useLineupSpeaker} from "@/state/useEventSpeakers"; +import SpeakerTalk from "@/components/speaker-card/SpeakerTalk.vue"; -const LOGGER = Logger.named("TalkDetailsPage"); +const LOGGER = Logger.named("SpeakerDetailsPage"); const ionRouter = useIonRouter(); function closeAndNavigateBack() { - goBackOrNavigateTo(ionRouter, `/events/${eventId.value.value}/speakers`, 0 /* talk details page is always opened through popups */) + goBackOrNavigateTo(ionRouter, `${getResolvedEventRootPathFromSpacedEventIdRef(spacedEventIdRef)}/speakers`, 0 /* talk details page is always opened through popups */) } const route = useRoute(); -const eventId = ref(new EventId(getRouteParamsValue(route, 'eventId'))); const speakerId = ref(new SpeakerId(getRouteParamsValue(route, 'speakerId'))); const spacedEventIdRef = useCurrentSpaceEventIdRef(); const {conferenceDescriptor: confDescriptor} = useSharedConferenceDescriptor(spacedEventIdRef); +const {speaker} = useLineupSpeaker(confDescriptor, speakerId) const { LL } = typesafeI18n() -const detailedSpeaker: VoxxrinDetailedSpeaker = { - id: new SpeakerId('42'), - fullName: "Frédéric Camblor", - companyName: "4SH", - photoUrl: "https://lh3.googleusercontent.com/a/AAcHTtdsbTGnaxXmrzSi178m_qpxj9c-z12qoL7SLB6cjUSfZhaQ=s96-c", - bio: `Retired Bordeaux JUG leader and co-creator of the BDX I/O conference in 2014, Frédéric enjoys mixing with different tech communities and learning new things. -Web developer at 4SH by day, and OSS commiter by night, he has created/contributed to some more or less well known projects: Voxxrin app, Vitemadose frontend during COVID Pandemic, Devoxx France CFP, RestX framework, as well as some (old) Jenkins plugins. -As a big fan of strong typing, he loves Typescript, but also like doing all kinds of stuff in Google Spreadsheets.`, - social: [ - {type:'twitter', url: 'https://www.twitter.com/fcamblor' } - ] -} - - // * TODO #74 Check perf impact * // const isScrollingDown = ref(false); const handleScrollStart = () => {}; diff --git a/mobile/src/views/event/SpeakersDirectoryPage.vue b/mobile/src/views/event/SpeakersDirectoryPage.vue index e587ea1b..8f42f7a5 100644 --- a/mobile/src/views/event/SpeakersDirectoryPage.vue +++ b/mobile/src/views/event/SpeakersDirectoryPage.vue @@ -61,11 +61,6 @@ async function openSpeakerDetails(speaker: VoxxrinSimpleSpeaker) { if(speaker) { - // TODO: Re-enable this once *tabbed* talk details as feedback viewer routing has been fixed - // const talkFeedbackViewerToken = toValue(talkFeedbackViewerTokensRef)?.find(t => t.talkId.isSameThan(talk.id)); - // const url = talkFeedbackViewerToken - // ?`/events/${eventId.value.value}/talks/${talk.id.value}/asFeedbackViewer/${talkFeedbackViewerToken.secretToken}/details` - // :`/events/${eventId.value.value}/talks/${talk.id.value}/details` const url = `${getResolvedEventRootPathFromSpacedEventIdRef(spacedEventIdRef)}/speakers/${speaker.id.value}/details` triggerTabbedPageNavigate(url, "forward", "push");