Skip to content

Commit 74af04c

Browse files
Done with the application. would be working on docs and possible fixes for unforseen bugs.
1 parent 53fd9db commit 74af04c

File tree

10 files changed

+173
-36
lines changed

10 files changed

+173
-36
lines changed

app/_layout.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { DownloadProvider } from "../contexts/DownloaderContext";
2727
import { useBackendUrl } from "../hooks/useBackendUrl";
2828

2929
const API_BASE =
30-
process.env.NEXT_PUBLIC_API_BASE || "https://0bea512690fc.ngrok-free.app";
30+
process.env.NEXT_PUBLIC_API_BASE || "https://seekbeat.onrender.com";
3131

3232
export default function Layout() {
3333
const [showSplashState, setShowSplashState] = useState(

app/settings.js

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { useShortcuts } from "../contexts/ShortCutContext";
1919
import { useResponsive } from "../contexts/ResponsiveContext";
2020
import { useBackendUrl } from "../hooks/useBackendUrl";
2121
import { HEXA } from "../lib/colors";
22+
import { ShortCutIcon } from "../components/ShortCutIcon";
23+
import InfoModal from "../components/InfoModal";
2224

2325
export default function Settings() {
2426
const { setRightSidebarKey } = useRightSidebar();
@@ -28,6 +30,7 @@ export default function Settings() {
2830
base: shades.base,
2931
}));
3032
const [inputUrl, setInputUrl] = useState("");
33+
const [openInfoModal, setsetOpenInfoModal] = useState(false);
3134
const { setShowShortcuts } = useShortcuts();
3235
const {
3336
saveSearchHistory,
@@ -41,12 +44,9 @@ export default function Settings() {
4144
forceProxy,
4245
setForceProxy,
4346
} = useAppStorage();
44-
const { isAtOrBelow, isBetween, isAtOrAbove } = useResponsive();
45-
const betweenTabletAndLaptop = isBetween("md", "lg");
47+
const { isAtOrBelow } = useResponsive();
4648
const mobileAndBelow = isAtOrBelow("sm");
47-
const tabletAndAbove = isAtOrAbove("md", true);
4849
const laptopAndBelow = isAtOrBelow("xl");
49-
const laptopAndAbove = isAtOrAbove("xl", true);
5050
const tabletAndBelow = isAtOrBelow("md", true);
5151

5252
const {
@@ -113,7 +113,7 @@ export default function Settings() {
113113
};
114114

115115
const handleResetURL = () => {
116-
if (confirm("Restore the default URL (http://127.0.0.1:8000)?")) {
116+
if (confirm("Restore the default URL (https://seekbeat.onrender.com)?")) {
117117
try {
118118
resetBackendUrl();
119119
// optionally show toast/snackbar
@@ -578,6 +578,14 @@ export default function Settings() {
578578
gap: 10, // RN 0.71+ supports gap; otherwise use margin
579579
}}
580580
>
581+
<TouchableOpacity onPress={() => setsetOpenInfoModal(true)}>
582+
<InfoIcon size={30} color={theme.accent} />
583+
</TouchableOpacity>
584+
<InfoModal
585+
visible={openInfoModal}
586+
onClose={() => setsetOpenInfoModal(false)}
587+
theme={theme}
588+
/>
581589
<TextInput
582590
value={inputUrl}
583591
onChangeText={setInputUrl}
@@ -595,7 +603,7 @@ export default function Settings() {
595603
color: theme.text,
596604
}}
597605
placeholderTextColor={theme.textSecondary}
598-
placeholder={backendUrl || "http://localhost:8000"}
606+
placeholder={backendUrl || "https://seekbeat.onrender.com"}
599607
/>
600608

601609
<TouchableOpacity
@@ -751,8 +759,16 @@ export default function Settings() {
751759
</Text>
752760
</View>
753761
<View style={styles.settingDescSet}>
754-
<TouchableOpacity onPress={() => setShowShortcuts(true)}>
755-
<InfoIcon size={50} color={theme.accent} />
762+
<TouchableOpacity
763+
onPress={() => setShowShortcuts(true)}
764+
style={{
765+
padding: 10,
766+
borderWidth: 1,
767+
borderColor: theme.accent,
768+
borderRadius: 100,
769+
}}
770+
>
771+
<ShortCutIcon size={50} color={theme.accent} />
756772
</TouchableOpacity>
757773
</View>
758774
</View>

components/AccordionGroup.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default function AccordionGroup({
1010
viewMode = "list",
1111
initiallyExpanded = false,
1212
}) {
13-
console.log("Accordion: ", block);
13+
// console.log("Accordion: ", block);
1414
const [expanded, setExpanded] = useState(initiallyExpanded);
1515
const { theme } = useTheme();
1616
const termText = block?.search_term?.query ?? "Search";

components/InfoModal.jsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React from "react";
2+
import {
3+
Modal,
4+
View,
5+
ScrollView,
6+
Text,
7+
Pressable,
8+
Linking,
9+
} from "react-native";
10+
import { HEXA } from "../lib/colors";
11+
12+
export default function InfoModal({ visible, onClose, theme }) {
13+
const openReleases = () =>
14+
Linking.openURL("https://github.com/Programming-Sai/SeekBeat/releases");
15+
16+
return (
17+
<Modal visible={visible} animationType="fade" transparent>
18+
<View
19+
style={{
20+
flex: 1,
21+
backgroundColor: HEXA(theme.text, 0.7),
22+
justifyContent: "center",
23+
alignItems: "center",
24+
}}
25+
>
26+
<View
27+
style={{
28+
backgroundColor: theme.background,
29+
borderRadius: 12,
30+
width: "90%",
31+
maxHeight: "85%",
32+
padding: 20,
33+
}}
34+
>
35+
<ScrollView showsVerticalScrollIndicator={false}>
36+
<Text style={{ fontSize: 22, color: theme.text, marginBottom: 16 }}>
37+
Seekbeat Companion Backend
38+
</Text>
39+
40+
<Text style={{ color: theme.textSecondary, marginBottom: 12 }}>
41+
Seekbeat relies on a companion backend for search, streaming, and
42+
downloads. The default hosted URL supports search only. To unlock
43+
full functionality, run the backend yourself.
44+
</Text>
45+
46+
<Text style={{ color: theme.text, fontWeight: "600" }}>
47+
Quick Setup
48+
</Text>
49+
<Text style={{ color: theme.textSecondary, marginBottom: 12 }}>
50+
1. Download the latest Windows .exe from{" "}
51+
<Text
52+
style={{ color: theme.accent, fontWeight: "bold" }}
53+
onPress={openReleases}
54+
>
55+
Github Releases
56+
</Text>
57+
.{"\n"}2. Double-click the .exe to start the server.
58+
{"\n"}3. Copy the URL shown in the console (e.g.
59+
http://192.168.x.x:8000).
60+
{"\n"}4. Paste it into Settings → Backend URL → Test.
61+
</Text>
62+
63+
<Text style={{ color: theme.text, fontWeight: "600" }}>
64+
Mobile Access
65+
</Text>
66+
<Text style={{ color: theme.textSecondary, marginBottom: 12 }}>
67+
• Run the backend on a laptop/desktop.
68+
{"\n"}• On the same Wi-Fi, use the http://192.168.x.x:8000 IP.
69+
{"\n"}• For remote access, install ngrok, run `ngrok http 8000`,
70+
and use the generated URL.
71+
</Text>
72+
73+
<Text style={{ color: theme.textSecondary }}>
74+
Keep the console open while using Seekbeat. Allow firewall access
75+
if prompted.
76+
</Text>
77+
</ScrollView>
78+
79+
<Pressable
80+
style={{
81+
marginTop: 20,
82+
alignSelf: "center",
83+
paddingHorizontal: 20,
84+
paddingVertical: 10,
85+
backgroundColor: theme.accent,
86+
borderRadius: 8,
87+
}}
88+
onPress={onClose}
89+
>
90+
<Text style={{ color: "#fff", fontWeight: "600" }}>Got it</Text>
91+
</Pressable>
92+
</View>
93+
</View>
94+
</Modal>
95+
);
96+
}

components/ShortCutIcon.jsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react";
2+
3+
export function ShortCutIcon({ size = 24, color = "currentColor" }) {
4+
return (
5+
<svg
6+
width={size}
7+
height={size}
8+
viewBox="0 0 24 24"
9+
fill="none"
10+
xmlns="http://www.w3.org/2000/svg"
11+
>
12+
<path
13+
d="M21 11L15 5V10H8C5.24 10 3 12.24 3 15V19H5V15C5 13.35 6.35 12 8 12H15V17L21 11Z"
14+
fill={color}
15+
/>
16+
</svg>
17+
);
18+
}

contexts/DownloaderContext.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// hooks/useDownloader.js
22
import { createContext, useCallback, useContext } from "react";
33
import { useAppStorage } from "../contexts/AppStorageContext";
4+
import { isNgrokUrl } from "../lib/utils";
45

56
/**
67
* useDownloader - returns a `download` function to trigger backend POST download.
@@ -9,10 +10,7 @@ import { useAppStorage } from "../contexts/AppStorageContext";
910
*/
1011
const DownloadContext = createContext(null);
1112

12-
export function DownloadProvider({
13-
children,
14-
downloadBase = "https://0bea512690fc.ngrok-free.app",
15-
}) {
13+
export function DownloadProvider({ children, downloadBase = null }) {
1614
const { addDownload, updateDownload, setDownloadStatus, getLastSearch } =
1715
useAppStorage();
1816

@@ -50,12 +48,18 @@ export function DownloadProvider({
5048
// set transient/persistent status (pending)
5149
setDownloadStatus(id, "pending");
5250

51+
const headers = {};
52+
if (isNgrokUrl(downloadBase)) {
53+
headers["ngrok-skip-browser-warning"] = "true";
54+
}
55+
headers["Content-Type"] = "application/json";
56+
5357
try {
5458
const res = await fetch(
5559
`${downloadBase}/api/stream/${encodeURIComponent(id)}/`,
5660
{
5761
method: "POST",
58-
headers: { "Content-Type": "application/json" },
62+
headers,
5963
body: JSON.stringify({ edits: edits || {} }),
6064
}
6165
);

contexts/PlayerContext.js

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import React, {
99
useState,
1010
} from "react";
1111
import { useAppStorage } from "./AppStorageContext";
12+
import { isNgrokUrl } from "../lib/utils";
1213

1314
/**
1415
* PlayerContext - adjusted:
@@ -18,10 +19,7 @@ import { useAppStorage } from "./AppStorageContext";
1819

1920
const PlayerContext = createContext(null);
2021

21-
export function PlayerProvider({
22-
children,
23-
streamBase = "https://0bea512690fc.ngrok-free.app",
24-
}) {
22+
export function PlayerProvider({ children, streamBase = null }) {
2523
const [queue, setQueue] = useState([]);
2624
const [currentIndex, setCurrentIndex] = useState(-1);
2725
const [isPlaying, setIsPlaying] = useState(false);
@@ -287,13 +285,14 @@ export function PlayerProvider({
287285

288286
const controller = new AbortController();
289287
const promise = (async () => {
288+
const headers = {};
289+
if (isNgrokUrl(streamBase)) {
290+
headers["ngrok-skip-browser-warning"] = "true";
291+
}
290292
try {
291293
const res = await fetch(endpoint, {
292294
signal: controller.signal,
293-
headers: {
294-
// other headers you need...
295-
"ngrok-skip-browser-warning": "true",
296-
},
295+
headers,
297296
});
298297
// if (!res.ok) throw new Error("fetch failed: " + res.status);
299298
const data = await res.json();
@@ -327,12 +326,6 @@ export function PlayerProvider({
327326
(song) => {
328327
if (!song) return null;
329328
if (streamBase) {
330-
console.log(
331-
"Should be streaming from here: ",
332-
`${streamBase}/api/stream/${encodeURIComponent(
333-
song.id || song.webpage_url || ""
334-
)}/`
335-
);
336329
return `${streamBase}/api/stream/${encodeURIComponent(
337330
song.id || song.webpage_url || ""
338331
)}/`;

contexts/SearchContext.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import React, {
77
useRef,
88
useState,
99
} from "react";
10-
import { normalizeSearchResponse } from "../lib/utils";
10+
import { isNgrokUrl, normalizeSearchResponse } from "../lib/utils";
1111
import { useAppStorage } from "./AppStorageContext";
1212

1313
const SearchContext = createContext(null);
@@ -56,7 +56,7 @@ function augmentWithId(normalized) {
5656
export function SearchProvider({
5757
children,
5858
defaultPageSize = 12,
59-
searchBase = "https://0bea512690fc.ngrok-free.app",
59+
searchBase = null,
6060
}) {
6161
const [query, setQuery] = useState("");
6262
const [response, setResponse] = useState(null);
@@ -110,13 +110,14 @@ export function SearchProvider({
110110
setError(null);
111111

112112
const url = `${searchBase}/api/search/?query=${encodeURIComponent(q)}`;
113+
const headers = {};
114+
if (isNgrokUrl(searchBase)) {
115+
headers["ngrok-skip-browser-warning"] = "true";
116+
}
113117
try {
114118
const res = await fetch(url, {
115119
signal: controller.signal,
116-
headers: {
117-
// other headers you need...
118-
"ngrok-skip-browser-warning": "true",
119-
},
120+
headers,
120121
});
121122
console.log("[SEARCH] Request URL:", url);
122123
console.log("[SEARCH] Response URL (final):", res.url);
@@ -166,6 +167,7 @@ export function SearchProvider({
166167
console.error("Search error:", err);
167168
setError(err.message || String(err));
168169
}
170+
console.log("Sorry Something went wrong: ", String(err), url);
169171
} finally {
170172
setIsLoading(false);
171173
abortRef.current = null;

hooks/useBackendUrl.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { useCallback, useEffect, useState } from "react";
33

44
const STORAGE_KEY = "seekbeat:backend_base";
5-
const DEFAULT_BACKEND = "http://127.0.0.1:8000";
5+
const DEFAULT_BACKEND = "https://seekbeat.onrender.com";
66

77
// simple in-window pubsub so multiple hook instances in same document sync immediately
88
const backendEventTarget =

lib/utils.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,11 @@ export function timeAgo(isoDateString) {
231231

232232
return "just now";
233233
}
234+
235+
export function isNgrokUrl(url = "") {
236+
return (
237+
url.includes("ngrok-free.app") || // free ngrok subdomains
238+
url.includes("ngrok.app") || // paid/ngrok.io custom ones
239+
/https:\/\/.*\.ngrok(-free)?\.app/i.test(url)
240+
);
241+
}

0 commit comments

Comments
 (0)