basic frontend and backend functionality. Need to test with actual livestream

This commit is contained in:
Lucas Jensen
2025-01-12 16:47:04 -08:00
parent 5b73cecee9
commit c482f6758c
12 changed files with 122 additions and 18 deletions

View File

@@ -10,6 +10,7 @@ interface EditModalProps {
form: JSX.Element;
entity?: MusicianProps | GroupObj | EventSeriesObj;
error?: string;
livestream_id?: string;
}
function EditModal(props: EditModalProps) {

View File

@@ -11,10 +11,12 @@ interface EditBioFormProps {
setGroup?: React.Dispatch<React.SetStateAction<GroupObj | undefined>>;
setMusician?: React.Dispatch<React.SetStateAction<MusicianObj>>;
token: string;
livestream_id?: string;
}
function EditBioForm(props: EditBioFormProps) {
const [formBio, setFormBio] = useState<string>(props.entity.bio);
const [formLivestreamId, setFormLivestreamId] = useState<string | undefined>(props.livestream_id)
const [canSubmit, setCanSubmit] = useState<boolean>(false);
const [error, setError] = useState<string>("");
@@ -23,6 +25,11 @@ function EditBioForm(props: EditBioFormProps) {
setCanSubmit(true);
};
const handleLivestreamChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setFormLivestreamId(event.target.value);
setCanSubmit(true);
}
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
if (props.entity instanceof MusicianObj) {
@@ -53,7 +60,6 @@ function EditBioForm(props: EditBioFormProps) {
}
})
.catch((error) => {
console.error(error);
setError("Failed to update bio: " + error.response.data.detail);
});
};
@@ -62,7 +68,8 @@ function EditBioForm(props: EditBioFormProps) {
accessToken: string,
group: GroupObj,
): Promise<void> => {
patchGroup(group.id, formBio, group.name, accessToken)
const livestream_id = formLivestreamId ? formLivestreamId : "";
patchGroup(group.id, formBio, livestream_id, group.name, accessToken)
.then((patchedGroup) => {
if (props.setGroup) {
props.setGroup(patchedGroup);
@@ -86,6 +93,26 @@ function EditBioForm(props: EditBioFormProps) {
return (
<Form onSubmit={handleSubmit}>
{/* need to account for empty string, which is falsy but the field should still show */}
{props.livestream_id != undefined && (
<Form.Group controlId="formLivestreamId">
<p className="text-muted">
A livestream id is the part of a youtube url following "v=".
For example, "ncyl7cTU9k8" but without the quotations.
Don't mess it up. <br></br>
To remove an embedded livestream, just clear this field and submit the form.
</p>
<Form.Label>Livestream ID:</Form.Label>
<Form.Control
as="textarea"
value={formLivestreamId}
rows={1}
onChange={handleLivestreamChange}
placeholder="ncyl7cTU9k8"
/>
</Form.Group>
)}
<Form.Group controlId="formBio">
<Form.Label>Bio</Form.Label>
<Form.Control

View File

@@ -5,16 +5,19 @@ import { useState } from "react";
import EditBioForm from "../Forms/Bio/BioForm";
import { faPen } from "@fortawesome/free-solid-svg-icons";
import EditButton from "../Buttons/EditButton/EditButton";
import LivestreamPlayer from "../LivestreamPlayer/LivestreamPlayer";
export class GroupObj {
id: number;
name: string;
bio: string;
livestream_id: string;
constructor(id: number, name: string, bio: string) {
constructor(id: number, name: string, bio: string, livestream_id: string) {
this.id = id;
this.name = name;
this.bio = bio;
this.livestream_id = livestream_id;
}
}
@@ -39,7 +42,7 @@ function Group(props: GroupProps) {
<EditButton
setModalShow={setModalShow}
faIcon={faPen}
actionName=" Group Bio"
actionName=" Group"
/>
<EditModal
show={modalShow}
@@ -53,6 +56,7 @@ function Group(props: GroupProps) {
onBioChange={props.onBioChange}
setGroup={props.setGroup}
token={props.token}
livestream_id={group.livestream_id}
/>
}
/>
@@ -67,6 +71,9 @@ function Group(props: GroupProps) {
<Card.Title>
<h1>{group.name}</h1>
</Card.Title>
{group.livestream_id && (
<LivestreamPlayer livestreamId={group.livestream_id} />
)}
{props.token && EditIcon}
<Card.Text className="lead group-bio">{group.bio}</Card.Text>
</Card.Body>

View File

@@ -0,0 +1,25 @@
import { Container } from "react-bootstrap";
interface LivestreamPlayerProps {
livestreamId: string;
}
const LivestreamPlayer = (props: LivestreamPlayerProps) => {
const iframeSrc = `https://www.youtube.com/embed/${props.livestreamId}`;
return (
<Container className="d-flex justify-content-center my-3">
<div className="ratio ratio-16x9">
<iframe
src={iframeSrc}
title="YouTube Livestream"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"
allowFullScreen
className="rounded border"
></iframe>
</div>
</Container>
);
};
export default LivestreamPlayer;

View File

@@ -38,6 +38,7 @@ export const getRoot = async (): Promise<TheGrapefruitsDuoAPI> => {
response.data.group.id,
response.data.group.name,
response.data.group.bio,
response.data.group.livestream_id,
),
response.data.musicians.map(
(musician: MusicianObj) =>
@@ -104,7 +105,12 @@ export const postUser = async (token: string): Promise<UserObj> => {
export const getGroup = async (): Promise<GroupObj> => {
const response = await api.get("/group/");
return new GroupObj(response.data.id, response.data.name, response.data.bio);
return new GroupObj(
response.data.id,
response.data.name,
response.data.bio,
response.data.livestream_id,
);
};
export const getMusicians = async (): Promise<MusicianObj[]> => {
@@ -143,15 +149,21 @@ export const patchMusician = async (
export const patchGroup = async (
id: number,
bio: string,
livestream_id: string,
name: string,
user_token: string,
): Promise<GroupObj> => {
const response = await api.patch(
`/group/`,
{ id, bio, name },
{ id, bio, livestream_id, name },
{ headers: { Authorization: `Bearer ${user_token}` } },
);
return new GroupObj(response.data.id, response.data.name, response.data.bio);
return new GroupObj(
response.data.id,
response.data.name,
response.data.bio,
response.data.livestream_id,
);
};
export const postHeadshot = async (