Feature : account creation with manual validation using Flexiapi.

Fix chat received time.
Fix account creation layouts (Busy indicator, dynamic sizes, text errors)
This commit is contained in:
Julien Wadel 2023-05-31 16:33:04 +02:00
parent 8d8acea734
commit 7755539a33
18 changed files with 137 additions and 58 deletions

View file

@ -11,11 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Bubble chat layout.
### Added
- VFS Encryption
- VFS Encryption.
- File viewer in chats (Image/Animated Image/Video/Texts/Pdf) with the option to export the file for VFS mode.
- Accept/decline CLI commands.
- Colored Emojis with its own font family.
- OAuth2 connection to retrieve remote provisioning (Experimental and not usable without configuration).
- Create an account with a manual validation (external captcha as of 5.1.0).
- Add/View contact from a message.
- Mute option for each chatrooms.
- New Chat Layout.
@ -28,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Create thumbnails into memory instead of disk.
- Display video thumbnails.
- Crop thumbnail and pictures if distored.
- Update SDK to 5.2.61
- Update SDK to 5.2.66
### Removed
- Picture zoom on mouse over.

View file

@ -158,11 +158,7 @@ list(APPEND APP_OPTIONS "-DENABLE_BUILD_VERBOSE=${ENABLE_BUILD_VERBOSE}")
list(APPEND APP_OPTIONS "-DENABLE_CONSOLE_UI=${ENABLE_CONSOLE_UI}")
list(APPEND APP_OPTIONS "-DENABLE_DAEMON=${ENABLE_DAEMON}")
list(APPEND APP_OPTIONS "-DENABLE_FFMPEG=${ENABLE_FFMPEG}")
if(ENABLE_UNIT_TESTS)
list(APPEND APP_OPTIONS "-DENABLE_FLEXIAPI=ON")
else()
list(APPEND APP_OPTIONS "-DENABLE_FLEXIAPI=${ENABLE_QRCODE}")
endif()
list(APPEND APP_OPTIONS "-DENABLE_FLEXIAPI=ON")
list(APPEND APP_OPTIONS "-DENABLE_LDAP=${ENABLE_LDAP}")
list(APPEND APP_OPTIONS "-DENABLE_NON_FREE_CODECS=${ENABLE_NON_FREE_CODECS}")
list(APPEND APP_OPTIONS "-DENABLE_OPENH264=${ENABLE_OPENH264}")

View file

@ -38,6 +38,8 @@
#include <QtDebug>
#include <QTimer>
#include <QDesktopServices>
#include <QJsonDocument>
// =============================================================================
@ -75,6 +77,7 @@ private:
else
emit mAssistant->createStatusChanged(tr("accountAlreadyExists"));
}
mAssistant->setIsProcessing(false);
}
void onIsAccountExist (
@ -92,6 +95,51 @@ private:
else
emit mAssistant->loginStatusChanged(tr("loginWithUsernameFailed"));
}
mAssistant->setIsProcessing(false);
}
virtual void onAccountCreationRequestToken(const std::shared_ptr<linphone::AccountCreator> & creator, linphone::AccountCreator::Status status, const std::string & response) override{
if( status == linphone::AccountCreator::Status::RequestOk){
QJsonDocument doc = QJsonDocument::fromJson(response.c_str());
bool error = false;
QVariantMap description = doc.toVariant().toMap();
if( description.contains("token") && description.contains("validation_url")){
QString url = description.value("validation_url").toString();
QString token = description.value("token").toString();
creator->setAccountCreationRequestToken(token.toStdString());
if(!QDesktopServices::openUrl(url)){
qCritical() << "Cannot open validation url for the account creation request token";
emit mAssistant->createStatusChanged("Cannot open validation url for the account creation request token");
mAssistant->setIsProcessing(false);
}else {
emit mAssistant->createStatusChanged("Waiting for validation at " + url);
creator->requestAccountCreationTokenUsingRequestToken();
}
}else{
qCritical() << "The answer of account creation request token doesn't have token and validation_url fields";
emit mAssistant->createStatusChanged("The answer of account creation request token doesn't have token and validation_url fields");
mAssistant->setIsProcessing(false);
}
}else{
qCritical() << "Cannot get request token for account creation (" << (int)status << ")";
emit mAssistant->createStatusChanged("Cannot get request token for account creation (" +QString::number((int)status) + ")");
mAssistant->setIsProcessing(false);
}
}
virtual void onAccountCreationTokenUsingRequestToken(const std::shared_ptr<linphone::AccountCreator> & creator, linphone::AccountCreator::Status status, const std::string & response) override{
if(status == linphone::AccountCreator::Status::RequestOk){
QJsonDocument doc = QJsonDocument::fromJson(response.c_str());
bool error = false;
QVariantMap description = doc.toVariant().toMap();
creator->setToken(description.value("token").toString().toStdString());
emit mAssistant->createStatusChanged("Creating account");
creator->createAccount();// it will automatically use the account creation token.
}else
QTimer::singleShot(2000, [creator](){
creator->requestAccountCreationTokenUsingRequestToken();
});
}
void onActivateAccount (
@ -130,6 +178,7 @@ private:
else
emit mAssistant->activateStatusChanged(tr("emailActivationFailed"));
}
mAssistant->setIsProcessing(false);
}
void onRecoverAccount (
@ -148,6 +197,7 @@ private:
else
emit mAssistant->recoverStatusChanged(tr("loginWithPhoneNumberFailed"));
}
mAssistant->setIsProcessing(false);
}
private:
@ -161,6 +211,7 @@ AssistantModel::AssistantModel (QObject *parent) : QObject(parent) {
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::foundQRCode, this, &AssistantModel::onQRCodeFound);
mIsReadingQRCode = false;
mIsProcessing = false;
mAccountCreator = core->createAccountCreator(
core->getConfig()->getString("assistant", "xmlrpc_url", Constants::DefaultXmlrpcUri)
);
@ -180,6 +231,7 @@ AssistantModel::~AssistantModel(){
// -----------------------------------------------------------------------------
void AssistantModel::activate () {
setIsProcessing(true);
if (mAccountCreator->getEmail().empty())
mAccountCreator->activateAccount();
else
@ -187,10 +239,13 @@ void AssistantModel::activate () {
}
void AssistantModel::create () {
mAccountCreator->createAccount();
setIsProcessing(true);
emit createStatusChanged("Requesting validation url");
mAccountCreator->requestAccountCreationRequestToken();
}
void AssistantModel::login () {
setIsProcessing(true);
if (!mCountryCode.isEmpty()) {
mAccountCreator->recoverAccount();
return;
@ -208,6 +263,7 @@ void AssistantModel::login () {
map["username"] = getUsername();
map["password"] = getPassword();
emit loginStatusChanged(addOtherSipAccount(map) ? QString("") : tr("unableToAddAccount"));
setIsProcessing(false);
}
void AssistantModel::reset () {
@ -572,6 +628,17 @@ void AssistantModel::setIsReadingQRCode(bool isReading){
}
}
bool AssistantModel::getIsProcessing() const{
return mIsProcessing;
}
void AssistantModel::setIsProcessing(bool isProcessing){
if(mIsProcessing != isProcessing){
mIsProcessing = isProcessing;
emit isProcessingChanged();
}
}
// -----------------------------------------------------------------------------
QString AssistantModel::mapAccountCreatorUsernameStatusToString (linphone::AccountCreator::UsernameStatus status) const {

View file

@ -43,6 +43,7 @@ class AssistantModel : public QObject {
Q_PROPERTY(QString activationCode READ getActivationCode WRITE setActivationCode NOTIFY activationCodeChanged);
Q_PROPERTY(QString configFilename READ getConfigFilename WRITE setConfigFilename NOTIFY configFilenameChanged);
Q_PROPERTY(bool isReadingQRCode READ getIsReadingQRCode WRITE setIsReadingQRCode NOTIFY isReadingQRCodeChanged);
Q_PROPERTY(bool isProcessing READ getIsProcessing WRITE setIsProcessing NOTIFY isProcessingChanged);
public:
AssistantModel (QObject *parent = Q_NULLPTR);
@ -81,6 +82,7 @@ signals:
void usernameChanged (const QString &username, const QString &error);
void displayNameChanged (const QString &displayName, const QString &error);
void activationCodeChanged (const QString &activationCode);
void isProcessingChanged();
void activateStatusChanged (const QString &error);
void createStatusChanged (const QString &error);
@ -103,8 +105,6 @@ signals:
void qRCodeNotAttached(QString message, int errorCode);
void apiReceived(QString apiKey);
private:
QString getEmail () const;
void setEmail (const QString &email);
@ -133,12 +133,17 @@ private:
bool getIsReadingQRCode() const;
void setIsReadingQRCode(bool isReading);
bool getIsProcessing() const;
void setIsProcessing(bool isProcessing);
QString mapAccountCreatorUsernameStatusToString (linphone::AccountCreator::UsernameStatus status) const;
QString mCountryCode;
QString mConfigFilename;
QString mToken;
bool mIsReadingQRCode;
bool mIsProcessing;
std::shared_ptr<linphone::AccountCreator> mAccountCreator;
std::shared_ptr<Handlers> mHandlers;

View file

@ -252,9 +252,9 @@ void ChatMessageModel::updateFileTransferInformation(){
QDateTime ChatMessageModel::initReceivedTimestamp(const std::shared_ptr<linphone::ChatMessage> &message, bool isNew){
auto appdata = ChatEvent::AppDataManager(QString::fromStdString(message->getAppdata()));
if(!appdata.mData.contains("receivedTime")){// Already set : Do not overwrite.
if(!appdata.mData.contains("receivedTime")){// If already set : Do not overwrite.
appdata.mData["receivedTime"] = QString::number(isNew ? QDateTime::currentMSecsSinceEpoch() : message->getTime()*1000);
qDebug() << "New message received at " << QDateTime::fromMSecsSinceEpoch(appdata.mData["receivedTime"].toLongLong()).toString("yyyy/MM/dd hh:mm:ss.zzz");
qDebug() << (isNew ? "New" : "Old") << " message received at " << QDateTime::fromMSecsSinceEpoch(appdata.mData["receivedTime"].toLongLong()).toString("yyyy/MM/dd hh:mm:ss.zzz") << QDateTime::fromMSecsSinceEpoch(message->getTime()*1000).toString("yyyy/MM/dd hh:mm:ss.zzz");
message->setAppdata(Utils::appStringToCoreString(appdata.toString()));
}
return QDateTime::fromMSecsSinceEpoch(appdata.mData["receivedTime"].toLongLong());

View file

@ -1366,6 +1366,8 @@ void ChatRoomModel::onChatMessageSending(const std::shared_ptr<linphone::ChatRoo
}
void ChatRoomModel::onChatMessageSent(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog){
auto message = eventLog->getChatMessage();
if(message) ChatMessageModel::initReceivedTimestamp(message, true); // Just in case
updateLastUpdateTime();
}

View file

@ -290,6 +290,7 @@ void CoreManager::createLinphoneCore (const QString &configPath) {
setOtherPaths();
mCore->enableFriendListSubscription(true);
mCore->enableRecordAware(true);
mCore->setAccountCreatorBackend(linphone::AccountCreatorBackend::FlexiAPI);
if(mCore->getAccountCreatorUrl() == "")
mCore->setAccountCreatorUrl(Constants::DefaultFlexiAPIURL);
if( mCore->getAccountList().size() == 0)

View file

@ -10,14 +10,16 @@ Column {
id: block
property var action
readonly property alias loading: block._loading
property bool _loading: false
property bool loading: false
// ----------------------------------------------------------------------------
function start () {
block.loading = true
action()
}
function execute () {
block._loading = true
action()
}
function setText(txt){
@ -27,7 +29,7 @@ Column {
function stop (error) {
errorBlock.text = error
block._loading = false
block.loading = false
}
// ----------------------------------------------------------------------------

View file

@ -38,6 +38,7 @@ AssistantAbstractView {
action: assistantModel.activate
width: parent.width
loading: assistantModel.isProcessing
}
}
@ -49,7 +50,7 @@ AssistantAbstractView {
target: assistantModel
onActivateStatusChanged: {
requestBlock.stop(error)
requestBlock.setText(error)
if (!error.length) {
function quitToHome (window) {
window.unlockView()

View file

@ -50,6 +50,7 @@ AssistantAbstractView {
action: assistantModel.activate
width: parent.width
loading: assistantModel.isProcessing
}
}
@ -61,7 +62,7 @@ AssistantAbstractView {
target: assistantModel
onActivateStatusChanged: {
requestBlock.stop(error)
requestBlock.setText(error)
if (!error.length) {
function quitToHome (window) {
window.unlockView()

View file

@ -32,8 +32,8 @@ Item {
// ---------------------------------------------------------------------------
height: (maximized?stack.height:AssistantAbstractViewStyle.content.height)
width: (maximized?stack.width:AssistantAbstractViewStyle.content.width)
//height: (maximized?stack.height:AssistantAbstractViewStyle.content.height)
//width: (maximized?stack.width:AssistantAbstractViewStyle.content.width)
anchors.horizontalCenter: maximized || !parent? undefined : parent.horizontalCenter
anchors.verticalCenter: maximized || !parent? undefined : parent.verticalCenter
@ -86,8 +86,8 @@ Item {
anchors.top:description.bottom
anchors.topMargin:(description.visible || title.visible?AssistantAbstractViewStyle.info.spacing:0)
anchors.bottom:buttons.top
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
anchors.left: parent.left
anchors.right: parent.right
}
// ---------------------------------------------------------------------------
@ -99,7 +99,7 @@ Item {
anchors {
bottom: parent.bottom
bottomMargin:AssistantAbstractViewStyle.info.spacing
bottomMargin: AssistantAbstractViewStyle.info.spacing
horizontalCenter: parent.horizontalCenter
}

View file

@ -4,6 +4,7 @@ import Common 1.0
import Linphone 1.0
import App.Styles 1.0
import Common.Styles 1.0
// =============================================================================
@ -32,7 +33,8 @@ AssistantAbstractView {
Form {
orientation: Qt.Vertical
width: parent.width
width: FormHGroupStyle.content.maxWidth + FormHGroupStyle.spacing
anchors.horizontalCenter: parent.horizontalCenter
FormLine {
FormGroup {
@ -99,6 +101,7 @@ AssistantAbstractView {
action: assistantModel.create
width: parent.width
loading: assistantModel.isProcessing
}
}
@ -122,7 +125,7 @@ AssistantAbstractView {
onUsernameChanged: usernameError = error
onCreateStatusChanged: {
requestBlock.stop(error)
requestBlock.setText(error)
if (error.length) {
return
}

View file

@ -3,6 +3,7 @@ import QtQuick 2.7
import Common 1.0
import Linphone 1.0
import Common.Styles 1.0
// =============================================================================
AssistantAbstractView {
@ -32,8 +33,8 @@ AssistantAbstractView {
Form {
orientation: Qt.Vertical
width: parent.width
width: FormHGroupStyle.content.maxWidth + FormHGroupStyle.spacing
anchors.horizontalCenter: parent.horizontalCenter
FormLine {
FormGroup {
label: qsTr('countryLabel')
@ -98,6 +99,7 @@ AssistantAbstractView {
action: assistantModel.create
width: parent.width
loading: assistantModel.isProcessing
}
}
@ -116,7 +118,7 @@ AssistantAbstractView {
onUsernameChanged: usernameError = error
onCreateStatusChanged: {
requestBlock.stop(error)
requestBlock.setText(error)
if (error.length) {
return
}

View file

@ -7,7 +7,6 @@ import Utils 1.0
import App.Styles 1.0
// =============================================================================
Item{
AssistantAbstractView {
id: mainItem
@ -138,7 +137,7 @@ Item{
Layout.alignment: Qt.AlignCenter
text: 'OAuth2'
visible: assistantModel.isOAuth2Available()
onClicked: {requestBlock.execute(); assistantModel.requestOauth2()}
onClicked: {requestBlock.start(); assistantModel.requestOauth2()}
capitalization: Font.AllUppercase
}
Text{
@ -291,10 +290,9 @@ Item{
Layout.fillHeight: true
}
}
}
Component.onCompleted: {
Component.onCompleted: {
if( !CoreManager.isLastRemoteProvisioningGood() )
//: 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed.
requestBlock.stop(qsTr('lastProvisioningFailed'))
}
}
}

View file

@ -6,6 +6,7 @@ import Utils 1.0
import ConstantsCpp 1.0
import App.Styles 1.0
import Common.Styles 1.0
// =============================================================================
@ -31,7 +32,8 @@ AssistantAbstractView {
id: loader
source: 'UseAppSipAccountWith' + (view.usePhoneNumber ? 'PhoneNumber' : 'Username') + '.qml'
width: parent.width
width: FormHGroupStyle.content.maxWidth + FormHGroupStyle.spacing
anchors.horizontalCenter: parent.horizontalCenter
}
CheckBoxText {
@ -40,10 +42,11 @@ AssistantAbstractView {
text: qsTr('useUsernameToLogin')
visible: SettingsModel.assistantSupportsPhoneNumbers
width: UseAppSipAccountStyle.checkBox.width
anchors.left: loader.left
onClicked: {
assistantModel.reset()
requestBlock.stop('')
requestBlock.setText('')
if (!checked) {
assistantModel.setCountryCode(telephoneNumbersModel.defaultIndex)
@ -51,7 +54,7 @@ AssistantAbstractView {
}
}
Text {
anchors.right:parent.right
anchors.right: loader.right
visible: ConstantsCpp.PasswordRecoveryUrl
elide: Text.ElideRight
font.pointSize: AboutStyle.copyrightBlock.url.pointSize
@ -77,6 +80,7 @@ AssistantAbstractView {
action: assistantModel.login
width: parent.width
loading: assistantModel.isProcessing
}
}
@ -109,7 +113,7 @@ AssistantAbstractView {
}
onLoginStatusChanged: {
requestBlock.stop(error)
requestBlock.setText(error)
if (!error.length) {
var codecInfo = VideoCodecsModel.getCodecInfo('H264')
if (codecInfo.downloadUrl) {
@ -124,11 +128,11 @@ AssistantAbstractView {
onRecoverStatusChanged: {
if (!view.usePhoneNumber) {
requestBlock.stop('')
requestBlock.setText('')
return
}
requestBlock.stop(error)
requestBlock.setText(error)
if (!error.length) {
window.lockView({
descriptionText: qsTr('quitWarning')

View file

@ -6,9 +6,9 @@ import Linphone 1.0
import ConstantsCpp 1.0
import App.Styles 1.0
import Common.Styles 1.0
// =============================================================================
Item{
AssistantAbstractView {
id: mainItem
@ -22,12 +22,6 @@ Item{
title: qsTr('useOtherSipAccountTitle')
width: mainStack.currentItem.implicitWidth
height: mainStack.currentItem.implicitHeight
+ (requestBlock.implicitHeight > 0 ? requestBlock.implicitHeight : 0)
+ mainItem.decorationHeight
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
property bool showWarning : true
// ---------------------------------------------------------------------------
@ -37,12 +31,13 @@ Item{
width: currentItem.implicitWidth>0 ? currentItem.implicitWidth : currentItem.width
height: currentItem.implicitHeight>0 ? currentItem.implicitHeight : currentItem.height
initialItem: warningComponent
anchors.horizontalCenter: parent.horizontalCenter
}
Component{
id: warningComponent
Column{
spacing: UseAppSipAccountStyle.warningBlock.spacing
width: AssistantAbstractViewStyle.content.width
width: FormHGroupStyle.content.maxWidth + FormHGroupStyle.spacing
Text {
elide: Text.ElideRight
font.pointSize: UseAppSipAccountStyle.warningBlock.pointSize
@ -84,7 +79,7 @@ Item{
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
color: UseAppSipAccountStyle.warningBlock.colorModel.color
linkColor: UseAppSipAccountStyle.warningBlock.contactUrl.color
linkColor: UseAppSipAccountStyle.warningBlock.contactUrl.colorModel.color
text: '<a href="'+ConstantsCpp.ContactUrl+'">'+ConstantsCpp.ContactUrl+'</a>'
visible: ConstantsCpp.ContactUrl != ''
@ -105,7 +100,7 @@ Item{
Component {
id: formComponent
Column {
width: AssistantAbstractViewStyle.content.width
width: FormHGroupStyle.content.maxWidth + FormHGroupStyle.spacing
property bool isValid: username.text.length &&
sipDomain.text.length &&
password.text.length
@ -121,7 +116,8 @@ Item{
Form {
orientation: Qt.Vertical
width: parent.width
width: FormHGroupStyle.content.maxWidth + FormHGroupStyle.spacing
anchors.horizontalCenter: parent.horizontalCenter
FormLine {
FormGroup {
@ -180,12 +176,13 @@ Item{
width: parent.width
anchors.top: mainStack.bottom
anchors.topMargin: UseAppSipAccountStyle.warningBlock.spacing
loading: assistantModel.isProcessing
action: (function () {
if(mainItem.showWarning) {
mainItem.showWarning = false
mainStack.push(formComponent);
requestBlock.stop('')
requestBlock.setText('')
}else{
if (!assistantModel.addOtherSipAccount({
username: mainStack.currentItem.usernameText,
@ -194,9 +191,9 @@ Item{
password: mainStack.currentItem.passwordText,
transport: mainStack.currentItem.getTransport()
})) {
requestBlock.stop(qsTr('addOtherSipAccountError'))
requestBlock.setText(qsTr('addOtherSipAccountError'))
} else {
requestBlock.stop('')
requestBlock.setText('')
window.setView('Home')
}
}
@ -208,4 +205,3 @@ Item{
configFilename: 'use-other-sip-account.rc'
}
}
}

View file

@ -98,7 +98,7 @@ TabContainer {
onClicked: {
Logic.cleanLogs()
sendLogsBlock.setText('')
sendLogsBlock.stop('')
}
}
@ -106,7 +106,7 @@ TabContainer {
enabled: !sendLogsBlock.loading && SettingsModel.logsEnabled
text: qsTr('sendLogs')
onClicked: sendLogsBlock.execute()
onClicked: sendLogsBlock.start()
}
}
RequestBlock {
@ -122,7 +122,7 @@ TabContainer {
onLogsUploaded: Logic.handleLogsUploaded(url)
}
}
onVisibleChanged: sendLogsBlock.setText('')
onVisibleChanged: sendLogsBlock.stop('')
// -------------------------------------------------------------------------
// LDAP

View file

@ -13,7 +13,7 @@ QtObject {
}
property QtObject content: QtObject {
property int height: 375+60//+button bar
property int height: 375+80//+button bar
property int width: 400
}