diff options
Diffstat (limited to 'settings/accounts/ui/fediverse-update.qml')
| -rw-r--r-- | settings/accounts/ui/fediverse-update.qml | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/settings/accounts/ui/fediverse-update.qml b/settings/accounts/ui/fediverse-update.qml new file mode 100644 index 0000000..fe75089 --- /dev/null +++ b/settings/accounts/ui/fediverse-update.qml @@ -0,0 +1,229 @@ +/* + * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import Sailfish.Accounts 1.0 +import com.jolla.settings.accounts 1.0 +import com.jolla.settings.accounts.fediverse 1.0 + +AccountCredentialsAgent { + id: root + + property bool _started + + readonly property string callbackUri: "http://ipv4.jolla.com/online/status.html" + readonly property string defaultServerHost: "mastodon.social" + + function normalizeApiHost(rawHost) { + var host = rawHost ? rawHost.trim() : "" + if (host.length === 0) { + host = defaultServerHost + } + + host = host.replace(/^https?:\/\//i, "") + var pathSeparator = host.indexOf("/") + if (pathSeparator !== -1) { + host = host.substring(0, pathSeparator) + } + host = host.replace(/\/+$/, "") + + if (host.length === 0) { + host = defaultServerHost + } + return "https://" + host.toLowerCase() + } + + function _valueFromServiceConfig(config, key) { + return config && config[key] ? config[key].toString() : "" + } + + function _extractAccountName(responseData) { + if (!responseData) { + return "" + } + + var candidates = [ + "AccountUsername", + "UserName", + "user_name", + "acct", + "username", + "preferred_username", + "login", + "ScreenName" + ] + for (var i = 0; i < candidates.length; ++i) { + var value = responseData[candidates[i]] + if (value) { + var userName = value.toString().trim() + if (userName.length > 0) { + return userName + } + } + } + + return "" + } + + function _formatFediverseAccountId(accountName, apiHost) { + var value = accountName ? accountName.toString().trim() : "" + if (value.length === 0) { + return "" + } + + value = value.replace(/^@+/, "") + if (value.indexOf("@") !== -1) { + return "@" + value + } + + var host = apiHost.replace(/^https?:\/\//i, "") + if (host.length === 0) { + return "" + } + + return "@" + value + "@" + host + } + + function _extractAccessToken(responseData) { + if (!responseData) { + return "" + } + + var token = responseData["AccessToken"] + if (!token || token.toString().trim().length === 0) { + token = responseData["access_token"] + } + return token ? token.toString().trim() : "" + } + + function _isFediverseAccountId(value) { + var text = value ? value.toString().trim() : "" + return /^@[^@]+@[^@]+$/.test(text) + } + + function _completeUpdate() { + root.credentialsUpdated(root.accountId) + root.goToEndDestination() + } + + function _saveDescription(description) { + if (description.length > 0) { + account.setConfigurationValue("", "description", description) + if (_isFediverseAccountId(description)) { + account.setConfigurationValue("", "default_credentials_username", description) + } + } + account.sync() + _completeUpdate() + } + + function _updateDescription(responseData) { + var config = account.configurationValues("fediverse-microblog") + var apiHost = normalizeApiHost(_valueFromServiceConfig(config, "api/Host")) + var description = _formatFediverseAccountId(_extractAccountName(responseData), apiHost) + if (description.length > 0) { + _saveDescription(description) + return + } + + var accessToken = _extractAccessToken(responseData) + if (accessToken.length === 0) { + _completeUpdate() + return + } + + var xhr = new XMLHttpRequest() + xhr.onreadystatechange = function() { + if (xhr.readyState !== XMLHttpRequest.DONE) { + return + } + + var fetchedDescription = "" + if (xhr.status >= 200 && xhr.status < 300) { + try { + var response = JSON.parse(xhr.responseText) + fetchedDescription = _formatFediverseAccountId(_extractAccountName(response), apiHost) + } catch (err) { + } + } + + if (fetchedDescription.length > 0) { + _saveDescription(fetchedDescription) + } else { + _completeUpdate() + } + } + + xhr.open("GET", apiHost + "/api/v1/accounts/verify_credentials") + xhr.setRequestHeader("Authorization", "Bearer " + accessToken) + xhr.send() + } + + function _startUpdate() { + if (_started || initialPage.status !== PageStatus.Active || account.status !== Account.Initialized) { + return + } + + var config = account.configurationValues("fediverse-microblog") + var apiHost = normalizeApiHost(_valueFromServiceConfig(config, "api/Host")) + var oauthHost = _valueFromServiceConfig(config, "auth/oauth2/web_server/Host") + if (oauthHost.length === 0) { + oauthHost = apiHost.replace(/^https?:\/\//i, "") + } + + var clientId = _valueFromServiceConfig(config, "auth/oauth2/web_server/ClientId") + var clientSecret = _valueFromServiceConfig(config, "auth/oauth2/web_server/ClientSecret") + if (clientId.length === 0 || clientSecret.length === 0) { + //% "Missing Fediverse OAuth client credentials" + credentialsUpdateError(qsTrId("settings-accounts-fediverse-la-missing_client_credentials")) + return + } + + _started = true + + var sessionData = { + "ClientId": clientId, + "ClientSecret": clientSecret, + "Host": oauthHost, + "AuthPath": "oauth/authorize", + "TokenPath": "oauth/token", + "ResponseType": "code", + "Scope": ["read", "write"], + "RedirectUri": callbackUri + } + initialPage.prepareAccountCredentialsUpdate(account, root.accountProvider, "fediverse-microblog", sessionData) + } + + Account { + id: account + identifier: root.accountId + + onStatusChanged: { + root._startUpdate() + } + } + + initialPage: OAuthAccountSetupPage { + onStatusChanged: { + root._startUpdate() + } + + onAccountCredentialsUpdated: { + root._updateDescription(responseData) + } + + onAccountCredentialsUpdateError: { + root.credentialsUpdateError(errorMessage) + } + + onPageContainerChanged: { + if (pageContainer == null) { + cancelSignIn() + } + } + } +} |
