Skip to content

Commit 17f16ae

Browse files
committed
custom theme in config
1 parent f5f59ac commit 17f16ae

4 files changed

Lines changed: 71 additions & 45 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,21 @@ Please join the [Discord server](https://discord.gg/BEGXEk29Up) for support and
1212
* Registration modes (open / closed / invite only)
1313
* Sending of invites
1414
* Account management (2FA, password resets etc.)
15-
* Bonus points system
15+
* Bonus points system (purchase invites, upload etc.)
1616
* Option to browse torrents without logging in (for search engine discovery)
1717
* Torrent management
1818
* Uploading torrents with rich metadata (title, description, source, mediainfo, category, tags etc.)
1919
* Searching torrents or browsing by category or tags
20-
* Freeleech options (specific torrents, sitewide)
20+
* Freeleech options (specific torrents, site-wide)
2121
* Torrent grouping (e.g. different formats of same movie)
2222
* Bookmarks
2323
* Upload / download tracking
2424
* Track how much content each user has uploaded / downloaded
2525
* Track ratios
2626
* Limit up / downloading per user based on ratio
27-
* Award invites based on ratio
27+
* Award bonus points based on upload
2828
* User interaction
29-
* Commenting on torrents
29+
* Commenting on torrents and announcements
3030
* Up / down voting torrents
3131
* Requests system
3232
* Moderation

api/src/utils/validateConfig.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import * as yup from "yup";
22

33
const httpRegex = /http(s)?:\/\/.*/;
44
const mongoRegex = /mongodb:\/\/.*/;
5+
const hexRegex = /#([a-f0-9]){6}/i;
56

67
const configSchema = yup
78
.object({
89
envs: yup
910
.object({
1011
SQ_SITE_NAME: yup.string().min(1).max(20).required(),
1112
SQ_SITE_DESCRIPTION: yup.string().min(1).max(80).required(),
12-
SQ_THEME_COLOUR: yup.string().matches(/#([a-f0-9]){6}/i),
1313
SQ_ALLOW_REGISTER: yup
1414
.string()
1515
.oneOf(["open", "invite", "closed"])
@@ -39,6 +39,14 @@ const configSchema = yup
3939
return yup.object(entries).required();
4040
}),
4141
SQ_ALLOW_UNREGISTERED_VIEW: yup.boolean().required(),
42+
SQ_CUSTOM_THEME: yup.object({
43+
primary: yup.string().matches(hexRegex),
44+
background: yup.string().matches(hexRegex),
45+
sidebar: yup.string().matches(hexRegex),
46+
border: yup.string().matches(hexRegex),
47+
text: yup.string().matches(hexRegex),
48+
grey: yup.string().matches(hexRegex),
49+
}),
4250
SQ_BASE_URL: yup.string().matches(httpRegex).required(),
4351
SQ_API_URL: yup.string().matches(httpRegex).required(),
4452
SQ_MONGO_URL: yup.string().matches(mongoRegex).required(),

client/pages/_app.js

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,31 @@ import { NotificationsProvider } from "../components/Notifications";
2727
import Text from "../components/Text";
2828
import LoadingContext from "../utils/LoadingContext";
2929

30-
const getThemeColours = (theme, primary = "#f45d48") => {
31-
switch (theme) {
30+
const getThemeColours = (themeName, customTheme = {}) => {
31+
switch (themeName) {
3232
case "light":
3333
return {
34-
primary,
35-
background: "#ffffff",
36-
sidebar: "#f8f8f8",
37-
text: "#202224",
38-
grey: "#747474",
34+
primary: customTheme.primary ?? "#f45d48",
35+
background: customTheme.background ?? "#ffffff",
36+
sidebar: customTheme.sidebar ?? "#f8f8f8",
37+
border: customTheme.border ?? "#deebf1",
38+
text: customTheme.text ?? "#202224",
39+
grey: customTheme.grey ?? "#747474",
3940
error: "#f33",
4041
success: "#44d944",
4142
info: "#427ee1",
42-
border: "#deebf1",
4343
};
4444
case "dark":
4545
return {
46-
primary,
47-
background: "#1f2023",
48-
sidebar: "#27282b",
49-
text: "#f8f8f8",
50-
grey: "#aaa",
46+
primary: customTheme.primary ?? "#f45d48",
47+
background: customTheme.background ?? "#1f2023",
48+
sidebar: customTheme.sidebar ?? "#27282b",
49+
border: customTheme.border ?? "#303236",
50+
text: customTheme.text ?? "#f8f8f8",
51+
grey: customTheme.grey ?? "#aaa",
5152
error: "#f33",
5253
success: "#44d944",
5354
info: "#427ee1",
54-
border: "#303236",
5555
};
5656
}
5757
};
@@ -169,13 +169,17 @@ const SqTracker = ({ Component, pageProps, initialTheme }) => {
169169

170170
const {
171171
publicRuntimeConfig: {
172-
SQ_THEME_COLOUR,
172+
SQ_CUSTOM_THEME,
173173
SQ_SITE_WIDE_FREELEECH,
174174
SQ_API_URL,
175175
SQ_MINIMUM_RATIO,
176176
},
177177
} = getConfig();
178178

179+
const allowThemeToggle = !Object.keys(SQ_CUSTOM_THEME ?? {}).some(
180+
(key) => key !== "primary"
181+
);
182+
179183
const setThemeAndSave = (theme) => {
180184
setTheme(theme);
181185
setCookie("theme", theme, {
@@ -193,12 +197,14 @@ const SqTracker = ({ Component, pageProps, initialTheme }) => {
193197
setIsMobile(matches);
194198
});
195199

196-
const { theme: themeCookie } = cookies;
197-
const themeQuery = window.matchMedia("(prefers-color-scheme: light)");
198-
if (!themeCookie) setThemeAndSave(themeQuery.matches ? "light" : "dark");
199-
themeQuery.addEventListener("change", ({ matches }) => {
200-
setThemeAndSave(matches ? "light" : "dark");
201-
});
200+
if (allowThemeToggle) {
201+
const { theme: themeCookie } = cookies;
202+
const themeQuery = window.matchMedia("(prefers-color-scheme: light)");
203+
if (!themeCookie) setThemeAndSave(themeQuery.matches ? "light" : "dark");
204+
themeQuery.addEventListener("change", ({ matches }) => {
205+
setThemeAndSave(matches ? "light" : "dark");
206+
});
207+
}
202208

203209
Router.events.on("routeChangeStart", () => setLoading(true));
204210
Router.events.on("routeChangeComplete", () => setLoading(false));
@@ -226,7 +232,7 @@ const SqTracker = ({ Component, pageProps, initialTheme }) => {
226232

227233
const appTheme = {
228234
...baseTheme,
229-
colors: getThemeColours(theme, SQ_THEME_COLOUR),
235+
colors: getThemeColours(theme, SQ_CUSTOM_THEME),
230236
name: theme,
231237
};
232238

@@ -353,22 +359,24 @@ const SqTracker = ({ Component, pageProps, initialTheme }) => {
353359
ref={searchRef}
354360
/>
355361
</Box>
356-
<Button
357-
variant="secondary"
358-
onClick={() => {
359-
setThemeAndSave(theme === "light" ? "dark" : "light");
360-
}}
361-
width="40px"
362-
px={2}
363-
py={2}
364-
ml={3}
365-
>
366-
{theme === "light" ? (
367-
<Sun size={24} />
368-
) : (
369-
<Moon size={24} />
370-
)}
371-
</Button>
362+
{allowThemeToggle && (
363+
<Button
364+
variant="secondary"
365+
onClick={() => {
366+
setThemeAndSave(theme === "light" ? "dark" : "light");
367+
}}
368+
width="40px"
369+
px={2}
370+
py={2}
371+
ml={3}
372+
>
373+
{theme === "light" ? (
374+
<Sun size={24} />
375+
) : (
376+
<Moon size={24} />
377+
)}
378+
</Button>
379+
)}
372380
</Box>
373381
)}
374382
</Box>

config.example.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,18 @@ module.exports = {
99
// A short description of your tracker site. Maximum 80 characters.
1010
SQ_SITE_DESCRIPTION: "A short description for your tracker site",
1111

12-
// A hex colour code used as the main theme colour of your site.
13-
SQ_THEME_COLOUR: "#f45d48",
12+
// A map of custom hex colours to use as the theme of your site.
13+
// If not specified, the default light and dark themes will be used.
14+
// If only "primary" is specified, the default light and dark themes will be used but with your main brand colour.
15+
// If the other values are specified, the fully custom theme will be used and not the default light/dark.
16+
SQ_CUSTOM_THEME: {
17+
primary: "#f45d48",
18+
// background: "#1f2023", // Page background colour
19+
// sidebar: "#27282b", // A secondary background colour, used for sidebar, infoboxes etc.
20+
// border: "#303236", // Border colour
21+
// text: "#f8f8f8", // Text colour. Should be readable against background and sidebar
22+
// grey: "#aaa", // Secondary text colour, used for less important text
23+
},
1424

1525
// Registration mode. Either "open", "invite" or "closed".
1626
// Open: anyone can register.

0 commit comments

Comments
 (0)