import React from "react"; import { remote } from "electron"; import { Avatar, IconButton, List, ListItem, ListItemAvatar, ListItemSecondaryAction, ListItemText, Paper } from "@material-ui/core"; import { Description, Folder, Delete, HelpOutline } from "@material-ui/icons"; import { action, computed, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import fse from "fs-extra"; import { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store"; import { Button } from "../button"; import { SubTitle } from "../layout/sub-title"; import { Spinner } from "../spinner"; import logger from "../../../main/logger"; import { iter } from "../../utils"; interface SyncInfo { type: "file" | "folder" | "unknown"; } interface Entry extends Value { filePath: string; } interface Value { data: KubeconfigSyncValue; info: SyncInfo; } async function getMapEntry({ filePath, ...data}: KubeconfigSyncEntry): Promise<[string, Value]> { try { // stat follows the stat(2) linux syscall spec, namely it follows symlinks const stats = await fse.stat(filePath); if (stats.isFile()) { return [filePath, { info: { type: "file" }, data }]; } if (stats.isDirectory()) { return [filePath, { info: { type: "folder" }, data }]; } logger.warn("[KubeconfigSyncs]: unknown stat entry", { stats }); return [filePath, { info: { type: "unknown" }, data }]; } catch (error) { logger.warn(`[KubeconfigSyncs]: failed to stat entry: ${error}`, { error }); return [filePath, { info: { type: "unknown" }, data }]; } } @observer export class KubeconfigSyncs extends React.Component { syncs = observable.map(); @observable loaded = false; async componentDidMount() { const mapEntries = await Promise.all( iter.map( UserStore.getInstance().syncKubeconfigEntries, ([filePath, ...value]) => getMapEntry({ filePath, ...value }), ), ); this.syncs.replace(mapEntries); this.loaded = true; disposeOnUnmount(this, [ reaction(() => Array.from(this.syncs.entries(), ([filePath, { data }]) => [filePath, data]), syncs => { UserStore.getInstance().syncKubeconfigEntries.replace(syncs); }) ]); } @computed get syncsList(): Entry[] | undefined { if (!this.loaded) { return undefined; } return Array.from(this.syncs.entries(), ([filePath, value]) => ({ filePath, ...value })); } @action openFileDialog = async () => { const { dialog, BrowserWindow } = remote; const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), { properties: ["openFile", "showHiddenFiles", "multiSelections", "openDirectory"], message: "Select kubeconfig file(s) and folder(s)", buttonLabel: "Sync", }); if (canceled) { return; } const newEntries = await Promise.all(filePaths.map(filePath => getMapEntry({ filePath }))); for (const [filePath, info] of newEntries) { this.syncs.set(filePath, info); } }; renderEntryIcon(entry: Entry) { switch (entry.info.type) { case "file": return ; case "folder": return ; case "unknown": return ; } } renderEntry = (entry: Entry) => { return ( {this.renderEntryIcon(entry)} this.syncs.delete(entry.filePath)} > ); }; renderEntries() { const entries = this.syncsList; if (!entries) { return (
); } return ( {entries.map(this.renderEntry)} ); } render() { return ( <>
); } }