Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contacts Implementation #184

Merged
merged 37 commits into from
Aug 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
89930bb
Initial 'MPW Contacts' implementation
JSKitty Aug 20, 2023
8cc62ff
Add new Receive dialog + input validator
JSKitty Aug 20, 2023
5a50b9b
Add URI-based Contact Requests
JSKitty Aug 21, 2023
c8b8f6e
Add Send QR Contacts Integration
JSKitty Aug 21, 2023
724462d
Add JSDoc param
JSKitty Aug 21, 2023
53139e0
Add Unencrypted wallet safeguards
JSKitty Aug 21, 2023
0a398ec
Further safeguards
JSKitty Aug 21, 2023
9090e12
Improve Scanner decoder
JSKitty Aug 21, 2023
074315e
Final (hopefully) decoding correction
JSKitty Aug 21, 2023
3eae422
Add dedicated Contact Scanner
JSKitty Aug 21, 2023
863239f
Add re-rendering + safeguards
JSKitty Aug 21, 2023
bfad291
Prettier
JSKitty Aug 21, 2023
cd4a47d
Add ability to edit Contact Name
JSKitty Aug 21, 2023
2b5bf36
Add Avatars + Avatar editing
JSKitty Aug 21, 2023
19512dc
[HTML+JS] confirmModal purple theme support
BreadJS Aug 23, 2023
8dd41bd
[HTML+CSS+JS] New contacts design
BreadJS Aug 23, 2023
cefec7b
Prettier
BreadJS Aug 23, 2023
fdd09ba
Fix Contact selector + add Back button
JSKitty Aug 23, 2023
88bc868
Add Delete confirmation + shorten XPubs
JSKitty Aug 23, 2023
38a2f02
Fix Confirm Popup weirdness
JSKitty Aug 23, 2023
8c31257
Add partial Activity integration
JSKitty Aug 23, 2023
1f01156
Prevent adding yourself as a Contact
JSKitty Aug 23, 2023
5c7ca01
Safeguard Contact selection from unsaved wallets
JSKitty Aug 23, 2023
6eaa2da
Check validity of Payment Prompts
JSKitty Aug 23, 2023
51dbfb8
Drop max name to 32 char, improve Edit safety
JSKitty Aug 23, 2023
dca47e2
Fix Avatar selection + further HTML sanitisation
JSKitty Aug 23, 2023
a51a00c
Add secure URI construction, encoding and copying
JSKitty Aug 23, 2023
2b66437
Encode and Decode URI names in HEX
JSKitty Aug 23, 2023
56c5b36
Wrap long names
JSKitty Aug 23, 2023
4b728be
Add further Name safety-rails
JSKitty Aug 23, 2023
8fe4f8c
Check manual Contact entries for duplicates
JSKitty Aug 23, 2023
243ab4e
Merge branch 'master' into mpw-contacts
JSKitty Aug 24, 2023
b9f1236
Standardise XPub validation
JSKitty Aug 24, 2023
7c8dac4
Move P2PKH address checks to Standardised checker
JSKitty Aug 24, 2023
c8390a7
Full i18n system integration
JSKitty Aug 24, 2023
3eace80
Remove duplicate i18n key
JSKitty Aug 24, 2023
4f1fb7b
Fix Receive Toggle i18n rendering
JSKitty Aug 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions assets/style/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2396,6 +2396,10 @@ a {
color:#fff;
}

.exportKeysModalColor h3 {
color:#d5adff!important;
}

.dcWallet-privateKeyDiv {
display: flex;
width: 100%;
Expand Down Expand Up @@ -3434,4 +3438,83 @@ select.form-control option {
.sliderDisplay {
display: none;
}
}

@media (min-width: 768px) {
.max-w-450 {
max-width: 450px;
}
}

.contactsList .contactItem {
transition: all .25s ease-in-out;
}

.contactsList .contactItem:hover {
background-color:#0000001c!important;
}

.contactsList .contactItem:nth-child(odd) {
background-color:#00000017
}

.contactsList .contactItem:nth-child(even) {
background-color:#00000033
}

.contactsList .addContact .contactName input,
.contactsList .addContact .contactAddr input,
.contactsList .addContactBtn,
.contactsList .qrContactBtn {
color: #ffffff;
background-color: #f2f2f233;
border: 1px solid #f2f2f224;
}

.contactsList .addContact .contactName input::placeholder,
.contactsList .addContact .contactAddr input::placeholder {
color: #dddddd;
}

.contactsList .addContact .contactName input {
border-top-right-radius:0px;
border-bottom-right-radius:0px;
}

.contactsList .addContact .contactAddr input {
border-left:none;
border-right:none;
border-radius:0px;
}

.contactsList .addContactBtn {
cursor: pointer;
border-radius: 0px;
padding: 0px 10px;
font-weight: bold;
width: fit-content;
height: 43px;
display: flex;
align-items: center;
transition: all .125s ease-in-out;
border-right:none;
}

.contactsList .qrContactBtn {
cursor: pointer;
border-radius: 7px;
padding: 0px 10px;
font-weight: bold;
width: fit-content;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
height: 43px;
display: flex;
align-items: center;
transition: all .125s ease-in-out;
}

.contactsList .addContactBtn:hover,
.contactsList .qrContactBtn:hover {
background-color: #f2f2f24d;
}
44 changes: 36 additions & 8 deletions index.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,23 @@
<!-- // NAVBAR -->

<!-- // QR MODAL -->
<div class="modal fade" id="qrModal" tabindex="-1" role="dialog" aria-labelledby="qrModalLabel" aria-hidden="true">
<div class="modal fade" style="width: 375px;" id="qrModal" tabindex="-1" role="dialog" aria-labelledby="qrModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content exportKeysModalColor">
<div class="modal-header">
<h5 class="modal-title" id="qrModalLabel"><span data-i18n="address" >Address</span> QR</h5>
<h5 class="modal-title" data-i18n="receive" id="qrModalLabel">Receive</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<i class="fa-solid fa-xmark closeCross"></i>
</button>
</div>
<div class="modal-body center-text">
<code id="ModalQRLabel" class="wallet-code"></code>
<div id="ModalQR" style="padding: 17px 10px 10px 10px;" class="auto-fit"></div>
<center>
<div id="ModalQRReceiveTypeBtn" onclick="MPW.guiToggleReceiveType()" style="cursor: pointer; border: 0px; border-radius: 7px; padding: 6px 10px; margin: 0px 1px; background: linear-gradient(183deg, #9621ff9c, #7d21ffc7); color: #fff; font-weight: bold; width: fit-content; margin: 20px 0px;">
Change to Address
JSKitty marked this conversation as resolved.
Show resolved Hide resolved
</div>
</center>
</div>
</div>
</div>
Expand Down Expand Up @@ -130,7 +135,7 @@ <h5 data-i18n="balanceBreakdown" class="modal-title" id="walletBreakdownModalLab

<br />
<!-- // MNEMONIC MODAL -->
<div class="modal fade" id="mnemonicModal" tabindex="-1" role="dialog" aria-labelledby="qrModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal fade" id="mnemonicModal" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body center-text">
Expand Down Expand Up @@ -166,8 +171,8 @@ <h5 data-i18n="balanceBreakdown" class="modal-title" id="walletBreakdownModalLab
<br />
<!-- // CONFIRM MODAL -->
<div class="modal" id="confirmModal" tabindex="-1" style="z-index: 2000; background-color: #00000063;" role="dialog" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered max-w-600" role="document">
<div class="modal-content">
<div id="confirmModalDialog" class="modal-dialog modal-dialog-centered max-w-600" role="document">
<div id="confirmModalMain" class="modal-content">
<div class="modal-header" id="confirmModalHeader">
<h3 class="modal-title" id="confirmModalTitle" style="text-align: center; width: 100%; color: #8e21ff;"></h3>
</div>
Expand Down Expand Up @@ -411,13 +416,32 @@ <h3 class="modal-title" id="redeemCodeModalTitle" style="text-align: center; wid
</div>
<div class="modal-footer" hidden="true" id="redeemCodeModalButtons">
<button type="button" onclick="MPW.promoConfirm()" id="redeemCodeModalConfirmButton" class="pivx-button-big" style="float: right;">Redeem</button>
<button type="button" data-dismiss="modal" aria-label="Close" class="pivx-button-big" style="float: right; opacity: 0.7;">Close</button>
<button type="button" data-dismiss="modal" aria-label="Close" data-i18n="popupClose" class="pivx-button-big" style="float: right; opacity: 0.7;">Close</button>
</div>
</div>
</div>
</div>
<!-- // Redeem Code (PIVX Promos) -->

<!-- Contacts Modal -->
<div class="modal" id="contactsModal" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered max-w-450" role="document">
<div class="modal-content exportKeysModalColor">
<div class="modal-header" id="contactsModalHeader">
<h3 class="modal-title" id="contactsModalTitle" data-i18n="contacts" style="text-align: center; width: 100%; color: #d5adff;">Contacts</h3>
</div>
<div class="modal-body px-0">
<div id="contactsList" class="contactsList">
</div>
</div>
<div class="modal-footer" hidden="true">
<button type="button" data-dismiss="modal" aria-label="Close" class="pivx-button-big" data-i18n="popupClose" style="color:#fff; float: right; opacity: 0.8;">Close</button>
</div>
</div>
</div>
</div>
<!-- // Contacts Modal -->

<!-- WALLET FEATURES -->
<div id="guiWallet" style="display: none;">
<div class="row p-0">
Expand Down Expand Up @@ -445,6 +469,9 @@ <h3 class="noselect balance-title">
<a class="dropdown-item ptr" onclick="MPW.openExplorer()">
<i class="fa-solid fa-magnifying-glass"></i> <span data-i18n="viewOnExplorer">View on Explorer</span>
</a>
<a class="dropdown-item ptr" onclick="MPW.guiRenderContacts()" data-toggle="modal" data-target="#contactsModal">
<i class="fa-solid fa-address-book"></i> <span data-i18n="contactsBook">Contacts</span>
</a>
<a id="guiExportWalletItem" class="dropdown-item ptr" data-toggle="modal" data-target="#exportPrivateKeysModal" data-backdrop="static" data-keyboard="false" onclick="MPW.toggleExportUI()">
<i class="fas fa-key"></i> <span data-i18n="export">Export</span>
</a>
Expand Down Expand Up @@ -481,7 +508,7 @@ <h3 class="noselect balance-title">
</div>

<div class="col-6 d-flex" style="justify-content: flex-end;">
<div class="dcWallet-sDeposit" data-toggle="modal" data-target="#qrModal">
<div class="dcWallet-sDeposit" onclick="MPW.guiRenderCurrentReceiveModal()" data-toggle="modal" data-target="#qrModal">
<i class="fas fa-arrow-down"></i>
</div>
</div>
Expand Down Expand Up @@ -547,9 +574,10 @@ <h3 data-i18n="viewPrivateKey">View Private Key?</h3>
<label data-i18n="address">Address</label><br />

<div class="input-group mb-3">
<input class="btn-group-input" data-i18n="receivingAddress" style="font-family: monospace;" type="text" id="address1s" placeholder="Receiving address" autocomplete="nope" />
<input class="btn-group-input" data-i18n="receivingAddress" oninput='MPW.guiCheckRecipientInput(event)' style="font-family: monospace;" type="text" id="address1s" placeholder="Receiving address" autocomplete="nope" />
<div class="input-group-append">
<span class="input-group-text ptr" onclick="MPW.openSendQRScanner()"><i class="fa-solid fa-qrcode fa-2xl"></i></span>
<span class="input-group-text ptr" onclick="MPW.guiSelectContact(MPW.doms.domAddress1s)"><i class="fa-solid fa-address-book fa-2xl"></i></span>
</div>
</div>

Expand Down
44 changes: 44 additions & 0 deletions locale/de/translation.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,37 @@ export const de_translation = {
paymentRequestMessage: 'Beschreibung (vom Händler)', //Description (from the merchant)
send: 'Senden', //Send

// Contacts System
receive: '', //Receive
contacts: '', //Contacts
name: '', //Name
username: '', //Username
addressOrXPub: '', //Address or XPub
back: '', //Back
chooseAContact: '', //Choose a Contact
createContact: '', //Create Contact
encryptFirstForContacts: '', //Once you hit "{button}" in the Dashboard, you can create a Contact to make receiving PIV easier!
shareContactURL: '', //Share Contact URL
setupYourContact: '', //Setup your Contact
receiveWithContact: '', //Receive using a simple username-based Contact
onlyShareContactPrivately: '', //<b>Only</b> share your Contact with trusted people (family, friends)

/* Context: The "Change to" is used in-app with one of the Three options below it, i.e: "Change to Contact" */
changeTo: '', //Change to
contact: '', //Contact
xpub: '', //XPub

addContactTitle: '', //Add {strName} to Contacts
addContactSubtext: '', //Once added you\'ll be able to send transactions to {strName} by their name (either typing, or clicking), no more addresses, nice \'n easy.
addContactWarning: '', //Ensure that this is the real "{strName}", do not accept Contact requests from unknown sources!

editContactTitle: '', //Change "{strName}" Contact
newName: '', //New Name

removeContactTitle: '', //Remove {strName}?
removeContactSubtext: '', //Are you sure you wish to remove {strName} from your Contacts?
removeContactNote: '', //You can add them again any time in the future.

// Export
privateKey: 'Privater Schlüssel', //Private Key
viewPrivateKey: 'Zeige privaten Schlüssel', //View Private Key?
Expand Down Expand Up @@ -318,6 +349,19 @@ export const de_translation = {
MN_COLLAT_NOT_SUITABLE: 'Dies ist keine gültige UTXO für eine Masternode', //This is not a suitable UTXO for a Masternode
MN_CANT_CONNECT: 'Keine Verbindung zum RPC-Knoten möglich!', //Unable to connect to RPC node!

/* Contacts System Alerts */
CONTACTS_ENCRYPT_FIRST: '', //You need to hit "{button}" before you can use Contacts!
CONTACTS_NAME_REQUIRED: '', //A name is required!
CONTACTS_NAME_TOO_LONG: '', //That name is too long!
CONTACTS_CANNOT_ADD_YOURSELF: '', //You cannot add yourself as a Contact!
CONTACTS_ALREADY_EXISTS: '', //<b>Contact already exists!</b><br>You already saved this contact
CONTACTS_NAME_ALREADY_EXISTS: '', //<b>Contact name already exists!</b><br>This could potentially be a phishing attempt, beware!
CONTACTS_EDIT_NAME_ALREADY_EXISTS: '', //<b>Contact already exists!</b><br>A contact is already called "{strNewName}"!
CONTACTS_KEY_ALREADY_EXISTS: '', //<b>Contact already exists, but under a different name!</b><br>You have {newName} saved as <b>{oldName}</b> in your contacts
CONTACTS_NOT_A_CONTACT_QR: '', //This isn\'t a Contact QR!
CONTACTS_ADDED: '', //<b>New Contact added!</b><br>{strName} has been added, hurray!
CONTACTS_YOU_HAVE_NONE: '', //You have no contacts!

PROPOSAL_FINALISED: 'Antrag finalisiert!', //Proposal finalized!
PROPOSAL_UNCONFIRMED: 'Der Antrag wurde noch nicht bestätigt.', //The proposal hasn\'t been confirmed yet.
PROPOSAL_EXPIRED: 'Der Antrag ist ausgelaufen. Erstelle einen neuen.', //The proposal has expired. Create a new one.
Expand Down
55 changes: 55 additions & 0 deletions locale/en/translation.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,42 @@ export const en_translation = {
paymentRequestMessage: 'Description (from the merchant)', //
send: 'Send', //

// Contacts System
receive: 'Receive', //
contacts: 'Contacts', //
name: 'Name', //
username: 'Username', //
addressOrXPub: 'Address or XPub', //
back: 'Back', //
chooseAContact: 'Choose a Contact', //
createContact: 'Create Contact', //
encryptFirstForContacts:
'Once you hit "{button}" in the Dashboard, you can create a Contact to make receiving PIV easier!', //
shareContactURL: 'Share Contact URL', //
setupYourContact: 'Setup your Contact', //
receiveWithContact: 'Receive using a simple username-based Contact', //
onlyShareContactPrivately:
'<b>Only</b> share your Contact with trusted people (family, friends)', //

/* Context: The "Change to" is used in-app with one of the Three options below it, i.e: "Change to Contact" */
changeTo: 'Change to', //
contact: 'Contact', //
xpub: 'XPub', //

addContactTitle: 'Add {strName} to Contacts', //
addContactSubtext:
"Once added you'll be able to send transactions to {strName} by their name (either typing, or clicking), no more addresses, nice 'n easy.", //
addContactWarning:
'Ensure that this is the real "{strName}", do not accept Contact requests from unknown sources!', //

editContactTitle: 'Change "{strName}" Contact', //
newName: 'New Name', //

removeContactTitle: 'Remove {strName}?', //
removeContactSubtext:
'Are you sure you wish to remove {strName} from your Contacts?', //
removeContactNote: 'You can add them again any time in the future.', //

// Export
privateKey: 'Private Key', //
viewPrivateKey: 'View Private Key?', //
Expand Down Expand Up @@ -308,6 +344,25 @@ export const en_translation = {
MN_COLLAT_NOT_SUITABLE: 'This is not a suitable UTXO for a Masternode',
MN_CANT_CONNECT: 'Unable to connect to RPC node!',

/* Contacts System Alerts */
CONTACTS_ENCRYPT_FIRST:
'You need to hit "{button}" before you can use Contacts!',
CONTACTS_NAME_REQUIRED: 'A name is required!',
CONTACTS_NAME_TOO_LONG: 'That name is too long!',
CONTACTS_CANNOT_ADD_YOURSELF: 'You cannot add yourself as a Contact!',
CONTACTS_ALREADY_EXISTS:
'<b>Contact already exists!</b><br>You already saved this contact',
CONTACTS_NAME_ALREADY_EXISTS:
'<b>Contact name already exists!</b><br>This could potentially be a phishing attempt, beware!',
CONTACTS_EDIT_NAME_ALREADY_EXISTS:
'<b>Contact already exists!</b><br>A contact is already called "{strNewName}"!',
CONTACTS_KEY_ALREADY_EXISTS:
'<b>Contact already exists, but under a different name!</b><br>You have {newName} saved as <b>{oldName}</b> in your contacts',
CONTACTS_NOT_A_CONTACT_QR: "This isn't a Contact QR!",
CONTACTS_ADDED:
'<b>New Contact added!</b><br>{strName} has been added, hurray!',
CONTACTS_YOU_HAVE_NONE: 'You have no contacts!',

PROPOSAL_FINALISED: 'Proposal Launched!',
PROPOSAL_UNCONFIRMED: "The proposal hasn't confirmed yet",
PROPOSAL_EXPIRED: 'The proposal has expired. Create a new one.',
Expand Down
Loading
Loading