Skip to content

Commit

Permalink
#74 implemented speaker detail page
Browse files Browse the repository at this point in the history
  • Loading branch information
fcamblor committed Oct 31, 2024
1 parent 68a19aa commit 3071326
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 49 deletions.
4 changes: 3 additions & 1 deletion mobile/src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 16 additions & 0 deletions mobile/src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 = {
Expand Down
32 changes: 31 additions & 1 deletion mobile/src/state/useEventSpeakers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -45,6 +45,26 @@ export function useLineupSpeakers(eventDescriptorRef: Ref<VoxxrinConferenceDescr
}
}

export function useLineupSpeaker(eventDescriptorRef: Ref<VoxxrinConferenceDescriptor|undefined>, speakerIdRef: Ref<SpeakerId|undefined>) {

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 [];
Expand All @@ -56,3 +76,13 @@ export function eventLineupSpeakersCollections(eventDescriptor: VoxxrinConferenc
) as CollectionReference<LineupSpeaker>
];
}

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<LineupSpeaker>;
}
62 changes: 20 additions & 42 deletions mobile/src/views/SpeakerDetailsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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">
<ion-header class="stickyHeader">
<ion-toolbar>
<ion-button class="stickyHeader-close" shape="round" slot="start" size="small" fill="outline" @click="closeAndNavigateBack()"
Expand All @@ -15,55 +15,45 @@
</ion-button>
<div class="speakerInfoHeader" slot="start">
<ion-avatar class="speakerInfoHeader-avatar">
<speaker-thumbnail size="64px" :is-highlighted="false" :speaker="detailedSpeaker" />
<speaker-thumbnail size="64px" :is-highlighted="false" :speaker="speaker" />
</ion-avatar>
<ion-text class="speakerInfoHeader-title">{{detailedSpeaker.fullName}}</ion-text>
<ion-text class="speakerInfoHeader-title">{{speaker.fullName}}</ion-text>
</div>
</ion-toolbar>
</ion-header>

<div class="speakerDetailsView-container">
<div class="speakerDetailsView-head">
<div class="speakerDetailsView-head-stats ion-text-left">
<ion-label>Total talks</ion-label>
<span class="count">-</span>
</div>
<ion-avatar class="speakerAvatar">
<speaker-thumbnail size="64px" :is-highlighted="false" :speaker="detailedSpeaker" />
<speaker-thumbnail size="64px" :is-highlighted="false" :speaker="speaker" />
</ion-avatar>
<div class="speakerDetailsView-head-stats ion-text-right">
<ion-label>Total favorites</ion-label>
<span class="count">-</span>
</div>
</div>
<div class="speakerDetailsView-content">
<div class="avatarInfos">
<ion-text class="avatarInfos-title">{{detailedSpeaker.fullName}}</ion-text>
<ion-text class="avatarInfos-title">{{speaker.fullName}}</ion-text>
<ion-text class="avatarInfos-subTitle">
<ion-icon :icon="businessSharp" aria-hidden="true"></ion-icon>
{{detailedSpeaker.companyName}}
{{speaker.companyName}}
</ion-text>
</div>
<div class="speakerDetailsView-tabs">
<ion-segment class="tabsSelection _clearMode" value="talks">
<ion-segment-button value="talks">
<ion-label>Talks</ion-label>
</ion-segment-button>
<ion-segment-button value="infos">
<ion-label>Infos</ion-label>
</ion-segment-button>
</ion-segment>
<div class="sectionBloc">
<div class="sectionBloc" v-if="speaker.bio">
<VoxDivider>{{LL.Speaker_bio()}}</VoxDivider>
<ion-text>
{{detailedSpeaker.bio}}
</ion-text>
<ion-text v-html="speaker.bio" />
</div>
<div class="sectionBloc" v-if="speaker.talks.length > 0">
<VoxDivider>{{LL.Speaker_talks()}}</VoxDivider>
<speaker-talk v-for="talk in speaker.talks" :focused-speaker="speaker" :talk="talk" :key="talk.id.value" />
</div>

<div class="sectionBloc linksInfoSpeaker" v-if="detailedSpeaker.social.length">
<div class="sectionBloc linksInfoSpeaker" v-if="speaker.social.length">
<VoxDivider>{{ LL.Social_media() }}</VoxDivider>
<ul class="linksInfoSpeaker-list">
<li v-for="social in detailedSpeaker.social" :key="social.type">
<li v-for="social in speaker.social" :key="social.type">
<social-media-icon :href="social.url" :type="social.type"></social-media-icon>
</li>
</ul>
Expand All @@ -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 = () => {};
Expand Down
5 changes: 0 additions & 5 deletions mobile/src/views/event/SpeakersDirectoryPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down

0 comments on commit 3071326

Please sign in to comment.