fix(@desktop/sugnup): Profile omage chosen during signup sould be saved for the user and squish test for the same

fixes #6861
This commit is contained in:
Khushboo Mehta 2022-08-18 09:38:47 +02:00 committed by Khushboo-dev-cpp
parent b23414e999
commit 8eb328bae7
30 changed files with 172 additions and 466 deletions

View File

@ -241,10 +241,10 @@ method onNodeLogin*[T](self: Module[T], error: string) =
quit() # quit the app quit() # quit the app
if error.len == 0: if error.len == 0:
self.controller.cleanTmpData()
self.delegate.userLoggedIn() self.delegate.userLoggedIn()
if currStateObj.flowType() != FlowType.AppLogin: if currStateObj.flowType() != FlowType.AppLogin:
self.controller.storeIdentityImage() self.controller.storeIdentityImage()
self.controller.cleanTmpData()
else: else:
self.view.setAppState(AppState.StartupState) self.view.setAppState(AppState.StartupState)
if currStateObj.flowType() == FlowType.AppLogin: if currStateObj.flowType() == FlowType.AppLogin:
@ -300,4 +300,4 @@ method onSharedKeycarModuleFlowTerminated*[T](self: Module[T], lastStepInTheCurr
self.keycardSharedModule = nil self.keycardSharedModule = nil
if lastStepInTheCurrentFlow: if lastStepInTheCurrentFlow:
self.controller.cleanTmpData() self.controller.cleanTmpData()
self.view.setCurrentStartupState(newWelcomeState(FlowType.General, nil)) self.view.setCurrentStartupState(newWelcomeState(FlowType.General, nil))

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -28,6 +28,7 @@ _MAX_WAIT_OBJ_TIMEOUT = 5000
# The default minimum timeout to find ui object # The default minimum timeout to find ui object
_MIN_WAIT_OBJ_TIMEOUT = 500 _MIN_WAIT_OBJ_TIMEOUT = 500
_SEARCH_IMAGES_PATH = "../shared/searchImages/"
# Waits for the given object is loaded, visible and enabled. # Waits for the given object is loaded, visible and enabled.
# It returns a tuple: True in case it is found. Otherwise, false. And the object itself. # It returns a tuple: True in case it is found. Otherwise, false. And the object itself.
@ -297,3 +298,7 @@ def verify_not_found(realNameVarName: str, message: str, timeoutMSec: int = 500)
test.fail(message, f'Unexpected: the object "{realNameVarName}" was found.') test.fail(message, f'Unexpected: the object "{realNameVarName}" was found.')
except LookupError as err: except LookupError as err:
test.passes(message, f'Expected: the object "{realNameVarName}" was not found. Exception: {str(err)}.') test.passes(message, f'Expected: the object "{realNameVarName}" was not found. Exception: {str(err)}.')
def grabScreenshot_and_save(obj, imageName:str, delay:int = 0):
img = object.grabScreenshot(obj, {"delay": delay})
img.save(_SEARCH_IMAGES_PATH + imageName + ".png")

View File

@ -100,3 +100,6 @@ def log(text: str):
def verify_screenshot(vp: str): def verify_screenshot(vp: str):
test.vp(vp) test.vp(vp)
def imagePresent(imageName: str, tolerant: bool = True, threshold: int = 99.5, minScale: int = 50, maxScale: int = 200, multiscale: bool = True):
test.imagePresent(imageName, {"tolerant": tolerant, "threshold": threshold, "minScale": minScale, "maxScale": maxScale, "multiscale": multiscale})

View File

@ -16,12 +16,11 @@ from drivers.SquishDriverVerification import *
import time import time
class MainScreenComponents(Enum): class MainScreenComponents(Enum):
STATUS_ICON = "mainWindow_statusIcon_StatusIcon_2"
PUBLIC_CHAT_ICON = "mainWindow_public_chat_icon_StatusIcon" PUBLIC_CHAT_ICON = "mainWindow_public_chat_icon_StatusIcon"
CHAT_NAVBAR_ICON = "navBarListView_Chat_navbar_StatusNavBarTabButton" CHAT_NAVBAR_ICON = "navBarListView_Chat_navbar_StatusNavBarTabButton"
COMMUNITY_PORTAL_BUTTON = "navBarListView_Communities_Portal_navbar_StatusNavBarTabButton" COMMUNITY_PORTAL_BUTTON = "navBarListView_Communities_Portal_navbar_StatusNavBarTabButton"
JOIN_PUBLIC_CHAT = "join_public_chat_StatusMenuItemDelegate" JOIN_PUBLIC_CHAT = "join_public_chat_StatusMenuItemDelegate"
SETTINGS_BUTTON = "settings_navbar_settings_icon_StatusIcon" SETTINGS_BUTTON = "navBarListView_Settings_navbar_StatusNavBarTabButton"
WALLET_BUTTON = "wallet_navbar_wallet_icon_StatusIcon" WALLET_BUTTON = "wallet_navbar_wallet_icon_StatusIcon"
START_CHAT_BTN = "mainWindow_startChat" START_CHAT_BTN = "mainWindow_startChat"
CHAT_LIST = "chatList_Repeater" CHAT_LIST = "chatList_Repeater"

View File

@ -14,6 +14,7 @@ import sys
from drivers.SquishDriver import * from drivers.SquishDriver import *
from drivers.SquishDriverVerification import * from drivers.SquishDriverVerification import *
from common.SeedUtils import * from common.SeedUtils import *
from screens.StatusMainScreen import MainScreenComponents
class AgreementPopUp(Enum): class AgreementPopUp(Enum):
@ -36,7 +37,9 @@ class SignUpComponents(Enum):
CONFIRM_PSW_AGAIN_INPUT: str = "onboarding_confirmPswAgain_Input" CONFIRM_PSW_AGAIN_INPUT: str = "onboarding_confirmPswAgain_Input"
FINALIZE_PSW_BUTTON: str = "onboarding_finalise_password_button" FINALIZE_PSW_BUTTON: str = "onboarding_finalise_password_button"
PASSWORD_PREFERENCE: str = "mainWindow_I_prefer_to_use_my_password_StatusBaseText" PASSWORD_PREFERENCE: str = "mainWindow_I_prefer_to_use_my_password_StatusBaseText"
PROFILE_IMAGE_CROP_WORKFLOW_ITEM: str = "mainWindow_WelcomeScreen_Image_Crop_Workflow_Item"
PROFILE_IMAGE_CROPPER_ACCEPT_BUTTON: str = "mainWindow_WelcomeScreen_Image_Cropper_Accept_Button"
WELCOME_SCREEN_USER_PROFILE_IMAGE: str = "mainWindow_WelcomeScreen_User_Profile_Image"
class SeedPhraseComponents(Enum): class SeedPhraseComponents(Enum):
IMPORT_A_SEED_TEXT: str = "import_a_seed_phrase_StatusBaseText" IMPORT_A_SEED_TEXT: str = "import_a_seed_phrase_StatusBaseText"
@ -58,6 +61,20 @@ class PasswordStrengthPossibilities(Enum):
NUMBERS_SYMBOLS_LOWER_UPPER_GOOD = "numbers_symbols_lower_upper_good" NUMBERS_SYMBOLS_LOWER_UPPER_GOOD = "numbers_symbols_lower_upper_good"
NUMBERS_SYMBOLS_LOWER_UPPER_GREAT = "numbers_symbols_lower_upper_great" NUMBERS_SYMBOLS_LOWER_UPPER_GREAT = "numbers_symbols_lower_upper_great"
class MainScreen(Enum):
SETTINGS_BUTTON = "settings_navbar_settings_icon_StatusIcon"
class UserContextStatusMenu(Enum):
USER_CONTEXT_MENU_VIEW_MY_PROFILE_BUTTON: str = "userContextMenu_ViewMyProfile_Action"
class MyProfileModal(Enum):
MY_PROFILE_MODAL_USER_IMAGE: str = "myProfileModal_UserImage"
class LoginView(Enum):
LOGIN_VIEW_USER_IMAGE: str = "loginView_userImage"
PASSWORD_INPUT = "loginView_passwordInput"
SUBMIT_BTN = "loginView_submitBtn"
class StatusWelcomeScreen: class StatusWelcomeScreen:
def __init__(self): def __init__(self):
@ -99,6 +116,7 @@ class StatusWelcomeScreen:
def input_username(self, username: str): def input_username(self, username: str):
type(SignUpComponents.USERNAME_INPUT.value, username) type(SignUpComponents.USERNAME_INPUT.value, username)
click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value) click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value)
# There is another page with the same Next button # There is another page with the same Next button
click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value) click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value)
@ -152,4 +170,60 @@ class StatusWelcomeScreen:
verify_screenshot("VP-PWStrength-numbers_symbols_lower_upper_great") verify_screenshot("VP-PWStrength-numbers_symbols_lower_upper_great")
# TODO: Get screenshots in Linux # TODO: Get screenshots in Linux
def input_profile_image(self, profileImageUrl: str):
workflow = get_obj(SignUpComponents.PROFILE_IMAGE_CROP_WORKFLOW_ITEM.value)
workflow.cropImage(profileImageUrl)
click_obj_by_name(SignUpComponents.PROFILE_IMAGE_CROPPER_ACCEPT_BUTTON.value)
def input_username_and_grab_profile_image_sreenshot(self, username: str):
type(SignUpComponents.USERNAME_INPUT.value, username)
click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value)
# take a screenshot of the profile image to compare it later with the main screen
profileIcon = wait_and_get_obj(SignUpComponents.WELCOME_SCREEN_USER_PROFILE_IMAGE.value)
grabScreenshot_and_save(profileIcon, "profiletestimage", 200)
# There is another page with the same Next button
click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value)
def input_username_profileImage_password_and_finalize_sign_up(self, profileImageUrl: str, username: str, password: str):
self.input_profile_image(profileImageUrl)
self.input_username_and_grab_profile_image_sreenshot(username)
self.input_password(password)
self.input_confirmation_password(password)
if sys.platform == "darwin":
click_obj_by_name(SignUpComponents.PASSWORD_PREFERENCE.value)
def profile_modal_image_is_updated(self):
click_obj_by_name(MainScreenComponents.PROFILE_NAVBAR_BUTTON.value)
click_obj_by_name(UserContextStatusMenu.USER_CONTEXT_MENU_VIEW_MY_PROFILE_BUTTON.value)
imagePresent("profiletestimage", True, 97, 95, 100, True)
def profile_settings_image_is_updated(self):
# first time clicking on settings button closes the my profile modal
click_obj_by_name(MainScreen.SETTINGS_BUTTON.value)
click_obj_by_name(MainScreen.SETTINGS_BUTTON.value)
imagePresent("profiletestimage", True, 97, 100, 183, True)
def grab_screenshot(self):
# take a screenshot of the profile image to compare it later with the main screen
loginUserName = wait_and_get_obj(LoginView.LOGIN_VIEW_USER_IMAGE.value)
grabScreenshot_and_save(loginUserName, "loginUserName", 200)
def profile_image_is_updated(self):
profileNavBarButton = wait_and_get_obj(MainScreenComponents.PROFILE_NAVBAR_BUTTON.value)
imagePresent("loginUserName", True, 98, 85, 100, True)
click_obj(profileNavBarButton)
imagePresent("loginUserName", True, 99, 95, 100, True)
def enter_password(self, password):
click_obj_by_name(LoginView.PASSWORD_INPUT.value)
type(LoginView.PASSWORD_INPUT.value, password)
click_obj_by_name(LoginView.SUBMIT_BTN.value)

View File

@ -1,9 +1,11 @@
from remotesystem import RemoteSystem
from drivers.SquishDriverVerification import *
import os import os
import os.path as path import os.path as path
import shutil import shutil
import distutils.dir_util import distutils.dir_util
def erase_directory(dir: str): def erase_directory(dir: str):
directory = path.abspath(path.join(__file__, dir)) directory = path.abspath(path.join(__file__, dir))
if (os.path.isdir(directory)): if (os.path.isdir(directory)):
@ -27,3 +29,14 @@ def copy_directory(src: str, dst: str):
distutils.dir_util.copy_tree(src, dst) distutils.dir_util.copy_tree(src, dst)
except OSError: except OSError:
os.remove(dst) os.remove(dst)
def delete_created_searchImage(fileName: str):
try:
remoteOS = RemoteSystem()
verify(remoteOS.deleteFile(fileName), "screenshot file was not deleted" + fileName)
verify(not remoteOS.exists(fileName), "screenshot file was not deleted" + fileName)
except Exception as e:
verify_failure("RemoteSystem error" + str(e))

View File

@ -17,6 +17,8 @@ def hook(context):
context.userData = {} context.userData = {}
context.userData["aut_name"] = _status_desktop_app_name context.userData["aut_name"] = _status_desktop_app_name
context.userData["status_data_folder_path"] = _status_data_folder_path context.userData["status_data_folder_path"] = _status_data_folder_path
context.userData["fixtures_root"] = os.path.join(os.path.dirname(__file__), "../../../fixtures/")
context.userData["search_images"] = os.path.join(os.path.dirname(__file__), "../shared/searchImages/")
@OnScenarioEnd @OnScenarioEnd
def hook(context): def hook(context):

View File

@ -9,6 +9,10 @@ statusDesktop_mainWindow_AppMain_EmojiPopup_SearchTextInput = {"container": stat
mainWindow_ScrollView = {"container": statusDesktop_mainWindow, "type": "StatusScrollView", "unnamed": 1, "visible": True} mainWindow_ScrollView = {"container": statusDesktop_mainWindow, "type": "StatusScrollView", "unnamed": 1, "visible": True}
mainWindow_ScrollView_2 = {"container": statusDesktop_mainWindow, "occurrence": 2, "type": "StatusScrollView", "unnamed": 1, "visible": True} mainWindow_ScrollView_2 = {"container": statusDesktop_mainWindow, "occurrence": 2, "type": "StatusScrollView", "unnamed": 1, "visible": True}
mainWindow_ProfileNavBarButton = {"container": statusDesktop_mainWindow, "objectName": "statusProfileNavBarTabButton", "type": "StatusNavBarTabButton", "visible": True} mainWindow_ProfileNavBarButton = {"container": statusDesktop_mainWindow, "objectName": "statusProfileNavBarTabButton", "type": "StatusNavBarTabButton", "visible": True}
settings_navbar_settings_icon_StatusIcon = {"container": mainWindow_navBarListView_ListView, "objectName": "settings-icon", "type": "StatusIcon", "visible": True}
# User Status Profile Menu
userContextMenu_ViewMyProfile_Action = {"container": statusDesktop_mainWindow_overlay, "objectName": "userStatusViewMyProfileAction", "type": "StatusMenuItemDelegate", "visible": True}
# popups # popups
close_popup_StatusFlatRoundButton = {"container": statusDesktop_mainWindow_overlay, "id": "closeButton", "type": "StatusFlatRoundButton", "unnamed": 1, "visible": True} close_popup_StatusFlatRoundButton = {"container": statusDesktop_mainWindow_overlay, "id": "closeButton", "type": "StatusFlatRoundButton", "unnamed": 1, "visible": True}
@ -18,4 +22,3 @@ mainWindow_public_chat_icon_StatusIcon = {"container": statusDesktop_mainWindow,
chatList_Repeater = {"container": statusDesktop_mainWindow, "objectName": "chatListItems", "type": "Repeater"} chatList_Repeater = {"container": statusDesktop_mainWindow, "objectName": "chatListItems", "type": "Repeater"}
mainWindow_startChat = {"checkable": True, "container": statusDesktop_mainWindow, "objectName": "startChatButton", "type": "StatusIconTabButton"} mainWindow_startChat = {"checkable": True, "container": statusDesktop_mainWindow, "objectName": "startChatButton", "type": "StatusIconTabButton"}
join_public_chat_StatusMenuItemDelegate = {"checkable": False, "container": statusDesktop_mainWindow_overlay, "enabled": True, "text": "Join public chat", "type": "StatusMenuItemDelegate", "unnamed": 1, "visible": True} join_public_chat_StatusMenuItemDelegate = {"checkable": False, "container": statusDesktop_mainWindow_overlay, "enabled": True, "text": "Join public chat", "type": "StatusMenuItemDelegate", "unnamed": 1, "visible": True}
mainWindow_statusIcon_StatusIcon_2 = {"container": statusDesktop_mainWindow, "id": "statusIcon", "source": "qrc:/StatusQ/src/assets/img/icons/public-chat.svg", "type": "StatusIcon", "unnamed": 1, "visible": True}

View File

@ -7,3 +7,4 @@ loginView_submitBtn = {"container": statusDesktop_mainWindow, "type": "StatusRou
loginView_main = {"container": statusDesktop_mainWindow, "type": "LoginView", "visible": True} loginView_main = {"container": statusDesktop_mainWindow, "type": "LoginView", "visible": True}
loginView_errMsgLabel = {"container": statusDesktop_mainWindow, "objectName": "loginPassworkInputValidationErrorText", "type": "StatusBaseText", "visible": True} loginView_errMsgLabel = {"container": statusDesktop_mainWindow, "objectName": "loginPassworkInputValidationErrorText", "type": "StatusBaseText", "visible": True}
accountsView_accountListPanel = {"container": statusDesktop_mainWindow, "type": "ListView", "visible": True} # This probably is missing an objectName accountsView_accountListPanel = {"container": statusDesktop_mainWindow, "type": "ListView", "visible": True} # This probably is missing an objectName
loginView_userImage = {"container": statusDesktop_mainWindow, "objectName": "loginViewUserImage", "type": "UserImage", "visible": True}

View File

@ -17,6 +17,9 @@ onboarding_DiplayName_Input = {"container": statusDesktop_mainWindow, "objectNam
onboarding_DetailsView_NextButton = {"container": statusDesktop_mainWindow, "objectName": "onboardingDetailsViewNextButton", "type": "StatusButton"} onboarding_DetailsView_NextButton = {"container": statusDesktop_mainWindow, "objectName": "onboardingDetailsViewNextButton", "type": "StatusButton"}
mainWindow_I_prefer_to_use_my_password_StatusBaseText = {"container": statusDesktop_mainWindow, "objectName": "touchIdIPreferToUseMyPasswordText", "type": "StatusBaseText"} mainWindow_I_prefer_to_use_my_password_StatusBaseText = {"container": statusDesktop_mainWindow, "objectName": "touchIdIPreferToUseMyPasswordText", "type": "StatusBaseText"}
mainWindow_Ok_got_it_StatusBaseText = {"container": statusDesktop_mainWindow, "type": "StatusButton", "objectName": "allowNotificationsOnboardingOkButton", "visible": True} mainWindow_Ok_got_it_StatusBaseText = {"container": statusDesktop_mainWindow, "type": "StatusButton", "objectName": "allowNotificationsOnboardingOkButton", "visible": True}
mainWindow_WelcomeScreen_User_Profile_Image = {"container": statusDesktop_mainWindow, "type": "StatusSmartIdenticon", "objectName": "welcomeScreenUserProfileImage"}
mainWindow_WelcomeScreen_Image_Crop_Workflow_Item= {"container": statusDesktop_mainWindow, "type": "Item", "objectName": "imageCropWorkflow"}
mainWindow_WelcomeScreen_Image_Cropper_Accept_Button= {"container": statusDesktop_mainWindow, "type": "StatusButton", "objectName": "imageCropperAcceptButton"}
# Seed phrase form: # Seed phrase form:
import_a_seed_phrase_StatusBaseText = {"container": statusDesktop_mainWindow, "text": "Import a seed phrase", "type": "StatusBaseText", "unnamed": 1, "visible": True} import_a_seed_phrase_StatusBaseText = {"container": statusDesktop_mainWindow, "text": "Import a seed phrase", "type": "StatusBaseText", "unnamed": 1, "visible": True}

View File

@ -3,3 +3,4 @@
from objectmaphelper import * from objectmaphelper import *
from scripts.onboarding_names import * from scripts.onboarding_names import *
from scripts.login_names import *

View File

@ -44,6 +44,33 @@ def step(context, seed_phrase):
def step(context): def step(context):
_welcomeScreen.seed_phrase_visible() _welcomeScreen.seed_phrase_visible()
@When("the user logs in with password |any|")
def step(context, password: str):
_welcomeScreen.enter_password(password)
@Then("the user is online") @Then("the user is online")
def step(context): def step(context):
_mainScreen.user_is_online() _mainScreen.user_is_online()
@When("the user signs up with profileImage |any|, username |any| and password |any|")
def step(context, profileImageUrl, username, password):
_welcomeScreen.input_username_profileImage_password_and_finalize_sign_up("file:///"+context.userData["fixtures_root"]+"images/"+profileImageUrl, username, password)
@Then("my profile modal has the updated profile image")
def step(context):
_welcomeScreen.profile_modal_image_is_updated()
@Then("the profile setting has the updated profile image")
def step(context):
_welcomeScreen.profile_settings_image_is_updated()
@When("a screenshot of the profileImage is taken")
def step(context):
_welcomeScreen.grab_screenshot()
@Then("the profile navigation bar has the updated profile image")
def step(context):
_welcomeScreen.profile_image_is_updated()
delete_created_searchImage(context.userData["search_images"] +"profiletestimage.png")
delete_created_searchImage(context.userData["search_images"]+"loginUserName.png")

View File

@ -134,4 +134,15 @@ Feature: Status Desktop Sign Up
Scenario: After Signing up the Profile state should be online Scenario: After Signing up the Profile state should be online
Given A first time user lands on the status desktop and generates new key Given A first time user lands on the status desktop and generates new key
When user signs up with username tester123 and password TesTEr16843/!@00 When user signs up with username tester123 and password TesTEr16843/!@00
Then the user is online Then the user is online
Scenario: User signs up with a profile image
Given A first time user lands on the status desktop and generates new key
When the user signs up with profileImage doggo.jpeg, username tester123 and password TesTEr16843/!@00
Then my profile modal has the updated profile image
And the profile setting has the updated profile image
When the user restarts the app
And a screenshot of the profileImage is taken
And the user logs in with password TesTEr16843/!@00
Then the profile navigation bar has the updated profile image

View File

@ -8,7 +8,7 @@ from scripts.onboarding_names import *
from sections.chat_names import * from sections.chat_names import *
from sections.community_names import * from sections.community_names import *
from sections.community_portal_names import * from sections.community_portal_names import *
from sections.login_names import * from scripts.login_names import *
from sections.search_names import * from sections.search_names import *
from sections.settings_names import * from sections.settings_names import *
from sections.wallet_names import * from sections.wallet_names import *

View File

@ -28,7 +28,6 @@ class SettingsSubsection(Enum):
# Main: # Main:
navBarListView_Settings_navbar_StatusNavBarTabButton = {"checkable": True, "container": mainWindow_navBarListView_ListView, "objectName": "Settings-navbar", "type": "StatusNavBarTabButton", "visible": True} navBarListView_Settings_navbar_StatusNavBarTabButton = {"checkable": True, "container": mainWindow_navBarListView_ListView, "objectName": "Settings-navbar", "type": "StatusNavBarTabButton", "visible": True}
settings_navbar_settings_icon_StatusIcon = {"container": navBarListView_Settings_navbar_StatusNavBarTabButton, "objectName": "settings-icon", "type": "StatusIcon", "visible": True}
settingsSave_StatusButton = {"container": statusDesktop_mainWindow, "objectName": "settingsDirtyToastMessageSaveButton", "type": "StatusButton", "visible": True} settingsSave_StatusButton = {"container": statusDesktop_mainWindow, "objectName": "settingsDirtyToastMessageSaveButton", "type": "StatusButton", "visible": True}
settings_Sidebar_ENS_Item = {"container": mainWindow_ScrollView, "objectName": "ENS usernames-MainMenu", "type": "StatusNavigationListItem"} settings_Sidebar_ENS_Item = {"container": mainWindow_ScrollView, "objectName": "ENS usernames-MainMenu", "type": "StatusNavigationListItem"}
@ -101,6 +100,9 @@ languageView_language_StatusPickerButton = {"container": languageView_language_S
languageView_language_ListView = {"container": languageView_language_StatusListPicker, "type": "ListView", "unnamed": 1} languageView_language_ListView = {"container": languageView_language_StatusListPicker, "type": "ListView", "unnamed": 1}
languageView_language_StatusInput = {"container": languageView_language_ListView, "type": "StatusInput", "unnamed": 1} languageView_language_StatusInput = {"container": languageView_language_ListView, "type": "StatusInput", "unnamed": 1}
# My Profile Modal
myProfileModal_UserImage = {"container": statusDesktop_mainWindow_overlay, "objectName": "myProfileModalUserImage", "type": "UserImage", "visible": True}
# Backup seed phrase: # Backup seed phrase:
backup_seed_phrase_popup_Acknowledgements_havePen_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "Acknowledgements_havePen", "type": "StatusCheckBox", "checkable": True, "visible": True} backup_seed_phrase_popup_Acknowledgements_havePen_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "Acknowledgements_havePen", "type": "StatusCheckBox", "checkable": True, "visible": True}
backup_seed_phrase_popup_Acknowledgements_writeDown_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "Acknowledgements_writeDown", "type": "StatusCheckBox", "checkable": True, "visible": True} backup_seed_phrase_popup_Acknowledgements_writeDown_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "Acknowledgements_writeDown", "type": "StatusCheckBox", "checkable": True, "visible": True}

View File

@ -88,6 +88,7 @@ Item {
StatusSmartIdenticon { StatusSmartIdenticon {
anchors.left: parent.left anchors.left: parent.left
id: userImage id: userImage
objectName: "welcomeScreenUserProfileImage"
image { image {
width: 86 width: 86
height: 86 height: 86

View File

@ -188,6 +188,7 @@ Item {
UserImage { UserImage {
id: userImage id: userImage
objectName: "loginViewUserImage"
image: root.startupStore.selectedLoginAccount.thumbnailImage image: root.startupStore.selectedLoginAccount.thumbnailImage
name: root.startupStore.selectedLoginAccount.username name: root.startupStore.selectedLoginAccount.username
colorId: root.startupStore.selectedLoginAccount.colorId colorId: root.startupStore.selectedLoginAccount.colorId

View File

@ -1,115 +0,0 @@
import QtQuick 2.13
import QtQuick.Dialogs 1.3
import utils 1.0
import StatusQ.Controls 0.1
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
// TODO: replace with StatusModal
ModalPopup {
id: popup
title: qsTr("Profile picture")
property var profileStore
property string selectedImage // selectedImage is for us to be able to analyze it before setting it as current
property string uploadError
property url largeImage: popup.profileStore.profileLargeImage
property bool hasIdentityImage: !!popup.profileStore.profileLargeImage
onClosed: {
destroy()
}
onSelectedImageChanged: {
if (!selectedImage) {
return
}
cropImageModal.open()
}
Item {
anchors.fill: parent
RoundedImage {
id: profilePic
source: popup.largeImage
width: 160
height: 160
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
border.width: 1
border.color: Style.current.border
onClicked: imageDialog.open()
}
StyledText {
visible: !!uploadError
text: uploadError
anchors.left: parent.left
anchors.right: parent.right
anchors.top: profilePic.bottom
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 13
wrapMode: Text.WordWrap
anchors.topMargin: 13
font.weight: Font.Thin
color: Style.current.danger
}
ImageCropperModal {
id: cropImageModal
selectedImage: popup.selectedImage
ratio: "1:1"
onCropFinished: {
popup.uploadError = popup.profileStore.uploadImage(selectedImage, aX, aY, bX, bY)
}
}
}
footer: Item {
width: parent.width
height: uploadBtn.height
StatusFlatButton {
visible: popup.hasIdentityImage
type: StatusBaseButton.Type.Danger
text: qsTr("Remove")
anchors.right: uploadBtn.left
anchors.rightMargin: Style.current.padding
anchors.bottom: parent.bottom
onClicked: {
popup.uploadError = popup.profileStore.removeImage()
}
}
StatusButton {
id: uploadBtn
text: qsTr("Upload")
anchors.right: parent.right
anchors.bottom: parent.bottom
onClicked: {
imageDialog.open()
}
FileDialog {
id: imageDialog
title: qsTr("Please choose an image")
folder: shortcuts.pictures
nameFilters: [
qsTr("Image files (*.jpg *.jpeg *.png)")
]
onAccepted: {
selectedImage = imageDialog.fileUrls[0]
}
}
}
}
}

View File

@ -1,2 +1 @@
ChangeProfilePicModal 1.0 ChangeProfilePicModal.qml
BackupSeedModal 1.0 BackupSeedModal.qml BackupSeedModal 1.0 BackupSeedModal.qml

View File

@ -1,213 +0,0 @@
import QtQuick 2.13
import "../panels"
import utils 1.0
Item {
id: root
property Image image
property alias selectorRectangle: selectorRectangle
property string ratio: ""
property var splitRatio: !!ratio ? ratio.split(":") : null
property int widthRatio: !!ratio ? parseInt(splitRatio[0]) : -1
property int heightRatio: !!ratio ? parseInt(splitRatio[1]) : -1
property bool settingCorners: false
property int draggedCorner: 0
property bool ready: false
readonly property int topLeft: 0
readonly property int topRight: 1
readonly property int bottomLeft: 2
readonly property int bottomRight: 3
width: image.width
height: image.height
Rectangle {
id: selectorRectangle
visible: false
x: 0
y: 0
border.width: 2
border.color: Style.current.orange
color: Style.current.transparent
function fitRatio(makeBigger) {
if (!!ratio) {
if ((makeBigger && selectorRectangle.width < selectorRectangle.height) || (!makeBigger && selectorRectangle.width > selectorRectangle.height)) {
selectorRectangle.width = (selectorRectangle.height/heightRatio) * widthRatio
} else {
selectorRectangle.height = (selectorRectangle.width/widthRatio) * heightRatio
}
}
}
function initialSetup() {
selectorRectangle.width = image.width
selectorRectangle.height = image.height
fitRatio()
topLeftCorner.x = 0
topLeftCorner.y = 0
topRightCorner.x = selectorRectangle.width - topRightCorner.width
topRightCorner.y = 0
bottomLeftCorner.x = 0
bottomLeftCorner.y = selectorRectangle.height - topRightCorner.height
bottomRightCorner.x = selectorRectangle.width - topRightCorner.width
bottomRightCorner.y = selectorRectangle.height - topRightCorner.height
}
function adjustRectangleSize() {
if (!selectorRectangle.visible) {
return
}
selectorRectangle.width = bottomRightCorner.x + bottomRightCorner.width - topLeftCorner.x
selectorRectangle.height = bottomRightCorner.y + bottomRightCorner.height - topLeftCorner.y
selectorRectangle.x = topLeftCorner.x
selectorRectangle.y = topLeftCorner.y
if (!!ratio) {
// FIXME with a ratio that is not 1:1, the rectangle can go out of bounds
fitRatio()
switch(draggedCorner) {
case topLeft:
selectorRectangle.x = topLeftCorner.x
selectorRectangle.y = topLeftCorner.y
break
case topRight:
selectorRectangle.x = topRightCorner.x - selectorRectangle.width + topRightCorner.width
selectorRectangle.y = topRightCorner.y
break
case bottomLeft:
selectorRectangle.x = bottomLeftCorner.x
selectorRectangle.y = bottomLeftCorner.y - selectorRectangle.height + bottomLeftCorner.height
break
case bottomRight:
selectorRectangle.x = bottomRightCorner.x - selectorRectangle.width + bottomRightCorner.width
selectorRectangle.y = bottomRightCorner.y - selectorRectangle.height + bottomRightCorner.height
break
}
}
}
Connections {
target: image
onStatusChanged: {
if (image.status === Image.Ready) {
selectorRectangle.initialSetup()
selectorRectangle.visible = true
root.ready = true
}
}
}
}
function putCorners() {
settingCorners = true
topLeftCorner.x = selectorRectangle.x
topLeftCorner.y = selectorRectangle.y
topRightCorner.x = selectorRectangle.x + selectorRectangle.width - topRightCorner.width
topRightCorner.y = selectorRectangle.y
bottomLeftCorner.x = selectorRectangle.x
bottomLeftCorner.y = selectorRectangle.y + selectorRectangle.height - topRightCorner.height
bottomRightCorner.x = selectorRectangle.x + selectorRectangle.width - topRightCorner.width
bottomRightCorner.y = selectorRectangle.y + selectorRectangle.height - topRightCorner.height
settingCorners = false
}
// Size calculations are only done on top-left and bottom-right, because the other two corners follow them
CropCornerRectangle {
id: topLeftCorner
onXChanged: {
if (settingCorners) return
if (x < 0) x = 0
if (x > topRightCorner.x - width) x = topRightCorner.x - width
bottomLeftCorner.x = x
selectorRectangle.adjustRectangleSize()
}
onYChanged: {
if (settingCorners) return
if (y < 0) y = 0
if (y > bottomRightCorner.y - height) y = bottomRightCorner.y - height
topRightCorner.y = y
selectorRectangle.adjustRectangleSize()
}
onPressed: {
draggedCorner = topLeft
}
onReleased: {
putCorners()
}
}
CropCornerRectangle {
id: topRightCorner
onXChanged: {
if (settingCorners) return
bottomRightCorner.x = x
}
onYChanged: {
if (settingCorners) return
topLeftCorner.y = y
}
onPressed: {
draggedCorner = topRight
}
onReleased: {
putCorners()
}
}
CropCornerRectangle {
id: bottomLeftCorner
onXChanged: {
if (settingCorners) return
topLeftCorner.x = x
}
onYChanged: {
if (settingCorners) return
bottomRightCorner.y = y
}
onPressed: {
draggedCorner = bottomLeft
}
onReleased: {
putCorners()
}
}
CropCornerRectangle {
id: bottomRightCorner
onXChanged: {
if (settingCorners) return
if (x < topLeftCorner.x + topLeftCorner.width) x = topLeftCorner.x + topLeftCorner.width
if (x > image.width - width) x = image.width - width
topRightCorner.x = x
selectorRectangle.adjustRectangleSize()
}
onYChanged: {
if (settingCorners) return
if (y < topRightCorner.y + topRightCorner.height) y = topRightCorner.y + topRightCorner.height
if (y > image.height - height) y = image.height - height
bottomLeftCorner.y = y
selectorRectangle.adjustRectangleSize()
}
onPressed: {
draggedCorner = bottomRight
}
onReleased: {
putCorners()
}
}
}

View File

@ -72,6 +72,7 @@ Item {
UserImage { UserImage {
id: userImage id: userImage
objectName: "myProfileModalUserImage"
name: root.displayName name: root.displayName
pubkey: root.pubkey pubkey: root.pubkey
image: root.icon image: root.icon

View File

@ -8,7 +8,6 @@ FormGroup 1.0 FormGroup.qml
GasSelector 1.0 GasSelector.qml GasSelector 1.0 GasSelector.qml
GasSelectorButton 1.0 GasSelectorButton.qml GasSelectorButton 1.0 GasSelectorButton.qml
GasValidator 1.0 GasValidator.qml GasValidator 1.0 GasValidator.qml
ImageCropper 1.0 ImageCropper.qml
Input 1.0 Input.qml Input 1.0 Input.qml
RadioButtonSelector 1.0 RadioButtonSelector.qml RadioButtonSelector 1.0 RadioButtonSelector.qml
RecipientSelector 1.0 RecipientSelector.qml RecipientSelector 1.0 RecipientSelector.qml

View File

@ -1,29 +0,0 @@
import QtQuick 2.13
import utils 1.0
Rectangle {
signal released()
signal pressed()
id: root
width: 25
height: 25
border.width: 2
border.color: Style.current.orange
color: Utils.setColorAlpha(Style.current.orange, 0.5)
Drag.active: dragArea.drag.active
MouseArea {
id: dragArea
property int oldX
property int oldY
anchors.fill: parent
drag.target: parent
cursorShape: Qt.PointingHandCursor
onReleased: root.released()
onPressed: root.pressed()
}
}

View File

@ -1,7 +1,6 @@
Address 1.0 Address.qml Address 1.0 Address.qml
AddressRequiredValidator 1.0 AddressRequiredValidator.qml AddressRequiredValidator 1.0 AddressRequiredValidator.qml
BalanceValidator 1.0 BalanceValidator.qml BalanceValidator 1.0 BalanceValidator.qml
CropCornerRectangle 1.0 CropCornerRectangle.qml
GlossaryEntry 1.0 GlossaryEntry.qml GlossaryEntry 1.0 GlossaryEntry.qml
GlossaryLetter 1.0 GlossaryLetter.qml GlossaryLetter 1.0 GlossaryLetter.qml
ImageLoader 1.0 ImageLoader.qml ImageLoader 1.0 ImageLoader.qml

View File

@ -12,6 +12,7 @@ import utils 1.0
Item { Item {
id: root id: root
objectName: "imageCropWorkflow"
property alias aspectRatio: imageCropper.aspectRatio property alias aspectRatio: imageCropper.aspectRatio
property alias windowStyle: imageCropper.windowStyle property alias windowStyle: imageCropper.windowStyle
@ -26,6 +27,11 @@ Item {
fileDialog.open() fileDialog.open()
} }
function cropImage(imageUrl) {
imageCropper.source = imageUrl
imageCropperModal.open()
}
FileDialog { FileDialog {
id: fileDialog id: fileDialog
@ -34,8 +40,7 @@ Item {
nameFilters: [qsTr("Supported image formats (%1)").arg("*.jpg *.jpeg *.jfif *.webp *.png *.heif")] nameFilters: [qsTr("Supported image formats (%1)").arg("*.jpg *.jpeg *.jfif *.webp *.png *.heif")]
onAccepted: { onAccepted: {
if (fileDialog.fileUrls.length > 0) { if (fileDialog.fileUrls.length > 0) {
imageCropper.source = fileDialog.fileUrls[0] cropImage(fileDialog.fileUrls[0])
imageCropperModal.open()
} }
} }
} // FileDialog } // FileDialog
@ -51,15 +56,16 @@ Item {
StatusImageCropPanel { StatusImageCropPanel {
id: imageCropper id: imageCropper
objectName: "profileImageCropper"
implicitHeight: root.roundedImage ? 350 : 370 implicitHeight: root.roundedImage ? 350 : 370
anchors { anchors {
fill: parent fill: parent
leftMargin: Style.current.bigPadding + Style.current.halfPadding / 2 leftMargin: Style.current.bigPadding + Style.current.halfPadding / 2
rightMargin: Style.current.bigPadding + Style.current.halfPadding / 2 rightMargin: Style.current.bigPadding + Style.current.halfPadding / 2
topMargin: Style.current.bigPadding topMargin: Style.current.bigPadding
bottomMargin: Style.current.bigPadding bottomMargin: Style.current.bigPadding
} }
margins: root.roundedImage ? 10 : 20 margins: root.roundedImage ? 10 : 20
@ -69,6 +75,7 @@ Item {
rightButtons: [ rightButtons: [
StatusButton { StatusButton {
objectName: "imageCropperAcceptButton"
text: root.acceptButtonText text: root.acceptButtonText
enabled: imageCropper.sourceSize.width > 0 && imageCropper.sourceSize.height > 0 enabled: imageCropper.sourceSize.width > 0 && imageCropper.sourceSize.height > 0

View File

@ -1,88 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import utils 1.0
import StatusQ.Controls 0.1
import "../controls"
import "."
// TODO: replace with StatusModal
ModalPopup {
property string selectedImage
property string ratio: "1:1"
property int aX: 0
property int aY: 0
property int bX: 0
property int bY: 0
signal cropFinished(aX: int, aY: int, bX: int, bY: int)
id: cropImageModal
width: image.width + 50
height: image.height + 170
title: qsTr("Crop your image (optional)")
Image {
id: image
width: 400
source: cropImageModal.selectedImage
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
fillMode: Image.PreserveAspectFit
}
ImageCropper {
id: imageCropper
x: image.x
y: image.y
image: image
ratio: cropImageModal.ratio
onReadyChanged: {
if (ready) {
// cropImageModal.calculateCrop()
cropImageModal.aX = Qt.binding(function() {
const aXPercent = imageCropper.selectorRectangle.x / image.width
return Math.round(aXPercent * image.sourceSize.width)
})
cropImageModal.aY = Qt.binding(function() {
const aYPercent = imageCropper.selectorRectangle.y / image.height
return Math.round(aYPercent * image.sourceSize.height)
})
cropImageModal.bX = Qt.binding(function() {
const bXPercent = (imageCropper.selectorRectangle.x + imageCropper.selectorRectangle.width) / image.width
return Math.round(bXPercent * image.sourceSize.width)
})
cropImageModal.bY = Qt.binding(function() {
const bYPercent = (imageCropper.selectorRectangle.y + imageCropper.selectorRectangle.height) / image.height
return Math.round(bYPercent * image.sourceSize.height)
})
}
}
}
footer: StatusButton {
id: doneBtn
text: qsTr("Finish")
anchors.right: parent.right
anchors.bottom: parent.bottom
onClicked: {
const aXPercent = imageCropper.selectorRectangle.x / image.width
const aYPercent = imageCropper.selectorRectangle.y / image.height
const bXPercent = (imageCropper.selectorRectangle.x + imageCropper.selectorRectangle.width) / image.width
const bYPercent = (imageCropper.selectorRectangle.y + imageCropper.selectorRectangle.height) / image.height
const aX = Math.round(aXPercent * image.sourceSize.width)
const aY = Math.round(aYPercent * image.sourceSize.height)
const bX = Math.round(bXPercent * image.sourceSize.width)
const bY = Math.round(bYPercent * image.sourceSize.height)
cropImageModal.cropFinished(aX, aY, bX, bY)
cropImageModal.close()
}
}
}

View File

@ -29,6 +29,7 @@ StatusPopupMenu {
} }
StatusMenuItem { StatusMenuItem {
objectName: "userStatusViewMyProfileAction"
text: qsTr("View My Profile") text: qsTr("View My Profile")
icon.name: "profile" icon.name: "profile"
onTriggered: { onTriggered: {

View File

@ -6,7 +6,6 @@ CommunityIntroDialog 1.0 CommunityIntroDialog.qml
ContactVerificationRequestPopup 1.0 ContactVerificationRequestPopup.qml ContactVerificationRequestPopup 1.0 ContactVerificationRequestPopup.qml
DownloadModal 1.0 DownloadModal.qml DownloadModal 1.0 DownloadModal.qml
DownloadPage 1.0 DownloadPage.qml DownloadPage 1.0 DownloadPage.qml
ImageCropperModal 1.0 ImageCropperModal.qml
InviteFriendsPopup 1.0 InviteFriendsPopup.qml InviteFriendsPopup 1.0 InviteFriendsPopup.qml
NicknamePopup 1.0 NicknamePopup.qml NicknamePopup 1.0 NicknamePopup.qml
ModalPopup 1.0 ModalPopup.qml ModalPopup 1.0 ModalPopup.qml