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;
}
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/api.ts b/src/js/api.ts
index 518ce0b..3021467 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/siteinfo"
+ if (this.ENV !== "production") {
+ url = "http://localhost:8080/api/siteinfo"
+ }
+
+ const resp = await fetch(url,
+ {
+ method: "GET",
+ })
+ const data = resp.json()
+
+ return data as unknown as SiteInfo
+ }
}
diff --git a/src/js/app.tsx b/src/js/app.tsx
index 7dfe717..69c93e6 100644
--- a/src/js/app.tsx
+++ b/src/js/app.tsx
@@ -1,92 +1,91 @@
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)
+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 | null) => {
+ 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 }
- })
- }
+ const updateTitle = () => {
+ api.siteInfo().then((info) => {
+ if (info.siteName != document.title) {
+ setTitle(info.siteName)
+ localStorage.setItem(titleKey, info.siteName)
+ }
+ })
+ return
}
- render() {
- return (
- <>
-
-
- >
- )
- }
+ setInterval(() => {
+ updateStreamList()
+ }, 10000)
+
+ useEffect(() => {
+ updateStreamList()
+ }, [])
+
+ useEffect(() => {
+ updateTitle()
+ }, [])
+
+ return (
+ <>
+
+
+ >
+ )
}
const rootElement = document.getElementById("app");
+setTitleFromLocalstorage()
if (rootElement) {
- const root = createRoot(rootElement);
- root.render()
+ const root = createRoot(rootElement)
+ 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
}
diff --git a/src/js/menu.tsx b/src/js/menu.tsx
index bc947e3..ec8f87f 100644
--- a/src/js/menu.tsx
+++ b/src/js/menu.tsx
@@ -1,45 +1,42 @@
import { createRoot } from "react-dom/client";
import React from "react";
-type StreamSelectCallback = (selected: string) => void
+type StreamSelectCallback = (selected: string | null) => 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 title = document.title
+ 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 (
+
+ )
+
}