136 lines
3.1 KiB
JavaScript
136 lines
3.1 KiB
JavaScript
|
/*
|
||
|
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
|
||
|
};
|