1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-09-27 14:50:11 +00:00

add preview to Announcements

This commit is contained in:
Harvey Tindall 2021-05-03 18:35:27 +01:00
parent 2c6d08319b
commit c0f316d049
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
8 changed files with 84 additions and 32 deletions

5
api.go
View File

@ -1712,6 +1712,9 @@ func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) {
} }
writeVars = func(variables []string) { app.storage.customEmails.UserExpired.Variables = variables } writeVars = func(variables []string) { app.storage.customEmails.UserExpired.Variables = variables }
values = app.email.userExpiredValues(app, false) values = app.email.userExpiredValues(app, false)
// Just send the email html
case "Announcement":
content = ""
default: default:
respondBool(400, false, gc) respondBool(400, false, gc)
return return
@ -1747,7 +1750,7 @@ func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) {
respondBool(500, false, gc) respondBool(500, false, gc)
return return
} }
email, err := app.email.constructTemplate("", "<div id=\"preview-content\"></div>", app) email, err := app.email.constructTemplate("", "<div class=\"preview-content\"></div>", app)
if err != nil { if err != nil {
respondBool(500, false, gc) respondBool(500, false, gc)
return return

View File

@ -156,18 +156,24 @@
</form> </form>
</div> </div>
<div id="modal-announce" class="modal"> <div id="modal-announce" class="modal">
<form class="modal-content card" id="form-announce" href=""> <form class="modal-content wide card" id="form-announce" href="">
<span class="heading"><span id="header-announce"></span> <span class="modal-close">&times;</span></span> <span class="heading"><span id="header-announce"></span> <span class="modal-close">&times;</span></span>
<div class="content mt-half"> <div class="row">
<label class="label supra" for="announce-subject"> {{ .strings.subject }}</label> <div class="col flex-col content mt-half">
<input type="text" id="announce-subject" class="input ~neutral !normal mb-1 mt-half"> <label class="label supra" for="announce-subject"> {{ .strings.subject }}</label>
<label class="label supra" for="textarea-announce">{{ .strings.message }}</label> <input type="text" id="announce-subject" class="input ~neutral !normal mb-1 mt-half">
<textarea id="textarea-announce" class="textarea full-width ~neutral !normal mt-half monospace"></textarea> <label class="label supra" for="textarea-announce">{{ .strings.message }}</label>
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p> <textarea id="textarea-announce" class="textarea full-width ~neutral !normal mt-half monospace"></textarea>
<label> <p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
<input type="submit" class="unfocused"> <label>
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span> <input type="submit" class="unfocused">
</label> <span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
</label>
</div>
<div class="col card ~neutral !low">
<span class="subheading supra">{{ .strings.preview }}</span>
<div class="mt-half" id="announce-preview"></div>
</div>
</div> </div>
</form> </form>
</div> </div>

13
package-lock.json generated
View File

@ -9,6 +9,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@ts-stack/markdown": "^1.3.0", "@ts-stack/markdown": "^1.3.0",
"@types/node": "^15.0.1",
"a17t": "^0.4.0", "a17t": "^0.4.0",
"esbuild": "^0.8.57", "esbuild": "^0.8.57",
"lodash": "^4.17.19", "lodash": "^4.17.19",
@ -35,9 +36,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "14.14.16", "version": "15.0.1",
"resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-14.14.16.tgz?cache=0&sync_timestamp=1608756036972&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.14.16.tgz", "resolved": "https://registry.nlark.com/@types/node/download/@types/node-15.0.1.tgz?cache=0&sync_timestamp=1619534647758&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-15.0.1.tgz",
"integrity": "sha1-PMNR+NSBAd6t/tTJ5PEWBI1De0s=" "integrity": "sha1-7zTeoIgQKNETmL5b9OhWdD49w1o="
}, },
"node_modules/a17t": { "node_modules/a17t": {
"version": "0.4.0", "version": "0.4.0",
@ -1835,9 +1836,9 @@
} }
}, },
"@types/node": { "@types/node": {
"version": "14.14.16", "version": "15.0.1",
"resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-14.14.16.tgz?cache=0&sync_timestamp=1608756036972&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.14.16.tgz", "resolved": "https://registry.nlark.com/@types/node/download/@types/node-15.0.1.tgz?cache=0&sync_timestamp=1619534647758&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-15.0.1.tgz",
"integrity": "sha1-PMNR+NSBAd6t/tTJ5PEWBI1De0s=" "integrity": "sha1-7zTeoIgQKNETmL5b9OhWdD49w1o="
}, },
"a17t": { "a17t": {
"version": "0.4.0", "version": "0.4.0",

View File

@ -18,6 +18,7 @@
"homepage": "https://github.com/hrfee/jfa-go#readme", "homepage": "https://github.com/hrfee/jfa-go#readme",
"dependencies": { "dependencies": {
"@ts-stack/markdown": "^1.3.0", "@ts-stack/markdown": "^1.3.0",
"@types/node": "^15.0.1",
"a17t": "^0.4.0", "a17t": "^0.4.0",
"esbuild": "^0.8.57", "esbuild": "^0.8.57",
"lodash": "^4.17.19", "lodash": "^4.17.19",

View File

@ -1,4 +1,7 @@
import { _get, _post, _delete, toggleLoader, toDateString } from "../modules/common.js"; import { _get, _post, _delete, toggleLoader, toDateString } from "../modules/common.js";
import { templateEmail } from "../modules/settings.js";
import { Marked } from "@ts-stack/markdown";
import { stripMarkdown } from "../modules/stripmd.js";
interface User { interface User {
id: string; id: string;
@ -193,6 +196,9 @@ export class accountsList {
private _addUserButton = document.getElementById("accounts-add-user") as HTMLSpanElement; private _addUserButton = document.getElementById("accounts-add-user") as HTMLSpanElement;
private _announceButton = document.getElementById("accounts-announce") as HTMLSpanElement; private _announceButton = document.getElementById("accounts-announce") as HTMLSpanElement;
private _announcePreview: HTMLElement;
private _previewLoaded = false;
private _announceTextarea = document.getElementById("textarea-announce") as HTMLTextAreaElement;
private _deleteUser = document.getElementById("accounts-delete-user") as HTMLSpanElement; private _deleteUser = document.getElementById("accounts-delete-user") as HTMLSpanElement;
private _disableEnable = document.getElementById("accounts-disable-enable") as HTMLSpanElement; private _disableEnable = document.getElementById("accounts-disable-enable") as HTMLSpanElement;
private _deleteNotify = document.getElementById("delete-user-notify") as HTMLInputElement; private _deleteNotify = document.getElementById("delete-user-notify") as HTMLInputElement;
@ -432,7 +438,16 @@ export class accountsList {
} }
}, true); }, true);
} }
loadPreview = () => {
let content = this._announceTextarea.value;
if (!this._previewLoaded) {
content = stripMarkdown(content);
this._announcePreview.textContent = content;
} else {
content = Marked.parse(content);
this._announcePreview.innerHTML = content;
}
}
announce = () => { announce = () => {
const modalHeader = document.getElementById("header-announce"); const modalHeader = document.getElementById("header-announce");
modalHeader.textContent = window.lang.quantity("announceTo", this._collectUsers().length); modalHeader.textContent = window.lang.quantity("announceTo", this._collectUsers().length);
@ -440,16 +455,16 @@ export class accountsList {
let list = this._collectUsers(); let list = this._collectUsers();
const button = form.querySelector("span.submit") as HTMLSpanElement; const button = form.querySelector("span.submit") as HTMLSpanElement;
const subject = document.getElementById("announce-subject") as HTMLInputElement; const subject = document.getElementById("announce-subject") as HTMLInputElement;
const message = document.getElementById("textarea-announce") as HTMLTextAreaElement;
subject.value = ""; subject.value = "";
message.value = ""; this._announceTextarea.value = "";
form.onsubmit = (event: Event) => { form.onsubmit = (event: Event) => {
event.preventDefault(); event.preventDefault();
toggleLoader(button); toggleLoader(button);
let send = { let send = {
"users": list, "users": list,
"subject": subject.value, "subject": subject.value,
"message": message.value "message": this._announceTextarea.value
} }
_post("/users/announce", send, (req: XMLHttpRequest) => { _post("/users/announce", send, (req: XMLHttpRequest) => {
if (req.readyState == 4) { if (req.readyState == 4) {
@ -463,7 +478,29 @@ export class accountsList {
} }
}); });
}; };
window.modals.announce.show(); _get("/config/emails/Announcement", null, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
const preview = document.getElementById("announce-preview") as HTMLDivElement;
if (req.status != 200) {
preview.innerHTML = `<pre class="preview-content" class="monospace"></pre>`;
window.modals.announce.show();
this._previewLoaded = false;
return;
}
let templ = req.response as templateEmail;
if (!templ.html) {
preview.innerHTML = `<pre class="preview-content" class="monospace"></pre>`;
this._previewLoaded = false;
} else {
preview.innerHTML = templ.html;
this._previewLoaded = true;
}
this._announcePreview = preview.getElementsByClassName("preview-content")[0] as HTMLElement;
this.loadPreview();
window.modals.announce.show();
}
});
} }
enableDisableUsers = () => { enableDisableUsers = () => {
@ -750,6 +787,8 @@ export class accountsList {
} }
this._checkCheckCount(); this._checkCheckCount();
}; };
this._announceTextarea.onkeyup = this.loadPreview;
} }
reload = () => _get("/users", null, (req: XMLHttpRequest) => { reload = () => _get("/users", null, (req: XMLHttpRequest) => {

View File

@ -157,7 +157,7 @@ class DOMInvite implements Invite {
this._createdUnix = unix; this._createdUnix = unix;
const el = this._middle.querySelector("strong.inv-created"); const el = this._middle.querySelector("strong.inv-created");
if (unix == 0) { if (unix == 0) {
el.textContent = "n/a"; el.textContent = window.lang.strings("unknown");
} else { } else {
el.textContent = toDateString(new Date(unix*1000)); el.textContent = toDateString(new Date(unix*1000));
} }
@ -479,7 +479,7 @@ export class inviteList implements inviteList {
} }
function parseInvite(invite: { [f: string]: string | number | string[][] | boolean }): Invite { function parseInvite(invite: { [f: string]: string | number | { [name: string]: number } | boolean }): Invite {
let parsed: Invite = {}; let parsed: Invite = {};
parsed.code = invite["code"] as string; parsed.code = invite["code"] as string;
parsed.email = invite["email"] as string || ""; parsed.email = invite["email"] as string || "";
@ -509,8 +509,8 @@ function parseInvite(invite: { [f: string]: string | number | string[][] | boole
parsed.userExpiry = invite["user-expiry"] as boolean; parsed.userExpiry = invite["user-expiry"] as boolean;
parsed.userExpiryTime = userExpiryTime.slice(0, -1); parsed.userExpiryTime = userExpiryTime.slice(0, -1);
parsed.remainingUses = invite["no-limit"] ? "∞" : String(invite["remaining-uses"]) parsed.remainingUses = invite["no-limit"] ? "∞" : String(invite["remaining-uses"])
parsed.usedBy = invite["used-by"] as string[][] || []; parsed.usedBy = invite["used-by"] as { [name: string]: number } || {} ;
parsed.created = invite["created"] as string || window.lang.strings("unknown"); parsed.created = invite["created"] as number || 0;
parsed.profile = invite["profile"] as string || ""; parsed.profile = invite["profile"] as string || "";
parsed.notifyExpiry = invite["notify-expiry"] as boolean || false; parsed.notifyExpiry = invite["notify-expiry"] as boolean || false;
parsed.notifyCreation = invite["notify-creation"] as boolean || false; parsed.notifyCreation = invite["notify-creation"] as boolean || false;

View File

@ -766,7 +766,7 @@ class ombiDefaults {
} }
} }
interface templateEmail { export interface templateEmail {
content: string; content: string;
variables: string[]; variables: string[];
conditionals: string[]; conditionals: string[];
@ -829,11 +829,11 @@ class EmailEditor {
this._templ = req.response as templateEmail; this._templ = req.response as templateEmail;
this._textArea.value = this._templ.content; this._textArea.value = this._templ.content;
if (this._templ.html == "") { if (this._templ.html == "") {
this._preview.innerHTML = `<pre id="preview-content" class="monospace"></pre>`; this._preview.innerHTML = `<pre class="preview-content" class="monospace"></pre>`;
} else { } else {
this._preview.innerHTML = this._templ.html; this._preview.innerHTML = this._templ.html;
} }
this._previewContent = document.getElementById("preview-content"); this._previewContent = this._preview.getElementsByClassName("preview-content")[0] as HTMLElement;
this.loadPreview(); this.loadPreview();
this._content = this._templ.content; this._content = this._templ.content;
const colors = ["info", "urge", "positive", "neutral"]; const colors = ["info", "urge", "positive", "neutral"];

View File

@ -53,13 +53,15 @@ func (app *appContext) pushResources(gc *gin.Context, admin bool) {
gc.Header("Link", cssHeader) gc.Header("Link", cssHeader)
} }
type Page int
const ( const (
AdminPage = iota + 1 AdminPage Page = iota + 1
FormPage FormPage
PWRPage PWRPage
) )
func (app *appContext) getLang(gc *gin.Context, page int, chosen string) string { func (app *appContext) getLang(gc *gin.Context, page Page, chosen string) string {
lang := gc.Query("lang") lang := gc.Query("lang")
cookie, err := gc.Cookie("lang") cookie, err := gc.Cookie("lang")
if lang != "" { if lang != "" {