Add changes
This commit is contained in:
parent
4686993334
commit
8d9d0d5e2c
12 changed files with 379 additions and 3 deletions
BIN
instance/favicon.png
Normal file
BIN
instance/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
1
instance/panel.html
Normal file
1
instance/panel.html
Normal file
|
@ -0,0 +1 @@
|
|||
<p> <a href="/main/all" rel="noopener noreferrer nofollow">Pleroma FE</a> | <a href="https://soap.samtherapy.net" rel="noopener noreferrer nofollow">Soapbox FE</a></p>
|
7
instance/pleroma-mod-config.json
Normal file
7
instance/pleroma-mod-config.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"modDirectory": "/instance/pleroma-mods/",
|
||||
"mods": [
|
||||
"imgsearch",
|
||||
"math"
|
||||
]
|
||||
}
|
1
instance/pleroma-mods/pleroma-mod-imgsearch
Submodule
1
instance/pleroma-mods/pleroma-mod-imgsearch
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 36d159b665ed16e7436ec08011e41dc582e14a3b
|
1
instance/pleroma-mods/pleroma-mod-math
Submodule
1
instance/pleroma-mods/pleroma-mod-math
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 00a26b59a9792baab787f89425f1137fe07f0950
|
13
src/App.js
13
src/App.js
|
@ -72,6 +72,9 @@ export default {
|
|||
!this.$store.getters.mergedConfig.hideISP &&
|
||||
this.$store.state.instance.instanceSpecificPanelContent
|
||||
},
|
||||
thirdColumnEnabled () {
|
||||
return this.$store.getters.mergedConfig.showThirdColumn || false
|
||||
},
|
||||
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
||||
hideShoutbox () {
|
||||
return this.$store.getters.mergedConfig.hideShoutbox
|
||||
|
@ -83,6 +86,16 @@ export default {
|
|||
'order': this.$store.getters.mergedConfig.sidebarRight ? 99 : 0
|
||||
}
|
||||
},
|
||||
notifsAlign () {
|
||||
return {
|
||||
'order': this.$store.getters.mergedConfig.sidebarRight ? 0 : 99
|
||||
}
|
||||
},
|
||||
thirdColumnLayout () {
|
||||
return {
|
||||
'max-width': this.$store.getters.mergedConfig.showThirdColumn ? '1400px' : '980px'
|
||||
}
|
||||
},
|
||||
...mapGetters(['mergedConfig'])
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -573,9 +573,10 @@ nav {
|
|||
}
|
||||
|
||||
.main {
|
||||
flex-basis: 50%;
|
||||
flex-basis: 25%;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
order: 50;
|
||||
}
|
||||
|
||||
.sidebar-bounds {
|
||||
|
|
24
src/App.vue
24
src/App.vue
|
@ -12,6 +12,7 @@
|
|||
<div class="app-bg-wrapper app-container-wrapper" />
|
||||
<div
|
||||
id="content"
|
||||
:style="thirdColumnLayout"
|
||||
class="container underlay"
|
||||
>
|
||||
<div
|
||||
|
@ -27,13 +28,15 @@
|
|||
<instance-specific-panel v-if="showInstanceSpecificPanel" />
|
||||
<features-panel v-if="!currentUser && showFeaturesPanel" />
|
||||
<who-to-follow-panel v-if="currentUser && suggestionsEnabled" />
|
||||
<notifications v-if="currentUser" />
|
||||
<notifications v-if="currentUser && !thirdColumnEnabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div
|
||||
class="main"
|
||||
>
|
||||
<div
|
||||
v-if="!currentUser"
|
||||
class="login-hint panel panel-default"
|
||||
|
@ -47,6 +50,23 @@
|
|||
</div>
|
||||
<router-view />
|
||||
</div>
|
||||
<div
|
||||
v-if="thirdColumnEnabled"
|
||||
class="sidebar-flexer mobile-hidden"
|
||||
:style="notifsAlign"
|
||||
>
|
||||
<div class="sidebar-bounds">
|
||||
<div class="sidebar-scroller">
|
||||
<div class="sidebar">
|
||||
<div v-if="!isMobileLayout">
|
||||
<notifications v-if="currentUser" />
|
||||
<features-panel v-if="!currentUser && showFeaturesPanel" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<media-modal />
|
||||
</div>
|
||||
<shout-panel
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
{{ $t('settings.right_sidebar') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="showThirdColumn">
|
||||
{{ $t('settings.show_third_column') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="instanceWallpaperUsed">
|
||||
<BooleanSetting path="hideInstanceWallpaper">
|
||||
{{ $t('settings.hide_wallpaper') }}
|
||||
|
|
|
@ -449,6 +449,7 @@
|
|||
"reset_banner_confirm": "Do you really want to reset the banner?",
|
||||
"reset_background_confirm": "Do you really want to reset the background?",
|
||||
"settings": "Settings",
|
||||
"show_third_column": "Show separate notifications column",
|
||||
"subject_input_always_show": "Always show subject field",
|
||||
"subject_line_behavior": "Copy subject when replying",
|
||||
"subject_line_email": "Like email: \"re: subject\"",
|
||||
|
|
|
@ -56,6 +56,7 @@ export const defaultState = {
|
|||
hideScopeNotice: false,
|
||||
useStreamingApi: false,
|
||||
sidebarRight: undefined, // instance default
|
||||
showThirdColumn: false, // instance default
|
||||
scopeCopy: undefined, // instance default
|
||||
subjectLineBehavior: undefined, // instance default
|
||||
alwaysShowSubjectInput: undefined, // instance default
|
||||
|
|
325
static/pleroma-mod-loader.js
Normal file
325
static/pleroma-mod-loader.js
Normal file
|
@ -0,0 +1,325 @@
|
|||
function PleromaModLoader () {
|
||||
this.config = {
|
||||
"modDirectory": "/instance/pleroma-mods/",
|
||||
"mods": []
|
||||
};
|
||||
this.loadConfig();
|
||||
this.loadedMods = {};
|
||||
this.classes = {};
|
||||
}
|
||||
[
|
||||
function loadMods () {
|
||||
for (const mod of this.config.mods) {
|
||||
const modObject = new PleromaMod(mod);
|
||||
modObject.enabled = true;
|
||||
if (localStorage.getItem("pleroma_mod_" + mod + "_enabled") === "false") {
|
||||
modObject.enabled = false;
|
||||
}
|
||||
localStorage.setItem("pleroma_mod_" + mod + "_enabled", modObject.enabled);
|
||||
modObject.include();
|
||||
this.loadedMods[mod] = modObject;
|
||||
}
|
||||
},
|
||||
|
||||
function loadConfig () {
|
||||
window.fetch("/instance/pleroma-mod-config.json").then((response) => {
|
||||
if (response.ok) {
|
||||
response.json().then((json) => {
|
||||
for (const key in json) {
|
||||
this.config[key] = json[key];
|
||||
}
|
||||
this.loadMods();
|
||||
}).catch((error) => {
|
||||
console.error("can't parse loader config");
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.warn("can't load mod loader config");
|
||||
console.warn(error);
|
||||
});
|
||||
},
|
||||
|
||||
function registerClass (className, object) {
|
||||
this.classes[className] = object;
|
||||
},
|
||||
|
||||
function waitUntilReady () {
|
||||
const postPanel = document.getElementsByClassName("post-status-form");
|
||||
const loginPanel = document.getElementsByClassName("login-form");
|
||||
if (postPanel.length > 0 || loginPanel.length > 0) {
|
||||
for (var modName in this.loadedMods) {
|
||||
const settings = document.querySelector(".settings div[label]:first-child");
|
||||
if (settings) {
|
||||
if (!settings.querySelector(".mod-settings")) {
|
||||
this.appendModSettings(settings);
|
||||
}
|
||||
}
|
||||
const mod = this.loadedMods[modName];
|
||||
if (mod.enabled && mod.instance) {
|
||||
if (mod.instance.onReady) {
|
||||
mod.instance.onReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.createObserver();
|
||||
} else {
|
||||
window.setTimeout(() => { this.waitUntilReady(); }, 1000);
|
||||
}
|
||||
},
|
||||
|
||||
function createCheckbox (label, mod) {
|
||||
const labelElement = document.createElement("label");
|
||||
labelElement.classList.add("checkbox");
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.setAttribute("type", "checkbox");
|
||||
input.checked = mod.enabled;
|
||||
input.addEventListener("change", (event) => {
|
||||
if (event.target.checked) {
|
||||
mod.enable();
|
||||
} else {
|
||||
mod.disable();
|
||||
}
|
||||
});
|
||||
labelElement.appendChild(input);
|
||||
|
||||
const fakeCheckbox = document.createElement("i");
|
||||
fakeCheckbox.classList.add("checkbox-indicator");
|
||||
labelElement.appendChild(fakeCheckbox);
|
||||
|
||||
const text = document.createElement("span");
|
||||
text.classList.add("label");
|
||||
text.innerText = label;
|
||||
|
||||
labelElement.appendChild(text);
|
||||
|
||||
return labelElement;
|
||||
},
|
||||
|
||||
function appendModSettings (element) {
|
||||
const container = document.createElement("div");
|
||||
container.classList.add("setting-item");
|
||||
container.classList.add("mod-settings");
|
||||
|
||||
const title = document.createElement("h2");
|
||||
title.innerText = "Mods";
|
||||
container.appendChild(title);
|
||||
|
||||
const optionList = document.createElement("ul");
|
||||
optionList.classList.add("setting-list");
|
||||
|
||||
const modNames = Object.keys(this.loadedMods).sort();
|
||||
for (const mod of modNames) {
|
||||
const li = document.createElement("li");
|
||||
|
||||
const enable = this.createCheckbox("enable " + mod, this.loadedMods[mod]);
|
||||
|
||||
li.appendChild(enable);
|
||||
|
||||
optionList.appendChild(li);
|
||||
}
|
||||
|
||||
container.appendChild(optionList);
|
||||
|
||||
element.appendChild(container);
|
||||
},
|
||||
|
||||
function createObserver () {
|
||||
this.containers = {
|
||||
main: document.querySelector(".main"),
|
||||
notifications: document.querySelector(".notifications"),
|
||||
userPanel: document.querySelector(".user-panel"),
|
||||
settingsModal: document.querySelector(".settings-modal")
|
||||
};
|
||||
|
||||
const observerConfig = { subtree: true, childList: true };
|
||||
this.observer = new MutationObserver((mutations, observer) => {
|
||||
if (
|
||||
mutations.length > 0 &&
|
||||
mutations[0].addedNodes.length > 0 &&
|
||||
mutations[0].addedNodes[0].classList &&
|
||||
mutations[0].addedNodes[0].classList.contains("settings_tab-switcher")
|
||||
) {
|
||||
this.appendModSettings(mutations[0].addedNodes[0].querySelector("div[label]:first-child"));
|
||||
}
|
||||
for (var modName in this.loadedMods) {
|
||||
const mod = this.loadedMods[modName];
|
||||
if (mod.instance && mod.enabled) {
|
||||
if (mod.instance.onMutation) {
|
||||
for (const mutation of mutations) {
|
||||
let filter = null;
|
||||
if (mod.instance.config.filter) {
|
||||
filter = new RegExp(mod.instance.config.filter.join("|"));
|
||||
}
|
||||
if (!filter || filter.test(mutation.target.className)) {
|
||||
mod.instance.onMutation(mutation, observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.observer.observe(this.containers.main, observerConfig);
|
||||
if (this.containers.notifications) {
|
||||
this.observer.observe(this.containers.notifications, observerConfig);
|
||||
}
|
||||
if (this.containers.userPanel) {
|
||||
this.observer.observe(this.containers.userPanel, observerConfig);
|
||||
}
|
||||
if (this.containers.settingsModal) {
|
||||
this.observer.observe(this.containers.settingsModal, observerConfig);
|
||||
}
|
||||
}
|
||||
].forEach((fn) => { PleromaModLoader.prototype[fn.name] = fn; });
|
||||
[
|
||||
function registerMod (mod) {
|
||||
window.__pleromaModLoader.registerClass(mod.name, mod);
|
||||
},
|
||||
|
||||
function includeModScript (src) {
|
||||
return PleromaModLoader.includeScript(window.__pleromaModLoader.config.modDirectory + src);
|
||||
},
|
||||
|
||||
function includeModCss (src) {
|
||||
return PleromaModLoader.includeCss(window.__pleromaModLoader.config.modDirectory + src);
|
||||
},
|
||||
|
||||
function excludeModScript (src) {
|
||||
return PleromaModLoader.excludeScript(window.__pleromaModLoader.config.modDirectory + src);
|
||||
},
|
||||
|
||||
function excludeModCss (src) {
|
||||
return PleromaModLoader.excludeCss(window.__pleromaModLoader.config.modDirectory + src);
|
||||
},
|
||||
|
||||
function includeScript (src) {
|
||||
return new Promise((resolve) => {
|
||||
const body = document.getElementsByTagName("body")[0];
|
||||
const script = document.createElement("script");
|
||||
script.setAttribute("src", src);
|
||||
script.setAttribute("type", "text/javascript");
|
||||
script.onload = () => { resolve(); };
|
||||
body.appendChild(script);
|
||||
});
|
||||
},
|
||||
|
||||
function getVueScope (element) {
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
if (element.__vue__) {
|
||||
return element.__vue__;
|
||||
}
|
||||
if (element.parentNode) {
|
||||
return PleromaModLoader.getVueScope(element.parentNode);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
function getRootVueScope () {
|
||||
return document.querySelector("#app").__vue__;
|
||||
},
|
||||
|
||||
function includeCss (src) {
|
||||
return new Promise((resolve) => {
|
||||
const head = document.getElementsByTagName("head")[0];
|
||||
const link = document.createElement("link");
|
||||
link.setAttribute("href", src);
|
||||
link.setAttribute("rel", "stylesheet");
|
||||
link.setAttribute("type", "text/css");
|
||||
link.onload = () => { resolve(); };
|
||||
head.appendChild(link);
|
||||
});
|
||||
},
|
||||
|
||||
function excludeScript (src) {
|
||||
return new Promise((resolve) => {
|
||||
const script = document.querySelector('script[src="' + src + '"]');
|
||||
if(script) {
|
||||
script.remove();
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
||||
function excludeCss (src) {
|
||||
return new Promise((resolve) => {
|
||||
const link = document.querySelector('link[href="' + src + '"]');
|
||||
if(link) {
|
||||
link.remove();
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
||||
function getToken () {
|
||||
return PleromaModLoader.getRootVueScope().$store._vm.getUserToken();
|
||||
},
|
||||
|
||||
function getModDir () {
|
||||
return window.__pleromaModLoader.config.modDirectory;
|
||||
}
|
||||
].forEach((fn) => { PleromaModLoader[fn.name] = fn; });
|
||||
|
||||
function PleromaMod (name) {
|
||||
this.name = name;
|
||||
this.instance = null;
|
||||
this.enabled = localStorage.getItem("pleroma_mod_" + this.name + "_enabled") !== "false";
|
||||
}
|
||||
[
|
||||
function getClassName () {
|
||||
let className = "PleromaMod";
|
||||
const nameParts = this.name.split("-");
|
||||
for (const namePart of nameParts) {
|
||||
className += namePart.substring(0, 1).toUpperCase();
|
||||
className += namePart.substring(1);
|
||||
}
|
||||
return className;
|
||||
},
|
||||
|
||||
function enable () {
|
||||
this.enabled = true;
|
||||
this.modLoaded();
|
||||
if (this.instance.onReady) {
|
||||
this.instance.onReady();
|
||||
}
|
||||
localStorage.setItem("pleroma_mod_" + this.name + "_enabled", this.enabled);
|
||||
},
|
||||
|
||||
function disable () {
|
||||
this.enabled = false;
|
||||
if (this.instance.onDestroy) {
|
||||
this.instance.onDestroy();
|
||||
}
|
||||
console.log(this.name + " unloaded");
|
||||
this.instance = null;
|
||||
localStorage.setItem("pleroma_mod_" + this.name + "_enabled", this.enabled);
|
||||
},
|
||||
|
||||
function include () {
|
||||
console.log("loading " + this.name);
|
||||
PleromaModLoader.includeScript(
|
||||
PleromaModLoader.getModDir() + "pleroma-mod-" + this.name + "/mod.js"
|
||||
).then(() => {
|
||||
if (this.enabled) {
|
||||
this.modLoaded();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
function modLoaded () {
|
||||
console.log(this.name + " loaded");
|
||||
this.instance = new window.__pleromaModLoader.classes[this.getClassName()]();
|
||||
this.run();
|
||||
},
|
||||
|
||||
function run () {
|
||||
if (this.instance) {
|
||||
this.instance.run();
|
||||
}
|
||||
}
|
||||
].forEach((fn) => { PleromaMod.prototype[fn.name] = fn; });
|
||||
|
||||
window.__pleromaModLoader = new PleromaModLoader();
|
||||
window.__pleromaModLoader.waitUntilReady();
|
Loading…
Reference in a new issue