gotosocial/web/source/settings/lib/navigation/index.js

136 lines
3.1 KiB
JavaScript
Raw Normal View History

/*
GoToSocial
Copyright (C) GoToSocial Authors admin@gotosocial.org
SPDX-License-Identifier: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const React = require("react");
const { nanoid } = require("nanoid");
const { Redirect } = require("wouter");
const { urlSafe } = require("./util");
const {
Sidebar,
ViewRouter,
MenuComponent
} = require("./components");
function createNavigation(rootUrl, menus) {
const root = {
url: rootUrl,
links: [],
};
const routing = [];
const menuTree = menus.map((creatorFunc) =>
creatorFunc(root, routing)
);
return {
Sidebar: Sidebar(menuTree, routing),
ViewRouter: ViewRouter(routing, root.redirectUrl)
};
}
function MenuEntry(name, opts, contents) {
if (contents == undefined) { // opts argument is optional
contents = opts;
opts = {};
}
return function createMenuEntry(root, routing) {
const type = Array.isArray(contents) ? "category" : "view";
let urlParts = [root.url];
if (opts.url != "") {
urlParts.push(opts.url ?? urlSafe(name));
}
const url = urlParts.join("/");
let routingUrl = url;
if (opts.wildcard) {
routingUrl += "/:wildcard*";
}
const entry = {
name, type,
url, routingUrl,
key: nanoid(),
permissions: opts.permissions ?? false,
icon: opts.icon,
links: [routingUrl],
level: (root.level ?? -1) + 1,
redirectUrl: opts.defaultUrl
};
if (type == "category") {
let entries = contents.map((creatorFunc) => creatorFunc(entry, routing));
let routes = [];
entries.forEach((e) => {
// move empty wildcard routes to end of category, to prevent overlap
if (e.url == entry.url) {
routes.unshift(e);
} else {
routes.push(e);
}
});
routes.reverse();
routing.push(...routes);
if (opts.redirectUrl != entry.url) {
routing.push({
key: entry.key,
url: entry.url,
permissions: entry.permissions,
routingUrl: entry.redirectUrl + "/:fallback*",
view: React.createElement(Redirect, { to: entry.redirectUrl })
});
entry.url = entry.redirectUrl;
}
root.links.push(...entry.links);
entry.MenuEntry = React.createElement(
MenuComponent,
entry,
entries.map((e) => e.MenuEntry)
);
} else {
entry.links.push(routingUrl);
root.links.push(routingUrl);
entry.view = React.createElement(contents, { baseUrl: url });
entry.MenuEntry = React.createElement(MenuComponent, entry);
}
if (root.redirectUrl == undefined) {
root.redirectUrl = entry.url;
}
return entry;
};
}
module.exports = {
createNavigation,
Menu: MenuEntry,
Item: MenuEntry
};