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:
parent
b23414e999
commit
8eb328bae7
|
@ -241,10 +241,10 @@ method onNodeLogin*[T](self: Module[T], error: string) =
|
|||
quit() # quit the app
|
||||
|
||||
if error.len == 0:
|
||||
self.controller.cleanTmpData()
|
||||
self.delegate.userLoggedIn()
|
||||
if currStateObj.flowType() != FlowType.AppLogin:
|
||||
self.controller.storeIdentityImage()
|
||||
self.controller.cleanTmpData()
|
||||
else:
|
||||
self.view.setAppState(AppState.StartupState)
|
||||
if currStateObj.flowType() == FlowType.AppLogin:
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
|
@ -28,6 +28,7 @@ _MAX_WAIT_OBJ_TIMEOUT = 5000
|
|||
# The default minimum timeout to find ui object
|
||||
_MIN_WAIT_OBJ_TIMEOUT = 500
|
||||
|
||||
_SEARCH_IMAGES_PATH = "../shared/searchImages/"
|
||||
|
||||
# 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.
|
||||
|
@ -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.')
|
||||
except LookupError as 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")
|
||||
|
|
|
@ -100,3 +100,6 @@ def log(text: str):
|
|||
|
||||
def verify_screenshot(vp: str):
|
||||
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})
|
||||
|
|
|
@ -16,12 +16,11 @@ from drivers.SquishDriverVerification import *
|
|||
import time
|
||||
|
||||
class MainScreenComponents(Enum):
|
||||
STATUS_ICON = "mainWindow_statusIcon_StatusIcon_2"
|
||||
PUBLIC_CHAT_ICON = "mainWindow_public_chat_icon_StatusIcon"
|
||||
CHAT_NAVBAR_ICON = "navBarListView_Chat_navbar_StatusNavBarTabButton"
|
||||
COMMUNITY_PORTAL_BUTTON = "navBarListView_Communities_Portal_navbar_StatusNavBarTabButton"
|
||||
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"
|
||||
START_CHAT_BTN = "mainWindow_startChat"
|
||||
CHAT_LIST = "chatList_Repeater"
|
||||
|
|
|
@ -14,6 +14,7 @@ import sys
|
|||
from drivers.SquishDriver import *
|
||||
from drivers.SquishDriverVerification import *
|
||||
from common.SeedUtils import *
|
||||
from screens.StatusMainScreen import MainScreenComponents
|
||||
|
||||
|
||||
class AgreementPopUp(Enum):
|
||||
|
@ -36,7 +37,9 @@ class SignUpComponents(Enum):
|
|||
CONFIRM_PSW_AGAIN_INPUT: str = "onboarding_confirmPswAgain_Input"
|
||||
FINALIZE_PSW_BUTTON: str = "onboarding_finalise_password_button"
|
||||
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):
|
||||
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_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:
|
||||
|
||||
def __init__(self):
|
||||
|
@ -99,6 +116,7 @@ class StatusWelcomeScreen:
|
|||
def input_username(self, username: str):
|
||||
type(SignUpComponents.USERNAME_INPUT.value, username)
|
||||
click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value)
|
||||
|
||||
# There is another page with the same Next button
|
||||
click_obj_by_name(SignUpComponents.DETAILS_NEXT_BUTTON.value)
|
||||
|
||||
|
@ -153,3 +171,59 @@ class StatusWelcomeScreen:
|
|||
|
||||
# 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)
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from remotesystem import RemoteSystem
|
||||
from drivers.SquishDriverVerification import *
|
||||
|
||||
import os
|
||||
import os.path as path
|
||||
import shutil
|
||||
import distutils.dir_util
|
||||
|
||||
|
||||
def erase_directory(dir: str):
|
||||
directory = path.abspath(path.join(__file__, dir))
|
||||
if (os.path.isdir(directory)):
|
||||
|
@ -27,3 +29,14 @@ def copy_directory(src: str, dst: str):
|
|||
distutils.dir_util.copy_tree(src, dst)
|
||||
except OSError:
|
||||
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))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ def hook(context):
|
|||
context.userData = {}
|
||||
context.userData["aut_name"] = _status_desktop_app_name
|
||||
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
|
||||
def hook(context):
|
||||
|
|
|
@ -9,6 +9,10 @@ statusDesktop_mainWindow_AppMain_EmojiPopup_SearchTextInput = {"container": stat
|
|||
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_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
|
||||
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"}
|
||||
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}
|
||||
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}
|
|
@ -7,3 +7,4 @@ loginView_submitBtn = {"container": statusDesktop_mainWindow, "type": "StatusRou
|
|||
loginView_main = {"container": statusDesktop_mainWindow, "type": "LoginView", "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
|
||||
loginView_userImage = {"container": statusDesktop_mainWindow, "objectName": "loginViewUserImage", "type": "UserImage", "visible": True}
|
|
@ -17,6 +17,9 @@ onboarding_DiplayName_Input = {"container": statusDesktop_mainWindow, "objectNam
|
|||
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_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:
|
||||
import_a_seed_phrase_StatusBaseText = {"container": statusDesktop_mainWindow, "text": "Import a seed phrase", "type": "StatusBaseText", "unnamed": 1, "visible": True}
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
from objectmaphelper import *
|
||||
|
||||
from scripts.onboarding_names import *
|
||||
from scripts.login_names import *
|
||||
|
|
|
@ -44,6 +44,33 @@ def step(context, seed_phrase):
|
|||
def step(context):
|
||||
_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")
|
||||
def step(context):
|
||||
_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")
|
||||
|
||||
|
|
|
@ -135,3 +135,14 @@ Feature: Status Desktop Sign Up
|
|||
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
|
||||
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
|
||||
|
|
|
@ -8,7 +8,7 @@ from scripts.onboarding_names import *
|
|||
from sections.chat_names import *
|
||||
from sections.community_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.settings_names import *
|
||||
from sections.wallet_names import *
|
||||
|
|
|
@ -28,7 +28,6 @@ class SettingsSubsection(Enum):
|
|||
|
||||
# Main:
|
||||
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}
|
||||
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_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_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}
|
||||
|
|
|
@ -88,6 +88,7 @@ Item {
|
|||
StatusSmartIdenticon {
|
||||
anchors.left: parent.left
|
||||
id: userImage
|
||||
objectName: "welcomeScreenUserProfileImage"
|
||||
image {
|
||||
width: 86
|
||||
height: 86
|
||||
|
|
|
@ -188,6 +188,7 @@ Item {
|
|||
|
||||
UserImage {
|
||||
id: userImage
|
||||
objectName: "loginViewUserImage"
|
||||
image: root.startupStore.selectedLoginAccount.thumbnailImage
|
||||
name: root.startupStore.selectedLoginAccount.username
|
||||
colorId: root.startupStore.selectedLoginAccount.colorId
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1 @@
|
|||
ChangeProfilePicModal 1.0 ChangeProfilePicModal.qml
|
||||
BackupSeedModal 1.0 BackupSeedModal.qml
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ Item {
|
|||
|
||||
UserImage {
|
||||
id: userImage
|
||||
objectName: "myProfileModalUserImage"
|
||||
name: root.displayName
|
||||
pubkey: root.pubkey
|
||||
image: root.icon
|
||||
|
|
|
@ -8,7 +8,6 @@ FormGroup 1.0 FormGroup.qml
|
|||
GasSelector 1.0 GasSelector.qml
|
||||
GasSelectorButton 1.0 GasSelectorButton.qml
|
||||
GasValidator 1.0 GasValidator.qml
|
||||
ImageCropper 1.0 ImageCropper.qml
|
||||
Input 1.0 Input.qml
|
||||
RadioButtonSelector 1.0 RadioButtonSelector.qml
|
||||
RecipientSelector 1.0 RecipientSelector.qml
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
Address 1.0 Address.qml
|
||||
AddressRequiredValidator 1.0 AddressRequiredValidator.qml
|
||||
BalanceValidator 1.0 BalanceValidator.qml
|
||||
CropCornerRectangle 1.0 CropCornerRectangle.qml
|
||||
GlossaryEntry 1.0 GlossaryEntry.qml
|
||||
GlossaryLetter 1.0 GlossaryLetter.qml
|
||||
ImageLoader 1.0 ImageLoader.qml
|
||||
|
|
|
@ -12,6 +12,7 @@ import utils 1.0
|
|||
|
||||
Item {
|
||||
id: root
|
||||
objectName: "imageCropWorkflow"
|
||||
|
||||
property alias aspectRatio: imageCropper.aspectRatio
|
||||
property alias windowStyle: imageCropper.windowStyle
|
||||
|
@ -26,6 +27,11 @@ Item {
|
|||
fileDialog.open()
|
||||
}
|
||||
|
||||
function cropImage(imageUrl) {
|
||||
imageCropper.source = imageUrl
|
||||
imageCropperModal.open()
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
|
||||
|
@ -34,8 +40,7 @@ Item {
|
|||
nameFilters: [qsTr("Supported image formats (%1)").arg("*.jpg *.jpeg *.jfif *.webp *.png *.heif")]
|
||||
onAccepted: {
|
||||
if (fileDialog.fileUrls.length > 0) {
|
||||
imageCropper.source = fileDialog.fileUrls[0]
|
||||
imageCropperModal.open()
|
||||
cropImage(fileDialog.fileUrls[0])
|
||||
}
|
||||
}
|
||||
} // FileDialog
|
||||
|
@ -51,6 +56,7 @@ Item {
|
|||
|
||||
StatusImageCropPanel {
|
||||
id: imageCropper
|
||||
objectName: "profileImageCropper"
|
||||
|
||||
implicitHeight: root.roundedImage ? 350 : 370
|
||||
|
||||
|
@ -69,6 +75,7 @@ Item {
|
|||
|
||||
rightButtons: [
|
||||
StatusButton {
|
||||
objectName: "imageCropperAcceptButton"
|
||||
text: root.acceptButtonText
|
||||
|
||||
enabled: imageCropper.sourceSize.width > 0 && imageCropper.sourceSize.height > 0
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ StatusPopupMenu {
|
|||
}
|
||||
|
||||
StatusMenuItem {
|
||||
objectName: "userStatusViewMyProfileAction"
|
||||
text: qsTr("View My Profile")
|
||||
icon.name: "profile"
|
||||
onTriggered: {
|
||||
|
|
|
@ -6,7 +6,6 @@ CommunityIntroDialog 1.0 CommunityIntroDialog.qml
|
|||
ContactVerificationRequestPopup 1.0 ContactVerificationRequestPopup.qml
|
||||
DownloadModal 1.0 DownloadModal.qml
|
||||
DownloadPage 1.0 DownloadPage.qml
|
||||
ImageCropperModal 1.0 ImageCropperModal.qml
|
||||
InviteFriendsPopup 1.0 InviteFriendsPopup.qml
|
||||
NicknamePopup 1.0 NicknamePopup.qml
|
||||
ModalPopup 1.0 ModalPopup.qml
|
||||
|
|
Loading…
Reference in New Issue