mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Fix authorization and display issues in mailbox view (#1610)
* Mailbox: Fix authorization issues. Remove backdrop * Fix linting issues * Use HA Dialog system. Fix authorization * Add back missing backdrop * Linting errors * Use callApi. Add error checking and spinner * linting error * more linting errors * minor requested fixes * Use let/const. Fix lint issues * Remove blob test that can never fail * More let vs var fixes * More minor requested fixes * Rework code to use fetchWithAuth * Async tweaks * Lint * Fix onboarding * Add credentials for onboarding * Lint
This commit is contained in:
parent
0997274f29
commit
494e3dc62c
@ -13,6 +13,7 @@ import EventsMixin from '../../mixins/events-mixin.js';
|
||||
|
||||
import { getState } from '../../util/ha-pref-storage.js';
|
||||
import { getActiveTranslation } from '../../util/hass-translation.js';
|
||||
import { fetchWithAuth } from '../../util/fetch-with-auth.js';
|
||||
import hassCallApi from '../../util/hass-call-api.js';
|
||||
import computeStateName from '../../common/entity/compute_state_name.js';
|
||||
import { subscribePanels } from '../../data/ws-panels';
|
||||
@ -89,23 +90,9 @@ export default superClass =>
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
callApi: async (method, path, parameters) => {
|
||||
const host = window.location.protocol + '//' + window.location.host;
|
||||
|
||||
try {
|
||||
if (auth.expired) await auth.refreshAccessToken();
|
||||
} catch (err) {
|
||||
if (err === ERR_INVALID_AUTH) {
|
||||
// Trigger auth flow
|
||||
location.reload();
|
||||
// ensure further JS is not executed
|
||||
await new Promise(() => {});
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
return await hassCallApi(host, auth, method, path, parameters);
|
||||
},
|
||||
callApi: async (method, path, parameters) =>
|
||||
hassCallApi(auth, method, path, parameters),
|
||||
fetchWithAuth: (path, init) => fetchWithAuth(auth, `${auth.data.hassUrl}${path}`, init),
|
||||
// For messages that do not get a response
|
||||
sendWS: (msg) => {
|
||||
// eslint-disable-next-line
|
||||
|
@ -4,11 +4,8 @@ import '@polymer/paper-input/paper-input.js';
|
||||
import '@polymer/paper-button/paper-button.js';
|
||||
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||
import hassCallApi from '../util/hass-call-api.js';
|
||||
import localizeLiteMixin from '../mixins/localize-lite-mixin.js';
|
||||
|
||||
const callApi = (method, path, data) => hassCallApi('', {}, method, path, data);
|
||||
|
||||
class HaOnboarding extends localizeLiteMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
@ -141,12 +138,23 @@ class HaOnboarding extends localizeLiteMixin(PolymerElement) {
|
||||
this._errorMsg = '';
|
||||
|
||||
try {
|
||||
await callApi('post', 'onboarding/users', {
|
||||
const response = await fetch('/api/onboarding/users', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
name: this._name,
|
||||
username: this._username,
|
||||
password: this._password,
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// eslint-disable-next-line
|
||||
throw {
|
||||
message: `Bad response from server: ${response.status}`
|
||||
};
|
||||
}
|
||||
|
||||
document.location = '/';
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
|
158
src/panels/mailbox/ha-dialog-show-audio-message.js
Normal file
158
src/panels/mailbox/ha-dialog-show-audio-message.js
Normal file
@ -0,0 +1,158 @@
|
||||
import '@polymer/paper-button/paper-button.js';
|
||||
import '@polymer/paper-dialog/paper-dialog.js';
|
||||
import '@polymer/paper-spinner/paper-spinner.js';
|
||||
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||
|
||||
import '../../resources/ha-style.js';
|
||||
|
||||
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||
|
||||
/*
|
||||
* @appliesMixin LocalizeMixin
|
||||
*/
|
||||
class HaDialogShowAudioMessage extends LocalizeMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="ha-style-dialog">
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
@media all and (max-width: 500px) {
|
||||
paper-dialog {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
max-height: calc(100% - 64px);
|
||||
|
||||
position: fixed !important;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
overflow: scroll;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
paper-dialog {
|
||||
border-radius: 2px;
|
||||
}
|
||||
paper-dialog p {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.icon {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
<paper-dialog id="mp3dialog" with-backdrop opened="{{_opened}}" on-opened-changed="_openedChanged">
|
||||
<h2>
|
||||
[[localize('ui.panel.mailbox.playback_title')]]
|
||||
<div class='icon'>
|
||||
<template is="dom-if" if="[[_loading]]">
|
||||
<paper-spinner active></paper-spinner>
|
||||
</template>
|
||||
<template is="dom-if" if="[[!_loading]]">
|
||||
<paper-icon-button
|
||||
on-click='openDeleteDialog'
|
||||
icon='hass:delete'
|
||||
></paper-icon-button>
|
||||
</template>
|
||||
</div>
|
||||
</h2>
|
||||
<div id="transcribe"></div>
|
||||
<div>
|
||||
<template is="dom-if" if="[[_errorMsg]]">
|
||||
<div class='error'>[[_errorMsg]]</div>
|
||||
</template>
|
||||
<audio id="mp3" preload="none" controls> <source id="mp3src" src="" type="audio/mpeg" /></audio>
|
||||
</div>
|
||||
</paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
|
||||
_currentMessage: Object,
|
||||
|
||||
// Error message when can't talk to server etc
|
||||
_errorMsg: String,
|
||||
|
||||
_loading: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
_opened: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
showDialog({ hass, message }) {
|
||||
this.hass = hass;
|
||||
this._loading = true;
|
||||
this._errorMsg = null;
|
||||
this._currentMessage = message;
|
||||
this._opened = true;
|
||||
this.$.transcribe.innerText = message.message;
|
||||
const platform = message.platform;
|
||||
const mp3 = this.$.mp3;
|
||||
mp3.src = null;
|
||||
const url = `/api/mailbox/media/${platform}/${message.sha}`;
|
||||
this.hass.fetchWithAuth(url)
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.blob();
|
||||
}
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
});
|
||||
})
|
||||
.then((blob) => {
|
||||
this._loading = false;
|
||||
mp3.src = window.URL.createObjectURL(blob);
|
||||
mp3.play();
|
||||
})
|
||||
.catch((err) => {
|
||||
this._loading = false;
|
||||
this._errorMsg = `Error loading audio: ${err.statusText}`;
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteDialog() {
|
||||
if (confirm(this.localize('ui.panel.mailbox.delete_prompt'))) {
|
||||
this.deleteSelected();
|
||||
}
|
||||
}
|
||||
|
||||
deleteSelected() {
|
||||
const msg = this._currentMessage;
|
||||
this.hass.callApi('DELETE', `mailbox/delete/${msg.platform}/${msg.sha}`);
|
||||
this._dialogDone();
|
||||
}
|
||||
|
||||
_dialogDone() {
|
||||
this.$.mp3.pause();
|
||||
this.setProperties({
|
||||
_currentMessage: null,
|
||||
_errorMsg: null,
|
||||
_loading: false,
|
||||
_opened: false,
|
||||
});
|
||||
}
|
||||
|
||||
_openedChanged(ev) {
|
||||
// Closed dialog by clicking on the overlay
|
||||
// Check against dialogClosedCallback to make sure we didn't change
|
||||
// programmatically
|
||||
if (!ev.detail.value) {
|
||||
this._dialogDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define('ha-dialog-show-audio-message', HaDialogShowAudioMessage);
|
@ -3,7 +3,6 @@ import '@polymer/app-layout/app-header/app-header.js';
|
||||
import '@polymer/app-layout/app-toolbar/app-toolbar.js';
|
||||
import '@polymer/paper-button/paper-button.js';
|
||||
import '@polymer/paper-card/paper-card.js';
|
||||
import '@polymer/paper-dialog/paper-dialog.js';
|
||||
import '@polymer/paper-input/paper-textarea.js';
|
||||
import '@polymer/paper-item/paper-item-body.js';
|
||||
import '@polymer/paper-item/paper-item.js';
|
||||
@ -17,6 +16,8 @@ import '../../resources/ha-style.js';
|
||||
import formatDateTime from '../../common/datetime/format_date_time.js';
|
||||
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||
|
||||
let registeredDialog = false;
|
||||
|
||||
/*
|
||||
* @appliesMixin LocalizeMixin
|
||||
*/
|
||||
@ -57,32 +58,8 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
paper-dialog {
|
||||
border-radius: 2px;
|
||||
}
|
||||
paper-dialog p {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
#mp3dialog paper-icon-button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px) {
|
||||
paper-dialog {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
max-height: calc(100% - 64px);
|
||||
|
||||
position: fixed !important;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
overflow: scroll;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
@ -128,28 +105,6 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
||||
</paper-card>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
|
||||
<paper-dialog with-backdrop id="mp3dialog" on-iron-overlay-closed="_mp3Closed">
|
||||
<h2>
|
||||
[[localize('ui.panel.mailbox.playback_title')]]
|
||||
<paper-icon-button
|
||||
on-click='openDeleteDialog'
|
||||
icon='hass:delete'
|
||||
></paper-icon-button>
|
||||
</h2>
|
||||
<div id="transcribe"></div>
|
||||
<div>
|
||||
<audio id="mp3" preload="none" controls> <source id="mp3src" src="" type="audio/mpeg" /></audio>
|
||||
</div>
|
||||
</paper-dialog>
|
||||
|
||||
<paper-dialog with-backdrop id="confirmdel">
|
||||
<p>[[localize('ui.panel.mailbox.delete_prompt')]]</p>
|
||||
<div class="buttons">
|
||||
<paper-button dialog-dismiss>[[localize('ui.common.cancel')]]</paper-button>
|
||||
<paper-button dialog-confirm autofocus on-click="deleteSelected">[[localize('ui.panel.mailbox.delete_button')]]</paper-button>
|
||||
</div>
|
||||
</paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -176,15 +131,19 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
||||
_messages: {
|
||||
type: Array,
|
||||
},
|
||||
|
||||
currentMessage: {
|
||||
type: Object,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (!registeredDialog) {
|
||||
registeredDialog = true;
|
||||
this.fire('register-dialog', {
|
||||
dialogShowEvent: 'show-audio-message-dialog',
|
||||
dialogTag: 'ha-dialog-show-audio-message',
|
||||
dialogImport: () => import('./ha-dialog-show-audio-message.js'),
|
||||
});
|
||||
}
|
||||
this.hassChanged = this.hassChanged.bind(this);
|
||||
this.hass.connection.subscribeEvents(this.hassChanged, 'mailbox_updated')
|
||||
.then(function (unsub) { this._unsubEvents = unsub; }.bind(this));
|
||||
@ -209,35 +168,19 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
||||
}
|
||||
|
||||
openMP3Dialog(event) {
|
||||
var platform = event.model.item.platform;
|
||||
this.currentMessage = event.model.item;
|
||||
this.$.mp3dialog.open();
|
||||
this.$.mp3src.src = '/api/mailbox/media/' + platform + '/' + event.model.item.sha;
|
||||
this.$.transcribe.innerText = event.model.item.message;
|
||||
this.$.mp3.load();
|
||||
this.$.mp3.play();
|
||||
this.fire('show-audio-message-dialog', {
|
||||
hass: this.hass,
|
||||
message: event.model.item,
|
||||
});
|
||||
}
|
||||
|
||||
_mp3Closed() {
|
||||
this.$.mp3.pause();
|
||||
}
|
||||
|
||||
openDeleteDialog() {
|
||||
this.$.confirmdel.open();
|
||||
}
|
||||
|
||||
deleteSelected() {
|
||||
var msg = this.currentMessage;
|
||||
this.hass.callApi('DELETE', 'mailbox/delete/' + msg.platform + '/' + msg.sha);
|
||||
this.$.mp3dialog.close();
|
||||
}
|
||||
getMessages() {
|
||||
const items = this.platforms.map(function (platform) {
|
||||
return this.hass.callApi('GET', 'mailbox/messages/' + platform).then(function (values) {
|
||||
var platformItems = [];
|
||||
var arrayLength = values.length;
|
||||
for (var i = 0; i < arrayLength; i++) {
|
||||
var datetime = formatDateTime(new Date(values[i].info.origtime * 1000));
|
||||
return this.hass.callApi('GET', `mailbox/messages/${platform}`).then(function (values) {
|
||||
const platformItems = [];
|
||||
const arrayLength = values.length;
|
||||
for (let i = 0; i < arrayLength; i++) {
|
||||
const datetime = formatDateTime(new Date(values[i].info.origtime * 1000));
|
||||
platformItems.push({
|
||||
timestamp: datetime,
|
||||
caller: values[i].info.callerid,
|
||||
@ -251,15 +194,9 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
||||
});
|
||||
}.bind(this));
|
||||
return Promise.all(items).then(function (platformItems) {
|
||||
var arrayLength = items.length;
|
||||
var final = [];
|
||||
for (var i = 0; i < arrayLength; i++) {
|
||||
final = final.concat(platformItems[i]);
|
||||
}
|
||||
final.sort(function (a, b) {
|
||||
return [].concat(...platformItems).sort(function (a, b) {
|
||||
return new Date(b.timestamp) - new Date(a.timestamp);
|
||||
});
|
||||
return final;
|
||||
});
|
||||
}
|
||||
|
||||
|
9
src/util/fetch-with-auth.js
Normal file
9
src/util/fetch-with-auth.js
Normal file
@ -0,0 +1,9 @@
|
||||
export const fetchWithAuth = async (auth, input, init = {}) => {
|
||||
if (auth.expired) await auth.refreshAccessToken();
|
||||
init.credentials = 'same-origin';
|
||||
if (!init.headers) {
|
||||
init.headers = {};
|
||||
}
|
||||
init.headers.authorization = `Bearer ${auth.accessToken}`;
|
||||
return await fetch(input, init);
|
||||
};
|
@ -1,52 +1,57 @@
|
||||
export default function hassCallApi(host, auth, method, path, parameters) {
|
||||
var url = host + '/api/' + path;
|
||||
import { fetchWithAuth } from './fetch-with-auth.js';
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.open(method, url, true);
|
||||
req.setRequestHeader('authorization', `Bearer ${auth.accessToken}`);
|
||||
/* eslint-disable no-throw-literal */
|
||||
|
||||
req.onload = function () {
|
||||
let body = req.responseText;
|
||||
const contentType = req.getResponseHeader('content-type');
|
||||
export default async function hassCallApi(auth, method, path, parameters) {
|
||||
const url = `${auth.data.hassUrl}/api/${path}`;
|
||||
|
||||
if (contentType && contentType.indexOf('application/json') !== -1) {
|
||||
try {
|
||||
body = JSON.parse(req.responseText);
|
||||
} catch (err) {
|
||||
reject({
|
||||
error: 'Unable to parse JSON response',
|
||||
status_code: req.status,
|
||||
body: body,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.status > 199 && req.status < 300) {
|
||||
resolve(body);
|
||||
} else {
|
||||
reject({
|
||||
error: 'Response error: ' + req.status,
|
||||
status_code: req.status,
|
||||
body: body
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
req.onerror = function () {
|
||||
reject({
|
||||
error: 'Request error',
|
||||
status_code: req.status,
|
||||
body: req.responseText,
|
||||
});
|
||||
const init = {
|
||||
method: method,
|
||||
headers: {},
|
||||
};
|
||||
|
||||
if (parameters) {
|
||||
req.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
|
||||
req.send(JSON.stringify(parameters));
|
||||
} else {
|
||||
req.send();
|
||||
init.headers['Content-Type'] = 'application/json;charset=UTF-8';
|
||||
init.body = JSON.stringify(parameters);
|
||||
}
|
||||
});
|
||||
|
||||
let response;
|
||||
|
||||
try {
|
||||
response = await fetchWithAuth(auth, url, init);
|
||||
} catch (err) {
|
||||
throw {
|
||||
error: 'Request error',
|
||||
status_code: undefined,
|
||||
body: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
let body = null;
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
try {
|
||||
body = await response.json();
|
||||
} catch (err) {
|
||||
throw {
|
||||
error: 'Unable to parse JSON response',
|
||||
status_code: err.status,
|
||||
body: null,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
body = await response.text();
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw {
|
||||
error: `Response error: ${response.status}`,
|
||||
status_code: response.status,
|
||||
body: body
|
||||
};
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user