refactor: redesign PrivateChatPopup to match new designs
This commit introduces the new design and behaviour of the modal that opens up when starting a new 1-on-1 chat. Main changes include: - New search UI/UX functionality of users and ENS resolutions - Composed view of existing contacts and contacts to be searched - Ability to add contacts from within the modal Closes: #1747
This commit is contained in:
parent
0c65551b45
commit
8977ba4931
|
@ -325,6 +325,9 @@ QtObject:
|
|||
self.messageList[id].clear(not channel.isNil and channel.chatType != ChatType.Profile)
|
||||
self.messagesCleared()
|
||||
|
||||
proc isAddedContact*(self: ChatsView, id: string): bool {.slot.} =
|
||||
result = self.status.contacts.isAdded(id)
|
||||
|
||||
proc pushMessages*(self:ChatsView, messages: var seq[Message]) =
|
||||
for msg in messages.mitems:
|
||||
self.upsertChannel(msg.chatId)
|
||||
|
@ -351,7 +354,7 @@ QtObject:
|
|||
self.newMessagePushed()
|
||||
|
||||
if not channel.muted:
|
||||
let isAddedContact = channel.chatType.isOneToOne and self.status.contacts.isAdded(channel.id)
|
||||
let isAddedContact = channel.chatType.isOneToOne and self.isAddedContact(channel.id)
|
||||
self.messageNotificationPushed(
|
||||
msg.chatId,
|
||||
escape_html(msg.text),
|
||||
|
@ -849,3 +852,4 @@ QtObject:
|
|||
self.activeCommunity.removeMember(pubKey)
|
||||
except Exception as e:
|
||||
error "Error removing user from the community", msg = e.msg
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ proc init*(self: ProfileController, account: Account) =
|
|||
self.status.events.on("contactAdded") do(e: Args):
|
||||
let contacts = self.status.contacts.getContacts()
|
||||
self.view.contacts.setContactList(contacts)
|
||||
self.view.contactsChanged()
|
||||
|
||||
self.status.events.on("contactBlocked") do(e: Args):
|
||||
let contacts = self.status.contacts.getContacts()
|
||||
|
|
|
@ -195,11 +195,14 @@ QtObject:
|
|||
self.mutedChatsListChanged()
|
||||
self.mutedContactsListChanged()
|
||||
|
||||
proc contactsChanged*(self: ProfileView) {.signal.}
|
||||
|
||||
proc getContacts*(self: ProfileView): QVariant {.slot.} =
|
||||
newQVariant(self.contacts)
|
||||
|
||||
QtProperty[QVariant] contacts:
|
||||
read = getContacts
|
||||
notify = contactsChanged
|
||||
|
||||
proc getDevices*(self: ProfileView): QVariant {.slot.} =
|
||||
newQVariant(self.devices)
|
||||
|
|
|
@ -38,7 +38,7 @@ QtObject:
|
|||
method rowCount(self: ContactList, index: QModelIndex = nil): int =
|
||||
return self.contacts.len
|
||||
|
||||
proc userName(self: ContactList, pubKey: string, defaultValue: string = ""): string {.slot.} =
|
||||
proc userName*(self: ContactList, pubKey: string, defaultValue: string = ""): string {.slot.} =
|
||||
for contact in self.contacts:
|
||||
if(contact.id != pubKey): continue
|
||||
return ens.userNameOrAlias(contact)
|
||||
|
|
|
@ -91,6 +91,9 @@ QtObject:
|
|||
weiValue = fromHex(Stuint[256], weiValue).toString()
|
||||
return status_utils.wei2Eth(weiValue, decimals)
|
||||
|
||||
proc generateAlias*(self: UtilsView, pk: string): string {.slot.} =
|
||||
result = status_accounts.generateAlias(pk)
|
||||
|
||||
proc generateIdenticon*(self: UtilsView, pk: string): string {.slot.} =
|
||||
result = status_accounts.generateIdenticon(pk)
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ Rectangle {
|
|||
property bool isHovered: false
|
||||
property var onItemChecked: (function(pubKey, itemChecked) { console.log(pubKey, itemChecked) })
|
||||
|
||||
property var onContactClicked
|
||||
|
||||
id: root
|
||||
visible: isVisible && (isContact || isUser)
|
||||
height: visible ? 64 : 0
|
||||
|
@ -85,6 +87,11 @@ Rectangle {
|
|||
hoverEnabled: root.clickable || root.showCheckbox
|
||||
onEntered: root.isHovered = true
|
||||
onExited: root.isHovered = false
|
||||
onClicked: assetCheck.clicked()
|
||||
onClicked: {
|
||||
if (typeof root.onContactClicked !== "function") {
|
||||
return assetCheck.clicked()
|
||||
}
|
||||
root.onContactClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,10 @@ ModalPopup {
|
|||
property string pubKey : "";
|
||||
property string ensUsername : "";
|
||||
|
||||
property bool loading: false;
|
||||
|
||||
function validate() {
|
||||
if (!Utils.isChatKey(chatKey.text) && !Utils.isValidETHNamePrefix(chatKey.text)) {
|
||||
validationError = "This needs to be a valid chat key or ENS username";
|
||||
validationError = qsTr("Enter a valid chat key or ENS username");
|
||||
pubKey = ""
|
||||
ensUsername.text = "";
|
||||
} else if (profileModel.profile.pubKey === chatKey.text) {
|
||||
validationError = qsTr("Can't chat with yourself");
|
||||
|
@ -27,12 +26,17 @@ ModalPopup {
|
|||
}
|
||||
|
||||
property var resolveENS: Backpressure.debounce(popup, 500, function (ensName){
|
||||
noContactsRect.visible = false
|
||||
searchResults.loading = true
|
||||
searchResults.showProfileNotFoundMessage = false
|
||||
chatsModel.resolveENS(ensName)
|
||||
loading = true
|
||||
});
|
||||
|
||||
function onKeyReleased(){
|
||||
searchResults.pubKey = ""
|
||||
if (!validate()) {
|
||||
searchResults.showProfileNotFoundMessage = false
|
||||
noContactsRect.visible = false
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -40,19 +44,27 @@ ModalPopup {
|
|||
|
||||
if (Utils.isChatKey(chatKey.text)){
|
||||
pubKey = chatKey.text;
|
||||
ensUsername.text = "";
|
||||
if (!profileModel.contacts.isAdded(pubKey)) {
|
||||
searchResults.username = utilsModel.generateAlias(pubKey)
|
||||
searchResults.userAlias = Utils.compactAddress(pubKey, 4)
|
||||
searchResults.pubKey = pubKey
|
||||
}
|
||||
noContactsRect.visible = false
|
||||
return;
|
||||
}
|
||||
|
||||
Qt.callLater(resolveENS, chatKey.text)
|
||||
}
|
||||
|
||||
function doJoin() {
|
||||
if (!validate() || pubKey.trim() === "" || validationError !== "") return;
|
||||
if(Utils.isChatKey(chatKey.text)){
|
||||
chatsModel.joinChat(pubKey, Constants.chatTypeOneToOne);
|
||||
function validateAndJoin(pk, ensName) {
|
||||
if (!validate() || pk.trim() === "" || validationError !== "") return;
|
||||
doJoin(pk, ensName)
|
||||
}
|
||||
function doJoin(pk, ensName) {
|
||||
if(Utils.isChatKey(pk)){
|
||||
chatsModel.joinChat(pk, Constants.chatTypeOneToOne);
|
||||
} else {
|
||||
chatsModel.joinChatWithENS(pubKey, chatKey.text);
|
||||
chatsModel.joinChatWithENS(pk, ensName);
|
||||
}
|
||||
|
||||
popup.close();
|
||||
|
@ -67,107 +79,111 @@ ModalPopup {
|
|||
pubKey = "";
|
||||
ensUsername.text = "";
|
||||
chatKey.forceActiveFocus(Qt.MouseFocusReason)
|
||||
noContactsRect.visible = !profileModel.contacts.list.hasAddedContacts()
|
||||
existingContacts.visible = profileModel.contacts.list.hasAddedContacts()
|
||||
noContactsRect.visible = !existingContacts.visible
|
||||
}
|
||||
|
||||
Input {
|
||||
id: chatKey
|
||||
//% "Enter ENS username or chat key"
|
||||
placeholderText: qsTrId("enter-contact-code")
|
||||
Keys.onEnterPressed: doJoin()
|
||||
Keys.onReturnPressed: doJoin()
|
||||
validationError: popup.validationError
|
||||
Keys.onEnterPressed: validateAndJoin(popup.pubKey, chatKey.text)
|
||||
Keys.onReturnPressed: validateAndJoin(popup.pubKey, chatKey.text)
|
||||
Keys.onReleased: {
|
||||
onKeyReleased();
|
||||
}
|
||||
textField.anchors.rightMargin: clearBtn.width + Style.current.padding + 2
|
||||
|
||||
Connections {
|
||||
target: chatsModel
|
||||
onEnsWasResolved: {
|
||||
if(chatKey.text == ""){
|
||||
ensUsername.text == "";
|
||||
ensUsername.text = "";
|
||||
pubKey = "";
|
||||
} else if(resolvedPubKey == ""){
|
||||
//% "User not found"
|
||||
ensUsername.text = qsTrId("user-not-found");
|
||||
pubKey = "";
|
||||
ensUsername.text = "";
|
||||
searchResults.pubKey = pubKey = "";
|
||||
searchResults.showProfileNotFoundMessage = true
|
||||
} else {
|
||||
if (profileModel.profile.pubKey === resolvedPubKey) {
|
||||
validationError = qsTr("Can't chat with yourself");
|
||||
popup.validationError = qsTr("Can't chat with yourself");
|
||||
} else {
|
||||
ensUsername.text = chatsModel.formatENSUsername(chatKey.text) + " • " + Utils.compactAddress(resolvedPubKey, 4)
|
||||
pubKey = resolvedPubKey;
|
||||
searchResults.username = chatsModel.formatENSUsername(chatKey.text)
|
||||
let userAlias = utilsModel.generateAlias(resolvedPubKey)
|
||||
userAlias = userAlias.length > 20 ? userAlias.substring(0, 19) + "..." : userAlias
|
||||
searchResults.userAlias = userAlias + " • " + Utils.compactAddress(resolvedPubKey, 4)
|
||||
searchResults.pubKey = pubKey = resolvedPubKey;
|
||||
}
|
||||
searchResults.showProfileNotFoundMessage = false
|
||||
}
|
||||
searchResults.loading = false;
|
||||
noContactsRect.visible = pubKey === "" && ensUsername.text === "" && !profileModel.contacts.list.hasAddedContacts() && !profileNotFoundMessage.visible
|
||||
}
|
||||
}
|
||||
loading = false;
|
||||
|
||||
StatusIconButton {
|
||||
id: clearBtn
|
||||
icon.name: "close-icon"
|
||||
type: "secondary"
|
||||
visible: chatKey.text !== ""
|
||||
icon.width: 14
|
||||
icon.height: 14
|
||||
width: 14
|
||||
height: 14
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
chatKey.text = ""
|
||||
chatKey.forceActiveFocus(Qt.MouseFocusReason)
|
||||
searchResults.showProfileNotFoundMessage = false
|
||||
searchResults.pubKey = popup.pubKey = ""
|
||||
noContactsRect.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: ensUsername
|
||||
id: validationErrorMessage
|
||||
text: popup.validationError
|
||||
visible: popup.validationError !== ""
|
||||
font.pixelSize: 13
|
||||
color: Style.current.danger
|
||||
anchors.top: chatKey.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
PrivateChatPopupExistingContacts {
|
||||
id: existingContacts
|
||||
anchors.topMargin: this.height > 0 ? Style.current.xlPadding : 0
|
||||
anchors.top: chatKey.bottom
|
||||
filterText: chatKey.text
|
||||
onContactClicked: function (contact) {
|
||||
doJoin(contact.pubKey, profileModel.contacts.addedContacts.userName(contact.pubKey, contact.name))
|
||||
}
|
||||
expanded: !searchResults.loading && popup.pubKey === "" && !searchResults.showProfileNotFoundMessage
|
||||
}
|
||||
|
||||
PrivateChatPopupSearchResults {
|
||||
id: searchResults
|
||||
anchors.top: existingContacts.visible ? existingContacts.bottom : chatKey.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
color: Style.current.darkGrey
|
||||
font.pixelSize: 12
|
||||
hasExistingContacts: existingContacts.visible
|
||||
loading: false
|
||||
|
||||
onResultClicked: validateAndJoin(popup.pubKey, chatKey.text)
|
||||
onAddToContactsButtonClicked: profileModel.contacts.addContact(popup.pubKey)
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.top: ensUsername.bottom
|
||||
anchors.topMargin: 90
|
||||
anchors.fill: parent
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: contactListView.contentHeight > contactListView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
clip: true
|
||||
id: contactListView
|
||||
model: profileModel.contacts.list
|
||||
delegate: Contact {
|
||||
showCheckbox: false
|
||||
pubKey: model.pubKey
|
||||
isContact: model.isContact
|
||||
isUser: false
|
||||
name: model.name
|
||||
address: model.address
|
||||
identicon: model.thumbnailImage || model.identicon
|
||||
onItemChecked: function(pubKey, itemChecked){
|
||||
chatsModel.joinChat(pubKey, Constants.chatTypeOneToOne);
|
||||
popup.close()
|
||||
}
|
||||
visible: model.isContact && (chatKey.text === "" ||
|
||||
model.name.toLowerCase().includes(chatKey.text.toLowerCase()) ||
|
||||
model.address.toLowerCase().includes(chatKey.text.toLowerCase()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NoFriendsRectangle {
|
||||
id: noContactsRect
|
||||
visible: profileModel.contacts.addedContacts.rowCount() === 0
|
||||
text: qsTr("You don’t have any contacts yet. Invite your friends to start chatting.")
|
||||
width: parent.width
|
||||
anchors.top: chatKey.bottom
|
||||
anchors.topMargin: Style.current.xlPadding * 3
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusButton {
|
||||
anchors.right: parent.right
|
||||
id: submitBtn
|
||||
state: loading ? "pending" : "default"
|
||||
text: qsTr("Start chat")
|
||||
enabled: pubKey !== ""
|
||||
onClicked : doJoin()
|
||||
}
|
||||
}
|
||||
|
||||
/*##^##
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "./"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
property string filterText: ""
|
||||
property bool expanded: true
|
||||
signal contactClicked(var contact)
|
||||
|
||||
function matchesAlias(name, filter) {
|
||||
let parts = name.split(" ")
|
||||
return parts.some(p => p.startsWith(filter))
|
||||
}
|
||||
|
||||
height: Math.min(contactListView.contentHeight, (expanded ? 320 : 192))
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: contactListView.contentHeight > contactListView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
clip: true
|
||||
id: contactListView
|
||||
model: profileModel.contacts.list
|
||||
delegate: Contact {
|
||||
showCheckbox: false
|
||||
pubKey: model.pubKey
|
||||
isContact: model.isContact
|
||||
isUser: false
|
||||
name: model.name
|
||||
address: model.address
|
||||
identicon: model.thumbnailImage || model.identicon
|
||||
visible: model.isContact && (root.filterText === "" ||
|
||||
root.matchesAlias(model.name.toLowerCase(), root.filterText.toLowerCase()) ||
|
||||
model.name.toLowerCase().includes(root.filterText.toLowerCase()) ||
|
||||
model.address.toLowerCase().includes(root.filterText.toLowerCase()))
|
||||
onContactClicked: function () {
|
||||
root.contactClicked(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "./"
|
||||
|
||||
|
||||
Item {
|
||||
id: root
|
||||
height: 64
|
||||
property bool hasExistingContacts: false
|
||||
property bool showProfileNotFoundMessage: false
|
||||
property bool loading: false
|
||||
property string username: ""
|
||||
property string userAlias: ""
|
||||
property string pubKey: ""
|
||||
|
||||
signal resultClicked(string pubKey)
|
||||
signal addToContactsButtonClicked(string pubKey)
|
||||
width: parent.width
|
||||
|
||||
StyledText {
|
||||
id: nonContactsLabel
|
||||
text: qsTr("Non contacts")
|
||||
anchors.top: parent.top
|
||||
color: Style.current.secondaryText
|
||||
font.pixelSize: 15
|
||||
visible: root.hasExistingContacts && (root.loading || root.pubKey !== "" || root.showProfileNotFoundMessage)
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.loading
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
sourceComponent: Component {
|
||||
LoadingAnimation {
|
||||
width: 18
|
||||
height: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: foundContact
|
||||
property bool hovered: false
|
||||
anchors.top: nonContactsLabel.visible ? nonContactsLabel.bottom : parent.top
|
||||
color: hovered ? Style.current.backgroundHover : Style.current.background
|
||||
radius: Style.current.radius
|
||||
width: parent.width
|
||||
height: 64
|
||||
visible: root.pubKey !== "" && !root.loading
|
||||
|
||||
StatusImageIdenticon {
|
||||
id: contactIdenticon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
source: utilsModel.generateIdenticon(root.pubKey)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: ensUsername
|
||||
font.pixelSize: 17
|
||||
color: Style.current.textColor
|
||||
anchors.top: contactIdenticon.top
|
||||
anchors.left: contactIdenticon.right
|
||||
anchors.leftMargin: Style.current.padding
|
||||
text: root.username
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: contactAlias
|
||||
font.pixelSize: 15
|
||||
color: Style.current.secondaryText
|
||||
anchors.top: ensUsername.bottom
|
||||
anchors.topMargin: 2
|
||||
anchors.left: ensUsername.left
|
||||
text: root.userAlias
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: foundContact.hovered = true
|
||||
onExited: foundContact.hovered = false
|
||||
onClicked: root.resultClicked(root.pubKey)
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: addContactBtn
|
||||
icon.name: "add-contact"
|
||||
highlightedBackgroundColor: Utils.setColorAlpha(Style.current.buttonHoveredBackgroundColor, 0.2)
|
||||
iconColor: Style.current.primary
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
width: 32
|
||||
height: 32
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !chatsModel.isAddedContact(root.pubKey) && !checkIcon.visible
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: {
|
||||
foundContact.hovered = true
|
||||
}
|
||||
onExited: {
|
||||
foundContact.hovered = false
|
||||
}
|
||||
onClicked: {
|
||||
root.addToContactsButtonClicked(root.pubKey)
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
id: checkIcon
|
||||
source: "../../../../app/img/check-2.svg"
|
||||
width: 19
|
||||
height: 19
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.smallPadding * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: foundContact.hovered && chatsModel.isAddedContact(root.pubKey)
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: profileNotFoundMessage
|
||||
color: Style.current.darkGrey
|
||||
visible: root.showProfileNotFoundMessage
|
||||
font.pixelSize: 15
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("No profile found")
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 8C17 10.7614 14.7614 13 12 13C9.23858 13 7 10.7614 7 8C7 5.23858 9.23858 3 12 3C14.7614 3 17 5.23858 17 8ZM15.5 8C15.5 9.933 13.933 11.5 12 11.5C10.067 11.5 8.5 9.933 8.5 8C8.5 6.067 10.067 4.5 12 4.5C13.933 4.5 15.5 6.067 15.5 8Z" fill="#4360DF"/>
|
||||
<path d="M12.1099 17.5007C12.5288 17.506 12.8997 17.2015 12.9345 16.7839C12.9685 16.3762 12.6689 16.0138 12.2599 16.0033C12.1735 16.0011 12.0868 16 11.9998 16C8.95932 16 6.23574 17.357 4.40167 19.4984C4.16181 19.7784 4.19319 20.1933 4.45392 20.454C4.77772 20.7779 5.31374 20.7312 5.61624 20.3874C7.17395 18.6171 9.45645 17.5 11.9998 17.5C12.0366 17.5 12.0732 17.5002 12.1099 17.5007Z" fill="#4360DF"/>
|
||||
<path d="M15.25 16.75C15.25 16.3358 15.5858 16 16 16H17.25C17.5261 16 17.75 15.7761 17.75 15.5V14.25C17.75 13.8358 18.0858 13.5 18.5 13.5C18.9142 13.5 19.25 13.8358 19.25 14.25V15.5C19.25 15.7761 19.4739 16 19.75 16H21C21.4142 16 21.75 16.3358 21.75 16.75C21.75 17.1642 21.4142 17.5 21 17.5H19.75C19.4739 17.5 19.25 17.7239 19.25 18V19.25C19.25 19.6642 18.9142 20 18.5 20C18.0858 20 17.75 19.6642 17.75 19.25V18C17.75 17.7239 17.5261 17.5 17.25 17.5H16C15.5858 17.5 15.25 17.1642 15.25 16.75Z" fill="#4360DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,3 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.416 0.376042C19.7607 0.605806 19.8538 1.07146 19.624 1.4161L7.62404 19.4161C7.4994 19.6031 7.2975 19.7243 7.0739 19.7464C6.8503 19.7686 6.62855 19.6893 6.46967 19.5304L0.46967 13.5304C0.176777 13.2375 0.176777 12.7626 0.46967 12.4698C0.762563 12.1769 1.23744 12.1769 1.53033 12.4698L6.88343 17.8229L18.376 0.584055C18.6057 0.239408 19.0714 0.146278 19.416 0.376042Z" fill="#4EBC60"/>
|
||||
</svg>
|
After Width: | Height: | Size: 540 B |
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 14C10.866 14 14 10.866 14 7C14 3.13401 10.866 0 7 0C3.13401 0 0 3.13401 0 7C0 10.866 3.13401 14 7 14ZM10.442 4.44194L7.88393 7L10.442 9.55806L9.55811 10.4419L7.00005 7.88389L4.44199 10.4419L3.55811 9.55806L6.11616 7L3.55811 4.44195L4.44199 3.55806L7.00005 6.11612L9.55811 3.55806L10.442 4.44194Z" fill="#8E8E93"/>
|
||||
</svg>
|
After Width: | Height: | Size: 469 B |
Loading…
Reference in New Issue