QuickConnect: init

This adds a page in the settings page that allows the user to enter a
quick connect code to allow another device to log in.
This commit is contained in:
Chris Josten 2024-01-02 19:51:27 +01:00
parent 13786f01c9
commit a66434afa8
10 changed files with 242 additions and 3 deletions

View File

@ -209,6 +209,14 @@ signals:
void supportedCommandsChanged();
void onlineChanged();
/**
* @brief Emitted after submitQuickConnectCode succeeded
*/
void quickConnectAccepted();
/**
* @brief Emitted after submitQuickConnectCode failed
*/
void quickConnectRejected();
/**
* @brief onUserDataChanged Emitted when the user data of an item is changed on the server.
@ -233,6 +241,7 @@ public slots:
*/
void setupConnection();
void authenticate(QString username, QString password, bool storeCredentials = false);
void submitQuickConnectCode(const QString &code);
/**
* @brief Logs the user out and clears the session.

View File

@ -256,6 +256,27 @@ public:
}
};
// Specialisation for endpoints that return "true" or "false" as response.
template<typename P>
class HttpLoaderBase<bool, P> : public Loader<bool, P> {
public:
explicit HttpLoaderBase(Jellyfin::ApiClient *apiClient)
: Loader<bool, P> (apiClient) {}
typename Loader<bool, P>::ResultType parseResponse(int statusCode, QByteArray response) {
QString text = QString::fromUtf8(response);
if (text == QStringLiteral("true")) {
return true;
} else if (text == QStringLiteral("false")) {
return false;
} else {
this->stopWithError(QStringLiteral("Could not parse boolean response: %1").arg(text));
return std::nullopt;
}
}
};
/**
* Implementation of Loader that loads Items over HTTP

View File

@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include <QSharedPointer>
#include "JellyfinQt/dto/clientcapabilitiesdto.h"
#include "JellyfinQt/loader/http/quickconnect.h"
#include "JellyfinQt/support/jsonconv.h"
#include "JellyfinQt/viewmodel/settings.h"
#include "JellyfinQt/websocket.h"
@ -428,6 +429,26 @@ void ApiClient::authenticate(QString username, QString password, bool storeCrede
setDefaultErrorHandler(rep);
}
void ApiClient::submitQuickConnectCode(const QString &code) {
using QQAuthorizeLoader = Loader::HTTP::AuthorizeLoader;
Loader::AuthorizeParams params;
params.setCode(code);
QQAuthorizeLoader *loader = new QQAuthorizeLoader(this);
loader->setParameters(params);
loader->load();
loader->connect(loader, &QQAuthorizeLoader::error, this, [this, loader](QString message) {
qDebug() << "QQ error: " << message;
emit this->quickConnectRejected();
loader->deleteLater();
});
loader->connect(loader, &QQAuthorizeLoader::ready, this, [this, loader]() {
emit this->quickConnectAccepted();
loader->deleteLater();
});
}
void ApiClient::deleteSession() {
QNetworkReply *rep = post("/Sessions/Logout");
connect(rep, &QNetworkReply::finished, this, [rep, this] {

View File

@ -16,6 +16,7 @@
- New features
- Allow remote controlling other Jellyfin clients. See the pulley item on the main screen named
"Remote Control"
- Let other devices log in using the Quick Connect feature of Jellyfin.
- New layout for artist pages
- New layout for the music library
- New layout for playlist pages

View File

@ -25,7 +25,7 @@ set(sailfin_QML_SOURCES
qml/components/music/SongDelegate.qml
qml/components/videoplayer/VideoError.qml
qml/components/videoplayer/VideoHud.qml
qml/components/ContributorsSection.qml
qml/components/ContributorsSection.qml
qml/components/IconListItem.qml
qml/components/ItemChildrenShowcase.qml
qml/components/JItem.qml
@ -49,6 +49,7 @@ set(sailfin_QML_SOURCES
qml/harbour-sailfin.qml
qml/pages/ConnectingPage.qml
qml/pages/ControllableDevicesPage.qml
qml/pages/QuickConnectDialog.qml
qml/pages/SettingsPage.qml
qml/pages/VideoPage.qml
qml/pages/itemdetails/BaseDetailPage.qml

View File

@ -0,0 +1,86 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
Dialog {
id: qqDialog
canAccept: qqCode.length > 0
acceptDestination: ConnectingPage {
id: loadingPage
onStatusChanged: {
if (status == PageStatus.Active) {
submit()
}
}
Connections {
target: apiClient
onQuickConnectAccepted: pageStack.pop(pageStack.previousPage(qqDialog))
onQuickConnectRejected: {
qqCode.errorHighlight = true
pageStack.pop(qqDialog)
}
}
function submit() {
console.log("Accepted QuickConnect with code", qqCode.text)
apiClient.submitQuickConnectCode(qqCode.text)
}
}
Column {
width: parent.width
DialogHeader {
//: Dialog title
title: "Quick Connect"
//: Accept button on dialog for submitting a Quick Connect code
defaultAcceptText: qsTr("Allow login")
}
Label {
anchors {
left: parent.left
right: parent.right
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
}
color: Theme.highlightColor
wrapMode: Text.WordWrap
//: Instructions on page that tells the user a bit about how Quick Connect works
text: qsTr("To log a device in with Quick Connect, select the Quick Connect button and enter the displayed code in the field below.")
}
TextField {
id: qqCode
//: Label for textfield for entering the Quick Connect codeyy
label: qsTr("Quick Connect code")
focus: true
//: Placeholder text for textfield for entering the Quick Connect codeyy
placeholderText: qsTr("Quick Connect code")
inputMethodHints: Qt.ImhDigitsOnly
EnterKey.iconSource: "image://theme/icon-m-enter-accept"
EnterKey.onClicked: accept()
onTextChanged: {
if (errorHighlight) {
errorHighlight = false
}
}
}
Label {
id: errorText
visible: qqCode.errorHighlight
anchors {
left: parent.left
right: parent.right
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
}
color: Theme.errorColor
//: Error message shown below the textfield when it is not connected
text: qsTr("The Quick Connect code was not accepted")
}
}
}

View File

@ -106,6 +106,11 @@ Page {
Item { width: 1; height: Theme.paddingLarge; }
ButtonLayout {
Button {
//: This is a name used by Jellyfin and seems to be untranslated in other languages
text: qsTr("Quick Connect")
onClicked: pageStack.push(Qt.resolvedUrl("QuickConnectDialog.qml"))
}
Button {
text: qsTr("Log out")
onClicked: remorse.execute(qsTr("Logging out"), apiClient.deleteSession)

View File

@ -441,6 +441,31 @@ Page title for the list of all artists within the music library</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QuickConnectDialog</name>
<message>
<source>Quick Connect code</source>
<extracomment>Label for textfield for entering the Quick Connect codeyy
----------
Placeholder text for textfield for entering the Quick Connect codeyy</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow login</source>
<extracomment>Accept button on dialog for submitting a Quick Connect code</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>To log a device in with Quick Connect, select the Quick Connect button and enter the displayed code in the field below.</source>
<extracomment>Instructions on page that tells the user a bit about how Quick Connect works</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>The Quick Connect code was not accepted</source>
<extracomment>Error message shown below the textfield when it is not connected</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SeasonPage</name>
<message>
@ -507,6 +532,11 @@ Page title for the list of all artists within the music library</extracomment>
<extracomment>About Sailfin settings menu itemy</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Quick Connect</source>
<extracomment>This is a name used by Jellyfin and seems to be untranslated in other languages</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SongDelegate</name>

View File

@ -433,6 +433,31 @@ Page title for the list of all artists within the music library</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QuickConnectDialog</name>
<message>
<source>Quick Connect code</source>
<extracomment>Label for textfield for entering the Quick Connect codeyy
----------
Placeholder text for textfield for entering the Quick Connect codeyy</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow login</source>
<extracomment>Accept button on dialog for submitting a Quick Connect code</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>To log a device in with Quick Connect, select the Quick Connect button and enter the displayed code in the field below.</source>
<extracomment>Instructions on page that tells the user a bit about how Quick Connect works</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>The Quick Connect code was not accepted</source>
<extracomment>Error message shown below the textfield when it is not connected</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SeasonPage</name>
<message>
@ -488,6 +513,11 @@ Page title for the list of all artists within the music library</extracomment>
<extracomment>Settings list item for settings related to streaming</extracomment>
<translation>Настройки стриминга</translation>
</message>
<message>
<source>Quick Connect</source>
<extracomment>This is a name used by Jellyfin and seems to be untranslated in other languages</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SongDelegate</name>

View File

@ -16,7 +16,12 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;p&gt;&lt;b&gt;Sailfin version %1&lt;/b&gt;&lt;br/&gt;Copyright © Chris Josten 2020%2&lt;/p&gt;&lt;p&gt;Sailfin is Free Software licensed under the &lt;a href=&apos;lgpl&apos;&gt;LGPL-v2.1&lt;/a&gt; or later, at your choice. Parts of the code of Sailfin are from other libraries. &lt;a href=&apos;3rdparty&apos;&gt;View their licenses here&lt;/a&gt;.&lt;/p&gt;</source>
<source>&lt;p&gt;&lt;b&gt;Sailfin version %1&lt;/b&gt;&lt;br/&gt;Copyright © Chris Josten 2020%2&lt;/p&gt;&lt;p&gt;Sailfin is Free Software licensed under the &lt;a href=&apos;lgpl&apos;&gt;LGPL-v2.1&lt;/a&gt; or later, at your choice. You can &lt;a href=&quot;github&quot;&gt;view its source code on GitHub&lt;/a&gt;. Parts of the code of Sailfin are from other libraries. &lt;a href=&apos;3rdparty&apos;&gt;View their licenses here&lt;/a&gt;.&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Contributors</source>
<extracomment>SectionHeader</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
@ -414,6 +419,31 @@ Page title for the list of all artists within the music library</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QuickConnectDialog</name>
<message>
<source>Quick Connect code</source>
<extracomment>Label for textfield for entering the Quick Connect codeyy
----------
Placeholder text for textfield for entering the Quick Connect codeyy</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Allow login</source>
<extracomment>Accept button on dialog for submitting a Quick Connect code</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>To log a device in with Quick Connect, select the Quick Connect button and enter the displayed code in the field below.</source>
<extracomment>Instructions on page that tells the user a bit about how Quick Connect works</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>The Quick Connect code was not accepted</source>
<extracomment>Error message shown below the textfield when it is not connected</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SeasonPage</name>
<message>
@ -445,7 +475,7 @@ Page title for the list of all artists within the music library</extracomment>
<message>
<source>About Sailfin</source>
<extracomment>About Sailfin settings menu itemy</extracomment>
<translation type="unfinished">About Sailfin1</translation>
<translation type="unfinished">About Sailfin</translation>
</message>
<message>
<source>Session</source>
@ -469,6 +499,11 @@ Page title for the list of all artists within the music library</extracomment>
<extracomment>Debug information settings menu itemy</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Quick Connect</source>
<extracomment>This is a name used by Jellyfin and seems to be untranslated in other languages</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SongDelegate</name>