Compare commits

...

6 commits

Author SHA1 Message Date
Dessalines
5a3db14872 Building themes on startup. 2022-02-24 16:48:15 -05:00
Dessalines
794011af62 Merge branch 'main' into custom-themes 2022-02-24 16:31:33 -05:00
Felix Ableitner
7700b668ab themes need to end with .min.css 2022-02-18 21:52:09 +01:00
Felix Ableitner
5e8fd5ccae use sendfile 2022-02-18 21:48:49 +01:00
Felix Ableitner
8049ce6abf try to get list of themes from server side 2022-02-16 15:18:25 +01:00
Felix Ableitner
904d33e895 wip: support custom themes 2022-02-16 12:49:00 +01:00
6 changed files with 618 additions and 465 deletions

View file

@ -19,6 +19,7 @@
"dependencies": { "dependencies": {
"@typescript-eslint/parser": "^5.6.0", "@typescript-eslint/parser": "^5.6.0",
"autosize": "^5.0.1", "autosize": "^5.0.1",
"buffer": "^6.0.3",
"check-password-strength": "^2.0.3", "check-password-strength": "^2.0.3",
"choices.js": "^10.0.0", "choices.js": "^10.0.0",
"classnames": "^2.3.1", "classnames": "^2.3.1",

View file

@ -1,4 +1,5 @@
import express from "express"; import express from "express";
import fs from "fs";
import { IncomingHttpHeaders } from "http"; import { IncomingHttpHeaders } from "http";
import { Helmet } from "inferno-helmet"; import { Helmet } from "inferno-helmet";
import { matchPath, StaticRouter } from "inferno-router"; import { matchPath, StaticRouter } from "inferno-router";
@ -23,6 +24,10 @@ const server = express();
const [hostname, port] = process.env["LEMMY_UI_HOST"] const [hostname, port] = process.env["LEMMY_UI_HOST"]
? process.env["LEMMY_UI_HOST"].split(":") ? process.env["LEMMY_UI_HOST"].split(":")
: ["0.0.0.0", "1234"]; : ["0.0.0.0", "1234"];
const extraThemesFolder =
process.env["LEMMY_UI_EXTRA_THEMES_FOLDER"] || "./extra_themes";
export const themeList = buildThemeList();
server.use(express.json()); server.use(express.json());
server.use(express.urlencoded({ extended: false })); server.use(express.urlencoded({ extended: false }));
@ -46,6 +51,86 @@ server.get("/robots.txt", async (_req, res) => {
res.send(robotstxt); res.send(robotstxt);
}); });
export const builtinThemes = [
"litera",
"materia",
"minty",
"solar",
"united",
"cyborg",
"darkly",
"journal",
"sketchy",
"vaporwave",
"vaporwave-dark",
"i386",
"litely",
"nord",
];
function buildThemeList(): string[] {
let data = fs.readdirSync(extraThemesFolder);
if (data != null) {
data = data
.filter(d => d.endsWith(".min.css"))
.map(d => d.replace(".min.css", ""));
data = builtinThemes.concat(data);
// use set to remove duplicate values
data = Array.from(new Set(data));
return data;
} else {
return builtinThemes;
}
}
server.get("/css/themes-list", async (req, res) => {
const builtinThemes = [
"litera",
"materia",
"minty",
"solar",
"united",
"cyborg",
"darkly",
"journal",
"sketchy",
"vaporwave",
"vaporwave-dark",
"i386",
"litely",
"nord",
];
fs.readdir(extraThemesFolder, function (_, data) {
if (data != null) {
data = data
.filter(d => d.endsWith(".min.css"))
.map(d => d.replace(".min.css", ""));
data = builtinThemes.concat(data);
// use set to remove duplicate values
data = Array.from(new Set(data));
res.send(data);
} else {
res.send(builtinThemes);
}
});
});
server.get("/css/themes/:name", async (req, res) => {
res.contentType("text/css");
const theme = req.params.name;
if (!theme.endsWith(".min.css")) {
res.send("Theme must be a css file");
}
const customTheme = path.resolve(`./${extraThemesFolder}/${theme}`);
if (fs.existsSync(customTheme)) {
res.sendFile(customTheme);
} else {
const internalTheme = path.resolve(`./dist/assets/css/themes/${theme}`);
res.sendFile(internalTheme);
}
});
// server.use(cookieParser()); // server.use(cookieParser());
server.get("/*", async (req, res) => { server.get("/*", async (req, res) => {
try { try {

View file

@ -17,21 +17,21 @@ export class Theme extends Component<Props> {
<link <link
rel="stylesheet" rel="stylesheet"
type="text/css" type="text/css"
href={`/static/assets/css/themes/${user.local_user_view.local_user.theme}.min.css`} href={`css/themes/${user.local_user_view.local_user.theme}.min.css`}
/> />
) : ( ) : (
[ [
<link <link
rel="stylesheet" rel="stylesheet"
type="text/css" type="text/css"
href="/static/assets/css/themes/litely.min.css" href="css/themes/litely.min.css"
id="default-light" id="default-light"
media="(prefers-color-scheme: light)" media="(prefers-color-scheme: light)"
/>, />,
<link <link
rel="stylesheet" rel="stylesheet"
type="text/css" type="text/css"
href="/static/assets/css/themes/darkly.min.css" href="css/themes/darkly.min.css"
id="default-dark" id="default-dark"
media="(prefers-color-scheme: no-preference), (prefers-color-scheme: dark)" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: dark)"
/>, />,

View file

@ -29,6 +29,7 @@ import {
debounce, debounce,
elementUrl, elementUrl,
fetchCommunities, fetchCommunities,
fetchThemes,
fetchUsers, fetchUsers,
getLanguages, getLanguages,
isBrowser, isBrowser,
@ -39,7 +40,6 @@ import {
setTheme, setTheme,
setupTippy, setupTippy,
showLocal, showLocal,
themes,
toast, toast,
updateCommunityBlock, updateCommunityBlock,
updatePersonBlock, updatePersonBlock,
@ -545,7 +545,7 @@ export class Settings extends Component<any, SettingsState> {
{i18n.t("theme")} {i18n.t("theme")}
</option> </option>
<option value="browser">{i18n.t("browser_default")}</option> <option value="browser">{i18n.t("browser_default")}</option>
{themes.map(theme => ( {fetchThemes().map(theme => (
<option value={theme}>{theme}</option> <option value={theme}>{theme}</option>
))} ))}
</select> </select>

View file

@ -77,22 +77,23 @@ export const mentionDropdownFetchLimit = 10;
export const relTags = "noopener nofollow"; export const relTags = "noopener nofollow";
export const themes = [ export function fetchThemes(): Promise<string[]> {
"litera", var promise = new Promise<string[]>((resolve, reject) => {
"materia", if (isBrowser()) {
"minty", const url = `${window.location.protocol}//${window.location.host}/css/themes-list`;
"solar", console.log(url);
"united", fetch(url).then(res => {
"cyborg", res.json().then(json => {
"darkly", console.log(json);
"journal", resolve(json);
"sketchy", });
"vaporwave", });
"vaporwave-dark", } else {
"i386", listThemes().then(themes => resolve(themes));
"litely", }
"nord", });
]; return promise;
}
const DEFAULT_ALPHABET = const DEFAULT_ALPHABET =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

954
yarn.lock

File diff suppressed because it is too large Load diff