Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add quick connect (login without typing password) #1096

Merged
merged 20 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- [Ryan Hartzell](https://github.com/ryan-hartzell)
- [Thibault Nocchi](https://github.com/ThibaultNocchi)
- [MrTimscampi](https://github.com/MrTimscampi)
- [ConfusedPolarBear](https://github.com/ConfusedPolarBear)
- [Sarab Singh](https://github.com/sarab97)
- [GuilhermeHideki](https://github.com/GuilhermeHideki)
- [Andrei Oanca](https://github.com/OancaAndrei)
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,15 @@
"src/components/playmenu.js",
"src/components/pluginManager.js",
"src/components/prompt/prompt.js",
"src/components/qualityOptions.js",
"src/components/quickConnectSettings/quickConnectSettings.js",
"src/components/recordingcreator/recordingbutton.js",
"src/components/recordingcreator/recordingcreator.js",
"src/components/recordingcreator/seriesrecordingeditor.js",
"src/components/recordingcreator/recordinghelper.js",
"src/components/refreshdialog/refreshdialog.js",
"src/components/recordingcreator/recordingeditor.js",
"src/components/recordingcreator/recordingfields.js",
"src/components/qualityOptions.js",
"src/components/remotecontrol/remotecontrol.js",
"src/components/sanatizefilename.js",
"src/components/scrollManager.js",
Expand Down Expand Up @@ -244,6 +245,7 @@
"src/controllers/dashboard/plugins/installed/index.js",
"src/controllers/dashboard/plugins/available/index.js",
"src/controllers/dashboard/plugins/repositories/index.js",
"src/controllers/dashboard/quickconnect.js",
"src/controllers/dashboard/scheduledtasks/scheduledtask.js",
"src/controllers/dashboard/scheduledtasks/scheduledtasks.js",
"src/controllers/dashboard/serveractivity.js",
Expand Down Expand Up @@ -292,6 +294,7 @@
"src/controllers/user/menu/index.js",
"src/controllers/user/playback/index.js",
"src/controllers/user/profile/index.js",
"src/controllers/user/quickConnect/index.js",
"src/controllers/user/subtitles/index.js",
"src/controllers/wizard/finish/index.js",
"src/controllers/wizard/remote/index.js",
Expand Down
41 changes: 41 additions & 0 deletions src/components/quickConnectSettings/quickConnectSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import globalize from 'globalize';
import toast from 'toast';

export class QuickConnectSettings {
constructor() { }

authorize(code) {
let url = ApiClient.getUrl('/QuickConnect/Authorize?Code=' + code);
ApiClient.ajax({
type: 'POST',
url: url
}, true).then(() => {
toast(globalize.translate('QuickConnectAuthorizeSuccess'));
}).catch(() => {
toast(globalize.translate('QuickConnectAuthorizeFail'));
});

// prevent bubbling
return false;
}

activate() {
let url = ApiClient.getUrl('/QuickConnect/Activate');
return ApiClient.ajax({
type: 'POST',
url: url
}).then(() => {
toast(globalize.translate('QuickConnectActivationSuccessful'));
return true;
}).catch((e) => {
console.error('Error activating quick connect. Error:', e);
Dashboard.alert({
title: globalize.translate('HeaderError'),
message: globalize.translate('DefaultErrorMessage')
});
throw e;
});
}
}

export default QuickConnectSettings;
58 changes: 58 additions & 0 deletions src/controllers/dashboard/quickconnect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import loading from 'loading';
import toast from 'toast';
import globalize from 'globalize';

const unavailable = 'Unavailable';
const available = 'Available';
const active = 'Active';
let page;

export default function(view) {
view.addEventListener('viewshow', function () {
page = this;
loading.show();
page.querySelector('#btnQuickConnectSubmit').onclick = onSubmit;
updatePage();
});
}

function loadPage(status) {
let check = status === available || status === active;

page.querySelector('#quickConnectStatus').textContent = status.toLocaleLowerCase();
page.querySelector('#chkQuickConnectAvailable').checked = check;

loading.hide();
}

function onSubmit() {
loading.show();

let newStatus = page.querySelector('#chkQuickConnectAvailable').checked ? available : unavailable;

let url = ApiClient.getUrl('/QuickConnect/Available?Status=' + newStatus);

ApiClient.ajax({
type: 'POST',
url: url
}, true).then(() => {
toast(globalize.translate('SettingsSaved'));
setTimeout(updatePage, 500);

return true;
}).catch((e) => {
console.error('Unable to set quick connect status. error:', e);
});

loading.hide();
return false;
}

function updatePage() {
ApiClient.getQuickConnect('Status').then((response) => {
loadPage(response);
return true;
}).catch((e) => {
console.error('Unable to get quick connect status. error:', e);
});
}
4 changes: 4 additions & 0 deletions src/controllers/session/login/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ <h1 style="margin-top:1em;">${HeaderPleaseSignIn}</h1>
<button is="emby-button" type="button" class="raised cancel block btnManual">
<span>${ButtonManualLogin}</span>
</button>

<button is="emby-button" type="button" class="raised cancel block btnQuick">
<span>${ButtonUseQuickConnect}</span>
</button>

<button is="emby-button" type="button" class="raised cancel block btnForgotPassword">
<span>${ButtonForgotPassword}</span>
Expand Down
63 changes: 61 additions & 2 deletions src/controllers/session/login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import 'emby-checkbox';
var user = result.User;
loading.hide();

Dashboard.onServerChanged(user.Id, result.AccessToken, apiClient);
Dashboard.navigate('home.html');
onLoginSuccessful(user.Id, result.AccessToken, apiClient);
}, function (response) {
page.querySelector('#txtManualName').value = '';
page.querySelector('#txtManualPassword').value = '';
Expand All @@ -41,6 +40,60 @@ import 'emby-checkbox';
});
}

function authenticateQuickConnect(apiClient) {
let url = apiClient.getUrl('/QuickConnect/Initiate');
apiClient.getJSON(url).then(function (json) {
if (!json.Secret || !json.Code) {
console.error('Malformed quick connect response', json);
return false;
}

Dashboard.alert({
message: globalize.translate('QuickConnectAuthorizeCode', json.Code),
title: globalize.translate('QuickConnect')
});

let connectUrl = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret);

let interval = setInterval(function() {
apiClient.getJSON(connectUrl).then(async function(data) {
if (!data.Authenticated) {
return;
}

clearInterval(interval);

let result = await apiClient.quickConnect(data.Authentication);
onLoginSuccessful(result.User.Id, result.AccessToken, apiClient);
}, function (e) {
clearInterval(interval);

Dashboard.alert({
message: globalize.translate('QuickConnectDeactivated'),
title: globalize.translate('HeaderError')
});

console.error('Unable to login with quick connect', e);
});
}, 5000, connectUrl);

return true;
}, function(e) {
Dashboard.alert({
message: globalize.translate('QuickConnectNotActive'),
title: globalize.translate('HeaderError')
});

console.error('Quick connect error: ', e);
return false;
});
}

function onLoginSuccessful(id, accessToken, apiClient) {
Dashboard.onServerChanged(id, accessToken, apiClient);
Dashboard.navigate('home.html');
}

function showManualForm(context, showCancel, focusPassword) {
context.querySelector('.chkRememberLogin').checked = appSettings.enableAutoLogin();
context.querySelector('.manualLoginForm').classList.remove('hide');
Expand Down Expand Up @@ -187,13 +240,19 @@ import 'emby-checkbox';
Dashboard.navigate('forgotpassword.html');
});
view.querySelector('.btnCancel').addEventListener('click', showVisualForm);
view.querySelector('.btnQuick').addEventListener('click', function () {
const apiClient = getApiClient();
authenticateQuickConnect(apiClient);
return false;
});
view.querySelector('.btnManual').addEventListener('click', function () {
view.querySelector('#txtManualName').value = '';
showManualForm(view, true);
});
view.querySelector('.btnSelectServer').addEventListener('click', function () {
Dashboard.selectServer();
});

view.addEventListener('viewshow', function (e) {
loading.show();
libraryMenu.setTransparentMenu(true);
Expand Down
10 changes: 10 additions & 0 deletions src/controllers/user/menu/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ <h2 class="sectionTitle headerUsername" style="padding-left:.25em;"></h2>
</div>
</a>


<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="lnkQuickConnectPreferences listItem-border">
<div class="listItem">
<em class="material-icons listItemIcon listItemIcon-transparent">tap_and_play</em>
<div class="listItemBody">
<div class="listItemBodyText">${QuickConnect}</div>
</div>
</div>
</a>

<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="clientSettings listItem-border">
<div class="listItem">
<span class="material-icons listItemIcon listItemIcon-transparent devices_other"></span>
Expand Down
1 change: 1 addition & 0 deletions src/controllers/user/menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default function (view, params) {
page.querySelector('.lnkHomePreferences').setAttribute('href', 'mypreferenceshome.html?userId=' + userId);
page.querySelector('.lnkPlaybackPreferences').setAttribute('href', 'mypreferencesplayback.html?userId=' + userId);
page.querySelector('.lnkSubtitlePreferences').setAttribute('href', 'mypreferencessubtitles.html?userId=' + userId);
page.querySelector('.lnkQuickConnectPreferences').setAttribute('href', 'mypreferencesquickconnect.html');

if (window.NativeShell && window.NativeShell.AppHost.supports('clientsettings')) {
page.querySelector('.clientSettings').classList.remove('hide');
Expand Down
17 changes: 17 additions & 0 deletions src/controllers/user/quickConnect/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div id="quickConnectPreferencesPage" data-role="page" class="page libraryPage userPreferencesPage noSecondaryNavPage" data-title="${QuickConnect}" data-backbutton="true" style="margin: 0 auto; max-width: 54em">
<button is="emby-button" id="btnQuickConnectActivate" type="button" class="raised button-submit block">
<span>${ButtonActivate}</span>
</button>

<form class="quickConnectSettingsContainer">
<div style="margin-bottom: 1em">
${QuickConnectDescription}
</div>
<div class="inputContainer">
<input is="emby-input" type="number" min="0" max="999999" required id="txtQuickConnectCode" label="${LabelQuickConnectCode}" autocomplete="off" />
</div>
<button id="btnQuickConnectAuthorize" is="emby-button" type="submit" class="raised button-submit block">
<span>${Authorize}</span>
</button>
</form>
</div>
61 changes: 61 additions & 0 deletions src/controllers/user/quickConnect/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import QuickConnectSettings from 'quickConnectSettings';
import globalize from 'globalize';
import toast from 'toast';

export default function (view) {
let quickConnectSettingsInstance = null;

view.addEventListener('viewshow', function () {
let codeElement = view.querySelector('#txtQuickConnectCode');

quickConnectSettingsInstance = new QuickConnectSettings();

view.querySelector('#btnQuickConnectActivate').addEventListener('click', () => {
quickConnectSettingsInstance.activate(quickConnectSettingsInstance).then(() => {
renderPage();
});
});

view.querySelector('#btnQuickConnectAuthorize').addEventListener('click', () => {
if (!codeElement.validity.valid) {
toast(globalize.translate('QuickConnectInvalidCode'));

return;
}

let code = codeElement.value;
quickConnectSettingsInstance.authorize(code);
});

view.querySelector('.quickConnectSettingsContainer').addEventListener('submit', (e) => {
e.preventDefault();
});

renderPage();
});

function renderPage(forceActive = false) {
ApiClient.getQuickConnect('Status').then((status) => {
let btn = view.querySelector('#btnQuickConnectActivate');
let container = view.querySelector('.quickConnectSettingsContainer');

// The activation button should only be visible when quick connect is unavailable (with the text replaced with an error) or when it is available (so it can be activated)
// The authorization container is only usable when quick connect is active, so it should be hidden otherwise
container.style.display = 'none';

if (status === 'Unavailable') {
btn.textContent = globalize.translate('QuickConnectNotAvailable');
btn.disabled = true;
btn.classList.remove('button-submit');
btn.classList.add('button');
} else if (status === 'Active' || forceActive) {
container.style.display = '';
btn.style.display = 'none';
}

return true;
}).catch((e) => {
throw e;
});
}
}
24 changes: 24 additions & 0 deletions src/quickconnect.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<div id="quickConnectPage" data-role="page" class="page type-interior advancedConfigurationPage">
<div class="content-primary">
<form class="quickConnectSettings">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">${QuickConnect}</h2>
</div>
</div>

<div>${LabelCurrentStatus}<span id="quickConnectStatus" style="padding:0 0.4em;"></span></div>

<div class="checkboxList paperList" style="padding:.5em 1em;">
<label>
<input type="checkbox" is="emby-checkbox" id="chkQuickConnectAvailable" />
<span>${EnableQuickConnect}</span>
</label>
</div>

<button is="emby-button" id="btnQuickConnectSubmit" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
</form>
</div>
</div>
6 changes: 6 additions & 0 deletions src/scripts/libraryMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,12 @@ import 'flexStyles';
pageIds: ['devicesPage', 'devicePage'],
icon: 'devices'
});
links.push({
name: globalize.translate('QuickConnect'),
href: 'quickConnect.html',
pageIds: ['quickConnectPage'],
icon: 'tap_and_play'
});
links.push({
name: globalize.translate('HeaderActivity'),
href: 'serveractivity.html',
Expand Down
Loading