diff --git a/' b/'
new file mode 100644
index 0000000..13c2425
--- /dev/null
+++ b/'
@@ -0,0 +1,239 @@
+.PHONY: configuration email typescript swagger copy compile compress inline-css variants-html install clean npm config-description config-default precompile
+
+all: compile
+
+GOESBUILD ?= off
+ifeq ($(GOESBUILD), on)
+ ESBUILD := esbuild
+else
+ ESBUILD := npx esbuild
+endif
+GOBINARY ?= go
+
+CSSVERSION ?= v3
+CSS_BUNDLE = $(DATA)/web/css/$(CSSVERSION)bundle.css
+
+VERSION ?= $(shell git describe --exact-match HEAD 2> /dev/null || echo vgit)
+VERSION := $(shell echo $(VERSION) | sed 's/v//g')
+COMMIT ?= $(shell git rev-parse --short HEAD || echo unknown)
+BUILDTIME ?= $(shell date +%s)
+
+UPDATER ?= off
+LDFLAGS := -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.cssVersion=$(CSSVERSION) -X main.buildTimeUnix=$(BUILDTIME) $(if $(BUILTBY),-X 'main.builtBy=$(BUILTBY)',)
+ifeq ($(UPDATER), on)
+ LDFLAGS := $(LDFLAGS) -X main.updater=binary
+else ifneq ($(UPDATER), off)
+ LDFLAGS := $(LDFLAGS) -X main.updater=$(UPDATER)
+endif
+
+
+
+INTERNAL ?= on
+TRAY ?= off
+E2EE ?= on
+TAGS := -tags "
+
+ifeq ($(INTERNAL), on)
+ DATA := data
+else
+ DATA := build/data
+ TAGS := $(TAGS) external
+endif
+
+ifeq ($(TRAY), on)
+ TAGS := $(TAGS) tray
+endif
+
+ifeq ($(E2EE), on)
+ TAGS := $(TAGS) e2ee
+endif
+
+TAGS := $(TAGS)"
+
+OS := $(shell go env GOOS)
+ifeq ($(TRAY)$(OS), onwindows)
+ LDFLAGS := $(LDFLAGS) -H=windowsgui
+endif
+
+DEBUG ?= off
+ifeq ($(DEBUG), on)
+ SOURCEMAP := --sourcemap
+ MINIFY :=
+ TYPECHECK := npx tsc -noEmit --project ts/tsconfig.json
+ # jank
+ COPYTS := rm -r $(DATA)/web/js/ts; cp -r tempts $(DATA)/web/js/ts
+ UNCSS := cp $(CSS_BUNDLE) $(DATA)/bundle.css
+ # TAILWIND := --content ""
+else
+ LDFLAGS := -s -w $(LDFLAGS)
+ SOURCEMAP :=
+ MINIFY := --minify
+ COPYTS :=
+ TYPECHECK :=
+ UNCSS := npx tailwindcss -i $(CSS_BUNDLE) -o $(DATA)/bundle.css --content "html/crash.html"
+ # UNCSS := npx uncss $(DATA)/crash.html --csspath web/css --output $(DATA)/bundle.css
+ TAILWIND :=
+endif
+
+RACE ?= off
+ifeq ($(RACE), on)
+ RACEDETECTOR := -race
+else
+ RACEDETECTOR :=
+endif
+
+ifeq (, $(shell which esbuild))
+ ESBUILDINSTALL := go install github.com/evanw/esbuild/cmd/esbuild@latest
+else
+ ESBUILDINSTALL :=
+endif
+
+ifeq ($(GOESBUILD), on)
+ NPMIGNOREOPTIONAL := --no-optional
+ NPMOPTS := $(NPMIGNOREOPTIONAL); $(ESBUILDINSTALL)
+else
+ NPMOPTS :=
+endif
+
+ifeq (, $(shell which swag))
+ SWAGINSTALL := $(GOBINARY) install github.com/swaggo/swag/cmd/swag@latest
+else
+ SWAGINSTALL :=
+endif
+
+CONFIG_BASE = config/config-base.yaml
+
+# CONFIG_DESCRIPTION = $(DATA)/config-base.json
+CONFIG_DEFAULT = $(DATA)/config-default.ini
+# $(CONFIG_DESCRIPTION) &: $(CONFIG_BASE)
+# $(info Fixing config-base)
+# -mkdir -p $(DATA)
+
+$(DATA):
+ mkdir -p $(DATA)
+
+$(CONFIG_DEFAULT): $(DATA) $(CONFIG_BASE)
+ $(info Generating config-default.ini)
+ go run scripts/ini/main.go -in $(CONFIG_BASE) -out $(DATA)/config-default.ini
+
+configuration: $(CONFIG_DEFAULT)
+
+EMAIL_SRC = $(wildcard mail/*)
+EMAIL_TARGET = mail/confirmation.html
+$(EMAIL_TARGET): $(EMAIL_SRC)
+ $(info Generating email html)
+ npx mjml mail/*.mjml -o $(DATA)/
+ $(info Copying plaintext mail)
+ cp mail/*.txt $(DATA)/
+
+TYPESCRIPT_FULLSRC = $(shell find ts/ -type f -name "*.ts")
+TYPESCRIPT_SRC = $(wildcard ts/*.ts)
+TYPESCRIPT_TEMPSRC = $(TYPESCRIPT_SRC:ts/%=tempts/%)
+# TYPESCRIPT_TARGET = $(patsubst %.ts,%.js,$(subst tempts/,./$(DATA)/web/js/,$(TYPESCRIPT_TEMPSRC)))
+TYPESCRIPT_TARGET = $(DATA)/web/js/admin.js
+$(TYPESCRIPT_TARGET): $(TYPESCRIPT_FULLSRC) ts/tsconfig.json
+ $(TYPECHECK)
+ rm -rf tempts
+ cp -r ts tempts
+ $(adding dark variants to typescript)
+ scripts/dark-variant.sh tempts
+ scripts/dark-variant.sh tempts/modules
+ $(info compiling typescript)
+ mkdir -p $(DATA)/web/js
+ $(foreach tempsrc,$(TYPESCRIPT_TEMPSRC),$(ESBUILD) --target=es6 --bundle $(tempsrc) $(SOURCEMAP) --outfile=$(patsubst %.ts,%.js,$(subst tempts/,./$(DATA)/web/js/,$(tempsrc))) $(MINIFY);)
+ mv $(DATA)/web/js/crash.js $(DATA)/
+ $(COPYTS)
+
+SWAGGER_SRC = $(wildcard api*.go) $(wildcard *auth.go) views.go
+SWAGGER_TARGET = docs/docs.go
+$(SWAGGER_TARGET): $(SWAGGER_SRC)
+ $(SWAGINSTALL)
+ swag init -g main.go
+
+VARIANTS_SRC = $(wildcard html/*.html)
+VARIANTS_TARGET = $(DATA)/html/admin.html
+$(VARIANTS_TARGET): $(VARIANTS_SRC)
+ $(info copying html)
+ cp -r html $(DATA)/
+ $(info adding dark variants to html)
+ node scripts/missing-colors.js html $(DATA)/html
+
+ICON_SRC = node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2
+ICON_TARGET = $(ICON_SRC:node_modules/remixicon/fonts/%=$(DATA)/web/css/%)
+CSS_SRC = $(wildcard css/*.css)
+CSS_TARGET = $(DATA)/web/css/part-bundle.css
+CSS_FULLTARGET = $(CSS_BUNDLE)
+ALL_CSS_SRC = $(ICON_SRC) $(CSS_SRC)
+ALL_CSS_TARGET = $(ICON_TARGET)
+
+$(CSS_FULLTARGET): $(TYPESCRIPT_TARGET) $(VARIANTS_TARGET) $(ALL_CSS_SRC) $(wildcard html/*.html)
+ mkdir -p $(DATA)/web/css
+ $(info copying fonts)
+ cp -r node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2 $(DATA)/web/css/
+ $(info bundling css)
+ $(ESBUILD) --bundle css/base.css --outfile=$(CSS_TARGET) --external:remixicon.css --external:../fonts/hanken* --minify
+
+ npx tailwindcss -i $(CSS_TARGET) -o $(CSS_FULLTARGET) $(TAILWIND)
+ rm $(CSS_TARGET)
+ # mv $(CSS_BUNDLE) $(DATA)/web/css/$(CSSVERSION)bundle.css
+ # npx postcss -o $(CSS_TARGET) $(CSS_TARGET)
+
+INLINE_SRC = html/crash.html
+INLINE_TARGET = $(DATA)/crash.html
+$(INLINE_TARGET): $(CSS_FULLTARGET) $(INLINE_SRC)
+ cp html/crash.html $(DATA)/crash.html
+ $(UNCSS) # generates $(DATA)/bundle.css for us
+ node scripts/inline.js root $(DATA) $(DATA)/crash.html $(DATA)/crash.html
+ rm $(DATA)/bundle.css
+
+LANG_SRC = $(shell find ./lang)
+LANG_TARGET = $(LANG_SRC:lang/%=$(DATA)/lang/%)
+STATIC_SRC = $(wildcard static/*)
+STATIC_TARGET = $(STATIC_SRC:static/%=$(DATA)/web/%)
+COPY_SRC = images/banner.svg jfa-go.service LICENSE $(LANG_SRC) $(STATIC_SRC)
+COPY_TARGET = $(DATA)/jfa-go.service
+# $(DATA)/LICENSE $(LANG_TARGET) $(STATIC_TARGET) $(DATA)/web/css/$(CSSVERSION)bundle.css
+$(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC)
+ $(info copying $(CONFIG_BASE))
+ cp $(CONFIG_BASE) $(DATA)/
+ $(info copying crash page)
+ cp $(DATA)/crash.html $(DATA)/html/
+ $(info copying static data)
+ mkdir -p $(DATA)/web
+ cp images/banner.svg static/banner.svg
+ cp -r static/* $(DATA)/web/
+ $(info copying systemd service)
+ cp jfa-go.service $(DATA)/
+ $(info copying language files)
+ cp -r lang $(DATA)/
+ cp LICENSE $(DATA)/
+
+precompile: $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET)
+
+GO_SRC = $(shell find ./ -name "*.go")
+GO_TARGET = build/jfa-go
+$(GO_TARGET): $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum
+ $(info Downloading deps)
+ $(GOBINARY) mod download
+ $(info Building)
+ mkdir -p build
+ $(GOBINARY) build $(RACEDETECTOR) -ldflags="$(LDFLAGS)" $(TAGS) -o $(GO_TARGET)
+
+compile: $(GO_TARGET)
+
+compress:
+ upx --lzma build/jfa-go
+
+install:
+ cp -r build $(DESTDIR)/jfa-go
+
+clean:
+ -rm -r $(DATA)
+ -rm -r build
+ -rm mail/*.html
+ -rm docs/docs.go docs/swagger.json docs/swagger.yaml
+ go clean
+
+npm:
+ $(info installing npm dependencies)
+ npm install $(NPMOPTS)
diff --git a/Makefile b/Makefile
index dfcef82..81ca24d 100644
--- a/Makefile
+++ b/Makefile
@@ -110,22 +110,18 @@ CONFIG_DEFAULT = $(DATA)/config-default.ini
# -mkdir -p $(DATA)
$(DATA):
- mkdir -p $(DATA)
+ mkdir -p $(DATA)/web/js
+ mkdir -p $(DATA)/web/css
-$(CONFIG_DEFAULT): $(DATA) $(CONFIG_BASE)
+$(CONFIG_DEFAULT): $(CONFIG_BASE)
$(info Generating config-default.ini)
go run scripts/ini/main.go -in $(CONFIG_BASE) -out $(DATA)/config-default.ini
configuration: $(CONFIG_DEFAULT)
-EMAIL_SRC_MJML = $(wildcard mail/*.mjml)
-EMAIL_SRC_TXT = $(wildcard mail/*.txt)
-EMAIL_DATA_MJML = $(EMAIL_SRC_MJML:mail/%=data/%)
-EMAIL_HTML = $(EMAIL_DATA_MJML:.mjml=.html)
-EMAIL_TXT = $(EMAIL_SRC_TXT:mail/%=data/%)
-EMAIL_ALL = $(EMAIL_HTML) $(EMAIL_TXT)
-EMAIL_TARGET = mail/confirmation.html
-$(EMAIL_TARGET): $(EMAIL_SRC_MJML) $(EMAIL_SRC_TXT)
+EMAIL_SRC = $(wildcard mail/*)
+EMAIL_TARGET = $(DATA)/confirmation.html
+$(EMAIL_TARGET): $(EMAIL_SRC)
$(info Generating email html)
npx mjml mail/*.mjml -o $(DATA)/
$(info Copying plaintext mail)
@@ -144,7 +140,6 @@ $(TYPESCRIPT_TARGET): $(TYPESCRIPT_FULLSRC) ts/tsconfig.json
scripts/dark-variant.sh tempts
scripts/dark-variant.sh tempts/modules
$(info compiling typescript)
- mkdir -p $(DATA)/web/js
$(foreach tempsrc,$(TYPESCRIPT_TEMPSRC),$(ESBUILD) --target=es6 --bundle $(tempsrc) $(SOURCEMAP) --outfile=$(patsubst %.ts,%.js,$(subst tempts/,./$(DATA)/web/js/,$(tempsrc))) $(MINIFY);)
mv $(DATA)/web/js/crash.js $(DATA)/
$(COPYTS)
@@ -172,7 +167,6 @@ ALL_CSS_SRC = $(ICON_SRC) $(CSS_SRC)
ALL_CSS_TARGET = $(ICON_TARGET)
$(CSS_FULLTARGET): $(TYPESCRIPT_TARGET) $(VARIANTS_TARGET) $(ALL_CSS_SRC) $(wildcard html/*.html)
- mkdir -p $(DATA)/web/css
$(info copying fonts)
cp -r node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2 $(DATA)/web/css/
$(info bundling css)
@@ -204,7 +198,6 @@ $(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC)
$(info copying crash page)
cp $(DATA)/crash.html $(DATA)/html/
$(info copying static data)
- mkdir -p $(DATA)/web
cp images/banner.svg static/banner.svg
cp -r static/* $(DATA)/web/
$(info copying systemd service)
@@ -213,11 +206,11 @@ $(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC)
cp -r lang $(DATA)/
cp LICENSE $(DATA)/
-precompile: $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET)
+precompile: $(DATA) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET)
GO_SRC = $(shell find ./ -name "*.go")
GO_TARGET = build/jfa-go
-$(GO_TARGET): $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum
+$(GO_TARGET): $(DATA) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum
$(info Downloading deps)
$(GOBINARY) mod download
$(info Building)
diff --git a/ts/admin.ts b/ts/admin.ts
index 86b540f..814fa37 100644
--- a/ts/admin.ts
+++ b/ts/admin.ts
@@ -1,7 +1,7 @@
import { ThemeManager } from "./modules/theme.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js";
-import { Tabs } from "./modules/tabs.js";
+import { Tabs, Tab } from "./modules/tabs.js";
import { inviteList, createInvite } from "./modules/invites.js";
import { accountsList } from "./modules/accounts.js";
import { settingsList } from "./modules/settings.js";
@@ -120,21 +120,41 @@ window.notifications = new notificationBox(document.getElementById('notification
userSelect.classList.toggle('unfocused');
}*/
+// Determine if url references an invite or account
+let isInviteURL = window.invites.isInviteURL();
+let isAccountURL = accounts.isAccountURL();
+
// load tabs
-const tabs: { url: string, reloader: () => void }[] = [
+const tabs: { id: string, url: string, reloader: () => void }[] = [
{
- url: "invites",
- reloader: window.invites.reload
+ id: "invites",
+ url: "",
+ reloader: () => window.invites.reload(() => {
+ if (isInviteURL) {
+ window.invites.loadInviteURL();
+ // Don't keep loading the same item on every tab refresh
+ isInviteURL = false;
+ }
+ }),
},
{
+ id: "accounts",
url: "accounts",
- reloader: accounts.reload
+ reloader: () => accounts.reload(() => {
+ if (isAccountURL) {
+ accounts.loadAccountURL();
+ // Don't keep loading the same item on every tab refresh
+ isAccountURL = false;
+ }
+ }),
},
{
+ id: "activity",
url: "activity",
reloader: activity.reload
},
{
+ id: "settings",
url: "settings",
reloader: settings.reload
}
@@ -145,41 +165,20 @@ const defaultTab = tabs[0];
window.tabs = new Tabs();
for (let tab of tabs) {
- window.tabs.addTab(tab.url, null, tab.reloader);
- if (window.location.pathname == window.URLBase + "/" + tab.url) {
+ window.tabs.addTab(tab.id, tab.url, null, tab.reloader);
+}
+
+let matchedTab = false
+for (let tab of tabs) {
+ if (window.location.pathname.startsWith(window.URLBase + "/" + tab.url)) {
window.tabs.switch(tab.url, true);
+ matchedTab = true;
}
}
-
-let isInviteURL = window.invites.isInviteURL();
-let isAccountURL = accounts.isAccountURL();
-
// Default tab
-if ((window.URLBase + "/").includes(window.location.pathname)) {
- window.tabs.switch(defaultTab.url, true);
-}
-
-document.addEventListener("tab-change", (event: CustomEvent) => {
- const urlParams = new URLSearchParams(window.location.search);
- const lang = urlParams.get('lang');
- let tab = window.URLBase + "/" + event.detail;
- if (event.detail == "") {
- tab = window.location.pathname;
- } else if (tab == window.URLBase + "/invites") {
- if (window.location.pathname == window.URLBase + "/") {
- tab = window.URLBase + "/";
- } else if (window.URLBase) { tab = window.URLBase; }
- else { tab = "../"; }
- }
- if (lang) {
- tab += "?lang=" + lang
- }
- window.history.pushState(event.detail, "Admin - jfa-go", tab);
-});
-
-window.onpopstate = (event: PopStateEvent) => {
- console.log(event.state);
- window.tabs.switch(event.state);
+// if ((window.URLBase + "/").includes(window.location.pathname)) {
+if (!matchedTab) {
+ window.tabs.switch("", true);
}
const login = new Login(window.modals.login as Modal, "/", window.loginAppearance);
@@ -189,35 +188,8 @@ login.onLogin = () => {
// FIXME: Decide whether to autoload activity or not
reloadProfileNames();
setInterval(() => { window.invites.reload(); accounts.reload(); }, 30*1000);
- const currentTab = window.tabs.current;
- switch (currentTab) {
- case "invites":
- window.invites.reload();
- break;
- case "accounts":
- accounts.reload();
- break;
- case "settings":
- settings.reload();
- break;
- case "activity": // FIXME: fix URL clash with route
- activity.reload();
- break;
- default:
- console.log(isAccountURL, isInviteURL);
- if (isInviteURL) {
- window.invites.reload(() => {
- window.invites.loadInviteURL();
- window.tabs.switch("invites", false, true);
- });
- } else if (isAccountURL) {
- accounts.reload(() => {
- accounts.loadAccountURL();
- window.tabs.switch("accounts", false, true);
- });
- }
- break;
- }
+ // Triggers pre and post funcs, even though we're already on that page
+ window.tabs.switch(window.tabs.current);
}
bindManualDropdowns();
diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts
index 812e476..dc39c22 100644
--- a/ts/modules/accounts.ts
+++ b/ts/modules/accounts.ts
@@ -1804,10 +1804,16 @@ export class accountsList {
this.focusAccount(event.detail);
});
- isAccountURL = () => { return window.location.pathname.startsWith(window.URLBase + "/accounts/user/"); }
+ // FIXME: Use Query Param! so it doesn't get cleared by pages.ts.
+ isAccountURL = () => {
+ const urlParams = new URLSearchParams(window.location.search);
+ const userID = urlParams.get("user");
+ return Boolean(userID);
+ }
loadAccountURL = () => {
- let userID = window.location.pathname.split(window.URLBase + "/accounts/user/")[1].split("?lang")[0];
+ const urlParams = new URLSearchParams(window.location.search);
+ const userID = urlParams.get("user");
this.focusAccount(userID);
}
diff --git a/ts/modules/activity.ts b/ts/modules/activity.ts
index d5bf3d3..48a2cc6 100644
--- a/ts/modules/activity.ts
+++ b/ts/modules/activity.ts
@@ -64,17 +64,17 @@ export class Activity implements activity, SearchableItem {
}
_genUserLink = (): string => {
- return `${this._genUserText()}`;
+ return `${this._genUserText()}`;
}
_genSrcUserLink = (): string => {
- return `${this._genSrcUserText()}`;
+ return `${this._genSrcUserText()}`;
}
private _renderInvText = (): string => { return `${this.value || this.invite_code || "???"}`; }
private _genInvLink = (): string => {
- return `${this._renderInvText()}`;
+ return `${this._renderInvText()}`;
}
@@ -307,17 +307,17 @@ export class Activity implements activity, SearchableItem {
const pseudoInvites = this._card.getElementsByClassName("activity-pseudo-link-invite") as HTMLCollectionOf;
for (let i = 0; i < pseudoUsers.length; i++) {
- const navigate = (event: Event) => {
+ /*const navigate = (event: Event) => {
event.preventDefault()
window.tabs.switch("accounts");
document.dispatchEvent(accountURLEvent(pseudoUsers[i].getAttribute("data-id")));
window.history.pushState(null, document.title, pseudoUsers[i].getAttribute("data-href"));
}
pseudoUsers[i].onclick = navigate;
- pseudoUsers[i].onkeydown = navigate;
+ pseudoUsers[i].onkeydown = navigate;*/
}
for (let i = 0; i < pseudoInvites.length; i++) {
- const navigate = (event: Event) => {
+ /*const navigate = (event: Event) => {
event.preventDefault();
window.invites.reload(() => {
window.tabs.switch("invites");
@@ -326,7 +326,7 @@ export class Activity implements activity, SearchableItem {
});
}
pseudoInvites[i].onclick = navigate;
- pseudoInvites[i].onkeydown = navigate;
+ pseudoInvites[i].onkeydown = navigate;*/
}
}
diff --git a/ts/modules/invites.ts b/ts/modules/invites.ts
index 0074aee..c9b759c 100644
--- a/ts/modules/invites.ts
+++ b/ts/modules/invites.ts
@@ -448,10 +448,15 @@ export class inviteList implements inviteList {
this.focusInvite(event.detail);
})
- isInviteURL = () => { return window.location.pathname.startsWith(window.URLBase + "/invites/"); }
+ isInviteURL = () => {
+ const urlParams = new URLSearchParams(window.location.search);
+ const inviteCode = urlParams.get("invite");
+ return Boolean(inviteCode);
+ }
loadInviteURL = () => {
- let inviteCode = window.location.pathname.split(window.URLBase + "/invites/")[1].split("?lang")[0];
+ const urlParams = new URLSearchParams(window.location.search);
+ const inviteCode = urlParams.get("invite");
this.focusInvite(inviteCode, window.lang.notif("errorInviteNotFound"));
}
diff --git a/ts/modules/pages.ts b/ts/modules/pages.ts
index 948474c..686f771 100644
--- a/ts/modules/pages.ts
+++ b/ts/modules/pages.ts
@@ -32,24 +32,21 @@ export class PageManager {
private _onpopstate = (event: PopStateEvent) => {
let name = event.state;
- if (!(event.state in this.pages)) {
+ if (!this.pages.has(event.state)) {
name = this.pageList[0]
}
- let success = this.pages[name].show();
+ let success = this.pages.get(name).show();
if (!success) {
- console.log("failed");
return;
}
if (!(this.hideOthers)) {
- console.log("shoudln't hide others");
return;
}
for (let k of this.pageList) {
if (name != k) {
- this.pages[k].hide();
+ this.pages.get(k).hide();
}
}
- console.log("loop ended", this);
}
constructor(c: PageConfig) {
@@ -65,42 +62,39 @@ export class PageManager {
setPage(p: Page) {
p.index = this.pageList.length;
- this.pages[p.name] = p;
+ this.pages.set(p.name, p);
this.pageList.push(p.name);
}
load(name: string = "") {
- if (!(name in this.pages)) return window.history.pushState(name || this.defaultName, this.defaultTitle, "")
- const p = this.pages[name];
+ if (!this.pages.has(name)) return window.history.pushState(name || this.defaultName, this.defaultTitle, "")
+ const p = this.pages.get(name);
this.loadPage(p);
}
loadPage (p: Page) {
- window.history.pushState(p.name || this.defaultName, p.title, p.url);
+ window.history.pushState(p.name || this.defaultName, p.title, p.url + window.location.search);
}
prev(name: string = "") {
- if (!(name in this.pages)) return console.error(`previous page ${name} not found`);
- let p = this.pages[name];
+ if (!this.pages.has(name)) return console.error(`previous page ${name} not found`);
+ let p = this.pages.get(name);
let shouldSkip = true;
while (shouldSkip && p.index > 0) {
- p = this.pages[this.pageList[p.index-1]];
+ p = this.pages.get(this.pageList[p.index-1]);
shouldSkip = p.shouldSkip();
}
this.loadPage(p);
}
next(name: string = "") {
- if (!(name in this.pages)) return console.error(`previous page ${name} not found`);
- let p = this.pages[name];
- console.log("next", name, p);
- console.log("pages", this.pages, this.pageList);
+ if (!this.pages.has(name)) return console.error(`previous page ${name} not found`);
+ let p = this.pages.get(name);
let shouldSkip = true;
while (shouldSkip && p.index < this.pageList.length) {
- p = this.pages[this.pageList[p.index+1]];
+ p = this.pages.get(this.pageList[p.index+1]);
shouldSkip = p.shouldSkip();
}
- console.log("next ended with", p);
this.loadPage(p);
}
};
diff --git a/ts/modules/settings.ts b/ts/modules/settings.ts
index 0fcfbdc..01aa5df 100644
--- a/ts/modules/settings.ts
+++ b/ts/modules/settings.ts
@@ -636,6 +636,7 @@ export class settingsList {
}
private _showPanel = (name: string) => {
+ console.log("showing", name);
for (let n in this._sections) {
if (n == name) {
this._sections[name].visible = true;
@@ -919,7 +920,11 @@ export class settingsList {
for (let i = 0; i < this._loader.children.length; i++) {
this._loader.children[i].classList.remove("invisible");
}
- this._showPanel(this._settings.sections[0].section);
+ for (let s of this._settings.sections) {
+ if (s.meta.disabled) continue;
+ this._showPanel(s.section);
+ break;
+ }
document.dispatchEvent(new CustomEvent("settings-loaded"));
document.dispatchEvent(new CustomEvent("settings-advancedState", { detail: false }));
this._saveButton.classList.add("unfocused");
diff --git a/ts/modules/tabs.ts b/ts/modules/tabs.ts
index ee6333e..628ed7e 100644
--- a/ts/modules/tabs.ts
+++ b/ts/modules/tabs.ts
@@ -1,44 +1,77 @@
+import { PageManager, Page } from "../modules/pages.js";
+
+export interface Tab {
+ page: Page;
+ tabEl: HTMLDivElement;
+ buttonEl: HTMLSpanElement;
+ preFunc?: () => void;
+ postFunc?: () => void;
+}
+
+
export class Tabs implements Tabs {
private _current: string = "";
- tabs: Array;
+ private _baseOffset = -1;
+ tabs: Map;
+ pages: PageManager;
constructor() {
- this.tabs = [];
+ this.tabs = new Map;
+ this.pages = new PageManager({
+ hideOthersOnPageShow: true,
+ defaultName: "invites",
+ defaultTitle: document.title,
+ });
}
- addTab = (tabID: string, preFunc = () => void {}, postFunc = () => void {}) => {
- let tab = {} as Tab;
- tab.tabID = tabID;
- tab.tabEl = document.getElementById("tab-" + tabID) as HTMLDivElement;
- tab.buttonEl = document.getElementById("button-tab-" + tabID) as HTMLSpanElement;
+ addTab = (tabID: string, url: string, preFunc = () => void {}, postFunc = () => void {},) => {
+ let tab: Tab = {
+ page: null,
+ tabEl: document.getElementById("tab-" + tabID) as HTMLDivElement,
+ buttonEl: document.getElementById("button-tab-" + tabID) as HTMLSpanElement,
+ preFunc: preFunc,
+ postFunc: postFunc,
+ };
+ if (this._baseOffset == -1) {
+ this._baseOffset = tab.buttonEl.offsetLeft;
+ }
+ tab.page = {
+ name: tabID,
+ title: document.title, /*FIXME: Get actual names from translations*/
+ url: window.URLBase + "/" + url,
+ show: () => {
+ tab.buttonEl.classList.add("active", "~urge");
+ tab.tabEl.classList.remove("unfocused");
+ tab.buttonEl.parentElement.scrollTo(tab.buttonEl.offsetLeft-this._baseOffset, 0);
+ document.dispatchEvent(new CustomEvent("tab-change", { detail: tabID }));
+ return true;
+ },
+ hide: () => {
+ tab.buttonEl.classList.remove("active");
+ tab.buttonEl.classList.remove("~urge");
+ tab.tabEl.classList.add("unfocused");
+ return true;
+ },
+ shouldSkip: () => false,
+ };
+ this.pages.setPage(tab.page);
tab.buttonEl.onclick = () => { this.switch(tabID); };
- tab.preFunc = preFunc;
- tab.postFunc = postFunc;
- this.tabs.push(tab);
+ this.tabs.set(tabID, tab);
}
get current(): string { return this._current; }
set current(tabID: string) { this.switch(tabID); }
- switch = (tabID: string, noRun: boolean = false, keepURL: boolean = false) => {
- this._current = tabID;
- let baseOffset = -1;
- for (let t of this.tabs) {
- if (baseOffset == -1) baseOffset = t.buttonEl.offsetLeft;
- if (t.tabID == tabID) {
- t.buttonEl.classList.add("active", "~urge");
- if (t.preFunc && !noRun) { t.preFunc(); }
- t.tabEl.classList.remove("unfocused");
- if (t.postFunc && !noRun) { t.postFunc(); }
- document.dispatchEvent(new CustomEvent("tab-change", { detail: keepURL ? "" : tabID }));
- // t.buttonEl.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })
-
- t.buttonEl.parentElement.scrollTo(t.buttonEl.offsetLeft-baseOffset, 0);
- } else {
- t.buttonEl.classList.remove("active");
- t.buttonEl.classList.remove("~urge");
- t.tabEl.classList.add("unfocused");
- }
+ switch = (tabID: string, noRun: boolean = false) => {
+ let t = this.tabs.get(tabID);
+ if (t == undefined) {
+ [t] = this.tabs.values();
}
+
+ this._current = t.page.name;
+
+ if (t.preFunc && !noRun) { t.preFunc(); }
+ this.pages.load(tabID);
+ if (t.postFunc && !noRun) { t.postFunc(); }
}
}
diff --git a/ts/typings/d.ts b/ts/typings/d.ts
index bcba96d..a53a3fb 100644
--- a/ts/typings/d.ts
+++ b/ts/typings/d.ts
@@ -79,20 +79,10 @@ declare interface NotificationBox {
declare interface Tabs {
current: string;
- tabs: Array;
- addTab: (tabID: string, preFunc?: () => void, postFunc?: () => void) => void;
+ addTab: (tabID: string, url: string, preFunc?: () => void, postFunc?: () => void) => void;
switch: (tabID: string, noRun?: boolean, keepURL?: boolean) => void;
}
-declare interface Tab {
- tabID: string;
- tabEl: HTMLDivElement;
- buttonEl: HTMLSpanElement;
- preFunc?: () => void;
- postFunc?: () => void;
-}
-
-
declare interface Modals {
about: Modal;
login: Modal;