diff --git a/.gitignore b/.gitignore
index 42a1b3c..8a0aca8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,4 +72,6 @@ link-plugins.sh
test.sh
.docker/**
-!**/.gitkeep
\ No newline at end of file
+!**/.gitkeep
+# stryker temp files
+.stryker-tmp
diff --git a/.strykerignore b/.strykerignore
new file mode 100644
index 0000000..d82c3c2
--- /dev/null
+++ b/.strykerignore
@@ -0,0 +1,3 @@
+build/
+public/
+plugins/
diff --git a/dump.rdb b/dump.rdb
index d7dcdca..4cd026c 100644
Binary files a/dump.rdb and b/dump.rdb differ
diff --git a/nodebb-theme-harmony/plugin.json b/nodebb-theme-harmony/plugin.json
index 9a5bb19..211ca8b 100644
--- a/nodebb-theme-harmony/plugin.json
+++ b/nodebb-theme-harmony/plugin.json
@@ -12,7 +12,12 @@
{ "hook": "filter:teasers.configureStripTags", "method": "filterTeasersConfigureStripTags"}
],
"scripts": [
- "public/harmony.js"
+ "public/harmony.js",
+ "public/darkmode.js",
+ "public/styles/darkmode.css"
+ ],
+ "css": [
+ "public/styles/darkmode.css"
],
"modules": {
"../admin/plugins/harmony.js": "public/admin.js",
diff --git a/nodebb-theme-harmony/public/darkmode.js b/nodebb-theme-harmony/public/darkmode.js
new file mode 100644
index 0000000..b85f0d4
--- /dev/null
+++ b/nodebb-theme-harmony/public/darkmode.js
@@ -0,0 +1,37 @@
+// $(document).ready(function () {
+// // Check user's saved preference and apply dark mode
+// if (config.user && config.user.settings.darkMode) {
+// $('body').addClass('dark-mode');
+// $('#darkModeToggle').prop('checked', true);
+// }
+
+// // Toggle dark mode when user changes the setting
+// $('#darkModeToggle').change(function () {
+// const isDarkMode = $(this).prop('checked');
+// $('body').toggleClass('dark-mode', isDarkMode);
+// $.post('/api/user/settings', { darkMode: isDarkMode ? '1' : '0' });
+// });
+// });
+
+document.addEventListener("DOMContentLoaded", function () {
+ const darkModeToggle = document.getElementById("darkModeToggle");
+
+ function applyDarkMode(isEnabled) {
+ if (isEnabled) {
+ document.body.classList.add("dark-mode");
+ localStorage.setItem("darkMode", "true");
+ } else {
+ document.body.classList.remove("dark-mode");
+ localStorage.setItem("darkMode", "false");
+ }
+ }
+
+ // Load setting from local storage
+ applyDarkMode(localStorage.getItem("darkMode") === "true");
+
+ if (darkModeToggle) {
+ darkModeToggle.addEventListener("change", function () {
+ applyDarkMode(this.checked);
+ });
+ }
+});
diff --git a/nodebb-theme-harmony/public/harmony.js b/nodebb-theme-harmony/public/harmony.js
index 6387055..bde9e94 100644
--- a/nodebb-theme-harmony/public/harmony.js
+++ b/nodebb-theme-harmony/public/harmony.js
@@ -11,6 +11,28 @@ $(document).ready(function () {
fixPlaceholders();
fixSidebarOverflow();
+ const darkModeToggle = document.getElementById("darkModeToggle");
+
+ if (darkModeToggle) {
+ // Apply stored preference
+ if (localStorage.getItem("darkMode") === "enabled") {
+ document.body.classList.add("dark-mode");
+ darkModeToggle.checked = true;
+ }
+
+ // Listen for changes
+ darkModeToggle.addEventListener("change", function () {
+ if (this.checked) {
+ document.body.classList.add("dark-mode");
+ localStorage.setItem("darkMode", "enabled");
+ } else {
+ document.body.classList.remove("dark-mode");
+ localStorage.setItem("darkMode", "disabled");
+ }
+ });
+ }
+
+
function setupSkinSwitcher() {
$('[component="skinSwitcher"]').on('click', '.dropdown-item', function () {
const skin = $(this).attr('data-value');
diff --git a/nodebb-theme-harmony/templates/account/settings.tpl b/nodebb-theme-harmony/templates/account/settings.tpl
index c4c78a1..7ae1c8a 100644
--- a/nodebb-theme-harmony/templates/account/settings.tpl
+++ b/nodebb-theme-harmony/templates/account/settings.tpl
@@ -65,11 +65,19 @@
[[user:browsing]]
+
[[user:open-links-in-new-tab]]
+
+
+ Enable Dark Mode
+
+
+
+
{{{ if inTopicSearchAvailable }}}
@@ -92,6 +100,11 @@
[[global:pagination]]
+
+
+ Enable Dark Mode
+
+
[[user:paginate-description]]
diff --git a/nodebb-theme-harmony/templates/account/theme.tpl b/nodebb-theme-harmony/templates/account/theme.tpl
index 67fdbc7..5772006 100644
--- a/nodebb-theme-harmony/templates/account/theme.tpl
+++ b/nodebb-theme-harmony/templates/account/theme.tpl
@@ -59,6 +59,13 @@
+
+
+
+ Enable Dark Mode
+
+
+
\ No newline at end of file
diff --git a/nodebb-theme-harmony/templates/partials/topic/post.tpl b/nodebb-theme-harmony/templates/partials/topic/post.tpl
index e2e92ad..512c7c3 100644
--- a/nodebb-theme-harmony/templates/partials/topic/post.tpl
+++ b/nodebb-theme-harmony/templates/partials/topic/post.tpl
@@ -39,7 +39,11 @@
{generateWroteReplied(@value, config.timeagoCutoff)}
-
+
+
+
+
+
[[global:last-edited-by, {posts.editor.username}]]
diff --git a/src/cli/index.js b/src/cli/index.js
index e6f0485..dfa49e7 100644
--- a/src/cli/index.js
+++ b/src/cli/index.js
@@ -103,21 +103,43 @@ global.env = process.env.NODE_ENV || 'production';
prestart.setupWinston();
// Alternate configuration file support
+// const configFile = path.resolve(paths.baseDir, nconf.get('config') || 'config.json');
+// const configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database'));
+
+// prestart.loadConfig(configFile);
+// prestart.versionCheck();
+
+// if (!configExists && process.argv[2] !== 'setup') {
+// require('./setup').webInstall();
+// return;
+// }
+
+// if (configExists) {
+// process.env.CONFIG = configFile;
+// }
+
+
+//code change by evelyn
const configFile = path.resolve(paths.baseDir, nconf.get('config') || 'config.json');
const configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database'));
prestart.loadConfig(configFile);
prestart.versionCheck();
-if (!configExists && process.argv[2] !== 'setup') {
- require('./setup').webInstall();
- return;
+function checkAndSetup() {
+ if (!configExists && process.argv[2] !== 'setup') {
+ require('./setup').webInstall();
+ return; // ✅ Now inside a function, so no syntax error
+ }
}
+checkAndSetup();
+
if (configExists) {
- process.env.CONFIG = configFile;
+ process.env.CONFIG = configFile;
}
+
// running commands
program
.command('start')
diff --git a/src/user/settings.js b/src/user/settings.js
index d85a712..c688e58 100644
--- a/src/user/settings.js
+++ b/src/user/settings.js
@@ -24,8 +24,10 @@ module.exports = function (User) {
let settings = await db.getObject(`user:${uid}:settings`);
settings = settings || {};
settings.uid = uid;
+ settings.darkMode = settings.darkMode || false; // Retrieve dark mode setting
return await onSettingsLoaded(uid, settings);
};
+
User.getMultipleUserSettings = async function (uids) {
if (!Array.isArray(uids) || !uids.length) {
@@ -148,6 +150,7 @@ module.exports = function (User) {
categoryWatchState: data.categoryWatchState,
categoryTopicSort: data.categoryTopicSort,
topicPostSort: data.topicPostSort,
+ darkMode: data.darkMode || false, // Save dark mode preference
};
const notificationTypes = await notifications.getAllNotificationTypes();
notificationTypes.forEach((notificationType) => {
@@ -172,7 +175,9 @@ module.exports = function (User) {
if (parseInt(uid, 10) <= 0) {
return;
}
-
+ if (key === 'darkMode') {
+ value = value ? true : false; // Ensure it's a boolean
+ }
await db.setObjectField(`user:${uid}:settings`, key, value);
};
};
diff --git a/stryker.config.json b/stryker.config.json
new file mode 100644
index 0000000..010d6f8
--- /dev/null
+++ b/stryker.config.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
+ "packageManager": "npm",
+ "reporters": [
+ "html",
+ "clear-text",
+ "progress"
+ ],
+ "testRunner": "mocha",
+ "coverageAnalysis": "perTest",
+ "mutate": [
+ "src/**/*.js"
+ ],
+ "sandbox": {
+ "filePatterns": [
+ "!build/**",
+ "!public/**",
+ "!plugins/**"
+ ]
+ }
+}
diff --git a/stryker_output.txt b/stryker_output.txt
new file mode 100644
index 0000000..b070d89
--- /dev/null
+++ b/stryker_output.txt
@@ -0,0 +1,23 @@
+[33m14:23:35 (34493) WARN OptionsValidator[39m Unknown stryker config option "sandbox".
+[33m14:23:35 (34493) WARN OptionsValidator[39m Possible causes:
+ * Is it a typo on your end?
+ * Did you only write this property as a comment? If so, please postfix it with "_comment".
+ * You might be missing a plugin that is supposed to use it. Stryker loaded plugins from: ["@stryker-mutator/*"]
+ * The plugin that is using it did not contribute explicit validation.
+ (disable "warnings.unknownOptions" to ignore this warning)
+[32m14:23:35 (34493) INFO ProjectReader[39m Found 548 of 10828 file(s) to be mutated.
+[32m14:23:38 (34493) INFO Instrumenter[39m Instrumented 548 source file(s) with 55842 mutant(s)
+[32m14:23:39 (34493) INFO ConcurrencyTokenProvider[39m Creating 7 test runner process(es).
+[91m14:23:40 (34493) ERROR Stryker[39m Unexpected error occurred while running Stryker Error: ENOTSUP: operation not supported on socket, copyfile '/Users/evelynlo/nodebb-s25-puffers/build/public/plugins/nodebb-plugin-markdown/styles' -> '/Users/evelynlo/nodebb-s25-puffers/.stryker-tmp/sandbox-oxK0Gt/build/public/plugins/nodebb-plugin-markdown/styles'
+ at async Object.copyFile (node:internal/fs/promises:621:10)
+ at async FileSystemAction.execute (file:///Users/evelynlo/nodebb-s25-puffers/node_modules/@stryker-mutator/core/dist/src/fs/file-system.js:16:28)
+ at async file:///Users/evelynlo/nodebb-s25-puffers/node_modules/@stryker-mutator/core/dist/src/fs/file-system.js:33:9 {
+ errno: -45,
+ code: 'ENOTSUP',
+ syscall: 'copyfile',
+ path: '/Users/evelynlo/nodebb-s25-puffers/build/public/plugins/nodebb-plugin-markdown/styles',
+ dest: '/Users/evelynlo/nodebb-s25-puffers/.stryker-tmp/sandbox-oxK0Gt/build/public/plugins/nodebb-plugin-markdown/styles'
+}
+[32m14:23:40 (34493) INFO Stryker[39m This might be a known problem with a solution documented in our troubleshooting guide.
+[32m14:23:40 (34493) INFO Stryker[39m You can find it at https://stryker-mutator.io/docs/stryker-js/troubleshooting/
+[32m14:23:40 (34493) INFO Stryker[39m Still having trouble figuring out what went wrong? Try `npx stryker run --fileLogLevel trace --logLevel debug` to get some more info.