Skip to content

Commit

Permalink
Merge pull request #31 from ubq-testing/fix/org-choices
Browse files Browse the repository at this point in the history
Fix/org choices
  • Loading branch information
rndquu authored Dec 5, 2024
2 parents ae1a823 + f9de8b7 commit f3e6f62
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 32 deletions.
5 changes: 5 additions & 0 deletions static/manifest-gui.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ header #uos-logo path {
max-height: calc(100vh - 192px);
scrollbar-width: thin;
scrollbar-color: #303030 #101010;
opacity: 1;
transition: opacity 0.5s ease;
}

.readme-container p {
Expand Down Expand Up @@ -231,6 +233,9 @@ button {
border-radius: 4px;
opacity: 0.75;
font-family: "Proxima Nova", sans-serif;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
button:hover {
background-color: #404040;
Expand Down
31 changes: 28 additions & 3 deletions static/scripts/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createClient, SupabaseClient, Session } from "@supabase/supabase-js";
import { Octokit } from "@octokit/rest";
import { GitHubUser } from "../types/github";
import { CONFIG_ORG_REPO } from "@ubiquity-os/plugin-sdk/constants";

declare const SUPABASE_URL: string;
declare const SUPABASE_ANON_KEY: string;
Expand Down Expand Up @@ -131,9 +132,33 @@ export class AuthService {
}

public async getGitHubUserOrgs(): Promise<string[]> {
const octokit = await this.getOctokit();
const response = await octokit.rest.orgs.listForAuthenticatedUser();
return response.data.map((org: { login: string }) => org.login);
const user = await this.octokit?.rest.users.getAuthenticated();
const listForAuthUser = await this.octokit?.rest.orgs.listForAuthenticatedUser();
if (!user || !listForAuthUser) return [];
const listForUserPublic = await this.octokit?.rest.orgs.listForUser({ username: user?.data.login });
const allOrgs = [...(listForAuthUser?.data || []), ...(listForUserPublic?.data || [])].map((org) => org.login);

const orgConfigPermissions: Record<string, string> = {};

const getOrgConfigRepoUserPermissions = async (org: string) => {
try {
const p = await this.octokit?.rest.repos.getCollaboratorPermissionLevel({ owner: org, repo: CONFIG_ORG_REPO, username: user?.data.login });
orgConfigPermissions[org] = p?.data.permission || "none";
} catch (er) {
console.error(`[getOrgConfigPermissions] - ${org}::`, er);
}
};

for (const org of allOrgs) {
await getOrgConfigRepoUserPermissions(org);
}

return Array.from(
new Set([
...(listForAuthUser?.data.map((org) => org.login) || []),
...Object.keys(orgConfigPermissions).filter((org) => orgConfigPermissions[org] !== "none"),
])
);
}

public async getGitHubUserOrgRepos(orgs: string[]): Promise<Record<string, string[]>> {
Expand Down
15 changes: 10 additions & 5 deletions static/scripts/rendering/config-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,27 @@ export function renderConfigEditor(renderer: ManifestRenderer, pluginManifest: M
let value: string;

if (typeof currentObj === "object" || Array.isArray(currentObj)) {
value = JSON.stringify(currentObj, null, 2);
value = currentObj[key] ? JSON.stringify(currentObj[key]) : "";
if (value === "") {
// no-op
} else if (!value) {
value = currentObj ? JSON.stringify(currentObj) : "";
}
} else if (typeof currentObj === "boolean") {
value = currentObj ? "true" : "false";
} else if (!currentObj) {
value = "";
} else {
value = currentObj as string;
}

if (input.tagName === "TEXTAREA") {
(input as HTMLTextAreaElement).value = value;
} else if (input.tagName === "INPUT" && (input as HTMLInputElement).type === "checkbox") {
(input as HTMLInputElement).checked = value === "true";
} else {
(input as HTMLInputElement).value = value;
}

if (input.tagName === "INPUT" && (input as HTMLInputElement).type === "checkbox") {
(input as HTMLInputElement).checked = value === "true";
}
});
}

Expand Down
14 changes: 13 additions & 1 deletion static/scripts/rendering/input-parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export function processProperties(
if (!prop) {
return;
}

if (prop.type === "object" && prop.properties) {
processProperties(renderer, manifest, prop.properties, fullKey);
} else if ("anyOf" in prop && Array.isArray(prop.anyOf)) {
Expand Down Expand Up @@ -108,6 +107,19 @@ export function parseConfigInputs(
missing.push(key);
}
}

/**
* We've ID'd the required fields that are missing, now we check if there are any fields
* that have null | undefined values and remove them from the configuration object,
* since the defaults will be used the config prop does not need to be present.
*/

Object.keys(config).forEach((key) => {
if (config[key] === null || config[key] === undefined || config[key] === "") {
delete config[key];
}
});

return { config, missing };
} else {
throw new Error("Invalid configuration: " + JSON.stringify(validate.errors, null, 2));
Expand Down
2 changes: 1 addition & 1 deletion static/scripts/rendering/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function createBackButton(renderer: ManifestRenderer): HTMLButtonElement
return backButton;
}

function handleBackButtonClick(renderer: ManifestRenderer): void {
export function handleBackButtonClick(renderer: ManifestRenderer): void {
renderer.manifestGui?.classList.remove("plugin-editor");
const readmeContainer = document.querySelector(".readme-container");
if (readmeContainer) {
Expand Down
25 changes: 25 additions & 0 deletions static/scripts/rendering/write-add-remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { parseConfigInputs } from "./input-parsing";
import { getOfficialPluginConfig } from "../../utils/storage";
import { renderConfigEditor } from "./config-editor";
import { normalizePluginName } from "./utils";
import { handleBackButtonClick } from "./navigation";

/**
* Writes the new configuration to the config file. This does not push the config to GitHub
Expand Down Expand Up @@ -68,13 +69,22 @@ export function writeNewConfig(renderer: ManifestRenderer, option: "add" | "remo
],
};

removePushNotificationIfPresent();

if (option === "add") {
handleAddPlugin(renderer, plugin, pluginManifest);
} else if (option === "remove") {
handleRemovePlugin(renderer, plugin, pluginManifest);
}
}

function removePushNotificationIfPresent() {
const notification = document.querySelector(".toast.toast-success.show");
if (notification) {
notification.remove();
}
}

function handleAddPlugin(renderer: ManifestRenderer, plugin: Plugin, pluginManifest: Manifest): void {
renderer.configParser.addPlugin(plugin);
toastNotification(`Configuration for ${pluginManifest.name} saved successfully. Do you want to push to GitHub?`, {
Expand Down Expand Up @@ -127,6 +137,21 @@ async function notificationConfigPush(renderer: ManifestRenderer) {
type: "success",
shouldAutoDismiss: true,
});

const container = document.querySelector("#manifest-gui") as HTMLElement | null;
const readmeContainer = document.querySelector(".readme-container") as HTMLElement | null;
if (container && readmeContainer) {
container.style.transition = "opacity 0.5s ease";
container.style.opacity = "0";
readmeContainer.style.transition = "opacity 0.5s ease";
readmeContainer.style.opacity = "0";
setTimeout(() => {
handleBackButtonClick(renderer);
container.style.opacity = "1";
}, 500);
} else {
handleBackButtonClick(renderer);
}
}

export function handleResetToDefault(renderer: ManifestRenderer, pluginManifest: Manifest | null) {
Expand Down
23 changes: 14 additions & 9 deletions static/utils/element-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Manifest } from "../types/plugins";
import { toastNotification } from "./toaster";

const CONFIG_INPUT_STR = "config-input";

Expand Down Expand Up @@ -37,7 +38,7 @@ export function createInputRow(
valueCell.className = "table-data-value";
valueCell.ariaRequired = `${required}`;

const input = createInput(key, prop.default, prop);
const input = createInput(key, prop.default, prop.type);
valueCell.appendChild(input);

row.appendChild(valueCell);
Expand All @@ -49,21 +50,24 @@ export function createInputRow(
items: prop.items ? { type: prop.items.type } : null,
};
}
export function createInput(key: string, defaultValue: unknown, prop: Manifest["configuration"]): HTMLElement {
export function createInput(key: string, defaultValue: unknown, prop: string): HTMLElement {
if (!key) {
throw new Error("Input name is required");
}

let ele: HTMLElement;

const dataType = prop.type;

if (dataType === "object" || dataType === "array") {
ele = createTextareaInput(key, defaultValue, dataType);
} else if (dataType === "boolean") {
if (prop === "object" || prop === "array") {
ele = createTextareaInput(key, defaultValue, prop);
} else if (prop === "boolean") {
ele = createBooleanInput(key, defaultValue);
} else {
ele = createStringInput(key, defaultValue, dataType);
ele = createStringInput(key, defaultValue, prop);
}

if (!ele) {
toastNotification("An error occurred while creating an input element", { type: "error" });
throw new Error("Input type is required");
}

return ele;
Expand All @@ -76,7 +80,8 @@ export function createStringInput(key: string, defaultValue: string | unknown, d
"data-config-key": key,
"data-type": dataType,
class: CONFIG_INPUT_STR,
value: `${defaultValue}`,
value: defaultValue ? `${defaultValue}` : "",
placeholder: defaultValue ? "" : `Enter ${dataType}`,
});
}
export function createBooleanInput(key: string, defaultValue: boolean | unknown): HTMLElement {
Expand Down
39 changes: 26 additions & 13 deletions static/utils/toaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export function toastNotification(
});

closeButton.addEventListener("click", () => {
toastElement.classList.remove("show");
setTimeout(() => toastElement.remove(), 250);
kill();
});

toastElement.appendChild(messageElement);
Expand Down Expand Up @@ -68,21 +67,35 @@ export function toastNotification(
toastElement.classList.add("show");
});

function kill(withTimeout = false) {
if (withTimeout) {
setTimeout(() => {
toastElement.classList.remove("show");
setTimeout(() => toastElement.remove(), 250);
}, duration);
} else {
toastElement.classList.remove("show");
setTimeout(() => toastElement.remove(), 250);
let autoDismissTimeout: number | undefined;

function kill() {
toastElement.classList.remove("show");
setTimeout(() => toastElement.remove(), 250);
if (autoDismissTimeout) {
clearTimeout(autoDismissTimeout);
}
}

if (shouldAutoDismiss) {
kill(shouldAutoDismiss);
function startAutoDismiss() {
if (shouldAutoDismiss) {
autoDismissTimeout = window.setTimeout(() => {
kill();
}, duration);
}
}

toastElement.addEventListener("mouseenter", () => {
if (autoDismissTimeout) {
clearTimeout(autoDismissTimeout);
}
});

toastElement.addEventListener("mouseleave", () => {
startAutoDismiss();
});

startAutoDismiss();

return kill;
}

0 comments on commit f3e6f62

Please sign in to comment.