From c193e5da2dfe97370ae82626e62660d399e54069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Tue, 5 Dec 2023 02:21:38 +0100 Subject: [PATCH 1/6] Replace menu with functional component --- src/js/menu.tsx | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/js/menu.tsx b/src/js/menu.tsx index bc947e3..59d5dc0 100644 --- a/src/js/menu.tsx +++ b/src/js/menu.tsx @@ -3,43 +3,37 @@ import React from "react"; type StreamSelectCallback = (selected: string) => void -type MenuProps = { +interface MenuProps { items: string[] - selectedItem: string | undefined + selectedItem: string | null selectCallback: StreamSelectCallback } -export class Menu extends React.Component { - constructor(props: MenuProps) { - super(props) - } - - render() { - return ( - - ) - } - - private menuitems() { +export function Menu({ items, selectedItem, selectCallback }: MenuProps) { + const menuitems = () => { return ( <> - {this.props.items.map((value, idx) => { - if (this.props.selectedItem == value) { + {items.map((value, idx) => { + if (selectedItem == value) { return
  • {value}
  • } else { return
  • { - this.props.selectCallback(value) + selectCallback(value) }} className="menu-item">{value}
  • } })} ) } + return ( + + ) + } -- 2.45.2 From 03a5fb549738208648ca09869fd7c258f52088ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Tue, 5 Dec 2023 02:22:06 +0100 Subject: [PATCH 2/6] Replace app with functional component --- src/js/app.tsx | 111 +++++++++++++++++------------------------------ src/js/media.tsx | 5 ++- 2 files changed, 43 insertions(+), 73 deletions(-) diff --git a/src/js/app.tsx b/src/js/app.tsx index 7dfe717..54806f0 100644 --- a/src/js/app.tsx +++ b/src/js/app.tsx @@ -1,92 +1,61 @@ import { createRoot } from "react-dom/client"; -import React from "react"; import { Menu } from "./menu"; -import { createContext, useState } from "react"; +import { createContext, useEffect, useState } from "react"; import { MediaContainer } from "./media"; import { MinistreamApiClient } from "./api"; +import React from "react"; -type ThemeContextType = "light" | "dark"; - -const ThemeContext = createContext("light"); - -type AppState = { - streamList: string[] - selectedStream: string | undefined +interface AppProps { + api: MinistreamApiClient } -type AppProps = {} -export class App extends React.Component { - apiClient: MinistreamApiClient - constructor(props: AppProps) { - super(props) - this.state = { - streamList: [], - selectedStream: undefined - } - this.apiClient = new MinistreamApiClient() - this.updateSelect = this.updateSelect.bind(this) +export function App({ api }: AppProps) { + const [streamList, setStreamList] = useState([]) + const [selectedStream, setSelectedStream] = useState(null) + + const updateSelect = (item: string) => { + setSelectedStream(item) } - async componentDidMount(): Promise { - await this.updateStreamList() - setInterval(() => { - this.updateStreamList() - }, 10000) - } - - async updateStreamList() { - const streams = await this.apiClient.listStreams() - const sortedStreams = streams.sort((a, b) => { - if (a > b) { - return -1 - } else if (a < b) { - return 1 + const updateStreamList = () => { + api.listStreams().then((list) => { + setStreamList(list) + if (list.length != streamList.length) { + setStreamList(list) } - return 0 - }) - if (sortedStreams.length != this.state.streamList.length) { - this.setStreamList(streams) - } - - if (!sortedStreams.every((_, idx) => { - return sortedStreams[idx] === this.state.streamList[idx] - })) { - this.setStreamList(streams) - } - } - - setStreamList(streamList: string[]) { - console.log("stream list updated") - this.setState((state) => { - return { selectedStream: state.selectedStream, streamList: streamList } + if (!list.every((_, idx) => { + return list[idx] === streamList[idx] + })) { + setStreamList(list) + } }) } - updateSelect(selected: string): void { - if (selected !== this.state.selectedStream) { - this.setState((state) => { - return { streamList: state.streamList, selectedStream: selected } - }) - } - } + setInterval(() => { + updateStreamList() + }, 10000) - render() { - return ( - <> - - - - ) - } + useEffect(() => { + updateStreamList() + },[]) + + + return ( + <> + + + + ) } const rootElement = document.getElementById("app"); if (rootElement) { const root = createRoot(rootElement); - root.render() + const api = new MinistreamApiClient() + root.render() } \ No newline at end of file diff --git a/src/js/media.tsx b/src/js/media.tsx index 2ea5df7..c76a9d7 100644 --- a/src/js/media.tsx +++ b/src/js/media.tsx @@ -1,9 +1,10 @@ -import React, { ComponentProps, useRef, useEffect, useState } from "react" +import { ComponentProps, useRef, useEffect, useState } from "react" import { MinistreamApiClient } from "./api" import { resolve } from "path" +import React from "react" type MediaContainerProps = { - selectedStream: string | undefined + selectedStream: string | null api: MinistreamApiClient } -- 2.45.2 From 031505600ca07f713b61fb54711e4a67b6e7037c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Tue, 5 Dec 2023 02:22:30 +0100 Subject: [PATCH 3/6] Add info enpoint to api --- src/js/api.ts | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/js/api.ts b/src/js/api.ts index 518ce0b..137675c 100644 --- a/src/js/api.ts +++ b/src/js/api.ts @@ -1,4 +1,8 @@ +export interface SiteInfo { + siteName: string +} + export class MinistreamApiClient { ENV: string @@ -21,14 +25,22 @@ export class MinistreamApiClient { const resp = await fetch( url, ); - const res = resp.json() as unknown as string[]; - data = res; + data = await resp.json() as unknown as string[]; } catch (e) { throw e; } - return data; + const sortedStreams = data.sort((a, b) => { + if (a > b) { + return -1 + } else if (a < b) { + return 1 + } + return 0 + }) + + return sortedStreams; } async postOffer(streamKey: string, offer_sdp: RTCSessionDescription): Promise { @@ -43,8 +55,23 @@ export class MinistreamApiClient { body: offer_sdp.sdp }) const body = await resp.text() - const answer = new RTCSessionDescription({type: "answer", sdp: body}) + const answer = new RTCSessionDescription({ type: "answer", sdp: body }) return answer } + + async siteInfo(): Promise { + var url = "/api/info" + if (this.ENV !== "production") { + url = "http://localhost:8080/api/info" + } + + const resp = await fetch(url, + { + method: "GET", + }) + const data = resp.json() + + return data as unknown as SiteInfo + } } -- 2.45.2 From f0dd632922a9c41356f6a3a52fc3391afd116e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Tue, 5 Dec 2023 02:37:57 +0100 Subject: [PATCH 4/6] Fix color in menu --- src/css/style.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/css/style.css b/src/css/style.css index bb41558..f57ab03 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -60,7 +60,6 @@ aside { .menu-list { list-style: none; - background-color: brown; margin-top: 0px; } -- 2.45.2 From 2a5f0ea6010f3324092ad4cb9c98cc17cc46abdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Tue, 5 Dec 2023 02:41:03 +0100 Subject: [PATCH 5/6] Set page title --- src/js/api.ts | 4 ++-- src/js/app.tsx | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/js/api.ts b/src/js/api.ts index 137675c..3021467 100644 --- a/src/js/api.ts +++ b/src/js/api.ts @@ -61,9 +61,9 @@ export class MinistreamApiClient { } async siteInfo(): Promise { - var url = "/api/info" + var url = "/api/siteinfo" if (this.ENV !== "production") { - url = "http://localhost:8080/api/info" + url = "http://localhost:8080/api/siteinfo" } const resp = await fetch(url, diff --git a/src/js/app.tsx b/src/js/app.tsx index 54806f0..dbb607f 100644 --- a/src/js/app.tsx +++ b/src/js/app.tsx @@ -32,13 +32,37 @@ export function App({ api }: AppProps) { }) } + const updateTitle = () => { + const titleKey = "ministream.title" + var title = localStorage.getItem(titleKey) + const setTitle = (title: string) => { + const el = document.querySelector('title') + if (el) { + el.textContent = title + } + } + if (title) { + setTitle(title) + } + api.siteInfo().then((info) => { + if (info.siteName != title) { + setTitle(info.siteName) + localStorage.setItem(titleKey, info.siteName) + } + }) + return + } + setInterval(() => { updateStreamList() }, 10000) useEffect(() => { updateStreamList() - },[]) + }, []) + useEffect(() => { + updateTitle() + }, []) return ( @@ -57,5 +81,5 @@ const rootElement = document.getElementById("app"); if (rootElement) { const root = createRoot(rootElement); const api = new MinistreamApiClient() - root.render() + root.render() } \ No newline at end of file -- 2.45.2 From fbe9153bfc6ee50830ad251b8fb1692b19140d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Tue, 5 Dec 2023 03:19:01 +0100 Subject: [PATCH 6/6] Set title from siteinfo endpoint --- src/index.html | 2 +- src/js/app.tsx | 38 ++++++++++++++++++++++---------------- src/js/menu.tsx | 7 +++++-- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/index.html b/src/index.html index 4a80a66..730294f 100644 --- a/src/index.html +++ b/src/index.html @@ -2,7 +2,7 @@ - stream.t-juice.club + diff --git a/src/js/app.tsx b/src/js/app.tsx index dbb607f..69c93e6 100644 --- a/src/js/app.tsx +++ b/src/js/app.tsx @@ -9,11 +9,27 @@ interface AppProps { api: MinistreamApiClient } +const titleKey = "ministream.title" + +function setTitleFromLocalstorage() { + var title = localStorage.getItem(titleKey) + if (title) { + setTitle(title) + } +} + +function setTitle(title: string) { + const el = document.querySelector('title') + if (el) { + el.textContent = title + } +} + export function App({ api }: AppProps) { const [streamList, setStreamList] = useState([]) const [selectedStream, setSelectedStream] = useState(null) - const updateSelect = (item: string) => { + const updateSelect = (item: string | null) => { setSelectedStream(item) } @@ -33,19 +49,8 @@ export function App({ api }: AppProps) { } const updateTitle = () => { - const titleKey = "ministream.title" - var title = localStorage.getItem(titleKey) - const setTitle = (title: string) => { - const el = document.querySelector('title') - if (el) { - el.textContent = title - } - } - if (title) { - setTitle(title) - } - api.siteInfo().then((info) => { - if (info.siteName != title) { + api.siteInfo().then((info) => { + if (info.siteName != document.title) { setTitle(info.siteName) localStorage.setItem(titleKey, info.siteName) } @@ -60,11 +65,11 @@ export function App({ api }: AppProps) { useEffect(() => { updateStreamList() }, []) + useEffect(() => { updateTitle() }, []) - return ( <> ) } \ No newline at end of file diff --git a/src/js/menu.tsx b/src/js/menu.tsx index 59d5dc0..ec8f87f 100644 --- a/src/js/menu.tsx +++ b/src/js/menu.tsx @@ -1,7 +1,7 @@ import { createRoot } from "react-dom/client"; import React from "react"; -type StreamSelectCallback = (selected: string) => void +type StreamSelectCallback = (selected: string | null) => void interface MenuProps { items: string[] @@ -10,6 +10,7 @@ interface MenuProps { } export function Menu({ items, selectedItem, selectCallback }: MenuProps) { + const title = document.title const menuitems = () => { return ( <> @@ -28,7 +29,9 @@ export function Menu({ items, selectedItem, selectCallback }: MenuProps) { return (