Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions hassio/src/addon-store/hassio-addon-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
PropertyValues,
} from "lit-element";
import { html, TemplateResult } from "lit-html";
import { atLeastVersion } from "../../../src/common/config/version";
import "../../../src/common/search/search-input";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-svg-icon";
Expand All @@ -24,6 +25,7 @@ import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../src/types";
import { showRegistriesDialog } from "../dialogs/registries/show-dialog-registries";
import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories";
import { supervisorTabs } from "../hassio-tabs";
import "./hassio-addon-repository";
Expand Down Expand Up @@ -113,6 +115,12 @@ class HassioAddonStore extends LitElement {
<mwc-list-item>
Reload
</mwc-list-item>
${this.hass.userData?.showAdvanced &&
atLeastVersion(this.hass.config.version, 0, 117)
? html`<mwc-list-item>
Registries
</mwc-list-item>`
: ""}
</ha-button-menu>
${repos.length === 0
? html`<hass-loading-screen no-toolbar></hass-loading-screen>`
Expand Down Expand Up @@ -157,6 +165,9 @@ class HassioAddonStore extends LitElement {
case 1:
this.refreshData();
break;
case 2:
this._manageRegistries();
break;
}
}

Expand All @@ -173,6 +184,10 @@ class HassioAddonStore extends LitElement {
});
}

private async _manageRegistries() {
showRegistriesDialog(this);
}

private async _loadData() {
try {
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
Expand Down
245 changes: 245 additions & 0 deletions hassio/src/dialogs/registries/dialog-hassio-registries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button/mwc-icon-button";
import "@material/mwc-list/mwc-list-item";
import { mdiDelete } from "@mdi/js";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-svg-icon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
addHassioDockerRegistry,
fetchHassioDockerRegistries,
removeHassioDockerRegistry,
} from "../../../../src/data/hassio/docker";
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";

@customElement("dialog-hassio-registries")
class HassioRegistriesDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property({ attribute: false }) private _registries?: {
registry: string;
username: string;
}[];

@internalProperty() private _registry?: string;

@internalProperty() private _username?: string;

@internalProperty() private _password?: string;

@internalProperty() private _opened = false;

@internalProperty() private _addingRegistry = false;

protected render(): TemplateResult {
return html`
<ha-dialog
.open=${this._opened}
@closing=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
this._addingRegistry
? "Add New Docker Registry"
: "Manage Docker Registries"
)}
>
<div class="form">
${this._addingRegistry
? html`
<paper-input
@value-changed=${this._inputChanged}
class="flex-auto"
name="registry"
label="Registry"
required
auto-validate
></paper-input>
<paper-input
@value-changed=${this._inputChanged}
class="flex-auto"
name="username"
label="Username"
required
auto-validate
></paper-input>
<paper-input
@value-changed=${this._inputChanged}
class="flex-auto"
name="password"
label="Password"
type="password"
required
auto-validate
></paper-input>

<mwc-button
?disabled=${Boolean(
!this._registry || !this._username || !this._password
)}
@click=${this._addNewRegistry}
>
Add registry
</mwc-button>
`
: html`${this._registries?.length
? this._registries.map((entry) => {
return html`
<mwc-list-item class="option" hasMeta twoline>
<span>${entry.registry}</span>
<span slot="secondary"
>Username: ${entry.username}</span
>
<mwc-icon-button
.entry=${entry}
title="Remove"
slot="meta"
@click=${this._removeRegistry}
>
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button>
</mwc-list-item>
`;
})
: html`
<mwc-list-item>
<span>No registries configured</span>
</mwc-list-item>
`}
<mwc-button @click=${this._addRegistry}>
Add new registry
</mwc-button> `}
</div>
</ha-dialog>
`;
}

private _inputChanged(ev: Event) {
const target = ev.currentTarget as PaperInputElement;
this[`_${target.name}`] = target.value;
}

public async showDialog(_dialogParams: any): Promise<void> {
this._opened = true;
await this._loadRegistries();
await this.updateComplete;
}

public closeDialog(): void {
this._addingRegistry = false;
this._opened = false;
}

public focus(): void {
this.updateComplete.then(() =>
(this.shadowRoot?.querySelector(
"[dialogInitialFocus]"
) as HTMLElement)?.focus()
);
}

private async _loadRegistries(): Promise<void> {
const registries = await fetchHassioDockerRegistries(this.hass);
this._registries = Object.keys(registries!.registries).map((key) => ({
registry: key,
username: registries.registries[key].username,
}));
}

private _addRegistry(): void {
this._addingRegistry = true;
}

private async _addNewRegistry(): Promise<void> {
const data = {};
data[this._registry!] = {
username: this._username,
password: this._password,
};

try {
await addHassioDockerRegistry(this.hass, data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if thas call returns the newly created entry, the next call would not be needed and it could just be appended?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not, most of the supervisor API does not return for POST calls

await this._loadRegistries();
this._addingRegistry = false;
} catch (err) {
showAlertDialog(this, {
title: "Failed to add registry",
text: extractApiErrorMessage(err),
});
}
}

private async _removeRegistry(ev: Event): Promise<void> {
const entry = (ev.currentTarget as any).entry;

try {
await removeHassioDockerRegistry(this.hass, entry.registry);
await this._loadRegistries();
} catch (err) {
showAlertDialog(this, {
title: "Failed to remove registry",
text: extractApiErrorMessage(err),
});
}
}

static get styles(): CSSResult[] {
return [
haStyle,
haStyleDialog,
css`
ha-dialog.button-left {
--justify-action-buttons: flex-start;
}
paper-icon-item {
cursor: pointer;
}
.form {
color: var(--primary-text-color);
}
.option {
border: 1px solid var(--divider-color);
border-radius: 4px;
margin-top: 4px;
}
mwc-button {
margin-left: 8px;
}
mwc-icon-button {
color: var(--error-color);
margin: -10px;
}
mwc-list-item {
cursor: default;
}
mwc-list-item span[slot="secondary"] {
color: var(--secondary-text-color);
}
ha-paper-dropdown-menu {
display: block;
}
`,
];
}
}

declare global {
interface HTMLElementTagNameMap {
"dialog-hassio-registries": HassioRegistriesDialog;
}
}
13 changes: 13 additions & 0 deletions hassio/src/dialogs/registries/show-dialog-registries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "./dialog-hassio-registries";

export const showRegistriesDialog = (element: HTMLElement): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-hassio-registries",
dialogImport: () =>
import(
/* webpackChunkName: "dialog-hassio-registries" */ "./dialog-hassio-registries"
),
dialogParams: {},
});
};
36 changes: 36 additions & 0 deletions src/data/hassio/docker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { HomeAssistant } from "../../types";
import { hassioApiResultExtractor, HassioResponse } from "./common";

interface HassioDockerRegistries {
[key: string]: { username: string; password?: string };
}

export const fetchHassioDockerRegistries = async (hass: HomeAssistant) => {
return hassioApiResultExtractor(
await hass.callApi<HassioResponse<HassioDockerRegistries>>(
"GET",
"hassio/docker/registries"
)
);
};

export const addHassioDockerRegistry = async (
hass: HomeAssistant,
data: HassioDockerRegistries
) => {
await hass.callApi<HassioResponse<HassioDockerRegistries>>(
"POST",
"hassio/docker/registries",
data
);
};

export const removeHassioDockerRegistry = async (
hass: HomeAssistant,
registry: string
) => {
await hass.callApi<HassioResponse<void>>(
"DELETE",
`hassio/docker/registries/${registry}`
);
};