Merge pull request #231 from cypherstack/desktop

Desktop
This commit is contained in:
Diego Salazar 2022-11-22 17:39:24 -07:00 committed by GitHub
commit c667276f7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
131 changed files with 11239 additions and 4003 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
assets/images/glasses.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

3
assets/svg/lock-open.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 1.75C5.79141 1.75 4.8125 2.72945 4.8125 3.9375V5.25H11.375C12.3402 5.25 13.125 6.03477 13.125 7V12.25C13.125 13.2152 12.3402 14 11.375 14H2.625C1.6584 14 0.875 13.2152 0.875 12.25V7C0.875 6.03477 1.6584 5.25 2.625 5.25H3.0625V3.9375C3.0625 1.76285 4.82617 0 7 0C8.57227 0 9.92578 0.921211 10.5574 2.24957C10.7652 2.68598 10.5793 3.20742 10.1199 3.41523C9.68242 3.62305 9.18476 3.43711 8.97695 2.99961C8.62422 2.25941 7.87227 1.75 7 1.75ZM7.875 10.5C8.35898 10.5 8.75 10.109 8.75 9.625C8.75 9.14102 8.35898 8.75 7.875 8.75H6.125C5.64102 8.75 5.25 9.14102 5.25 9.625C5.25 10.109 5.64102 10.5 6.125 10.5H7.875Z" fill="#0056D2"/>
</svg>

After

Width:  |  Height:  |  Size: 741 B

View file

@ -0,0 +1,28 @@
<svg width="200" height="162" viewBox="0 0 200 162" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_518_22068)">
<rect width="200" height="162" rx="8" fill="url(#paint0_linear_518_22068)"/>
<rect x="10" y="10" width="180" height="20" rx="2" fill="#C2DAE2"/>
<rect x="16" y="16" width="106" height="8" rx="1" fill="#227386"/>
<rect x="10" y="40" width="180" height="20" rx="2" fill="#FEFEFE"/>
<rect x="16" y="46" width="106" height="8" rx="1" fill="#BDD5DB"/>
<rect x="10" y="62" width="180" height="20" rx="2" fill="#FEFEFE"/>
<rect x="16" y="68" width="106" height="8" rx="1" fill="#BDD5DB"/>
<rect x="10" y="84" width="180" height="20" rx="2" fill="#FEFEFE"/>
<rect x="16" y="90" width="106" height="8" rx="1" fill="#BDD5DB"/>
<rect x="10" y="106" width="180" height="20" rx="2" fill="#FEFEFE"/>
<rect x="16" y="112" width="106" height="8" rx="1" fill="#BDD5DB"/>
<rect x="10" y="128" width="180" height="20" rx="2" fill="#FEFEFE"/>
<rect x="16" y="134" width="106" height="8" rx="1" fill="#BDD5DB"/>
<rect x="10" y="150" width="180" height="20" rx="2" fill="#FEFEFE"/>
<rect x="16" y="156" width="106" height="8" rx="1" fill="#BDD5DB"/>
</g>
<defs>
<linearGradient id="paint0_linear_518_22068" x1="100" y1="0" x2="100" y2="162" gradientUnits="userSpaceOnUse">
<stop stop-color="#F3F7FA"/>
<stop offset="1" stop-color="#E8F2F9"/>
</linearGradient>
<clipPath id="clip0_518_22068">
<rect width="200" height="162" rx="8" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5 17.5C12.5 17.9193 12.2383 18.3672 11.7695 18.6797C11.3008 18.9922 10.6289 19.1667 10 19.1667C9.33594 19.1667 8.69922 18.9922 8.23047 18.6797C7.76172 18.3672 7.5 17.9193 7.5 17.5H12.5Z" fill="#227386"/>
<path d="M11.1903 1.98716V2.67947C13.9059 3.2142 15.9519 5.54245 15.9519 8.33331V9.0112C15.9519 10.7095 16.5955 12.3429 17.7561 13.6122L18.0314 13.9114C18.3439 14.254 18.422 14.7372 18.2286 15.1518C18.0351 15.5665 17.611 15.8333 17.1423 15.8333H2.85739C2.38867 15.8333 1.96351 15.5665 1.77148 15.1518C1.57945 14.7372 1.65626 14.254 1.96771 13.9114L2.24359 13.6122C3.40573 12.3429 4.0478 10.7095 4.0478 9.0112V8.33331C4.0478 5.54245 6.06034 3.2142 8.80945 2.67947V1.98716C8.80945 1.35002 9.34141 0.833313 9.99986 0.833313C10.6583 0.833313 11.1903 1.35002 11.1903 1.98716Z" fill="#227386"/>
<ellipse cx="17.0833" cy="2.91665" rx="2.08333" ry="2.08333" fill="#D34E50"/>
</svg>

After

Width:  |  Height:  |  Size: 987 B

View file

@ -0,0 +1,18 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_519_18707)">
<g opacity="0.4">
<path d="M22.2 6C23.3297 5.37187 24 4.59422 24 3.75C24 1.67906 19.9688 0 15 0C9.98906 0 6 1.67906 6 3.75C6 4.59422 6.67031 5.37187 7.8 6C7.80937 6.00469 7.81758 6.00937 7.82578 6.01406C7.83398 6.01875 7.84219 6.02344 7.85156 6.02813C8.23125 6.00938 8.61094 6 9 6C11.6344 6 14.0906 6.44062 15.9422 7.21406C16.1203 7.28906 16.2984 7.36875 16.4672 7.44844C18.8062 7.28906 20.8359 6.75469 22.2 6Z" fill="#227386"/>
<path d="M19.9435 12.9151C19.7958 12.9551 19.6477 12.9951 19.5 13.0359V13.5C20.7602 13.5 21.9296 13.8885 22.8951 14.5522C23.5995 14.0172 24 13.4028 24 12.75V11.0906C23.4141 11.5734 22.7063 11.9672 21.9422 12.2859C21.3382 12.5376 20.6447 12.7253 19.9435 12.9151Z" fill="#227386"/>
<path d="M18.3703 8.74688C19.0031 9.37969 19.5 10.2234 19.5 11.25V11.4984C20.4328 11.2734 21.2625 10.9781 21.9469 10.6359C21.9739 10.6209 22.0009 10.6021 22.0279 10.5833C22.0852 10.5432 22.1426 10.5032 22.2 10.5C23.3297 9.87187 24 9.09375 24 8.25V6.59063C23.4141 7.07344 22.7063 7.46719 21.9422 7.78594C20.9109 8.2125 19.6969 8.54063 18.3703 8.74688Z" fill="#227386"/>
</g>
<path d="M16.2 13.5C17.3297 12.8719 18 12.0938 18 11.25C18 9.17813 13.9688 7.5 9 7.5C4.02938 7.5 0 9.17813 0 11.25C0 12.0938 0.669375 12.8719 1.79953 13.5C1.85443 13.5031 1.91057 13.5415 1.96782 13.5807C1.9966 13.6004 2.02567 13.6203 2.055 13.6359C3.70594 14.4703 6.20625 15 9 15C11.9438 15 14.5594 14.4094 16.2 13.5Z" fill="#227386"/>
<path d="M14.8788 15.6729C13.1948 16.2046 11.1571 16.5 9 16.5C6.36562 16.5 3.91125 16.0594 2.05922 15.2859C1.29469 14.9672 0.583594 14.5734 0 14.0906V15.75C0 16.5938 0.669375 17.3719 1.79953 18C3.44109 18.9094 6.05625 19.5 9 19.5C10.6471 19.5 12.1916 19.3159 13.5211 18.9937C13.6261 17.7367 14.1186 16.5898 14.8788 15.6729Z" fill="#227386"/>
<path d="M13.5862 20.5191C13.7529 21.4936 14.1547 22.3879 14.731 23.1415C13.1742 23.6778 11.1771 24 9 24C4.02938 24 0 22.3219 0 20.25V18.5906C0.583594 19.0734 1.29469 19.4672 2.05922 19.7859C3.91125 20.5594 6.36562 21 9 21C10.6307 21 12.1932 20.8312 13.5862 20.5191Z" fill="#227386"/>
<path d="M24 19.5C24 21.9844 21.9844 24 19.5 24C17.0156 24 15 21.9844 15 19.5C15 17.0156 17.0156 15 19.5 15C21.9844 15 24 17.0156 24 19.5ZM19 17.4719V18.9719H17.5C17.225 18.9719 17 19.225 17 19.4719C17 19.775 17.225 19.9719 17.5 19.9719H19V21.4719C19 21.775 19.225 21.9719 19.5 21.9719C19.775 21.9719 20 21.775 20 21.4719V19.9719H21.5C21.775 19.9719 22 19.775 22 19.4719C22 19.225 21.775 18.9719 21.5 18.9719H20V17.4719C20 17.225 19.775 16.9719 19.5 16.9719C19.225 16.9719 19 17.225 19 17.4719Z" fill="#227386"/>
</g>
<defs>
<clipPath id="clip0_519_18707">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.5 6.5L20.4343 7.33045C20.8552 6.85685 20.8552 6.14315 20.4343 5.66955L19.5 6.5ZM16.4343 1.16955C15.9756 0.653567 15.1855 0.607091 14.6695 1.06574C14.1536 1.52439 14.1071 2.31448 14.5657 2.83045L16.4343 1.16955ZM14.5657 10.1695C14.1071 10.6855 14.1536 11.4756 14.6695 11.9343C15.1855 12.3929 15.9756 12.3464 16.4343 11.8305L14.5657 10.1695ZM0.75 10.5C0.75 11.1904 1.30964 11.75 2 11.75C2.69036 11.75 3.25 11.1904 3.25 10.5H0.75ZM6 7.75H19.5V5.25H6V7.75ZM14.5657 2.83045L18.5657 7.33045L20.4343 5.66955L16.4343 1.16955L14.5657 2.83045ZM16.4343 11.8305L20.4343 7.33045L18.5657 5.66955L14.5657 10.1695L16.4343 11.8305ZM3.25 10.5C3.25 8.98122 4.48122 7.75 6 7.75V5.25C3.10051 5.25 0.75 7.60051 0.75 10.5H3.25Z" fill="#227386"/>
<path opacity="0.4" d="M4.5 18L3.56574 17.1695C3.14475 17.6432 3.14475 18.3568 3.56574 18.8305L4.5 18ZM7.56574 23.3305C8.02439 23.8464 8.81448 23.8929 9.33045 23.4343C9.84643 22.9756 9.89291 22.1855 9.43426 21.6695L7.56574 23.3305ZM9.43426 14.3305C9.89291 13.8145 9.84643 13.0244 9.33046 12.5657C8.81448 12.1071 8.02439 12.1536 7.56574 12.6695L9.43426 14.3305ZM23.25 14C23.25 13.3096 22.6904 12.75 22 12.75C21.3096 12.75 20.75 13.3096 20.75 14L23.25 14ZM18 16.75L4.5 16.75L4.5 19.25L18 19.25L18 16.75ZM9.43426 21.6695L5.43426 17.1695L3.56574 18.8305L7.56574 23.3305L9.43426 21.6695ZM7.56574 12.6695L3.56574 17.1695L5.43426 18.8305L9.43426 14.3305L7.56574 12.6695ZM20.75 14C20.75 15.5188 19.5188 16.75 18 16.75L18 19.25C20.8995 19.25 23.25 16.8995 23.25 14L20.75 14Z" fill="#227386"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,5 @@
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M41.3715 9.57675C37.2965 7.22564 32.2041 10.1695 32.2041 14.8717C32.2041 19.5739 34.4762 23.6489 38.2004 26.163L53.9717 35.3057L54.0112 35.2908L69.9948 26.0543L41.3715 9.57675Z" fill="#B3B3B3"/>
<path d="M38.2014 26.163C34.4771 23.6489 32.205 19.4159 32.205 14.8717C32.205 12.6342 33.3757 10.7671 35.0402 9.7101C34.9612 9.75455 35.1192 9.66564 35.0402 9.7101L10.0917 23.7279L6.08593 26.1481L3.35449 27.7188C5.07337 26.8446 7.22692 26.7754 9.14831 27.8917L16.0189 31.8037L22.0399 35.2859L38.0236 44.5076L53.9677 35.2958L38.1964 26.1531L38.2014 26.163Z" fill="#666666"/>
<path d="M70 44.5187L38.0278 62.9917L31.992 59.5095L31.9673 59.4848L6.06054 44.5187C4.28733 43.3629 2.84505 41.7872 1.82755 40.014C0.642111 37.9691 0 35.618 0 33.1829C0 30.9899 1.10147 29.1771 2.70181 28.1004C2.91914 27.967 3.13153 27.8435 3.35874 27.725C5.07762 26.8507 7.23116 26.7816 9.15256 27.8979L15.9836 31.8394L22.0047 35.3068L22.0442 35.292L38.0278 44.5137L53.9719 35.3019L70 44.5137V44.5187Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#0056D2"/>
<path d="M5.30071 12.4C4.91018 12.7905 4.91018 13.4236 5.30071 13.8142C5.69123 14.2047 6.32439 14.2047 6.71492 13.8142L5.30071 12.4ZM13.0789 6.03599L14.0787 6.05567C14.0839 5.78863 13.9821 5.53058 13.796 5.33904C13.6098 5.1475 13.3548 5.03839 13.0877 5.03603L13.0789 6.03599ZM9.00968 5.00004C8.45741 4.99516 8.00576 5.43891 8.00089 5.99117C7.99601 6.54344 8.43976 6.99509 8.99202 6.99996L9.00968 5.00004ZM12.001 9.98032C11.9902 10.5325 12.429 10.9889 12.9812 10.9998C13.5333 11.0107 13.9898 10.5719 14.0007 10.0197L12.001 9.98032ZM18.6429 11.6C19.0334 11.2095 19.0334 10.5764 18.6429 10.1858C18.2524 9.79531 17.6192 9.79531 17.2287 10.1858L18.6429 11.6ZM10.8647 17.964L9.8653 17.9297C9.85604 18.1992 9.95602 18.461 10.1426 18.6557C10.3291 18.8505 10.5864 18.9616 10.856 18.964L10.8647 17.964ZM14.9922 19C15.5444 19.0048 15.996 18.561 16.0008 18.0087C16.0056 17.4564 15.5618 17.0048 15.0096 17L14.9922 19ZM12.0003 14.0343C12.0192 13.4824 11.5871 13.0195 11.0352 13.0006C10.4832 12.9816 10.0204 13.4137 10.0014 13.9657L12.0003 14.0343ZM6.71492 13.8142L13.786 6.7431L12.3718 5.32889L5.30071 12.4L6.71492 13.8142ZM8.99202 6.99996L13.0701 7.03595L13.0877 5.03603L9.00968 5.00004L8.99202 6.99996ZM12.0791 6.01631L12.001 9.98032L14.0007 10.0197L14.0787 6.05567L12.0791 6.01631ZM17.2287 10.1858L10.1576 17.2569L11.5718 18.6711L18.6429 11.6L17.2287 10.1858ZM15.0096 17L10.8734 16.964L10.856 18.964L14.9922 19L15.0096 17ZM11.8641 17.9983L12.0003 14.0343L10.0014 13.9657L9.8653 17.9297L11.8641 17.9983Z" fill="#0056D2"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#C00205"/>
<path d="M19.4395 19.4395L20.5001 20.5001L21.5608 21.5608" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.5 21.5605L20.5607 20.4999L21.6213 19.4392" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#0056D2"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#F4C517"/>
<path d="M20.5 19V20.5H21.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.30071 12.4C4.91018 12.7905 4.91018 13.4236 5.30071 13.8142C5.69123 14.2047 6.32439 14.2047 6.71492 13.8142L5.30071 12.4ZM13.0789 6.03599L14.0787 6.05567C14.0839 5.78863 13.9821 5.53058 13.796 5.33904C13.6098 5.1475 13.3548 5.03839 13.0877 5.03603L13.0789 6.03599ZM9.00968 5.00004C8.45741 4.99516 8.00576 5.43891 8.00089 5.99117C7.99601 6.54344 8.43976 6.99509 8.99202 6.99996L9.00968 5.00004ZM12.001 9.98032C11.9902 10.5325 12.429 10.9889 12.9812 10.9998C13.5333 11.0107 13.9898 10.5719 14.0007 10.0197L12.001 9.98032ZM18.6429 11.6C19.0334 11.2095 19.0334 10.5764 18.6429 10.1858C18.2524 9.79531 17.6192 9.79531 17.2287 10.1858L18.6429 11.6ZM10.8647 17.964L9.8653 17.9297C9.85605 18.1992 9.95602 18.461 10.1426 18.6557C10.3291 18.8505 10.5864 18.9616 10.856 18.964L10.8647 17.964ZM14.9922 19C15.5444 19.0048 15.996 18.561 16.0008 18.0087C16.0056 17.4564 15.5618 17.0048 15.0096 17L14.9922 19ZM12.0003 14.0343C12.0192 13.4824 11.5871 13.0195 11.0352 13.0006C10.4832 12.9816 10.0204 13.4137 10.0014 13.9657L12.0003 14.0343ZM6.71492 13.8142L13.786 6.7431L12.3718 5.32889L5.30071 12.4L6.71492 13.8142ZM8.99202 6.99996L13.0701 7.03595L13.0877 5.03603L9.00968 5.00004L8.99202 6.99996ZM12.0791 6.01631L12.001 9.98032L14.0007 10.0197L14.0787 6.05567L12.0791 6.01631ZM17.2287 10.1858L10.1576 17.2569L11.5718 18.6711L18.6429 11.6L17.2287 10.1858ZM15.0096 17L10.8734 16.964L10.856 18.964L14.9922 19L15.0096 17ZM11.8641 17.9983L12.0003 14.0343L10.0014 13.9657L9.8653 17.9297L11.8641 17.9983Z" fill="#0056D2"/>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle opacity="0.4" cx="12" cy="12" r="12" fill="#0056D2"/>
<path d="M5.30071 12.4C4.91018 12.7905 4.91018 13.4236 5.30071 13.8142C5.69123 14.2047 6.32439 14.2047 6.71492 13.8142L5.30071 12.4ZM13.0789 6.03599L14.0787 6.05567C14.0839 5.78863 13.9821 5.53058 13.796 5.33904C13.6098 5.1475 13.3548 5.03839 13.0877 5.03603L13.0789 6.03599ZM9.00968 5.00004C8.45741 4.99516 8.00576 5.43891 8.00089 5.99117C7.99601 6.54344 8.43976 6.99509 8.99202 6.99996L9.00968 5.00004ZM12.001 9.98032C11.9902 10.5325 12.429 10.9889 12.9812 10.9998C13.5333 11.0107 13.9898 10.5719 14.0007 10.0197L12.001 9.98032ZM18.6429 11.6C19.0334 11.2095 19.0334 10.5764 18.6429 10.1858C18.2524 9.79531 17.6192 9.79531 17.2287 10.1858L18.6429 11.6ZM10.8647 17.964L9.8653 17.9297C9.85604 18.1992 9.95602 18.461 10.1426 18.6557C10.3291 18.8505 10.5864 18.9616 10.856 18.964L10.8647 17.964ZM14.9922 19C15.5444 19.0048 15.996 18.561 16.0008 18.0087C16.0056 17.4564 15.5618 17.0048 15.0096 17L14.9922 19ZM12.0003 14.0343C12.0192 13.4824 11.5871 13.0195 11.0352 13.0006C10.4832 12.9816 10.0204 13.4137 10.0014 13.9657L12.0003 14.0343ZM6.71492 13.8142L13.786 6.7431L12.3718 5.32889L5.30071 12.4L6.71492 13.8142ZM8.99202 6.99996L13.0701 7.03595L13.0877 5.03603L9.00968 5.00004L8.99202 6.99996ZM12.0791 6.01631L12.001 9.98032L14.0007 10.0197L14.0787 6.05567L12.0791 6.01631ZM17.2287 10.1858L10.1576 17.2569L11.5718 18.6711L18.6429 11.6L17.2287 10.1858ZM15.0096 17L10.8734 16.964L10.856 18.964L14.9922 19L15.0096 17ZM11.8641 17.9983L12.0003 14.0343L10.0014 13.9657L9.8653 17.9297L11.8641 17.9983Z" fill="#0056D2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" fill-rule="evenodd" clip-rule="evenodd" d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#00A578"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#C00205"/>
<path d="M16 8L8 16M8 16H14M8 16V10" stroke="#00A578" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.4395 19.4395L20.5001 20.5001L21.5608 21.5608" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.5 21.5605L20.5607 20.4999L21.6213 19.4392" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 882 B

View file

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#00A578"/>
<path d="M16 8L8 16M8 16H14M8 16V10" stroke="#00A578" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5 24C22.433 24 24 22.433 24 20.5C24 18.567 22.433 17 20.5 17C18.567 17 17 18.567 17 20.5C17 22.433 18.567 24 20.5 24ZM21 19C21 18.7239 20.7761 18.5 20.5 18.5C20.2239 18.5 20 18.7239 20 19V20.5C20 20.7761 20.2239 21 20.5 21H21.5C21.7761 21 22 20.7761 22 20.5C22 20.2239 21.7761 20 21.5 20H21V19Z" fill="#F4C517"/>
</svg>

After

Width:  |  Height:  |  Size: 912 B

View file

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle opacity="0.4" cx="12" cy="12" r="12" fill="#00A578"/>
<path d="M16 8L8 16M8 16H14M8 16V10" stroke="#00A578" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 287 B

View file

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#FE805C"/>
<path d="M8 16L16 8M16 8L10 8M16 8L16 14" stroke="#FE805C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#C00205"/>
<path d="M19.4395 19.4395L20.5001 20.5001L21.5608 21.5608" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.5 21.5605L20.5607 20.4999L21.6213 19.4392" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 847 B

View file

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#FE805C"/>
<path d="M8 16L16 8M16 8L10 8M16 8L16 14" stroke="#FE805C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#F4C517"/>
<path d="M20.5 19V20.5H21.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 697 B

View file

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle opacity="0.4" cx="12" cy="12" r="12" fill="#FE805C"/>
<path d="M8 16L16 8M16 8L10 8M16 8L16 14" stroke="#FE805C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 292 B

View file

@ -57,6 +57,7 @@ import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/utilities/theme/color_theme.dart';
import 'package:stackwallet/utilities/theme/dark_colors.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/ocean_breeze_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:window_size/window_size.dart';
@ -76,7 +77,7 @@ void main() async {
if (Util.isDesktop) {
setWindowTitle('Stack Wallet');
setWindowMinSize(const Size(1200, 1100));
setWindowMinSize(const Size(1220, 900));
setWindowMaxSize(Size.infinite);
}
@ -301,6 +302,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
case "dark":
themeType = ThemeType.dark;
break;
case "oceanBreeze":
themeType = ThemeType.oceanBreeze;
break;
case "light":
default:
themeType = ThemeType.light;
@ -314,8 +318,11 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
WidgetsBinding.instance.addPostFrameCallback((_) async {
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
themeType == ThemeType.dark ? DarkColors() : LightColors());
StackColors.fromStackColorTheme(themeType == ThemeType.dark
? DarkColors()
: (themeType == ThemeType.light
? LightColors()
: OceanBreezeColors()));
if (Platform.isAndroid) {
// fetch open file if it exists

View file

@ -6,6 +6,8 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
export 'package:stackwallet/utilities/enums/flush_bar_type.dart';
Future<dynamic> showFloatingFlushBar({
required FlushBarType type,
required String message,

View file

@ -32,6 +32,11 @@ class SearchableCoinList extends ConsumerWidget {
// remove firo testnet regardless
_coins.remove(Coin.firoTestNet);
// Kidgloves for Wownero on desktop
if(isDesktop) {
_coins.remove(Coin.wownero);
}
return _coins;
}

View file

@ -18,17 +18,21 @@ import 'package:stackwallet/widgets/address_book_card.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class AddressBookView extends ConsumerStatefulWidget {
const AddressBookView({Key? key, this.coin}) : super(key: key);
const AddressBookView({
Key? key,
this.coin,
this.filterTerm,
}) : super(key: key);
static const String routeName = "/addressBook";
final Coin? coin;
final String? filterTerm;
@override
ConsumerState<AddressBookView> createState() => _AddressBookViewState();
@ -39,9 +43,6 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
final _searchFocusNode = FocusNode();
List<Contact>? _cache;
List<Contact>? _cacheFav;
String _searchTerm = "";
@override
@ -50,8 +51,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
ref.refresh(addressBookFilterProvider);
if (widget.coin == null) {
List<Coin> coins =
Coin.values.where((e) => !(e == Coin.epicCash)).toList();
List<Coin> coins = Coin.values.toList();
coins.remove(Coin.firoTestNet);
bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins;
@ -59,8 +59,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
if (showTestNet) {
ref.read(addressBookFilterProvider).addAll(coins, false);
} else {
ref.read(addressBookFilterProvider).addAll(
coins.getRange(0, coins.length - kTestNetCoinCount + 1), false);
ref
.read(addressBookFilterProvider)
.addAll(coins.getRange(0, coins.length - kTestNetCoinCount), false);
}
} else {
ref.read(addressBookFilterProvider).add(widget.coin!, false);
@ -100,8 +101,8 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final addressBookEntriesFuture = ref.watch(
addressBookServiceProvider.select((value) => value.addressBookEntries));
final contacts =
ref.watch(addressBookServiceProvider.select((value) => value.contacts));
final isDesktop = Util.isDesktop;
return ConditionalParent(
@ -199,7 +200,12 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: child,
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height - 271,
),
child: child,
),
),
),
),
@ -209,196 +215,157 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
),
);
},
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height - 271,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: !isDesktop
? TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: (value) {
setState(() {
_searchTerm = value;
});
},
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: !isDesktop
? TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: (value) {
setState(() {
_searchTerm = value;
});
},
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
});
},
),
],
),
),
)
: null,
),
)
: null,
),
if (!isDesktop) const SizedBox(height: 16),
Text(
"Favorites",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
FutureBuilder(
future: addressBookEntriesFuture,
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
_cacheFav = snapshot.data!;
}
if (_cacheFav == null) {
// TODO proper loading animation
return const LoadingIndicator();
} else {
if (_cacheFav!.isNotEmpty) {
return RoundedWhiteContainer(
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
child: Column(
children: [
..._cacheFav!
.where((element) => element.addresses
.where((e) => ref.watch(
addressBookFilterProvider.select(
(value) =>
value.coins.contains(e.coin))))
.isNotEmpty)
.where((e) =>
e.isFavorite &&
ref
.read(addressBookServiceProvider)
.matches(_searchTerm, e))
.where((element) => element.isFavorite)
.map(
(e) => AddressBookCard(
key: Key("favContactCard_${e.id}_key"),
contactId: e.id,
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
_searchTerm = "";
});
},
),
],
),
),
],
),
);
} else {
return RoundedWhiteContainer(
child: Center(
child: Text(
"Your favorite contacts will appear here",
style: STextStyles.itemSubtitle(context),
)
: null,
),
)
: null,
),
if (!isDesktop) const SizedBox(height: 16),
Text(
"Favorites",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
if (contacts.isNotEmpty)
RoundedWhiteContainer(
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
child: Column(
children: [
...contacts
.where((element) => element.addresses
.where((e) => ref.watch(addressBookFilterProvider
.select((value) => value.coins.contains(e.coin))))
.isNotEmpty)
.where((e) =>
e.isFavorite &&
ref
.read(addressBookServiceProvider)
.matches(widget.filterTerm ?? _searchTerm, e))
.where((element) => element.isFavorite)
.map(
(e) => AddressBookCard(
key: Key("favContactCard_${e.id}_key"),
contactId: e.id,
),
),
);
}
}
},
],
),
),
const SizedBox(
height: 16,
if (contacts.isEmpty)
RoundedWhiteContainer(
child: Center(
child: Text(
"Your favorite contacts will appear here",
style: STextStyles.itemSubtitle(context),
),
),
),
Text(
"All contacts",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
FutureBuilder(
future: addressBookEntriesFuture,
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
_cache = snapshot.data!;
}
if (_cache == null) {
// TODO proper loading animation
return const LoadingIndicator();
} else {
if (_cache!.isNotEmpty) {
return Column(
const SizedBox(
height: 16,
),
Text(
"All contacts",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
if (contacts.isNotEmpty)
Column(
children: [
RoundedWhiteContainer(
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
RoundedWhiteContainer(
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
..._cache!
.where((element) => element.addresses
.where((e) => ref.watch(
addressBookFilterProvider.select(
(value) => value.coins
.contains(e.coin))))
.isNotEmpty)
.where((e) => ref
.read(addressBookServiceProvider)
.matches(_searchTerm, e))
.where((element) => !element.isFavorite)
.map(
(e) => AddressBookCard(
key: Key(
"desktopContactCard_${e.id}_key"),
contactId: e.id,
),
),
],
...contacts
.where((element) => element.addresses
.where((e) => ref.watch(
addressBookFilterProvider.select((value) =>
value.coins.contains(e.coin))))
.isNotEmpty)
.where((e) => ref
.read(addressBookServiceProvider)
.matches(widget.filterTerm ?? _searchTerm, e))
.map(
(e) => AddressBookCard(
key: Key("desktopContactCard_${e.id}_key"),
contactId: e.id,
),
),
),
),
],
);
} else {
return RoundedWhiteContainer(
child: Center(
child: Text(
"Your contacts will appear here",
style: STextStyles.itemSubtitle(context),
),
),
);
}
}
},
),
),
),
],
),
],
),
if (contacts.isEmpty)
RoundedWhiteContainer(
child: Center(
child: Text(
"Your contacts will appear here",
style: STextStyles.itemSubtitle(context),
),
),
),
],
),
);
}

View file

@ -21,6 +21,8 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/emoji_select_sheet.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
@ -191,33 +193,6 @@ class _AddAddressBookEntryViewState
style: STextStyles.desktopH3(context),
textAlign: TextAlign.center,
),
const SizedBox(width: 10),
AppBarIconButton(
key:
const Key("addAddressBookEntryFavoriteButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context)
.extension<StackColors>()!
.background,
icon: SvgPicture.asset(
Assets.svg.star,
color: _isFavorite
? Theme.of(context)
.extension<StackColors>()!
.favoriteStarActive
: Theme.of(context)
.extension<StackColors>()!
.favoriteStarInactive,
width: 20,
height: 20,
),
onPressed: () {
setState(() {
_isFavorite = !_isFavorite;
});
},
),
],
),
),
@ -225,10 +200,15 @@ class _AddAddressBookEntryViewState
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: child,
)),
child: Padding(
padding: const EdgeInsets.only(
left: 10,
right: 10,
bottom: 32,
),
child: child,
),
),
],
);
},
@ -238,16 +218,17 @@ class _AddAddressBookEntryViewState
padding: const EdgeInsets.symmetric(horizontal: 12),
child: SingleChildScrollView(
controller: scrollController,
padding: const EdgeInsets.only(
padding: EdgeInsets.only(
// top: 8,
left: 4,
right: 4,
bottom: 16,
bottom: isDesktop ? 0 : 16,
),
child: ConstrainedBox(
constraints: BoxConstraints(
// subtract top and bottom padding set in parent
minHeight: constraint.maxHeight - 16, // - 8,
minHeight:
constraint.maxHeight - (isDesktop ? 0 : 16), // - 8,
),
child: IntrinsicHeight(
child: Column(
@ -258,174 +239,111 @@ class _AddAddressBookEntryViewState
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
if (_selectedEmoji != null) {
setState(() {
_selectedEmoji = null;
});
return;
}
SizedBox(
height: 56,
width: 56,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
if (_selectedEmoji != null) {
setState(() {
_selectedEmoji = null;
});
return;
}
///TODO if desktop make dialog
!isDesktop
? showModalBottomSheet<dynamic>(
backgroundColor:
Colors.transparent,
context: context,
shape:
const RoundedRectangleBorder(
borderRadius:
BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) =>
const EmojiSelectSheet(),
).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
})
: showDialog<dynamic>(
showDialog<dynamic>(
context: context,
builder: (context) {
return DesktopDialog(
return const DesktopDialog(
maxHeight: 700,
maxWidth: 700,
child: Column(
children: [
Row(
children: [
Padding(
padding:
const EdgeInsets
.all(32),
child: Text(
"Select emoji",
style: STextStyles
.desktopH3(
context),
textAlign:
TextAlign
.center,
),
),
],
),
Expanded(
child: LayoutBuilder(
builder: (context,
constraints) {
return SingleChildScrollView(
scrollDirection:
Axis.vertical,
child:
ConstrainedBox(
constraints:
BoxConstraints(
minHeight:
constraints
.maxHeight,
minWidth:
constraints
.maxWidth,
),
child:
IntrinsicHeight(
child: Column(
children: const [
Padding(
padding:
EdgeInsets.symmetric(horizontal: 32),
// child:
// EmojiSelectSheet(),
),
],
),
),
),
);
},
),
),
],
maxWidth: 600,
child: Padding(
padding: EdgeInsets.only(
left: 32,
right: 20,
top: 32,
bottom: 32,
),
child: EmojiSelectSheet(),
),
);
}).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
},
child: SizedBox(
height: 56,
width: 56,
child: Stack(
children: [
Container(
height: 56,
width: 56,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.user,
height: 30,
width: 30,
)
: Text(
_selectedEmoji!.char,
style: STextStyles
.pageTitleH1(context),
),
),
),
Align(
alignment: Alignment.bottomRight,
child: Container(
height: 14,
width: 14,
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
},
child: Stack(
children: [
Container(
height: 56,
width: 56,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(14),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
borderRadius:
BorderRadius.circular(100),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.plus,
color: Theme.of(context)
.extension<
StackColors>()!
.textWhite,
width: 12,
height: 12,
Assets.svg.user,
height: 30,
width: 30,
)
: SvgPicture.asset(
Assets.svg.thickX,
color: Theme.of(context)
.extension<
StackColors>()!
.textWhite,
width: 8,
height: 8,
: Text(
_selectedEmoji!.char,
style: STextStyles
.pageTitleH1(
context),
),
),
),
)
],
Align(
alignment: Alignment.bottomRight,
child: Container(
height: 14,
width: 14,
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(
14),
color: Theme.of(context)
.extension<
StackColors>()!
.accentColorDark),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.plus,
color: Theme.of(
context)
.extension<
StackColors>()!
.textWhite,
width: 12,
height: 12,
)
: SvgPicture.asset(
Assets.svg.thickX,
color: Theme.of(
context)
.extension<
StackColors>()!
.textWhite,
width: 8,
height: 8,
),
),
),
)
],
),
),
),
),
@ -501,100 +419,23 @@ class _AddAddressBookEntryViewState
return;
}
///TODO if desktop make dialog
!isDesktop
? showModalBottomSheet<dynamic>(
backgroundColor:
Colors.transparent,
context: context,
shape:
const RoundedRectangleBorder(
borderRadius:
BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) =>
const EmojiSelectSheet(),
).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
})
: showDialog<dynamic>(
context: context,
builder: (context) {
return DesktopDialog(
maxHeight: 700,
maxWidth: 700,
child: Column(
children: [
Row(
children: [
Padding(
padding:
const EdgeInsets
.all(32),
child: Text(
"Select emoji",
style: STextStyles
.desktopH3(
context),
textAlign:
TextAlign
.center,
),
),
],
),
Expanded(
child: LayoutBuilder(
builder: (context,
constraints) {
return SingleChildScrollView(
scrollDirection:
Axis.vertical,
child:
ConstrainedBox(
constraints:
BoxConstraints(
minHeight:
constraints
.maxHeight,
minWidth:
constraints
.maxWidth,
),
child:
IntrinsicHeight(
child: Column(
children: const [
Padding(
padding:
EdgeInsets.symmetric(horizontal: 32),
// child:
// EmojiSelectSheet(),
),
],
),
),
),
);
},
),
),
],
),
);
}).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) =>
const EmojiSelectSheet(),
).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
},
child: SizedBox(
height: 48,
@ -718,7 +559,7 @@ class _AddAddressBookEntryViewState
),
],
),
if (!isDesktop) const SizedBox(height: 8),
const SizedBox(height: 8),
if (forms.length <= 1)
const SizedBox(
height: 8,
@ -782,22 +623,16 @@ class _AddAddressBookEntryViewState
Row(
children: [
Expanded(
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
child: SecondaryButton(
label: "Cancel",
buttonHeight: isDesktop ? ButtonHeight.m : null,
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
if (!isDesktop &&
FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
const Duration(milliseconds: 75),
);
}
if (mounted) {
Navigator.of(context).pop();
@ -824,16 +659,11 @@ class _AddAddressBookEntryViewState
bool shouldEnableSave =
validForms && nameExists;
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(
context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
return PrimaryButton(
label: "Save",
buttonHeight:
isDesktop ? ButtonHeight.m : null,
enabled: shouldEnableSave,
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context)
@ -875,19 +705,6 @@ class _AddAddressBookEntryViewState
}
}
: null,
child: Text(
"Save",
style:
STextStyles.button(context).copyWith(
color: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary
: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimaryDisabled,
),
),
);
},
),

View file

@ -12,7 +12,11 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
class AddNewContactAddressView extends ConsumerStatefulWidget {
const AddNewContactAddressView({
@ -55,190 +59,170 @@ class _AddNewContactAddressViewState
final contact = ref.watch(addressBookServiceProvider
.select((value) => value.getContactById(contactId)));
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) => Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Add new address",
style: STextStyles.navBarTitle(context),
),
),
title: Text(
"Add new address",
style: STextStyles.navBarTitle(context),
),
),
body: LayoutBuilder(
builder: (context, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
children: [
Row(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: contact.emojiChar == null
? SvgPicture.asset(
Assets.svg.user,
height: 24,
width: 24,
)
: Text(
contact.emojiChar!,
style: STextStyles.pageTitleH1(context),
),
),
),
const SizedBox(
width: 16,
),
Expanded(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
contact.name,
style: STextStyles.pageTitleH2(context),
),
),
),
],
),
const SizedBox(
height: 16,
),
NewContactAddressEntryForm(
id: 0,
barcodeScanner: barcodeScanner,
clipboard: clipboard,
),
const SizedBox(
height: 16,
),
const Spacer(),
Row(
children: [
Expanded(
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Builder(
builder: (context) {
bool shouldEnableSave =
ref.watch(validContactStateProvider([0]));
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(
context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75),
);
}
List<ContactAddressEntry> entries =
contact.addresses;
entries.add(ref
.read(
addressEntryDataProvider(0))
.buildAddressEntry());
Contact editedContact = contact
.copyWith(addresses: entries);
if (await ref
.read(
addressBookServiceProvider)
.editContact(editedContact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
}
: null,
child: Text(
"Save",
style: STextStyles.button(context),
),
);
},
),
),
],
)
],
body: LayoutBuilder(
builder: (context, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: child,
),
),
),
),
),
);
},
);
},
),
),
child: Column(
children: [
Row(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: contact.emojiChar == null
? SvgPicture.asset(
Assets.svg.user,
height: 24,
width: 24,
)
: Text(
contact.emojiChar!,
style: STextStyles.pageTitleH1(context),
),
),
),
const SizedBox(
width: 16,
),
if (isDesktop)
Text(
contact.name,
style: STextStyles.pageTitleH2(context),
),
if (!isDesktop)
Expanded(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
contact.name,
style: STextStyles.pageTitleH2(context),
),
),
),
],
),
const SizedBox(
height: 16,
),
NewContactAddressEntryForm(
id: 0,
barcodeScanner: barcodeScanner,
clipboard: clipboard,
),
const SizedBox(
height: 16,
),
const Spacer(),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: () async {
if (!isDesktop && FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Save",
enabled: ref.watch(validContactStateProvider([0])),
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75),
);
}
List<ContactAddressEntry> entries = contact.addresses;
entries.add(ref
.read(addressEntryDataProvider(0))
.buildAddressEntry());
Contact editedContact =
contact.copyWith(addresses: entries);
if (await ref
.read(addressBookServiceProvider)
.editContact(editedContact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
},
),
),
],
)
],
),
);
}

View file

@ -38,7 +38,7 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
} else {
_coins = coins
.toList(growable: false)
.getRange(0, coins.length - kTestNetCoinCount + 1)
.getRange(0, coins.length - kTestNetCoinCount)
.toList(growable: false);
}
super.initState();
@ -159,7 +159,7 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
children: [
SecondaryButton(
width: 248,
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: true,
label: "Cancel",
onPressed: () {
@ -169,7 +169,7 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
// const SizedBox(width: 16),
PrimaryButton(
width: 248,
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: true,
label: "Apply",
onPressed: () {

View file

@ -469,7 +469,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
..._cachedTransactions.map(
(e) => TransactionCard(
key: Key(
"contactDetailsTransaction_${e.item2.txid}_cardKey"),
"contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"),
transaction: e.item2,
walletId: e.item1,
),
@ -499,7 +499,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
..._cachedTransactions.map(
(e) => TransactionCard(
key: Key(
"contactDetailsTransaction_${e.item2.txid}_cardKey"),
"contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"),
transaction: e.item2,
walletId: e.item1,
),

View file

@ -12,7 +12,11 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
class EditContactAddressView extends ConsumerStatefulWidget {
const EditContactAddressView({
@ -44,6 +48,42 @@ class _EditContactAddressViewState
late final BarcodeScannerInterface barcodeScanner;
late final ClipboardInterface clipboard;
Future<void> save(Contact contact) async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75),
);
}
List<ContactAddressEntry> entries = contact.addresses.toList();
final entry = entries.firstWhere(
(e) =>
e.label == addressEntry.label &&
e.address == addressEntry.address &&
e.coin == addressEntry.coin,
);
final index = entries.indexOf(entry);
entries.remove(entry);
ContactAddressEntry editedEntry =
ref.read(addressEntryDataProvider(0)).buildAddressEntry();
entries.insert(index, editedEntry);
Contact editedContact = contact.copyWith(addresses: entries);
if (await ref.read(addressBookServiceProvider).editContact(editedContact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
}
@override
void initState() {
contactId = widget.contactId;
@ -59,236 +99,181 @@ class _EditContactAddressViewState
final contact = ref.watch(addressBookServiceProvider
.select((value) => value.getContactById(contactId)));
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
final bool isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) => Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Edit address",
style: STextStyles.navBarTitle(context),
),
),
title: Text(
"Edit address",
style: STextStyles.navBarTitle(context),
),
),
body: LayoutBuilder(
builder: (context, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
children: [
Row(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: contact.emojiChar == null
? SvgPicture.asset(
Assets.svg.user,
height: 24,
width: 24,
)
: Text(
contact.emojiChar!,
style: STextStyles.pageTitleH1(context),
),
),
),
const SizedBox(
width: 16,
),
Expanded(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
contact.name,
style: STextStyles.pageTitleH2(context),
),
),
),
],
),
const SizedBox(
height: 16,
),
NewContactAddressEntryForm(
id: 0,
barcodeScanner: barcodeScanner,
clipboard: clipboard,
),
const SizedBox(
height: 24,
),
GestureDetector(
onTap: () async {
// delete address
final _addresses = contact.addresses;
final entry = _addresses.firstWhere(
(e) =>
e.label == addressEntry.label &&
e.address == addressEntry.address &&
e.coin == addressEntry.coin,
);
_addresses.remove(entry);
Contact editedContact =
contact.copyWith(addresses: _addresses);
if (await ref
.read(addressBookServiceProvider)
.editContact(editedContact)) {
Navigator.of(context).pop();
// TODO show success notification
} else {
// TODO show error notification
}
},
child: Text(
"Delete address",
style: STextStyles.link(context),
),
),
const Spacer(),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Builder(
builder: (context) {
bool shouldEnableSave =
ref.watch(validContactStateProvider([0]));
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(
context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75),
);
}
List<ContactAddressEntry> entries =
contact.addresses.toList();
final entry = entries.firstWhere(
(e) =>
e.label ==
addressEntry.label &&
e.address ==
addressEntry.address &&
e.coin == addressEntry.coin,
);
final index =
entries.indexOf(entry);
entries.remove(entry);
ContactAddressEntry editedEntry = ref
.read(
addressEntryDataProvider(0))
.buildAddressEntry();
entries.insert(index, editedEntry);
Contact editedContact = contact
.copyWith(addresses: entries);
if (await ref
.read(
addressBookServiceProvider)
.editContact(editedContact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
}
: null,
child: Text(
"Save",
style: STextStyles.button(context),
),
);
},
),
),
],
),
],
body: LayoutBuilder(
builder: (context, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: child,
),
),
),
),
);
},
),
),
child: Column(
children: [
Row(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: contact.emojiChar == null
? SvgPicture.asset(
Assets.svg.user,
height: 24,
width: 24,
)
: Text(
contact.emojiChar!,
style: STextStyles.pageTitleH1(context),
),
),
),
const SizedBox(
width: 16,
),
if (isDesktop)
Text(
contact.name,
style: STextStyles.pageTitleH2(context),
),
if (!isDesktop)
Expanded(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
contact.name,
style: STextStyles.pageTitleH2(context),
),
),
),
],
),
const SizedBox(
height: 16,
),
NewContactAddressEntryForm(
id: 0,
barcodeScanner: barcodeScanner,
clipboard: clipboard,
),
const SizedBox(
height: 24,
),
ConditionalParent(
condition: isDesktop,
builder: (child) => MouseRegion(
cursor: SystemMouseCursors.click,
child: child,
),
);
},
child: GestureDetector(
onTap: () async {
// delete address
final _addresses = contact.addresses;
final entry = _addresses.firstWhere(
(e) =>
e.label == addressEntry.label &&
e.address == addressEntry.address &&
e.coin == addressEntry.coin,
);
_addresses.remove(entry);
Contact editedContact = contact.copyWith(addresses: _addresses);
if (await ref
.read(addressBookServiceProvider)
.editContact(editedContact)) {
Navigator.of(context).pop();
// TODO show success notification
} else {
// TODO show error notification
}
},
child: Text(
"Delete address",
style: STextStyles.link(context),
),
),
),
const Spacer(),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: () async {
if (!isDesktop && FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Save",
enabled: ref.watch(validContactStateProvider([0])),
onPressed: () => save(contact),
buttonHeight: isDesktop ? ButtonHeight.l : null,
),
),
],
),
],
),
);
}

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:emojis/emoji.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -7,14 +9,17 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/emoji_select_sheet.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/utilities/util.dart';
class EditContactNameEmojiView extends ConsumerStatefulWidget {
const EditContactNameEmojiView({
Key? key,
@ -69,268 +74,323 @@ class _EditContactNameEmojiViewState
final contact = ref.watch(addressBookServiceProvider
.select((value) => value.getContactById(contactId)));
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Edit contact",
style: STextStyles.navBarTitle(context),
),
),
body: LayoutBuilder(
builder: (context, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
children: [
GestureDetector(
onTap: () {
if (_selectedEmoji != null) {
setState(() {
_selectedEmoji = null;
});
return;
}
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => const EmojiSelectSheet(),
).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
},
child: SizedBox(
height: 48,
width: 48,
child: Stack(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.user,
height: 24,
width: 24,
)
: Text(
_selectedEmoji!.char,
style: STextStyles.pageTitleH1(
context),
),
),
),
Align(
alignment: Alignment.bottomRight,
child: Container(
height: 14,
width: 14,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.plus,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 12,
height: 12,
)
: SvgPicture.asset(
Assets.svg.thickX,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 8,
height: 8,
),
),
),
)
],
),
),
),
const SizedBox(
height: 8,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: nameController,
focusNode: nameFocusNode,
style: STextStyles.field(context),
onChanged: (_) => setState(() {}),
decoration: standardInputDecoration(
"Enter contact name",
nameFocusNode,
context,
).copyWith(
suffixIcon: nameController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
nameController.text = "";
});
},
),
],
),
),
)
: null,
),
),
),
const Spacer(),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Builder(
builder: (context) {
bool shouldEnableSave =
nameController.text.isNotEmpty;
final isDesktop = Util.isDesktop;
final double emojiSize = isDesktop ? 56 : 48;
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(
context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75),
);
}
final editedContact =
contact.copyWith(
shouldCopyEmojiWithNull: true,
name: nameController.text,
emojiChar: _selectedEmoji == null
? null
: _selectedEmoji!.char,
);
ref
.read(
addressBookServiceProvider)
.editContact(
editedContact,
);
if (mounted) {
Navigator.of(context).pop();
}
}
: null,
child: Text(
"Save",
style: STextStyles.button(context),
),
);
},
),
),
],
)
],
return ConditionalParent(
condition: !isDesktop,
builder: (child) => Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Edit contact",
style: STextStyles.navBarTitle(context),
),
),
body: LayoutBuilder(
builder: (context, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: child,
),
),
),
),
);
},
),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
if (_selectedEmoji != null) {
setState(() {
_selectedEmoji = null;
});
return;
}
if (isDesktop) {
showDialog<dynamic>(
barrierColor: Colors.transparent,
context: context,
builder: (context) {
return const DesktopDialog(
maxHeight: 700,
maxWidth: 600,
child: Padding(
padding: EdgeInsets.only(
left: 32,
right: 20,
top: 32,
bottom: 32,
),
child: EmojiSelectSheet(),
),
);
}).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
} else {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => const EmojiSelectSheet(),
).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
}
},
child: SizedBox(
height: emojiSize,
width: emojiSize,
child: Stack(
children: [
Container(
height: emojiSize,
width: emojiSize,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(emojiSize / 2),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.user,
height: emojiSize / 2,
width: emojiSize / 2,
)
: Text(
_selectedEmoji!.char,
style: isDesktop
? STextStyles.desktopH3(context)
: STextStyles.pageTitleH1(context),
),
),
),
Align(
alignment: Alignment.bottomRight,
child: Container(
height: 14,
width: 14,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.plus,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 12,
height: 12,
)
: SvgPicture.asset(
Assets.svg.thickX,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 8,
height: 8,
),
),
),
)
],
),
),
),
if (isDesktop)
const SizedBox(
width: 8,
),
if (isDesktop)
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: nameController,
focusNode: nameFocusNode,
style: STextStyles.field(context),
onChanged: (_) => setState(() {}),
decoration: standardInputDecoration(
"Enter contact name",
nameFocusNode,
context,
).copyWith(
suffixIcon: nameController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
nameController.text = "";
});
},
),
],
),
),
)
: null,
),
),
),
),
],
),
if (!isDesktop)
const SizedBox(
height: 8,
),
);
},
if (!isDesktop)
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: nameController,
focusNode: nameFocusNode,
style: STextStyles.field(context),
onChanged: (_) => setState(() {}),
decoration: standardInputDecoration(
"Enter contact name",
nameFocusNode,
context,
).copyWith(
suffixIcon: nameController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
nameController.text = "";
});
},
),
],
),
),
)
: null,
),
),
),
const Spacer(),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: () async {
if (!isDesktop && FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Save",
enabled: nameController.text.isNotEmpty,
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: () async {
if (!isDesktop && FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75),
);
}
final editedContact = contact.copyWith(
shouldCopyEmojiWithNull: true,
name: nameController.text,
emojiChar:
_selectedEmoji == null ? null : _selectedEmoji!.char,
);
unawaited(
ref.read(addressBookServiceProvider).editContact(
editedContact,
),
);
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
],
)
],
),
);
}

View file

@ -1,8 +1,10 @@
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/address_book_views/subviews/coin_select_sheet.dart';
import 'package:stackwallet/providers/providers.dart';
// import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
import 'package:stackwallet/utilities/address_utils.dart';
@ -47,6 +49,8 @@ class _NewContactAddressEntryFormState
late final FocusNode addressLabelFocusNode;
late final FocusNode addressFocusNode;
List<Coin> coins = [];
@override
void initState() {
addressLabelController = TextEditingController()
@ -55,6 +59,7 @@ class _NewContactAddressEntryFormState
..text = ref.read(addressEntryDataProvider(widget.id)).address ?? "";
addressLabelFocusNode = FocusNode();
addressFocusNode = FocusNode();
coins = [...Coin.values];
super.initState();
}
@ -70,86 +75,179 @@ class _NewContactAddressEntryFormState
@override
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
bool showTestNet = ref.watch(
prefsChangeNotifierProvider.select((value) => value.showTestNetCoins),
);
if (isDesktop) {
coins = [...Coin.values];
coins.remove(Coin.firoTestNet);
if (showTestNet) {
coins = coins.sublist(0, coins.length - kTestNetCoinCount);
}
}
return Column(
children: [
TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
readOnly: true,
style: STextStyles.field(context),
decoration: InputDecoration(
hintText: "Select cryptocurrency",
hintStyle: STextStyles.fieldLabel(context),
prefixIcon: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: RawMaterialButton(
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
if (isDesktop)
DropdownButtonHideUnderline(
child: DropdownButton2<Coin>(
hint: Text(
"Select cryptocurrency",
style: STextStyles.fieldLabel(context),
),
offset: const Offset(0, -10),
isExpanded: true,
dropdownElevation: 0,
value: ref.watch(addressEntryDataProvider(widget.id)
.select((value) => value.coin)),
onChanged: (value) {
if (value is Coin) {
ref.read(addressEntryDataProvider(widget.id)).coin = value;
}
},
icon: SvgPicture.asset(
Assets.svg.chevronDown,
width: 10,
height: 5,
color: Theme.of(context).extension<StackColors>()!.textDark3,
),
buttonPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 4,
),
buttonDecoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
dropdownDecoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
items: [
...coins.map(
(coin) => DropdownMenuItem<Coin>(
value: coin,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.iconFor(coin: coin),
height: 24,
width: 24,
),
const SizedBox(
width: 12,
),
Text(
coin.prettyName,
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
),
),
onPressed: () {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
builder: (_) => const CoinSelectSheet(),
).then((value) {
if (value is Coin) {
ref.read(addressEntryDataProvider(widget.id)).coin =
value;
}
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ref.watch(addressEntryDataProvider(widget.id)
.select((value) => value.coin)) ==
null
? Text(
"Select cryptocurrency",
style: STextStyles.fieldLabel(context),
)
: Row(
children: [
SvgPicture.asset(
Assets.svg.iconFor(
coin: ref.watch(
addressEntryDataProvider(widget.id)
.select((value) => value.coin))!),
height: 20,
width: 20,
),
const SizedBox(
width: 12,
),
Text(
ref
.watch(addressEntryDataProvider(widget.id)
.select((value) => value.coin))!
.prettyName,
style: STextStyles.itemSubtitle12(context),
),
],
),
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
),
],
),
),
if (!isDesktop)
TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
readOnly: true,
style: STextStyles.field(context),
decoration: InputDecoration(
hintText: "Select cryptocurrency",
hintStyle: STextStyles.fieldLabel(context),
prefixIcon: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: RawMaterialButton(
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
],
),
onPressed: () {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
builder: (_) => const CoinSelectSheet(),
).then((value) {
if (value is Coin) {
ref.read(addressEntryDataProvider(widget.id)).coin =
value;
}
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ref.watch(addressEntryDataProvider(widget.id)
.select((value) => value.coin)) ==
null
? Text(
"Select cryptocurrency",
style: STextStyles.fieldLabel(context),
)
: Row(
children: [
SvgPicture.asset(
Assets.svg.iconFor(
coin: ref.watch(
addressEntryDataProvider(widget.id)
.select(
(value) => value.coin))!),
height: 20,
width: 20,
),
const SizedBox(
width: 12,
),
Text(
ref
.watch(
addressEntryDataProvider(widget.id)
.select((value) => value.coin))!
.prettyName,
style: STextStyles.itemSubtitle12(context),
),
],
),
if (!isDesktop)
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
),
],
),
),
),
),
),
),
),
const SizedBox(
height: 8,
),
@ -253,9 +351,10 @@ class _NewContactAddressEntryFormState
},
child: const ClipboardIcon(),
),
if (ref.watch(addressEntryDataProvider(widget.id)
.select((value) => value.address)) ==
null)
if (!Util.isDesktop &&
ref.watch(addressEntryDataProvider(widget.id)
.select((value) => value.address)) ==
null)
TextFieldIconButton(
key: const Key("addAddressBookEntryScanQrButtonKey"),
onTap: () async {

View file

@ -7,15 +7,23 @@ import 'package:stackwallet/models/trade_wallet_lookup.dart';
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@ -29,6 +37,7 @@ class ConfirmChangeNowSendView extends ConsumerStatefulWidget {
this.routeOnSuccessName = WalletView.routeName,
required this.trade,
this.shouldSendPublicFiroFunds,
this.fromDesktopStep4 = false,
}) : super(key: key);
static const String routeName = "/confirmChangeNowSend";
@ -38,6 +47,7 @@ class ConfirmChangeNowSendView extends ConsumerStatefulWidget {
final String routeOnSuccessName;
final Trade trade;
final bool? shouldSendPublicFiroFunds;
final bool fromDesktopStep4;
@override
ConsumerState<ConfirmChangeNowSendView> createState() =>
@ -52,14 +62,16 @@ class _ConfirmChangeNowSendViewState
late final Trade trade;
Future<void> _attemptSend(BuildContext context) async {
unawaited(showDialog<void>(
context: context,
useSafeArea: false,
barrierDismissible: false,
builder: (context) {
return const SendingTransactionDialog();
},
));
unawaited(
showDialog<void>(
context: context,
useSafeArea: false,
barrierDismissible: false,
builder: (context) {
return const SendingTransactionDialog();
},
),
);
final String note = transactionInfo["note"] as String? ?? "";
final manager =
@ -93,6 +105,19 @@ class _ConfirmChangeNowSendViewState
// pop back to wallet
if (mounted) {
if (Util.isDesktop) {
Navigator.of(context, rootNavigator: true).pop();
// stupid hack
if (widget.fromDesktopStep4) {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context, rootNavigator: true).pop();
}
}
Navigator.of(context).popUntil(ModalRoute.withName(routeOnSuccessName));
}
} catch (e) {
@ -129,6 +154,60 @@ class _ConfirmChangeNowSendViewState
}
}
Future<void> _confirmSend() async {
final dynamic unlocked;
if (Util.isDesktop) {
unlocked = await showDialog<bool?>(
context: context,
builder: (context) => DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
DesktopDialogCloseButton(),
],
),
const Padding(
padding: EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: DesktopAuthSend(),
),
],
),
),
);
} else {
unlocked = await Navigator.push(
context,
RouteGenerator.getRoute(
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
builder: (_) => const LockscreenView(
showBackButton: true,
popOnSuccess: true,
routeOnSuccessArguments: true,
routeOnSuccess: "",
biometricsCancelButtonString: "CANCEL",
biometricsLocalizedReason: "Authenticate to send transaction",
biometricsAuthenticationTitle: "Confirm Transaction",
),
settings: const RouteSettings(name: "/confirmsendlockscreen"),
),
);
}
if (unlocked is bool && unlocked && mounted) {
await _attemptSend(context);
}
}
@override
void initState() {
transactionInfo = widget.transactionInfo;
@ -142,280 +221,503 @@ class _ConfirmChangeNowSendViewState
Widget build(BuildContext context) {
final managerProvider = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvider(walletId)));
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
leading: AppBarBackButton(
onPressed: () async {
// if (FocusScope.of(context).hasFocus) {
// FocusScope.of(context).unfocus();
// await Future<void>.delayed(Duration(milliseconds: 50));
// }
Navigator.of(context).pop();
},
),
title: Text(
"Confirm transaction",
style: STextStyles.navBarTitle(context),
),
),
body: LayoutBuilder(
builder: (builderContext, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}",
style: STextStyles.pageTitleH1(context),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Send from",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 4,
),
Text(
ref
.watch(walletsChangeNotifierProvider)
.getManager(walletId)
.walletName,
style: STextStyles.itemSubtitle12(context),
),
],
),
),
const SizedBox(
height: 12,
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"${trade.exchangeName} address",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 4,
),
Text(
"${transactionInfo["address"] ?? "ERROR"}",
style: STextStyles.itemSubtitle12(context),
),
],
),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Amount",
style: STextStyles.smallMed12(context),
),
Text(
"${Format.satoshiAmountToPrettyString(
transactionInfo["recipientAmt"] as int,
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider
.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Transaction fee",
style: STextStyles.smallMed12(context),
),
Text(
"${Format.satoshiAmountToPrettyString(
transactionInfo["fee"] as int,
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider
.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Note",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 4,
),
Text(
transactionInfo["note"] as String? ?? "",
style: STextStyles.itemSubtitle12(context),
),
],
),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Trade ID",
style: STextStyles.smallMed12(context),
),
Text(
trade.tradeId,
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
),
),
const SizedBox(
height: 12,
),
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.snackBarBackSuccess,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Total amount",
style:
STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
),
Text(
"${Format.satoshiAmountToPrettyString(
(transactionInfo["fee"] as int) +
(transactionInfo["recipientAmt"] as int),
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider
.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
),
],
),
),
const SizedBox(
height: 16,
),
const Spacer(),
TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
onPressed: () async {
final unlocked = await Navigator.push(
context,
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
builder: (_) => const LockscreenView(
showBackButton: true,
popOnSuccess: true,
routeOnSuccessArguments: true,
routeOnSuccess: "",
biometricsCancelButtonString: "CANCEL",
biometricsLocalizedReason:
"Authenticate to send transaction",
biometricsAuthenticationTitle:
"Confirm Transaction",
),
settings: const RouteSettings(
name: "/confirmsendlockscreen"),
),
);
if (unlocked is bool && unlocked && mounted) {
await _attemptSend(context);
}
},
child: Text(
"Send",
style: STextStyles.button(context),
),
),
],
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) {
return Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
leading: AppBarBackButton(
onPressed: () async {
// if (FocusScope.of(context).hasFocus) {
// FocusScope.of(context).unfocus();
// await Future<void>.delayed(Duration(milliseconds: 50));
// }
Navigator.of(context).pop();
},
),
title: Text(
"Confirm transaction",
style: STextStyles.navBarTitle(context),
),
),
body: LayoutBuilder(
builder: (builderContext, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: child,
),
),
),
),
);
},
),
);
},
child: ConditionalParent(
condition: isDesktop,
builder: (child) => DesktopDialog(
maxHeight: double.infinity,
maxWidth: 580,
child: Column(
children: [
Row(
children: [
const SizedBox(
width: 6,
),
const AppBarBackButton(
isCompact: true,
iconSize: 23,
),
const SizedBox(
width: 12,
),
Text(
"Confirm ${ref.watch(managerProvider.select((value) => value.coin)).ticker} transaction",
style: STextStyles.desktopH3(context),
)
],
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: Column(
children: [
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: child,
),
const SizedBox(
height: 16,
),
Row(
children: [
Text(
"Transaction fee",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
],
),
const SizedBox(
height: 10,
),
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"${Format.satoshiAmountToPrettyString(
(transactionInfo["fee"] as int),
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
),
const SizedBox(
height: 16,
),
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.snackBarBackSuccess,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Total amount",
style: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
),
Text(
"${Format.satoshiAmountToPrettyString(
(transactionInfo["fee"] as int) +
(transactionInfo["recipientAmt"] as int),
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
),
],
),
),
const SizedBox(
height: 16,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Send",
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: _confirmSend,
),
),
],
)
],
),
),
],
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ConditionalParent(
condition: isDesktop,
builder: (child) => Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.background,
borderRadius: BorderRadius.vertical(
top: Radius.circular(
Constants.size.circularBorderRadius,
),
),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
child,
],
),
),
),
child: Text(
"Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}",
style: isDesktop
? STextStyles.desktopTextMedium(context)
: STextStyles.pageTitleH1(context),
),
),
);
},
isDesktop
? Container(
color:
Theme.of(context).extension<StackColors>()!.background,
height: 1,
)
: const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Send from",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 4,
),
Text(
ref
.watch(walletsChangeNotifierProvider)
.getManager(walletId)
.walletName,
style: STextStyles.itemSubtitle12(context),
),
],
),
),
isDesktop
? Container(
color:
Theme.of(context).extension<StackColors>()!.background,
height: 1,
)
: const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"${trade.exchangeName} address",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 4,
),
Text(
"${transactionInfo["address"] ?? "ERROR"}",
style: STextStyles.itemSubtitle12(context),
),
],
),
),
isDesktop
? Container(
color:
Theme.of(context).extension<StackColors>()!.background,
height: 1,
)
: const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Amount",
style: STextStyles.smallMed12(context),
),
ConditionalParent(
condition: isDesktop,
builder: (child) => Row(
children: [
child,
Builder(builder: (context) {
final coin = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(walletId).coin));
final price = ref.watch(
priceAnd24hChangeNotifierProvider
.select((value) => value.getPrice(coin)));
final amount = Format.satoshisToAmount(
transactionInfo["recipientAmt"] as int,
coin: coin,
);
final value = price.item1 * amount;
final currency = ref.watch(prefsChangeNotifierProvider
.select((value) => value.currency));
return Text(
" | ${value.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} $currency",
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
),
);
})
],
),
child: Text(
"${Format.satoshiAmountToPrettyString(
transactionInfo["recipientAmt"] as int,
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
),
],
),
),
isDesktop
? Container(
color:
Theme.of(context).extension<StackColors>()!.background,
height: 1,
)
: const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Transaction fee",
style: STextStyles.smallMed12(context),
),
Text(
"${Format.satoshiAmountToPrettyString(
transactionInfo["fee"] as int,
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
),
),
isDesktop
? Container(
color:
Theme.of(context).extension<StackColors>()!.background,
height: 1,
)
: const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Note",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 4,
),
Text(
transactionInfo["note"] as String? ?? "",
style: STextStyles.itemSubtitle12(context),
),
],
),
),
isDesktop
? Container(
color:
Theme.of(context).extension<StackColors>()!.background,
height: 1,
)
: const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Trade ID",
style: STextStyles.smallMed12(context),
),
Text(
trade.tradeId,
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
],
),
),
if (!isDesktop)
const SizedBox(
height: 12,
),
if (!isDesktop)
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.snackBarBackSuccess,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Total amount",
style: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
),
Text(
"${Format.satoshiAmountToPrettyString(
(transactionInfo["fee"] as int) +
(transactionInfo["recipientAmt"] as int),
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
),
],
),
),
if (!isDesktop)
const SizedBox(
height: 16,
),
if (!isDesktop) const Spacer(),
if (!isDesktop)
PrimaryButton(
label: "Send",
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: _confirmSend,
),
],
),
),
);
}

View file

@ -8,6 +8,8 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
@ -16,8 +18,6 @@ import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:tuple/tuple.dart';
import 'package:stackwallet/utilities/util.dart';
class FixedRateMarketPairCoinSelectionView extends ConsumerStatefulWidget {
const FixedRateMarketPairCoinSelectionView({
Key? key,
@ -120,95 +120,106 @@ class _FixedRateMarketPairCoinSelectionViewState
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 50));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Choose a coin to exchange",
style: STextStyles.pageTitleH2(context),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) {
return Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 50));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Choose a coin to exchange",
style: STextStyles.pageTitleH2(context),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: child,
),
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!isDesktop)
const SizedBox(
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: filter,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: filter,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
});
},
),
],
),
),
)
: null,
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
});
},
),
],
),
),
)
: null,
),
),
const SizedBox(
height: 10,
),
Text(
"Popular coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Builder(builder: (context) {
),
const SizedBox(
height: 10,
),
Text(
"Popular coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Flexible(
child: Builder(builder: (context) {
final items = _markets
.where((e) => Coin.values
.where((coin) =>
@ -221,6 +232,7 @@ class _FixedRateMarketPairCoinSelectionViewState
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
primary: isDesktop ? false : null,
itemCount: items.length,
itemBuilder: (builderContext, index) {
final String ticker =
@ -282,84 +294,85 @@ class _FixedRateMarketPairCoinSelectionViewState
),
);
}),
const SizedBox(
height: 20,
),
Text(
"All coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Flexible(
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
itemCount: _markets.length,
itemBuilder: (builderContext, index) {
final String ticker =
isFrom ? _markets[index].from : _markets[index].to;
),
const SizedBox(
height: 20,
),
Text(
"All coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Flexible(
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
primary: isDesktop ? false : null,
itemCount: _markets.length,
itemBuilder: (builderContext, index) {
final String ticker =
isFrom ? _markets[index].from : _markets[index].to;
final tuple = _imageUrlAndNameFor(ticker);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(ticker);
},
child: RoundedWhiteContainer(
child: Row(
children: [
SizedBox(
final tuple = _imageUrlAndNameFor(ticker);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(ticker);
},
child: RoundedWhiteContainer(
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: SvgPicture.network(
tuple.item1,
width: 24,
height: 24,
child: SvgPicture.network(
tuple.item1,
width: 24,
height: 24,
placeholderBuilder: (_) =>
const LoadingIndicator(),
),
placeholderBuilder: (_) =>
const LoadingIndicator(),
),
const SizedBox(
width: 10,
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tuple.item2,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tuple.item2,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
),
],
),
),
);
},
),
),
);
},
),
),
],
),
),
],
),
);
}

View file

@ -6,6 +6,8 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
@ -13,8 +15,6 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/utilities/util.dart';
class FloatingRateCurrencySelectionView extends StatefulWidget {
const FloatingRateCurrencySelectionView({
Key? key,
@ -76,96 +76,109 @@ class _FloatingRateCurrencySelectionViewState
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 50));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Choose a coin to exchange",
style: STextStyles.pageTitleH2(context),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) {
return Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 50));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"Choose a coin to exchange",
style: STextStyles.pageTitleH2(context),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: child,
),
);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
children: [
if (!isDesktop)
const SizedBox(
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: filter,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: filter,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
desktopMed: isDesktop,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
});
filter("");
},
),
],
),
),
)
: null,
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
});
filter("");
},
),
],
),
),
)
: null,
),
),
const SizedBox(
height: 10,
),
Text(
"Popular coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Builder(builder: (context) {
),
const SizedBox(
height: 10,
),
Text(
"Popular coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Flexible(
child: Builder(builder: (context) {
final items = _currencies
.where((e) => Coin.values
.where((coin) =>
@ -177,6 +190,7 @@ class _FloatingRateCurrencySelectionViewState
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
primary: isDesktop ? false : null,
itemCount: items.length,
itemBuilder: (builderContext, index) {
return Padding(
@ -234,80 +248,81 @@ class _FloatingRateCurrencySelectionViewState
),
);
}),
const SizedBox(
height: 20,
),
Text(
"All coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Flexible(
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
itemCount: _currencies.length,
itemBuilder: (builderContext, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(_currencies[index]);
},
child: RoundedWhiteContainer(
child: Row(
children: [
SizedBox(
),
const SizedBox(
height: 20,
),
Text(
"All coins",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
Flexible(
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.builder(
shrinkWrap: true,
primary: isDesktop ? false : null,
itemCount: _currencies.length,
itemBuilder: (builderContext, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(_currencies[index]);
},
child: RoundedWhiteContainer(
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: SvgPicture.network(
_currencies[index].image,
width: 24,
height: 24,
child: SvgPicture.network(
_currencies[index].image,
width: 24,
height: 24,
placeholderBuilder: (_) =>
const LoadingIndicator(),
),
placeholderBuilder: (_) =>
const LoadingIndicator(),
),
const SizedBox(
width: 10,
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_currencies[index].name,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
_currencies[index].ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_currencies[index].name,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
_currencies[index].ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
),
],
),
),
);
},
),
),
);
},
),
),
],
),
),
],
),
);
}

File diff suppressed because it is too large Load diff

View file

@ -18,7 +18,6 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';

View file

@ -8,6 +8,7 @@ import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/pages/exchange_view/confirm_change_now_send.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
@ -19,9 +20,12 @@ import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/expandable.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@ -33,6 +37,8 @@ class SendFromView extends ConsumerStatefulWidget {
required this.trade,
required this.amount,
required this.address,
this.shouldPopRoot = false,
this.fromDesktopStep4 = false,
}) : super(key: key);
static const String routeName = "/sendFrom";
@ -41,6 +47,8 @@ class SendFromView extends ConsumerStatefulWidget {
final Decimal amount;
final String address;
final Trade trade;
final bool shouldPopRoot;
final bool fromDesktopStep4;
@override
ConsumerState<SendFromView> createState() => _SendFromViewState();
@ -90,21 +98,68 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
final walletIds = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getWalletIdsFor(coin: coin)));
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) {
return Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
),
title: Text(
"Send from",
style: STextStyles.navBarTitle(context),
),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
);
},
child: ConditionalParent(
condition: isDesktop,
builder: (child) => DesktopDialog(
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Send from Stack",
style: STextStyles.desktopH3(context),
),
),
DesktopDialogCloseButton(
onPressedOverride: Navigator.of(
context,
rootNavigator: widget.shouldPopRoot,
).pop,
),
],
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: child,
),
],
),
),
title: Text(
"Send from",
style: STextStyles.navBarTitle(context),
),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
@ -112,15 +167,23 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
children: [
Text(
"You need to send ${formatAmount(amount, coin)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.itemSubtitle(context),
),
],
),
const SizedBox(
height: 16,
),
Expanded(
ConditionalParent(
condition: !isDesktop,
builder: (child) => Expanded(
child: child,
),
child: ListView.builder(
primary: isDesktop ? false : null,
shrinkWrap: isDesktop,
itemCount: walletIds.length,
itemBuilder: (context, index) {
return Padding(
@ -130,6 +193,7 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
amount: amount,
address: address,
trade: trade,
fromDesktopStep4: widget.fromDesktopStep4,
),
);
},
@ -149,12 +213,14 @@ class SendFromCard extends ConsumerStatefulWidget {
required this.amount,
required this.address,
required this.trade,
this.fromDesktopStep4 = false,
}) : super(key: key);
final String walletId;
final Decimal amount;
final String address;
final Trade trade;
final bool fromDesktopStep4;
@override
ConsumerState<SendFromCard> createState() => _SendFromCardState();
@ -178,12 +244,23 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
useSafeArea: false,
barrierDismissible: false,
builder: (context) {
return BuildingTransactionDialog(
onCancel: () {
wasCancelled = true;
return ConditionalParent(
condition: Util.isDesktop,
builder: (child) => DesktopDialog(
maxWidth: 400,
maxHeight: double.infinity,
child: Padding(
padding: const EdgeInsets.all(32),
child: child,
),
),
child: BuildingTransactionDialog(
onCancel: () {
wasCancelled = true;
Navigator.of(context).pop();
},
Navigator.of(context).pop();
},
),
);
},
),
@ -229,7 +306,10 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
// pop building dialog
if (mounted) {
Navigator.of(context).pop();
Navigator.of(
context,
rootNavigator: Util.isDesktop,
).pop();
}
txData["note"] =
@ -243,9 +323,12 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
builder: (_) => ConfirmChangeNowSendView(
transactionInfo: txData,
walletId: walletId,
routeOnSuccessName: HomeView.routeName,
routeOnSuccessName: Util.isDesktop
? DesktopExchangeView.routeName
: HomeView.routeName,
trade: trade,
shouldSendPublicFiroFunds: shouldSendPublicFiroFunds,
fromDesktopStep4: widget.fromDesktopStep4,
),
settings: const RouteSettings(
name: ConfirmChangeNowSendView.routeName,
@ -339,10 +422,16 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
Constants.size.circularBorderRadius,
),
),
onPressed: () => _send(
manager,
shouldSendPublicFiroFunds: false,
),
onPressed: () async {
if (mounted) {
unawaited(
_send(
manager,
shouldSendPublicFiroFunds: false,
),
);
}
},
child: Container(
color: Colors.transparent,
child: Padding(
@ -418,10 +507,16 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
Constants.size.circularBorderRadius,
),
),
onPressed: () => _send(
manager,
shouldSendPublicFiroFunds: true,
),
onPressed: () async {
if (mounted) {
unawaited(
_send(
manager,
shouldSendPublicFiroFunds: true,
),
);
}
},
child: Container(
color: Colors.transparent,
child: Padding(
@ -504,7 +599,13 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
Constants.size.circularBorderRadius,
),
),
onPressed: () => _send(manager),
onPressed: () async {
if (mounted) {
unawaited(
_send(manager),
);
}
},
child: child,
),
child: Row(

View file

@ -15,7 +15,9 @@ import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class ExchangeProviderOptions extends ConsumerWidget {
@ -38,353 +40,403 @@ class ExchangeProviderOptions extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final isDesktop = Util.isDesktop;
return RoundedWhiteContainer(
padding: isDesktop ? const EdgeInsets.all(0) : const EdgeInsets.all(12),
borderColor: isDesktop
? Theme.of(context).extension<StackColors>()!.background
: null,
child: Column(
children: [
GestureDetector(
onTap: () {
if (ref.read(currentExchangeNameStateProvider.state).state !=
ChangeNowExchange.exchangeName) {
ref.read(currentExchangeNameStateProvider.state).state =
ChangeNowExchange.exchangeName;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(
ref.read(currentExchangeNameStateProvider.state).state);
}
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: ChangeNowExchange.exchangeName,
groupValue: ref
.watch(currentExchangeNameStateProvider.state)
.state,
onChanged: (value) {
if (value is String) {
ref
.read(currentExchangeNameStateProvider.state)
.state = value;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
ConditionalParent(
condition: isDesktop,
builder: (child) => MouseRegion(
cursor: SystemMouseCursors.click,
child: child,
),
child: GestureDetector(
onTap: () {
if (ref.read(currentExchangeNameStateProvider.state).state !=
ChangeNowExchange.exchangeName) {
ref.read(currentExchangeNameStateProvider.state).state =
ChangeNowExchange.exchangeName;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
.read(currentExchangeNameStateProvider.state)
.state);
}
},
child: Container(
color: Colors.transparent,
child: Padding(
padding: isDesktop
? const EdgeInsets.all(16)
: const EdgeInsets.all(0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: ChangeNowExchange.exchangeName,
groupValue: ref
.watch(currentExchangeNameStateProvider.state)
.state,
onChanged: (value) {
if (value is String) {
ref
.read(currentExchangeNameStateProvider.state)
.state);
}
},
),
),
const SizedBox(
width: 14,
),
SvgPicture.asset(
Assets.exchange.changeNow,
width: 24,
height: 24,
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ChangeNowExchange.exchangeName,
style: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
.state = value;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
.read(currentExchangeNameStateProvider
.state)
.state);
}
},
),
if (from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero)
FutureBuilder(
future: ChangeNowExchange().getEstimate(
from!,
to!,
reversed ? toAmount! : fromAmount!,
fixedRate,
reversed,
),
const SizedBox(
width: 14,
),
SvgPicture.asset(
Assets.exchange.changeNow,
width: isDesktop ? 32 : 24,
height: isDesktop ? 32 : 24,
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ChangeNowExchange.exchangeName,
style: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
),
builder: (context,
AsyncSnapshot<ExchangeResponse<Estimate>>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
final estimate = snapshot.data?.value;
if (estimate != null) {
Decimal rate;
if (estimate.reversed) {
rate =
(toAmount! / estimate.estimatedAmount)
if (from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero)
FutureBuilder(
future: ChangeNowExchange().getEstimate(
from!,
to!,
reversed ? toAmount! : fromAmount!,
fixedRate,
reversed,
),
builder: (context,
AsyncSnapshot<ExchangeResponse<Estimate>>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
final estimate = snapshot.data?.value;
if (estimate != null) {
Decimal rate;
if (estimate.reversed) {
rate = (toAmount! /
estimate.estimatedAmount)
.toDecimal(
scaleOnInfinitePrecision: 12);
} else {
rate = (estimate.estimatedAmount /
fromAmount!)
.toDecimal(
scaleOnInfinitePrecision: 12);
}
return Text(
"1 ${from!.toUpperCase()} ~ ${Format.localizedStringAsFixed(
value: rate,
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale),
),
decimalPlaces: to!.toUpperCase() ==
Coin.monero.ticker
.toUpperCase()
? Constants.decimalPlacesMonero
: Constants.decimalPlaces,
)} ${to!.toUpperCase()}",
style:
STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
Logging.instance.log(
"$runtimeType failed to fetch rate for ChangeNOW: ${snapshot.data}",
level: LogLevel.Warning,
);
return Text(
"Failed to fetch rate",
style:
STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
} else {
rate =
(estimate.estimatedAmount / fromAmount!)
.toDecimal(
scaleOnInfinitePrecision: 12);
}
return Text(
"1 ${from!.toUpperCase()} ~ ${Format.localizedStringAsFixed(
value: rate,
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
return AnimatedText(
stringsToLoopThrough: const [
"Loading",
"Loading.",
"Loading..",
"Loading...",
],
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
decimalPlaces: to!.toUpperCase() ==
Coin.monero.ticker.toUpperCase()
? Constants.decimalPlacesMonero
: Constants.decimalPlaces,
)} ${to!.toUpperCase()}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
Logging.instance.log(
"$runtimeType failed to fetch rate for ChangeNOW: ${snapshot.data}",
level: LogLevel.Warning,
);
return Text(
"Failed to fetch rate",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading",
"Loading.",
"Loading..",
"Loading...",
],
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
},
),
if (!(from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero))
Text(
"n/a",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
);
}
},
),
if (!(from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero))
Text(
"n/a",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
],
),
),
),
),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
if (ref.read(currentExchangeNameStateProvider.state).state !=
SimpleSwapExchange.exchangeName) {
ref.read(currentExchangeNameStateProvider.state).state =
SimpleSwapExchange.exchangeName;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(
ref.read(currentExchangeNameStateProvider.state).state);
}
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: SimpleSwapExchange.exchangeName,
groupValue: ref
.watch(currentExchangeNameStateProvider.state)
.state,
onChanged: (value) {
if (value is String) {
ref
.read(currentExchangeNameStateProvider.state)
.state = value;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
if (isDesktop)
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
if (!isDesktop)
const SizedBox(
height: 16,
),
ConditionalParent(
condition: isDesktop,
builder: (child) => MouseRegion(
cursor: SystemMouseCursors.click,
child: child,
),
child: GestureDetector(
onTap: () {
if (ref.read(currentExchangeNameStateProvider.state).state !=
SimpleSwapExchange.exchangeName) {
ref.read(currentExchangeNameStateProvider.state).state =
SimpleSwapExchange.exchangeName;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
.read(currentExchangeNameStateProvider.state)
.state);
}
},
child: Container(
color: Colors.transparent,
child: Padding(
padding: isDesktop
? const EdgeInsets.all(16)
: const EdgeInsets.all(0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: SimpleSwapExchange.exchangeName,
groupValue: ref
.watch(currentExchangeNameStateProvider.state)
.state,
onChanged: (value) {
if (value is String) {
ref
.read(currentExchangeNameStateProvider.state)
.state);
}
},
),
),
const SizedBox(
width: 14,
),
SvgPicture.asset(
Assets.exchange.simpleSwap,
width: 24,
height: 24,
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
SimpleSwapExchange.exchangeName,
style: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
.state = value;
ref.read(exchangeFormStateProvider).exchange =
Exchange.fromName(ref
.read(currentExchangeNameStateProvider
.state)
.state);
}
},
),
if (from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero)
FutureBuilder(
future: SimpleSwapExchange().getEstimate(
from!,
to!,
// reversed ? toAmount! : fromAmount!,
fromAmount!,
fixedRate,
// reversed,
false,
),
const SizedBox(
width: 14,
),
SvgPicture.asset(
Assets.exchange.simpleSwap,
width: isDesktop ? 32 : 24,
height: isDesktop ? 32 : 24,
),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
SimpleSwapExchange.exchangeName,
style: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
),
builder: (context,
AsyncSnapshot<ExchangeResponse<Estimate>>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
final estimate = snapshot.data?.value;
if (estimate != null) {
Decimal rate = (estimate.estimatedAmount /
fromAmount!)
.toDecimal(scaleOnInfinitePrecision: 12);
if (from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero)
FutureBuilder(
future: SimpleSwapExchange().getEstimate(
from!,
to!,
// reversed ? toAmount! : fromAmount!,
fromAmount!,
fixedRate,
// reversed,
false,
),
builder: (context,
AsyncSnapshot<ExchangeResponse<Estimate>>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
final estimate = snapshot.data?.value;
if (estimate != null) {
Decimal rate = (estimate.estimatedAmount /
fromAmount!)
.toDecimal(
scaleOnInfinitePrecision: 12);
return Text(
"1 ${from!.toUpperCase()} ~ ${Format.localizedStringAsFixed(
value: rate,
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
return Text(
"1 ${from!.toUpperCase()} ~ ${Format.localizedStringAsFixed(
value: rate,
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale),
),
decimalPlaces: to!.toUpperCase() ==
Coin.monero.ticker
.toUpperCase()
? Constants.decimalPlacesMonero
: Constants.decimalPlaces,
)} ${to!.toUpperCase()}",
style:
STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
Logging.instance.log(
"$runtimeType failed to fetch rate for SimpleSwap: ${snapshot.data}",
level: LogLevel.Warning,
);
return Text(
"Failed to fetch rate",
style:
STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading",
"Loading.",
"Loading..",
"Loading...",
],
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
decimalPlaces: to!.toUpperCase() ==
Coin.monero.ticker.toUpperCase()
? Constants.decimalPlacesMonero
: Constants.decimalPlaces,
)} ${to!.toUpperCase()}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
} else {
Logging.instance.log(
"$runtimeType failed to fetch rate for SimpleSwap: ${snapshot.data}",
level: LogLevel.Warning,
);
return Text(
"Failed to fetch rate",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading",
"Loading.",
"Loading..",
"Loading...",
],
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
);
}
},
),
// if (!(from != null &&
// to != null &&
// (reversed
// ? toAmount != null && toAmount! > Decimal.zero
// : fromAmount != null &&
// fromAmount! > Decimal.zero)))
if (!(from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero))
Text(
"n/a",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
);
}
},
),
// if (!(from != null &&
// to != null &&
// (reversed
// ? toAmount != null && toAmount! > Decimal.zero
// : fromAmount != null &&
// fromAmount! > Decimal.zero)))
if (!(from != null &&
to != null &&
toAmount != null &&
toAmount! > Decimal.zero &&
fromAmount != null &&
fromAmount! > Decimal.zero))
Text(
"n/a",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
],
),
),
),
),

View file

@ -7,8 +7,9 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class RateTypeToggle extends ConsumerWidget {
const RateTypeToggle({
@ -21,106 +22,177 @@ class RateTypeToggle extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
debugPrint("BUILD: $runtimeType");
final isDesktop = Util.isDesktop;
final estimated = ref.watch(prefsChangeNotifierProvider
.select((value) => value.exchangeRateType)) ==
ExchangeRateType.estimated;
return RoundedWhiteContainer(
return RoundedContainer(
padding: const EdgeInsets.all(0),
color: isDesktop
? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
: Theme.of(context).extension<StackColors>()!.popupBG,
child: Row(
children: [
Expanded(
child: GestureDetector(
onTap: () {
if (!estimated) {
ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.estimated;
onChanged?.call(ExchangeRateType.estimated);
}
},
child: RoundedContainer(
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG
: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.lock,
width: 12,
height: 14,
color: estimated
? Theme.of(context).extension<StackColors>()!.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
Text(
"Estimate rate",
style: STextStyles.smallMed12(context).copyWith(
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
child: ConditionalParent(
condition: isDesktop,
builder: (child) => MouseRegion(
cursor: estimated
? SystemMouseCursors.basic
: SystemMouseCursors.click,
child: child,
),
child: GestureDetector(
onTap: () {
if (!estimated) {
ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.estimated;
onChanged?.call(ExchangeRateType.estimated);
}
},
child: RoundedContainer(
padding: isDesktop
? const EdgeInsets.all(17)
: const EdgeInsets.all(0),
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG
: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.lockOpen,
width: 12,
height: 14,
color: isDesktop
? estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
const SizedBox(
width: 5,
),
Text(
"Estimate rate",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
)
: STextStyles.smallMed12(context).copyWith(
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () {
if (estimated) {
ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.fixed;
onChanged?.call(ExchangeRateType.fixed);
}
},
child: RoundedContainer(
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG
: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.lock,
width: 12,
height: 14,
color: !estimated
? Theme.of(context).extension<StackColors>()!.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
Text(
"Fixed rate",
style: STextStyles.smallMed12(context).copyWith(
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
child: ConditionalParent(
condition: isDesktop,
builder: (child) => MouseRegion(
cursor: !estimated
? SystemMouseCursors.basic
: SystemMouseCursors.click,
child: child,
),
child: GestureDetector(
onTap: () {
if (estimated) {
ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.fixed;
onChanged?.call(ExchangeRateType.fixed);
}
},
child: RoundedContainer(
padding: isDesktop
? const EdgeInsets.all(17)
: const EdgeInsets.all(0),
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG
: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.lock,
width: 12,
height: 14,
color: isDesktop
? !estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
const SizedBox(
width: 5,
),
Text(
"Fixed rate",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
)
: STextStyles.smallMed12(context).copyWith(
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
),
),

View file

@ -206,16 +206,59 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
padding: const EdgeInsets.only(
right: 12,
),
child: RoundedWhiteContainer(
borderColor: isDesktop
? Theme.of(context).extension<StackColors>()!.background
: null,
padding: const EdgeInsets.all(0),
child: ListView(
primary: false,
shrinkWrap: true,
children: children,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
RoundedWhiteContainer(
borderColor:
Theme.of(context).extension<StackColors>()!.background,
padding: const EdgeInsets.all(0),
child: ListView(
primary: false,
shrinkWrap: true,
children: children,
),
),
if (!hasTx &&
isStackCoin(trade.payInCurrency) &&
(trade.status == "New" ||
trade.status == "new" ||
trade.status == "waiting" ||
trade.status == "Waiting"))
const SizedBox(
height: 32,
),
if (!hasTx &&
isStackCoin(trade.payInCurrency) &&
(trade.status == "New" ||
trade.status == "new" ||
trade.status == "waiting" ||
trade.status == "Waiting"))
SecondaryButton(
label: "Send from Stack",
buttonHeight: ButtonHeight.l,
onPressed: () {
final amount = sendAmount;
final address = trade.payInAddress;
final coin =
coinFromTickerCaseInsensitive(trade.payInCurrency);
Navigator.of(context).pushNamed(
SendFromView.routeName,
arguments: Tuple4(
coin,
amount,
address,
trade,
),
);
},
),
const SizedBox(
height: 32,
),
],
),
),
),
@ -350,33 +393,94 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
padding: isDesktop
? const EdgeInsets.all(16)
: const EdgeInsets.all(12),
color: Theme.of(context)
.extension<StackColors>()!
.warningBackground,
child: RichText(
text: TextSpan(
text:
"You must send at least ${sendAmount.toStringAsFixed(
trade.payInCurrency.toLowerCase() == "xmr" ? 12 : 8,
)} ${trade.payInCurrency.toUpperCase()}. ",
style: STextStyles.label700(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
),
children: [
TextSpan(
text:
"If you send less than ${sendAmount.toStringAsFixed(
trade.payInCurrency.toLowerCase() == "xmr" ? 12 : 8,
)} ${trade.payInCurrency.toUpperCase()}, your transaction may not be converted and it may not be refunded.",
style: STextStyles.label(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
color: isDesktop
? Theme.of(context).extension<StackColors>()!.popupBG
: Theme.of(context)
.extension<StackColors>()!
.warningBackground,
child: ConditionalParent(
condition: isDesktop,
builder: (child) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Amount",
style: STextStyles.desktopTextExtraExtraSmall(
context),
),
const SizedBox(
height: 2,
),
Text(
"${trade.payInAmount} ${trade.payInCurrency.toUpperCase()}",
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
),
]),
IconCopyButton(
data: trade.payInAmount,
),
],
),
const SizedBox(
height: 6,
),
child,
],
),
child: RichText(
text: TextSpan(
text:
"You must send at least ${sendAmount.toStringAsFixed(
trade.payInCurrency.toLowerCase() == "xmr" ? 12 : 8,
)} ${trade.payInCurrency.toUpperCase()}. ",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorRed)
: STextStyles.label(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
),
children: [
TextSpan(
text:
"If you send less than ${sendAmount.toStringAsFixed(
trade.payInCurrency.toLowerCase() == "xmr"
? 12
: 8,
)} ${trade.payInCurrency.toUpperCase()}, your transaction may not be converted and it may not be refunded.",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorRed)
: STextStyles.label(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
),
),
]),
),
),
),
if (sentFromStack)
@ -1035,12 +1139,13 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
],
),
),
isDesktop
? const _Divider()
: const SizedBox(
height: 12,
),
if (isStackCoin(trade.payInCurrency) &&
if (!isDesktop)
const SizedBox(
height: 12,
),
if (!isDesktop &&
!hasTx &&
isStackCoin(trade.payInCurrency) &&
(trade.status == "New" ||
trade.status == "new" ||
trade.status == "waiting" ||

View file

@ -530,7 +530,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
});
}
: onGeneratePressed,
desktopMed: true,
buttonHeight: ButtonHeight.l,
),
if (isDesktop && didGenerate)
Row(
@ -586,7 +586,6 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
if (!isDesktop)
SecondaryButton(
width: 170,
desktopMed: true,
onPressed: () async {
await _capturePng(false);
},
@ -606,7 +605,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
),
PrimaryButton(
width: 170,
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () async {
// TODO: add save functionality instead of share
// save works on linux at the moment

View file

@ -37,6 +37,7 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
required this.transactionInfo,
required this.walletId,
this.routeOnSuccessName = WalletView.routeName,
this.isTradeTransaction = false,
}) : super(key: key);
static const String routeName = "/confirmTransactionView";
@ -44,6 +45,7 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
final Map<String, dynamic> transactionInfo;
final String walletId;
final String routeOnSuccessName;
final bool isTradeTransaction;
@override
ConsumerState<ConfirmTransactionView> createState() =>
@ -148,7 +150,7 @@ class _ConfirmTransactionViewState
const Spacer(),
Expanded(
child: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Ok",
onPressed: Navigator.of(context).pop,
),
@ -780,7 +782,7 @@ class _ConfirmTransactionViewState
: const EdgeInsets.all(0),
child: PrimaryButton(
label: "Send",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () async {
final dynamic unlocked;
@ -833,8 +835,19 @@ class _ConfirmTransactionViewState
);
}
if (unlocked is bool && unlocked && mounted) {
unawaited(_attemptSend(context));
if (mounted) {
if (unlocked == true) {
unawaited(_attemptSend(context));
} else {
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: Util.isDesktop
? "Invalid passphrase"
: "Invalid PIN",
context: context),
);
}
}
},
),

View file

@ -77,7 +77,7 @@ class _RestoringDialogState extends State<BuildingTransactionDialog>
height: 40,
),
SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Cancel",
onPressed: () {
onCancel.call();

View file

@ -161,7 +161,7 @@ class _FiroBalanceSelectionSheetState
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!} ${manager.coin.ticker}",
"${snapshot.data!.toStringAsFixed(8)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
);
@ -251,7 +251,7 @@ class _FiroBalanceSelectionSheetState
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!} ${manager.coin.ticker}",
"${snapshot.data!.toStringAsFixed(8)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
);

View file

@ -8,6 +8,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/color_theme.dart';
import 'package:stackwallet/utilities/theme/dark_colors.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/ocean_breeze_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
@ -18,6 +19,17 @@ class AppearanceSettingsView extends ConsumerWidget {
static const String routeName = "/appearanceSettings";
String chooseThemeType(ThemeType type) {
switch (type) {
case ThemeType.light:
return "Light theme";
case ThemeType.oceanBreeze:
return "Ocean theme";
case ThemeType.dark:
return "Dark theme";
}
}
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
@ -100,65 +112,39 @@ class AppearanceSettingsView extends ConsumerWidget {
height: 10,
),
RoundedWhiteContainer(
child: Consumer(
builder: (_, ref, __) {
return RawMaterialButton(
splashColor: Theme.of(context)
.extension<StackColors>()!
.highlight,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: null,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
padding: const EdgeInsets.all(0),
child: RawMaterialButton(
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
padding: const EdgeInsets.all(0),
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: null,
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Enable dark mode",
"Choose Theme",
style: STextStyles.titleBold12(context),
textAlign: TextAlign.left,
),
SizedBox(
height: 20,
width: 40,
child: DraggableSwitchButton(
isOn: (DB.instance.get<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme")
as String?) ==
"dark",
onValueChanged: (newValue) {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: (newValue
? ThemeType.dark
: ThemeType.light)
.name,
);
ref
.read(colorThemeProvider.state)
.state =
StackColors.fromStackColorTheme(
newValue
? DarkColors()
: LightColors());
},
),
)
const Padding(
padding: EdgeInsets.all(10),
child: ThemeOptionsView(),
),
],
),
),
);
},
],
),
),
),
),
],
@ -172,3 +158,274 @@ class AppearanceSettingsView extends ConsumerWidget {
);
}
}
class ThemeOptionsView extends ConsumerStatefulWidget {
const ThemeOptionsView({
Key? key,
}) : super(key: key);
@override
ConsumerState<ThemeOptionsView> createState() => _ThemeOptionsView();
}
class _ThemeOptionsView extends ConsumerState<ThemeOptionsView> {
late String _selectedTheme;
@override
void initState() {
_selectedTheme =
DB.instance.get<dynamic>(boxName: DB.boxNameTheme, key: "colorScheme")
as String? ??
"light";
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
MaterialButton(
splashColor: Colors.transparent,
hoverColor: Colors.transparent,
padding: const EdgeInsets.all(0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.light.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
LightColors(),
);
setState(() {
_selectedTheme = "light";
});
},
child: SizedBox(
width: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SizedBox(
width: 10,
height: 10,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "light",
groupValue: _selectedTheme,
onChanged: (newValue) {
if (newValue is String && newValue == "light") {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.light.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
LightColors(),
);
setState(() {
_selectedTheme = "light";
});
}
},
),
),
const SizedBox(
width: 14,
),
Text(
"Light",
style:
STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
),
],
),
],
),
),
),
const SizedBox(
height: 10,
),
MaterialButton(
splashColor: Colors.transparent,
hoverColor: Colors.transparent,
padding: const EdgeInsets.all(0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.oceanBreeze.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
OceanBreezeColors(),
);
setState(() {
_selectedTheme = "oceanBreeze";
});
},
child: SizedBox(
width: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SizedBox(
width: 10,
height: 10,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "oceanBreeze",
groupValue: _selectedTheme,
onChanged: (newValue) {
if (newValue is String && newValue == "oceanBreeze") {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.oceanBreeze.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
OceanBreezeColors(),
);
setState(() {
_selectedTheme = "oceanBreeze";
});
}
},
),
),
const SizedBox(
width: 14,
),
Text(
"Ocean Breeze",
style:
STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
),
],
),
],
),
),
),
const SizedBox(
height: 10,
),
MaterialButton(
splashColor: Colors.transparent,
hoverColor: Colors.transparent,
padding: const EdgeInsets.all(0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.dark.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
DarkColors(),
);
setState(() {
_selectedTheme = "dark";
});
},
child: SizedBox(
width: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SizedBox(
width: 10,
height: 10,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "dark",
groupValue: _selectedTheme,
onChanged: (newValue) {
if (newValue is String && newValue == "dark") {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.dark.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
DarkColors(),
);
setState(() {
_selectedTheme = "dark";
});
}
},
),
),
const SizedBox(
width: 14,
),
Text(
"Dark",
style:
STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
),
],
),
],
),
),
),
],
);
}
}

View file

@ -189,7 +189,7 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
Expanded(
child: SecondaryButton(
label: "Cancel",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
@ -199,7 +199,7 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
Expanded(
child: PrimaryButton(
label: "Save changes",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () {
ref.read(prefsChangeNotifierProvider).currency =
current;

View file

@ -238,7 +238,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
Expanded(
child: SecondaryButton(
label: "Cancel",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () => Navigator.of(
context,
rootNavigator: true,
@ -251,7 +251,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
Expanded(
child: PrimaryButton(
label: "Save",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () => Navigator.of(
context,
rootNavigator: true,
@ -494,6 +494,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
condition: isDesktop,
builder: (child) => DesktopDialog(
maxWidth: 580,
maxHeight: 500,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
@ -561,7 +562,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
child: SecondaryButton(
label: "Test connection",
enabled: testConnectionEnabled,
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: testConnectionEnabled
? () async {
await _testConnection();
@ -578,7 +579,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
child: PrimaryButton(
label: "Save",
enabled: saveEnabled,
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: saveEnabled ? attemptSave : null,
),
),
@ -830,110 +831,100 @@ class _NodeFormState extends ConsumerState<NodeForm> {
const SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
key: const Key("addCustomNodeNodeAddressFieldKey"),
readOnly: widget.readOnly,
enabled: enableField(_hostController),
controller: _hostController,
focusNode: _hostFocusNode,
style: STextStyles.field(context),
decoration: standardInputDecoration(
(widget.coin != Coin.monero &&
widget.coin != Coin.wownero &&
widget.coin != Coin.epicCash)
? "IP address"
: "Url",
_hostFocusNode,
context,
).copyWith(
suffixIcon:
!widget.readOnly && _hostController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
_hostController.text = "";
_updateState();
},
),
],
),
),
)
: null,
),
onChanged: (newValue) {
_updateState();
setState(() {});
},
),
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
key: const Key("addCustomNodeNodeAddressFieldKey"),
readOnly: widget.readOnly,
enabled: enableField(_hostController),
controller: _hostController,
focusNode: _hostFocusNode,
style: STextStyles.field(context),
decoration: standardInputDecoration(
(widget.coin != Coin.monero &&
widget.coin != Coin.wownero &&
widget.coin != Coin.epicCash)
? "IP address"
: "Url",
_hostFocusNode,
context,
).copyWith(
suffixIcon: !widget.readOnly && _hostController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
_hostController.text = "";
_updateState();
},
),
],
),
),
)
: null,
),
const SizedBox(
width: 12,
onChanged: (newValue) {
_updateState();
setState(() {});
},
),
),
const SizedBox(
height: 8,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
key: const Key("addCustomNodeNodePortFieldKey"),
readOnly: widget.readOnly,
enabled: enableField(_portController),
controller: _portController,
focusNode: _portFocusNode,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
keyboardType: TextInputType.number,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Port",
_portFocusNode,
context,
).copyWith(
suffixIcon: !widget.readOnly && _portController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
_portController.text = "";
_updateState();
},
),
],
),
),
)
: null,
),
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
key: const Key("addCustomNodeNodePortFieldKey"),
readOnly: widget.readOnly,
enabled: enableField(_portController),
controller: _portController,
focusNode: _portFocusNode,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
keyboardType: TextInputType.number,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Port",
_portFocusNode,
context,
).copyWith(
suffixIcon:
!widget.readOnly && _portController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
_portController.text = "";
_updateState();
},
),
],
),
),
)
: null,
),
onChanged: (newValue) {
_updateState();
setState(() {});
},
),
),
),
],
onChanged: (newValue) {
_updateState();
setState(() {});
},
),
),
const SizedBox(
height: 8,

View file

@ -349,7 +349,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
Expanded(
child: SecondaryButton(
label: "Test connection",
desktopMed: true,
buttonHeight: isDesktop ? ButtonHeight.l : null,
onPressed: () async {
await _testConnection(ref, context);
},
@ -364,7 +364,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
child: !nodeId.startsWith("default")
? PrimaryButton(
label: _desktopReadOnly ? "Edit" : "Save",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () async {
final shouldSave = _desktopReadOnly == false;
setState(() {

View file

@ -562,7 +562,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
Consumer(builder: (context, ref, __) {
return PrimaryButton(
width: 183,
desktopMed: true,
buttonHeight: ButtonHeight.m,
label: "Create backup",
enabled: shouldEnableCreate,
onPressed: !shouldEnableCreate
@ -735,7 +735,9 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
child:
PrimaryButton(
label: "Ok",
desktopMed: true,
buttonHeight:
ButtonHeight
.l,
onPressed: () {
int count = 0;
Navigator.of(
@ -778,7 +780,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
),
SecondaryButton(
width: 183,
desktopMed: true,
buttonHeight: ButtonHeight.m,
label: "Cancel",
onPressed: () {},
),

View file

@ -85,7 +85,7 @@ class CancelStackRestoreDialog extends StatelessWidget {
children: [
SecondaryButton(
width: 248,
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: true,
label: "Keep restoring",
onPressed: () {
@ -95,7 +95,7 @@ class CancelStackRestoreDialog extends StatelessWidget {
const SizedBox(width: 20),
PrimaryButton(
width: 248,
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: true,
label: "Cancel anyway",
onPressed: () {

View file

@ -754,7 +754,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
Expanded(
child: SecondaryButton(
label: "Cancel",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
@ -764,7 +764,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
Expanded(
child: PrimaryButton(
label: "Save",
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: shouldEnableCreate,
onPressed: onSavePressed,
),

View file

@ -389,7 +389,7 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
children: [
PrimaryButton(
width: 183,
desktopMed: true,
buttonHeight: ButtonHeight.m,
label: "Restore",
enabled: !(passwordController.text.isEmpty ||
fileLocationController.text.isEmpty),
@ -566,7 +566,7 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
),
SecondaryButton(
width: 183,
desktopMed: true,
buttonHeight: ButtonHeight.m,
label: "Cancel",
onPressed: () {},
),

View file

@ -108,7 +108,7 @@ class _StackRestoreProgressViewState
// children: [
// SecondaryButton(
// width: 248,
// desktopMed: true,
// buttonHeight: ButtonHeight.l,
// enabled: true,
// label: "Keep restoring",
// onPressed: () {
@ -118,7 +118,7 @@ class _StackRestoreProgressViewState
// const SizedBox(width: 16),
// PrimaryButton(
// width: 248,
// desktopMed: true,
// buttonHeight: ButtonHeight.l,
// enabled: true,
// label: "Cancel anyway",
// onPressed: () {
@ -681,7 +681,7 @@ class _StackRestoreProgressViewState
_success
? PrimaryButton(
width: 248,
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: true,
label: "Done",
onPressed: () async {
@ -690,7 +690,7 @@ class _StackRestoreProgressViewState
)
: SecondaryButton(
width: 248,
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: true,
label: "Cancel restore process",
onPressed: () async {

View file

@ -58,7 +58,7 @@ class ConfirmFullRescanDialog extends StatelessWidget {
children: [
Expanded(
child: SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
label: "Cancel",
),
@ -68,7 +68,7 @@ class ConfirmFullRescanDialog extends StatelessWidget {
),
Expanded(
child: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () {
Navigator.of(context).pop();
onConfirm.call();

View file

@ -77,7 +77,7 @@ class _WalletNetworkSettingsViewState
late double _percent;
late int _blocksRemaining;
bool _advancedIsExpanded = true;
bool _advancedIsExpanded = false;
Future<void> _attemptRescan() async {
if (!Platform.isLinux) await Wakelock.enable();
@ -855,8 +855,8 @@ class _WalletNetworkSettingsViewState
),
SvgPicture.asset(
_advancedIsExpanded
? Assets.svg.chevronDown
: Assets.svg.chevronUp,
? Assets.svg.chevronUp
: Assets.svg.chevronDown,
width: 12,
height: 6,
color: Theme.of(context)
@ -877,11 +877,11 @@ class _WalletNetworkSettingsViewState
text: "Rescan",
onTap: () async {
await Navigator.of(context).push(
FadePageRoute<void>(
FadePageRoute<void>(
ConfirmFullRescanDialog(
onConfirm: _attemptRescan,
),
const RouteSettings(),
const RouteSettings(),
),
);
// await showDialog<dynamic>(

View file

@ -3,14 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
class WalletBalanceToggleSheet extends ConsumerWidget {
const WalletBalanceToggleSheet({
Key? key,
@ -153,7 +152,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
snapshot.hasData &&
snapshot.data != null) {
return Text(
"${snapshot.data!}",
"${snapshot.data!.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} ${coin.ticker}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
@ -195,7 +194,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
snapshot.hasData &&
snapshot.data != null) {
return Text(
"${snapshot.data!}",
"${snapshot.data!.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} ${coin.ticker}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
@ -287,7 +286,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
snapshot.hasData &&
snapshot.data != null) {
return Text(
"${snapshot.data!}",
"${snapshot.data!.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} ${coin.ticker}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
@ -329,7 +328,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
snapshot.hasData &&
snapshot.data != null) {
return Text(
"${snapshot.data!}",
"${snapshot.data!.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} ${coin.ticker}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)

View file

@ -385,7 +385,7 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
),
if (isDesktop)
SecondaryButton(
desktopMed: isDesktop,
buttonHeight: ButtonHeight.l,
width: 200,
label: "Filter",
icon: SvgPicture.asset(

View file

@ -30,6 +30,8 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/copy_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/pencil_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -154,60 +156,142 @@ class _TransactionDetailsViewState
Future<bool> showExplorerWarning(String explorer) async {
final bool? shouldContinue = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (_) => StackDialog(
title: "Attention",
message:
"You are about to view this transaction in a block explorer. The explorer may log your IP address and link it to the transaction. Only proceed if you trust $explorer.",
icon: Row(
children: [
Consumer(builder: (_, ref, __) {
return Checkbox(
value: ref.watch(prefsChangeNotifierProvider
.select((value) => value.hideBlockExplorerWarning)),
onChanged: (value) {
if (value is bool) {
ref
.read(prefsChangeNotifierProvider)
.hideBlockExplorerWarning = value;
setState(() {});
}
context: context,
barrierDismissible: false,
builder: (_) {
if (!isDesktop) {
return StackDialog(
title: "Attention",
message:
"You are about to view this transaction in a block explorer. The explorer may log your IP address and link it to the transaction. Only proceed if you trust $explorer.",
icon: Row(
children: [
Consumer(builder: (_, ref, __) {
return Checkbox(
value: ref.watch(prefsChangeNotifierProvider
.select((value) => value.hideBlockExplorerWarning)),
onChanged: (value) {
if (value is bool) {
ref
.read(prefsChangeNotifierProvider)
.hideBlockExplorerWarning = value;
setState(() {});
}
},
);
}),
Text(
"Never show again",
style: STextStyles.smallMed14(context),
)
],
),
leftButton: TextButton(
onPressed: () {
Navigator.of(context).pop(false);
},
);
}),
Text(
"Never show again",
style: STextStyles.smallMed14(context),
)
],
),
leftButton: TextButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
onPressed: () {
Navigator.of(context).pop(true);
},
child: Text(
"Continue",
style: STextStyles.button(context),
),
),
),
);
.getPrimaryEnabledButtonColor(context),
onPressed: () {
Navigator.of(context).pop(true);
},
child: Text(
"Continue",
style: STextStyles.button(context),
),
),
);
} else {
return DesktopDialog(
maxWidth: 550,
maxHeight: 300,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 32, vertical: 20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Attention",
style: STextStyles.desktopH2(context),
),
Row(
children: [
Consumer(builder: (_, ref, __) {
return Checkbox(
value: ref.watch(prefsChangeNotifierProvider
.select((value) =>
value.hideBlockExplorerWarning)),
onChanged: (value) {
if (value is bool) {
ref
.read(prefsChangeNotifierProvider)
.hideBlockExplorerWarning = value;
setState(() {});
}
},
);
}),
Text(
"Never show again",
style: STextStyles.smallMed14(context),
)
],
),
],
),
const SizedBox(height: 16),
Text(
"You are about to view this transaction in a block explorer. The explorer may log your IP address and link it to the transaction. Only proceed if you trust $explorer.",
style: STextStyles.desktopTextSmall(context),
),
const SizedBox(height: 35),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SecondaryButton(
width: 200,
buttonHeight: ButtonHeight.l,
label: "Cancel",
onPressed: () {
Navigator.of(
context,
rootNavigator: true,
).pop(false);
},
),
const SizedBox(width: 20),
PrimaryButton(
width: 200,
buttonHeight: ButtonHeight.l,
label: "Continue",
onPressed: () {
Navigator.of(
context,
rootNavigator: true,
).pop(true);
},
),
],
),
],
),
),
);
}
});
return shouldContinue ?? false;
}
@ -995,15 +1079,17 @@ class _TransactionDetailsViewState
.externalApplication,
);
} catch (_) {
unawaited(showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title:
"Could not open in block explorer",
message:
"Failed to open \"${uri.toString()}\"",
unawaited(
showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title:
"Could not open in block explorer",
message:
"Failed to open \"${uri.toString()}\"",
),
),
));
);
} finally {
// Future<void>.delayed(
// const Duration(seconds: 1),

View file

@ -869,7 +869,7 @@ class _TransactionSearchViewState
Expanded(
child: SecondaryButton(
label: "Cancel",
desktopMed: isDesktop,
buttonHeight: ButtonHeight.l,
onPressed: () async {
if (!isDesktop) {
if (FocusScope.of(context).hasFocus) {
@ -919,7 +919,7 @@ class _TransactionSearchViewState
),
Expanded(
child: PrimaryButton(
desktopMed: isDesktop,
buttonHeight: ButtonHeight.l,
onPressed: () async {
await _onApplyPressed();
},

View file

@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/pages/exchange_view/exchange_form.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopExchangeView extends StatefulWidget {
const DesktopExchangeView({Key? key}) : super(key: key);
static const String routeName = "/desktopExchange";
@override
State<DesktopExchangeView> createState() => _DesktopExchangeViewState();
}
class _DesktopExchangeViewState extends State<DesktopExchangeView> {
@override
Widget build(BuildContext context) {
return DesktopScaffold(
appBar: DesktopAppBar(
isCompactHeight: true,
leading: Padding(
padding: const EdgeInsets.only(
left: 24,
),
child: Text(
"Exchange",
style: STextStyles.desktopH3(context),
),
),
),
body: Padding(
padding: const EdgeInsets.only(
left: 24,
right: 24,
bottom: 24,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Exchange details",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
const SizedBox(
height: 16,
),
const RoundedWhiteContainer(
padding: EdgeInsets.all(24),
child: ExchangeForm(),
),
],
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Row(
children: const [
Expanded(
child: DesktopTradeHistory(),
),
],
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
class StepScaffold extends StatefulWidget {
const StepScaffold({
Key? key,
required this.body,
required this.step,
required this.model,
}) : super(key: key);
final Widget body;
final int step;
final IncompleteExchangeModel model;
@override
State<StepScaffold> createState() => _StepScaffoldState();
}
class _StepScaffoldState extends State<StepScaffold> {
int currentStep = 0;
late final IncompleteExchangeModel model;
@override
void initState() {
currentStep = widget.step;
model = widget.model;
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
const AppBarBackButton(
isCompact: true,
iconSize: 23,
),
Text(
"Exchange ${model.sendTicker.toUpperCase()} to ${model.receiveTicker.toUpperCase()}",
style: STextStyles.desktopH3(context),
),
],
),
const SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32,
),
child: DesktopExchangeStepsIndicator(
currentStep: currentStep,
),
),
const SizedBox(
height: 32,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32,
),
child: widget.body,
),
],
);
}
}

View file

@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopStep1 extends ConsumerWidget {
const DesktopStep1({
Key? key,
required this.model,
}) : super(key: key);
final IncompleteExchangeModel model;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Column(
children: [
Text(
"Confirm amount",
style: STextStyles.desktopTextMedium(context),
),
const SizedBox(
height: 8,
),
Text(
"Network fees and other exchange charges are included in the rate.",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
const SizedBox(
height: 20,
),
RoundedWhiteContainer(
borderColor: Theme.of(context).extension<StackColors>()!.background,
padding: const EdgeInsets.all(0),
child: Column(
children: [
DesktopStepItem(
label: "Exchange",
value: ref.watch(currentExchangeNameStateProvider.state).state,
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: "You send",
value:
"${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}",
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: "You receive",
value:
"~${model.receiveAmount.toStringAsFixed(8)} ${model.receiveTicker.toUpperCase()}",
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: model.rateType == ExchangeRateType.estimated
? "Estimated rate"
: "Fixed rate",
value: model.rateInfo,
),
],
),
),
Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 32,
),
child: Row(
children: [
Expanded(
child: SecondaryButton(
label: "Back",
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Next",
buttonHeight: ButtonHeight.l,
onPressed: () async {
await showDialog<void>(
context: context,
barrierColor: Colors.transparent,
barrierDismissible: false,
builder: (context) {
return DesktopDialog(
maxWidth: 720,
maxHeight: double.infinity,
child: StepScaffold(
step: 2,
model: model,
body: DesktopStep2(
model: model,
),
),
);
},
);
},
),
),
],
),
),
],
);
}
}

View file

@ -0,0 +1,620 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
class DesktopStep2 extends ConsumerStatefulWidget {
const DesktopStep2({
Key? key,
required this.model,
this.clipboard = const ClipboardWrapper(),
}) : super(key: key);
final IncompleteExchangeModel model;
final ClipboardInterface clipboard;
@override
ConsumerState<DesktopStep2> createState() => _DesktopStep2State();
}
class _DesktopStep2State extends ConsumerState<DesktopStep2> {
late final IncompleteExchangeModel model;
late final ClipboardInterface clipboard;
late final TextEditingController _toController;
late final TextEditingController _refundController;
late final FocusNode _toFocusNode;
late final FocusNode _refundFocusNode;
bool enableNext = false;
bool isStackCoin(String ticker) {
try {
coinFromTickerCaseInsensitive(ticker);
return true;
} on ArgumentError catch (_) {
return false;
}
}
void selectRecipientAddressFromStack() async {
try {
final coin = coinFromTickerCaseInsensitive(
model.receiveTicker,
);
final address = await showDialog<String?>(
context: context,
barrierColor: Colors.transparent,
builder: (context) => DesktopDialog(
maxWidth: 720,
maxHeight: 670,
child: Padding(
padding: const EdgeInsets.all(32),
child: DesktopChooseFromStack(
coin: coin,
),
),
),
);
if (address is String) {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(address);
_toController.text = manager.walletName;
model.recipientAddress = await manager.currentReceivingAddress;
}
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Info);
}
setState(() {
enableNext =
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
});
}
void selectRefundAddressFromStack() async {
try {
final coin = coinFromTickerCaseInsensitive(
model.sendTicker,
);
final address = await showDialog<String?>(
context: context,
barrierColor: Colors.transparent,
builder: (context) => DesktopDialog(
maxWidth: 720,
maxHeight: 670,
child: Padding(
padding: const EdgeInsets.all(32),
child: DesktopChooseFromStack(
coin: coin,
),
),
),
);
if (address is String) {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(address);
_refundController.text = manager.walletName;
model.refundAddress = await manager.currentReceivingAddress;
}
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Info);
}
setState(() {
enableNext =
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
});
}
void selectRecipientFromAddressBook() async {
final coin = coinFromTickerCaseInsensitive(
model.receiveTicker,
);
final entry = await showDialog<ContactAddressEntry?>(
context: context,
barrierColor: Colors.transparent,
builder: (context) => DesktopDialog(
maxWidth: 720,
maxHeight: 670,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Address book",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: AddressBookAddressChooser(
coin: coin,
),
),
],
),
),
);
if (entry != null) {
_toController.text = entry.address;
model.recipientAddress = entry.address;
setState(() {
enableNext =
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
});
}
}
void selectRefundFromAddressBook() async {
final coin = coinFromTickerCaseInsensitive(
model.sendTicker,
);
final entry = await showDialog<ContactAddressEntry?>(
context: context,
barrierColor: Colors.transparent,
builder: (context) => DesktopDialog(
maxWidth: 720,
maxHeight: 670,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Address book",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: AddressBookAddressChooser(
coin: coin,
),
),
],
),
),
);
if (entry != null) {
_refundController.text = entry.address;
model.refundAddress = entry.address;
setState(() {
enableNext =
_toController.text.isNotEmpty && _refundController.text.isNotEmpty;
});
}
}
@override
void initState() {
model = widget.model;
clipboard = widget.clipboard;
_toController = TextEditingController();
_refundController = TextEditingController();
_toFocusNode = FocusNode();
_refundFocusNode = FocusNode();
final tuple = ref.read(exchangeSendFromWalletIdStateProvider.state).state;
if (tuple != null) {
if (model.receiveTicker.toLowerCase() ==
tuple.item2.ticker.toLowerCase()) {
ref
.read(walletsChangeNotifierProvider)
.getManager(tuple.item1)
.currentReceivingAddress
.then((value) {
_toController.text = value;
model.recipientAddress = _toController.text;
});
} else {
if (model.sendTicker.toUpperCase() ==
tuple.item2.ticker.toUpperCase()) {
ref
.read(walletsChangeNotifierProvider)
.getManager(tuple.item1)
.currentReceivingAddress
.then((value) {
_refundController.text = value;
model.refundAddress = _refundController.text;
});
}
}
}
super.initState();
}
@override
void dispose() {
_toController.dispose();
_refundController.dispose();
_toFocusNode.dispose();
_refundFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Enter exchange details",
style: STextStyles.desktopTextMedium(context),
textAlign: TextAlign.center,
),
const SizedBox(
height: 8,
),
Text(
"Enter your recipient and refund addresses",
style: STextStyles.desktopTextExtraExtraSmall(context),
textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Recipient Wallet",
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconRight),
),
if (isStackCoin(model.receiveTicker))
BlueTextButton(
text: "Choose from stack",
onTap: selectRecipientAddressFromStack,
),
],
),
const SizedBox(
height: 10,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
onTap: () {},
key: const Key("recipientExchangeStep2ViewAddressFieldKey"),
controller: _toController,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
// inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(RegExp("[a-zA-Z0-9]{34}")),
// ],
toolbarOptions: const ToolbarOptions(
copy: false,
cut: false,
paste: true,
selectAll: false,
),
focusNode: _toFocusNode,
style: STextStyles.field(context),
onChanged: (value) {
setState(() {
enableNext = _toController.text.isNotEmpty &&
_refundController.text.isNotEmpty;
});
},
decoration: standardInputDecoration(
"Enter the ${model.receiveTicker.toUpperCase()} payout address",
_toFocusNode,
context,
desktopMed: true,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _toController.text.isEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_toController.text.isNotEmpty
? TextFieldIconButton(
key: const Key(
"sendViewClearAddressFieldButtonKey"),
onTap: () {
_toController.text = "";
model.recipientAddress = _toController.text;
setState(() {
enableNext = _toController.text.isNotEmpty &&
_refundController.text.isNotEmpty;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
key: const Key(
"sendViewPasteAddressFieldButtonKey"),
onTap: () async {
final ClipboardData? data = await clipboard
.getData(Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
final content = data.text!.trim();
_toController.text = content;
model.recipientAddress = _toController.text;
setState(() {
enableNext =
_toController.text.isNotEmpty &&
_refundController.text.isNotEmpty;
});
}
},
child: _toController.text.isEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_toController.text.isEmpty &&
isStackCoin(model.receiveTicker))
TextFieldIconButton(
key: const Key("sendViewAddressBookButtonKey"),
onTap: selectRecipientFromAddressBook,
child: const AddressBookIcon(),
),
],
),
),
),
),
),
),
const SizedBox(
height: 10,
),
RoundedWhiteContainer(
borderColor: Theme.of(context).extension<StackColors>()!.background,
child: Text(
"This is the wallet where your ${model.receiveTicker.toUpperCase()} will be sent to.",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
const SizedBox(
height: 24,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Refund Wallet (required)",
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconRight),
),
if (isStackCoin(model.sendTicker))
BlueTextButton(
text: "Choose from stack",
onTap: selectRefundAddressFromStack,
),
],
),
const SizedBox(
height: 10,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("refundExchangeStep2ViewAddressFieldKey"),
controller: _refundController,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
// inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(RegExp("[a-zA-Z0-9]{34}")),
// ],
toolbarOptions: const ToolbarOptions(
copy: false,
cut: false,
paste: true,
selectAll: false,
),
focusNode: _refundFocusNode,
style: STextStyles.field(context),
onChanged: (value) {
setState(() {
enableNext = _toController.text.isNotEmpty &&
_refundController.text.isNotEmpty;
});
},
decoration: standardInputDecoration(
"Enter ${model.sendTicker.toUpperCase()} refund address",
_refundFocusNode,
context,
desktopMed: true,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 8,
right: 5,
),
suffixIcon: Padding(
padding: _refundController.text.isEmpty
? const EdgeInsets.only(right: 16)
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_refundController.text.isNotEmpty
? TextFieldIconButton(
key: const Key(
"sendViewClearAddressFieldButtonKey"),
onTap: () {
_refundController.text = "";
model.refundAddress = _refundController.text;
setState(() {
enableNext = _toController.text.isNotEmpty &&
_refundController.text.isNotEmpty;
});
},
child: const XIcon(),
)
: TextFieldIconButton(
key: const Key(
"sendViewPasteAddressFieldButtonKey"),
onTap: () async {
final ClipboardData? data = await clipboard
.getData(Clipboard.kTextPlain);
if (data?.text != null &&
data!.text!.isNotEmpty) {
final content = data.text!.trim();
_refundController.text = content;
model.refundAddress = _refundController.text;
setState(() {
enableNext =
_toController.text.isNotEmpty &&
_refundController.text.isNotEmpty;
});
}
},
child: _refundController.text.isEmpty
? const ClipboardIcon()
: const XIcon(),
),
if (_refundController.text.isEmpty &&
isStackCoin(model.sendTicker))
TextFieldIconButton(
key: const Key("sendViewAddressBookButtonKey"),
onTap: selectRefundFromAddressBook,
child: const AddressBookIcon(),
),
],
),
),
),
),
),
),
const SizedBox(
height: 10,
),
RoundedWhiteContainer(
borderColor: Theme.of(context).extension<StackColors>()!.background,
child: Text(
"In case something goes wrong during the exchange, we might need a refund address so we can return your coins back to you.",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 32,
),
child: Row(
children: [
Expanded(
child: SecondaryButton(
label: "Back",
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Next",
enabled: enableNext,
buttonHeight: ButtonHeight.l,
onPressed: () async {
await showDialog<void>(
context: context,
barrierColor: Colors.transparent,
barrierDismissible: false,
builder: (context) {
return DesktopDialog(
maxWidth: 720,
maxHeight: double.infinity,
child: StepScaffold(
step: 3,
model: model,
body: DesktopStep3(
model: model,
),
),
);
},
);
},
),
),
],
),
),
],
);
}
}

View file

@ -0,0 +1,254 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
import 'package:stackwallet/providers/exchange/current_exchange_name_state_provider.dart';
import 'package:stackwallet/providers/exchange/exchange_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/services/exchange/exchange_response.dart';
import 'package:stackwallet/services/notifications_api.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/desktop/simple_desktop_dialog.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopStep3 extends ConsumerStatefulWidget {
const DesktopStep3({
Key? key,
required this.model,
}) : super(key: key);
final IncompleteExchangeModel model;
@override
ConsumerState<DesktopStep3> createState() => _DesktopStep3State();
}
class _DesktopStep3State extends ConsumerState<DesktopStep3> {
late final IncompleteExchangeModel model;
Future<void> createTrade() async {
unawaited(
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Container(
color: Theme.of(context)
.extension<StackColors>()!
.overlay
.withOpacity(0.6),
child: const CustomLoadingOverlay(
message: "Creating a trade",
eventBus: null,
),
),
),
),
);
final ExchangeResponse<Trade> response =
await ref.read(exchangeProvider).createTrade(
from: model.sendTicker,
to: model.receiveTicker,
fixedRate: model.rateType != ExchangeRateType.estimated,
amount: model.reversed ? model.receiveAmount : model.sendAmount,
addressTo: model.recipientAddress!,
extraId: null,
addressRefund: model.refundAddress!,
refundExtraId: "",
rateId: model.rateId,
reversed: model.reversed,
);
if (response.value == null) {
if (mounted) {
Navigator.of(context).pop();
}
unawaited(
showDialog<void>(
context: context,
barrierDismissible: true,
builder: (_) => SimpleDesktopDialog(
title: "Failed to create trade",
message: response.exception?.toString() ?? ""),
),
);
return;
}
// save trade to hive
await ref.read(tradesServiceProvider).add(
trade: response.value!,
shouldNotifyListeners: true,
);
String status = response.value!.status;
model.trade = response.value!;
// extra info if status is waiting
if (status == "Waiting") {
status += " for deposit";
}
if (mounted) {
Navigator.of(context).pop();
}
unawaited(
NotificationApi.showNotification(
changeNowId: model.trade!.tradeId,
title: status,
body: "Trade ID ${model.trade!.tradeId}",
walletId: "",
iconAssetName: Assets.svg.arrowRotate,
date: model.trade!.timestamp,
shouldWatchForUpdates: true,
coinName: "coinName",
),
);
if (mounted) {
unawaited(
showDialog<void>(
context: context,
barrierColor: Colors.transparent,
barrierDismissible: false,
builder: (context) {
return DesktopDialog(
maxWidth: 720,
maxHeight: double.infinity,
child: StepScaffold(
step: 4,
model: model,
body: DesktopStep4(
model: model,
),
),
);
},
),
);
}
}
@override
void initState() {
model = widget.model;
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
"Confirm exchange details",
style: STextStyles.desktopTextMedium(context),
),
const SizedBox(
height: 20,
),
RoundedWhiteContainer(
borderColor: Theme.of(context).extension<StackColors>()!.background,
padding: const EdgeInsets.all(0),
child: Column(
children: [
DesktopStepItem(
label: "Exchange",
value: ref.watch(currentExchangeNameStateProvider.state).state,
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: "You send",
value:
"${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}",
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: "You receive",
value:
"~${model.receiveAmount.toStringAsFixed(8)} ${model.receiveTicker.toUpperCase()}",
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: model.rateType == ExchangeRateType.estimated
? "Estimated rate"
: "Fixed rate",
value: model.rateInfo,
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
vertical: true,
label: "Recipient ${model.receiveTicker.toUpperCase()} address",
value: model.recipientAddress!,
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
vertical: true,
label: "Refund ${model.sendTicker.toUpperCase()} address",
value: model.refundAddress!,
),
],
),
),
Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 32,
),
child: Row(
children: [
Expanded(
child: SecondaryButton(
label: "Back",
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Confirm",
buttonHeight: ButtonHeight.l,
onPressed: createTrade,
),
),
],
),
),
],
);
}
}

View file

@ -0,0 +1,301 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/pages/exchange_view/send_from_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopStep4 extends ConsumerStatefulWidget {
const DesktopStep4({
Key? key,
required this.model,
}) : super(key: key);
final IncompleteExchangeModel model;
@override
ConsumerState<DesktopStep4> createState() => _DesktopStep4State();
}
class _DesktopStep4State extends ConsumerState<DesktopStep4> {
late final IncompleteExchangeModel model;
String _statusString = "New";
Timer? _statusTimer;
bool _isWalletCoinAndHasWallet(String ticker) {
try {
final coin = coinFromTickerCaseInsensitive(ticker);
return ref
.read(walletsChangeNotifierProvider)
.managers
.where((element) => element.coin == coin)
.isNotEmpty;
} catch (_) {
return false;
}
}
Future<void> _updateStatus() async {
final statusResponse =
await ref.read(exchangeProvider).updateTrade(model.trade!);
String status = "Waiting";
if (statusResponse.value != null) {
status = statusResponse.value!.status;
}
// extra info if status is waiting
if (status == "Waiting") {
status += " for deposit";
}
if (mounted) {
setState(() {
_statusString = status;
});
}
}
@override
void initState() {
model = widget.model;
_statusTimer = Timer.periodic(const Duration(seconds: 60), (_) {
_updateStatus();
});
super.initState();
}
@override
void dispose() {
_statusTimer?.cancel();
_statusTimer = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
"Send ${model.sendTicker.toUpperCase()} to the address below",
style: STextStyles.desktopTextMedium(context),
),
const SizedBox(
height: 8,
),
Text(
"Send ${model.sendTicker.toUpperCase()} to the address below. Once it is received, ${model.trade!.exchangeName} will send the ${model.receiveTicker.toUpperCase()} to the recipient address you provided. You can find this trade details and check its status in the list of trades.",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
const SizedBox(
height: 20,
),
RoundedContainer(
color: Theme.of(context).extension<StackColors>()!.warningBackground,
child: RichText(
text: TextSpan(
text:
"You must send at least ${model.sendAmount.toString()} ${model.sendTicker}. ",
style: STextStyles.label700(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
fontSize: 14,
),
children: [
TextSpan(
text:
"If you send less than ${model.sendAmount.toString()} ${model.sendTicker}, your transaction may not be converted and it may not be refunded.",
style: STextStyles.label(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
fontSize: 14,
),
),
],
),
),
),
const SizedBox(
height: 20,
),
RoundedWhiteContainer(
borderColor: Theme.of(context).extension<StackColors>()!.background,
padding: const EdgeInsets.all(0),
child: Column(
children: [
DesktopStepItem(
vertical: true,
label: "Send ${model.sendTicker.toUpperCase()} to this address",
value: model.trade!.payInAddress,
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: "Amount",
value:
"${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}",
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
DesktopStepItem(
label: "Trade ID",
value: model.trade!.tradeId,
),
Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Status",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
Text(
_statusString,
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.colorForStatus(_statusString),
),
),
],
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 32,
),
child: Row(
children: [
Expanded(
child: SecondaryButton(
label: "Send from Stack Wallet",
buttonHeight: ButtonHeight.l,
onPressed: () {
final trade = model.trade!;
final amount = Decimal.parse(trade.payInAmount);
final address = trade.payInAddress;
final coin =
coinFromTickerCaseInsensitive(trade.payInCurrency);
showDialog<void>(
context: context,
builder: (context) => Navigator(
initialRoute: SendFromView.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
FadePageRoute(
SendFromView(
coin: coin,
trade: trade,
amount: amount,
address: address,
shouldPopRoot: true,
fromDesktopStep4: true,
),
const RouteSettings(
name: SendFromView.routeName,
),
),
];
},
),
);
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Show QR code",
buttonHeight: ButtonHeight.l,
onPressed: () {
showDialog<dynamic>(
context: context,
barrierColor: Colors.transparent,
barrierDismissible: true,
builder: (_) {
return DesktopDialog(
maxHeight: 720,
maxWidth: 720,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Send ${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker} to this address",
style: STextStyles.desktopH3(context),
),
const SizedBox(
height: 48,
),
Center(
child: QrImage(
// TODO: grab coin uri scheme from somewhere
// data: "${coin.uriScheme}:$receivingAddress",
data: model.trade!.payInAddress,
size: 290,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
const SizedBox(
height: 48,
),
SecondaryButton(
label: "Cancel",
width: 310,
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
],
),
);
},
);
},
),
),
],
),
),
],
);
}
}

View file

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
class DesktopStepItem extends StatelessWidget {
const DesktopStepItem(
{Key? key,
required this.label,
required this.value,
this.padding = const EdgeInsets.all(16),
this.vertical = false})
: super(key: key);
final String label;
final String value;
final EdgeInsets padding;
final bool vertical;
@override
Widget build(BuildContext context) {
return Padding(
padding: padding,
child: ConditionalParent(
condition: vertical,
builder: (child) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
child,
const SizedBox(
height: 2,
),
Text(
value,
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark,
),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: STextStyles.desktopTextExtraExtraSmall(context),
),
if (!vertical)
Text(
value,
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark,
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,329 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
class DesktopChooseFromStack extends ConsumerStatefulWidget {
const DesktopChooseFromStack({
Key? key,
required this.coin,
}) : super(key: key);
final Coin coin;
@override
ConsumerState<DesktopChooseFromStack> createState() =>
_DesktopChooseFromStackState();
}
class _DesktopChooseFromStackState
extends ConsumerState<DesktopChooseFromStack> {
late final TextEditingController _searchController;
late final FocusNode searchFieldFocusNode;
String _searchTerm = "";
List<String> filter(List<String> walletIds, String searchTerm) {
if (searchTerm.isEmpty) {
return walletIds;
}
final List<String> result = [];
for (final walletId in walletIds) {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if (manager.walletName.toLowerCase().contains(searchTerm.toLowerCase())) {
result.add(walletId);
}
}
return result;
}
@override
void initState() {
searchFieldFocusNode = FocusNode();
_searchController = TextEditingController();
super.initState();
}
@override
void dispose() {
_searchController.dispose();
searchFieldFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Choose from Stack",
style: STextStyles.desktopH3(context),
),
const SizedBox(
height: 28,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: false,
enableSuggestions: false,
controller: _searchController,
focusNode: searchFieldFocusNode,
onChanged: (value) {
setState(() {
_searchTerm = value;
});
},
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
height: 1.8,
),
decoration: standardInputDecoration(
"Search",
searchFieldFocusNode,
context,
desktopMed: true,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 18,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 20,
height: 20,
),
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
_searchTerm = "";
});
},
),
],
),
),
)
: null,
),
),
),
const SizedBox(
height: 16,
),
Flexible(
child: Builder(
builder: (context) {
List<String> walletIds = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getWalletIdsFor(coin: widget.coin),
),
);
if (walletIds.isEmpty) {
return Column(
children: [
RoundedWhiteContainer(
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: Center(
child: Text(
"No ${widget.coin.ticker.toUpperCase()} wallets",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
),
),
],
);
}
walletIds = filter(walletIds, _searchTerm);
return ListView.separated(
primary: false,
itemCount: walletIds.length,
separatorBuilder: (_, __) => const SizedBox(
height: 5,
),
itemBuilder: (context, index) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletIds[index])));
return RoundedWhiteContainer(
borderColor:
Theme.of(context).extension<StackColors>()!.background,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 14,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
children: [
WalletInfoCoinIcon(coin: widget.coin),
const SizedBox(
width: 12,
),
Text(
manager.walletName,
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
const Spacer(),
BalanceDisplay(
walletId: walletIds[index],
),
const SizedBox(
width: 80,
),
BlueTextButton(
text: "Select wallet",
onTap: () {
Navigator.of(context).pop(manager.walletId);
},
),
],
),
);
},
);
},
),
),
const SizedBox(
height: 20,
),
Row(
children: [
const Spacer(),
const SizedBox(
width: 16,
),
Expanded(
child: SecondaryButton(
label: "Cancel",
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
],
)
],
);
}
}
class BalanceDisplay extends ConsumerStatefulWidget {
const BalanceDisplay({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<BalanceDisplay> createState() => _BalanceDisplayState();
}
class _BalanceDisplayState extends ConsumerState<BalanceDisplay> {
late final String walletId;
Decimal? _cachedBalance;
static const loopedText = [
"Loading balance ",
"Loading balance. ",
"Loading balance.. ",
"Loading balance..."
];
@override
void initState() {
walletId = widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)));
final locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale));
return FutureBuilder(
future: manager.availableBalance,
builder: (context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData &&
snapshot.data != null) {
_cachedBalance = snapshot.data;
}
if (_cachedBalance == null) {
return AnimatedText(
stringsToLoopThrough: loopedText,
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
);
} else {
return Text(
"${Format.localizedStringAsFixed(
value: _cachedBalance!,
locale: locale,
decimalPlaces: 8,
)} ${manager.coin.ticker}",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
textAlign: TextAlign.right,
);
}
},
);
}
}

View file

@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
class DesktopExchangeStepsIndicator extends StatelessWidget {
const DesktopExchangeStepsIndicator({Key? key, required this.currentStep})
: super(key: key);
final int currentStep;
Color getColor(BuildContext context, int step) {
if (currentStep > step) {
return Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
.withOpacity(0.5);
} else if (currentStep < step) {
return Theme.of(context).extension<StackColors>()!.textSubtitle3;
} else {
return Theme.of(context).extension<StackColors>()!.accentColorBlue;
}
}
static const double verticalSpacing = 6;
static const double horizontalSpacing = 16;
static const double barHeight = 4;
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Column(
children: [
Text(
"Confirm amount",
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: getColor(context, 1),
),
),
const SizedBox(
height: verticalSpacing,
),
RoundedContainer(
color: getColor(context, 1),
height: barHeight,
width: double.infinity,
),
],
),
),
const SizedBox(
width: horizontalSpacing,
),
Expanded(
child: Column(
children: [
Text(
"Enter details",
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: getColor(context, 2),
),
),
const SizedBox(
height: verticalSpacing,
),
RoundedContainer(
color: getColor(context, 2),
height: barHeight,
width: double.infinity,
),
],
),
),
const SizedBox(
width: horizontalSpacing,
),
Expanded(
child: Column(
children: [
Text(
"Confirm details",
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: getColor(context, 3),
),
),
const SizedBox(
height: verticalSpacing,
),
RoundedContainer(
color: getColor(context, 3),
height: barHeight,
width: double.infinity,
),
],
),
),
const SizedBox(
width: horizontalSpacing,
),
Expanded(
child: Column(
children: [
Text(
"Complete exchange",
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: getColor(context, 4),
),
),
const SizedBox(
height: verticalSpacing,
),
RoundedContainer(
color: getColor(context, 4),
height: barHeight,
width: double.infinity,
),
],
),
),
],
);
}
}

View file

@ -0,0 +1,271 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/trade_card.dart';
import '../../../route_generator.dart';
import '../../../widgets/desktop/desktop_dialog.dart';
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
class DesktopTradeHistory extends ConsumerStatefulWidget {
const DesktopTradeHistory({Key? key}) : super(key: key);
@override
ConsumerState<DesktopTradeHistory> createState() =>
_DesktopTradeHistoryState();
}
class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
BorderRadius get _borderRadiusFirst {
return BorderRadius.only(
topLeft: Radius.circular(
Constants.size.circularBorderRadius,
),
topRight: Radius.circular(
Constants.size.circularBorderRadius,
),
);
}
BorderRadius get _borderRadiusLast {
return BorderRadius.only(
bottomLeft: Radius.circular(
Constants.size.circularBorderRadius,
),
bottomRight: Radius.circular(
Constants.size.circularBorderRadius,
),
);
}
@override
Widget build(BuildContext context) {
final trades =
ref.watch(tradesServiceProvider.select((value) => value.trades));
final tradeCount = trades.length;
final hasHistory = tradeCount > 0;
if (hasHistory) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Exchange details",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
const SizedBox(
height: 16,
),
Expanded(
child: ListView.separated(
shrinkWrap: true,
primary: false,
itemBuilder: (context, index) {
BorderRadius? radius;
if (index == tradeCount - 1) {
radius = _borderRadiusLast;
} else if (index == 0) {
radius = _borderRadiusFirst;
}
return Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: radius,
),
child: TradeCard(
key: Key("tradeCard_${trades[index].uuid}"),
trade: trades[index],
onTap: () async {
final String tradeId = trades[index].tradeId;
final lookup =
ref.read(tradeSentFromStackLookupProvider).all;
debugPrint("ALL: $lookup");
final String? txid = ref
.read(tradeSentFromStackLookupProvider)
.getTxidForTradeId(tradeId);
final List<String>? walletIds = ref
.read(tradeSentFromStackLookupProvider)
.getWalletIdsForTradeId(tradeId);
if (txid != null &&
walletIds != null &&
walletIds.isNotEmpty) {
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(walletIds.first);
debugPrint("name: ${manager.walletName}");
// TODO store tx data completely locally in isar so we don't lock up ui here when querying txData
final txData = await manager.transactionData;
final tx = txData.getAllTransactions()[txid];
if (mounted) {
await showDialog<void>(
context: context,
builder: (context) => Navigator(
initialRoute: TradeDetailsView.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
FadePageRoute(
DesktopDialog(
// maxHeight:
// MediaQuery.of(context).size.height - 64,
maxHeight: double.infinity,
maxWidth: 580,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
bottom: 16,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
"Trade details",
style: STextStyles.desktopH3(
context),
),
DesktopDialogCloseButton(
onPressedOverride:
Navigator.of(
context,
rootNavigator: true,
).pop,
),
],
),
),
Flexible(
child: TradeDetailsView(
tradeId: tradeId,
transactionIfSentFromStack: tx,
walletName: manager.walletName,
walletId: walletIds.first,
),
),
],
),
),
const RouteSettings(
name: TradeDetailsView.routeName,
),
),
];
},
),
);
}
} else {
unawaited(
showDialog<void>(
context: context,
builder: (context) => Navigator(
initialRoute: TradeDetailsView.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
FadePageRoute(
DesktopDialog(
// maxHeight:
// MediaQuery.of(context).size.height - 64,
maxHeight: double.infinity,
maxWidth: 580,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
bottom: 16,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
"Trade details",
style: STextStyles.desktopH3(
context),
),
DesktopDialogCloseButton(
onPressedOverride:
Navigator.of(
context,
rootNavigator: true,
).pop,
),
],
),
),
Flexible(
child: TradeDetailsView(
tradeId: tradeId,
transactionIfSentFromStack: null,
walletName: null,
walletId: walletIds?.first,
),
),
],
),
),
const RouteSettings(
name: TradeDetailsView.routeName,
),
),
];
},
),
),
);
}
},
),
);
},
separatorBuilder: (context, index) {
return Container(
height: 1,
color: Theme.of(context).extension<StackColors>()!.background,
);
},
itemCount: tradeCount,
),
),
],
);
} else {
return RoundedWhiteContainer(
child: Center(
child: Text(
"Trades will appear here",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
);
}
}
}

View file

@ -49,8 +49,15 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
unawaited(
showDialog(
context: context,
builder: (context) => const LoadingIndicator(
width: 200,
builder: (context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
LoadingIndicator(
width: 200,
height: 200,
),
],
),
),
);
@ -158,6 +165,12 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
autofocus: true,
onSubmitted: (_) {
if (_continueEnabled) {
login();
}
},
decoration: standardInputDecoration(
"Enter password",
passwordFocusNode,

View file

@ -2,21 +2,29 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/subwidgets/desktop_address_book_scaffold.dart';
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_details.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_book_filter_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/address_book_card.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
@ -34,13 +42,10 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
late final FocusNode _searchFocusNode;
List<Contact>? _cache;
List<Contact>? _cacheFav;
late bool hasContacts = false;
String _searchTerm = "";
String? currentContactId;
Future<void> selectCryptocurrency() async {
await showDialog<dynamic>(
context: context,
@ -76,6 +81,47 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
_searchController = TextEditingController();
_searchFocusNode = FocusNode();
ref.refresh(addressBookFilterProvider);
// if (widget.coin == null) {
List<Coin> coins = Coin.values.toList();
coins.remove(Coin.firoTestNet);
bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins;
if (showTestNet) {
ref.read(addressBookFilterProvider).addAll(coins, false);
} else {
ref
.read(addressBookFilterProvider)
.addAll(coins.getRange(0, coins.length - kTestNetCoinCount), false);
}
// } else {
// ref.read(addressBookFilterProvider).add(widget.coin!, false);
// }
WidgetsBinding.instance.addPostFrameCallback((_) async {
List<ContactAddressEntry> addresses = [];
final managers = ref.read(walletsChangeNotifierProvider).managers;
for (final manager in managers) {
addresses.add(
ContactAddressEntry(
coin: manager.coin,
address: await manager.currentReceivingAddress,
label: "Current Receiving",
other: manager.walletName,
),
);
}
final self = Contact(
name: "My Stack",
addresses: addresses,
isFavorite: true,
id: "default",
);
await ref.read(addressBookServiceProvider).editContact(self);
});
super.initState();
}
@ -90,7 +136,28 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final hasWallets = ref.watch(walletsChangeNotifierProvider).hasWallets;
final contacts =
ref.watch(addressBookServiceProvider.select((value) => value.contacts));
final allContacts = contacts
.where((element) => element.addresses
.where((e) => ref.watch(addressBookFilterProvider
.select((value) => value.coins.contains(e.coin))))
.isNotEmpty)
.where(
(e) => ref.read(addressBookServiceProvider).matches(_searchTerm, e))
.toList();
final favorites = contacts
.where((element) => element.addresses
.where((e) => ref.watch(addressBookFilterProvider
.select((value) => value.coins.contains(e.coin))))
.isNotEmpty)
.where((e) =>
e.isFavorite &&
ref.read(addressBookServiceProvider).matches(_searchTerm, e))
.where((element) => element.isFavorite)
.toList();
return DesktopScaffold(
appBar: DesktopAppBar(
@ -108,115 +175,262 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
),
),
body: Padding(
padding: const EdgeInsets.all(24),
child: Row(
children: [
Expanded(
flex: 6,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: (value) {
setState(() {
_searchTerm = value;
});
},
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 20,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
padding: const EdgeInsets.only(
left: 24,
right: 24,
bottom: 24,
),
child: DesktopAddressBookScaffold(
controlsLeft: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: (value) {
setState(() {
_searchTerm = value;
});
},
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search",
_searchFocusNode,
context,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 20,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
_searchTerm = "";
});
},
),
],
),
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
});
},
),
],
)
: null,
),
),
),
controlsRight: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SecondaryButton(
width: 184,
label: "Filter",
buttonHeight: ButtonHeight.l,
icon: SvgPicture.asset(
Assets.svg.filter,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
onPressed: selectCryptocurrency,
),
const SizedBox(
width: 20,
),
PrimaryButton(
width: 184,
label: "Add new",
buttonHeight: ButtonHeight.l,
icon: SvgPicture.asset(
Assets.svg.circlePlus,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
onPressed: newContact,
),
],
),
filterItems: Container(),
upperLabel: favorites.isEmpty && allContacts.isEmpty
? null
: Text(
favorites.isEmpty ? "All contacts" : "Favorites",
style: STextStyles.smallMed12(context),
),
lowerLabel: favorites.isEmpty
? null
: Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 12,
),
child: Text(
"All contacts",
style: STextStyles.smallMed12(context),
),
),
favorites: favorites.isEmpty
? contacts.isNotEmpty
? null
: RoundedWhiteContainer(
child: Center(
child: Text(
"Your favorite contacts will appear here",
style: STextStyles.itemSubtitle(context),
),
),
)
: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Column(
children: [
for (int i = 0; i < favorites.length; i++)
Column(
children: [
if (i > 0)
Container(
color: Theme.of(context)
.extension<StackColors>()!
.background,
height: 1,
),
Padding(
padding: const EdgeInsets.all(4),
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(
currentContactId == favorites[i].id
? 0.08
: 0,
),
child: RawMaterialButton(
onPressed: () {
setState(() {
currentContactId = favorites[i].id;
});
},
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: AddressBookCard(
key: Key(
"favContactCard_${favorites[i].id}_key"),
contactId: favorites[i].id,
desktopSendFrom: false,
),
),
)
: null,
),
),
),
const SizedBox(
height: 24,
),
const AddressBookView(),
],
),
),
const SizedBox(
width: 20,
),
Expanded(
flex: 5,
child: Column(
children: [
Row(
children: [
SecondaryButton(
width: 184,
label: "Filter",
desktopMed: true,
icon: SvgPicture.asset(
Assets.svg.filter,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
),
],
),
onPressed: selectCryptocurrency,
),
const SizedBox(
width: 20,
),
PrimaryButton(
width: 184,
label: "Add new",
desktopMed: true,
icon: SvgPicture.asset(
Assets.svg.circlePlus,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
onPressed: newContact,
),
],
),
],
),
),
],
),
all: allContacts.isEmpty
? contacts.isNotEmpty
? null
: RoundedWhiteContainer(
child: Center(
child: Text(
"Your contacts will appear here",
style: STextStyles.itemSubtitle(context),
),
),
)
: Column(
children: [
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Column(
children: [
for (int i = 0; i < allContacts.length; i++)
Column(
children: [
if (i > 0)
Container(
color: Theme.of(context)
.extension<StackColors>()!
.background,
height: 1,
),
Padding(
padding: const EdgeInsets.all(4),
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(
currentContactId == allContacts[i].id
? 0.08
: 0,
),
child: RawMaterialButton(
onPressed: () {
setState(() {
currentContactId = allContacts[i].id;
});
},
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: AddressBookCard(
key: Key(
"favContactCard_${allContacts[i].id}_key"),
contactId: allContacts[i].id,
desktopSendFrom: false,
),
),
),
),
],
),
],
),
),
],
),
details: currentContactId == null
? Container()
: DesktopContactDetails(
contactId: currentContactId!,
),
),
),
);

View file

@ -0,0 +1,112 @@
import 'package:flutter/widgets.dart';
class DesktopAddressBookScaffold extends StatelessWidget {
const DesktopAddressBookScaffold({
Key? key,
required this.controlsLeft,
required this.controlsRight,
required this.filterItems,
required this.upperLabel,
required this.lowerLabel,
required this.favorites,
required this.all,
required this.details,
}) : super(key: key);
final Widget? controlsLeft;
final Widget? controlsRight;
final Widget? filterItems;
final Widget? upperLabel;
final Widget? lowerLabel;
final Widget? favorites;
final Widget? all;
final Widget? details;
static const double weirdRowHeight = 30;
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
flex: 6,
child: controlsLeft ?? Container(),
),
const SizedBox(
width: 20,
),
Expanded(
flex: 5,
child: controlsRight ?? Container(),
),
],
),
const SizedBox(
height: 20,
),
Row(
children: [
Expanded(
child: filterItems ?? Container(),
),
],
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 6,
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
primary: false,
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: weirdRowHeight,
child: upperLabel,
),
favorites ?? Container(),
lowerLabel ?? Container(),
all ?? Container(),
],
),
),
),
);
},
),
),
const SizedBox(
width: 20,
),
Expanded(
flex: 5,
child: Column(
children: [
const SizedBox(
height: weirdRowHeight,
),
Flexible(
child: details ?? Container(),
),
],
),
),
],
),
)
],
);
}
}

View file

@ -0,0 +1,153 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
class DesktopAddressCard extends StatelessWidget {
const DesktopAddressCard({
Key? key,
required this.entry,
required this.contactId,
this.clipboard = const ClipboardWrapper(),
}) : super(key: key);
final ContactAddressEntry entry;
final String contactId;
final ClipboardInterface clipboard;
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset(
Assets.svg.iconFor(
coin: entry.coin,
),
height: 32,
width: 32,
),
const SizedBox(
width: 16,
),
Flexible(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
"${entry.label} (${entry.coin.ticker})",
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark,
),
),
const SizedBox(
height: 2,
),
SelectableText(
entry.address,
style: STextStyles.desktopTextExtraExtraSmall(context),
),
const SizedBox(
height: 8,
),
Row(
children: [
BlueTextButton(
text: "Copy",
onTap: () {
clipboard.setData(
ClipboardData(text: entry.address),
);
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
),
if (contactId != "default")
const SizedBox(
width: 16,
),
if (contactId != "default")
Consumer(
builder: (context, ref, child) {
return BlueTextButton(
text: "Edit",
onTap: () async {
ref.refresh(
addressEntryDataProviderFamilyRefresher);
ref.read(addressEntryDataProvider(0)).address =
entry.address;
ref.read(addressEntryDataProvider(0)).addressLabel =
entry.label;
ref.read(addressEntryDataProvider(0)).coin =
entry.coin;
await showDialog<void>(
context: context,
builder: (context) => DesktopDialog(
maxWidth: 580,
maxHeight: 566,
child: Column(
children: [
Row(
children: [
const SizedBox(
width: 8,
),
const AppBarBackButton(
isCompact: true,
),
Text(
"Edit address",
style: STextStyles.desktopH3(context),
),
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
top: 20,
left: 32,
right: 32,
bottom: 32,
),
child: EditContactAddressView(
contactId: contactId,
addressEntry: entry,
),
),
),
],
),
),
);
},
);
},
),
],
)
],
),
),
],
);
}
}

View file

@ -0,0 +1,352 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_new_contact_address_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/subwidgets/desktop_address_card.dart';
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/subwidgets/desktop_contact_options_menu_popup.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
import 'package:tuple/tuple.dart';
class DesktopContactDetails extends ConsumerStatefulWidget {
const DesktopContactDetails({
Key? key,
required this.contactId,
}) : super(key: key);
final String contactId;
@override
ConsumerState<DesktopContactDetails> createState() =>
_DesktopContactDetailsState();
}
class _DesktopContactDetailsState extends ConsumerState<DesktopContactDetails> {
List<Tuple2<String, Transaction>> _cachedTransactions = [];
bool _contactHasAddress(String address, Contact contact) {
for (final entry in contact.addresses) {
if (entry.address == address) {
return true;
}
}
return false;
}
Future<List<Tuple2<String, Transaction>>> _filteredTransactionsByContact(
List<Manager> managers,
) async {
final contact =
ref.read(addressBookServiceProvider).getContactById(widget.contactId);
// TODO: optimise
List<Tuple2<String, Transaction>> result = [];
for (final manager in managers) {
final transactions = (await manager.transactionData)
.getAllTransactions()
.values
.toList()
.where((e) => _contactHasAddress(e.address, contact));
for (final tx in transactions) {
result.add(Tuple2(manager.walletId, tx));
}
}
// sort by date
result.sort((a, b) => b.item2.timestamp - a.item2.timestamp);
return result;
}
@override
Widget build(BuildContext context) {
// provider hack to prevent trying to update widget with deleted contact
Contact? _contact;
try {
_contact = ref.watch(addressBookServiceProvider
.select((value) => value.getContactById(widget.contactId)));
} catch (_) {
return Container();
}
final contact = _contact!;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: contact.id == "default"
? Colors.transparent
: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(32),
),
child: contact.id == "default"
? Center(
child: SvgPicture.asset(
Assets.svg.stackIcon(context),
width: 32,
),
)
: contact.emojiChar != null
? Center(
child: Text(contact.emojiChar!),
)
: Center(
child: SvgPicture.asset(
Assets.svg.user,
width: 18,
),
),
),
const SizedBox(
width: 16,
),
Text(
contact.name,
style: STextStyles.desktopTextSmall(context),
),
],
),
if (widget.contactId != "default")
SecondaryButton(
label: "Options",
width: 96,
buttonHeight: ButtonHeight.xxs,
onPressed: () async {
await showDialog<void>(
context: context,
barrierColor: Colors.transparent,
builder: (context) {
return DesktopContactOptionsMenuPopup(
contactId: contact.id,
);
},
);
},
),
],
),
const SizedBox(
height: 24,
),
Flexible(
child: ListView(
primary: false,
shrinkWrap: true,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Addresses",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
BlueTextButton(
text: "Add new",
onTap: () async {
ref.refresh(
addressEntryDataProviderFamilyRefresher);
await showDialog<void>(
context: context,
builder: (context) => DesktopDialog(
maxWidth: 580,
maxHeight: 566,
child: Column(
children: [
Row(
children: [
const SizedBox(
width: 8,
),
const AppBarBackButton(
isCompact: true,
),
Text(
"Add new address",
style:
STextStyles.desktopH3(context),
),
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
top: 20,
left: 32,
right: 32,
bottom: 32,
),
child: AddNewContactAddressView(
contactId: widget.contactId,
),
),
),
],
),
),
);
},
),
],
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (int i = 0; i < contact.addresses.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
children: [
if (i > 0)
Container(
color: Theme.of(context)
.extension<StackColors>()!
.background,
height: 1,
),
Padding(
padding: const EdgeInsets.all(18),
child: DesktopAddressCard(
entry: contact.addresses[i],
contactId: contact.id,
),
),
],
),
],
),
),
Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 12,
),
child: Text(
"Transaction history",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
),
FutureBuilder(
future: _filteredTransactionsByContact(
ref.watch(walletsChangeNotifierProvider).managers),
builder: (_,
AsyncSnapshot<List<Tuple2<String, Transaction>>>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
_cachedTransactions = snapshot.data!;
if (_cachedTransactions.isNotEmpty) {
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
..._cachedTransactions.map(
(e) => TransactionCard(
key: Key(
"contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"),
transaction: e.item2,
walletId: e.item1,
),
),
],
),
);
} else {
return RoundedWhiteContainer(
child: Center(
child: Text(
"No transactions found",
style: STextStyles.itemSubtitle(context),
),
),
);
}
} else {
// TODO: proper loading animation
if (_cachedTransactions.isEmpty) {
return const LoadingIndicator();
} else {
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
..._cachedTransactions.map(
(e) => TransactionCard(
key: Key(
"contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"),
transaction: e.item2,
walletId: e.item1,
),
),
],
),
);
}
}
},
),
],
),
),
],
),
),
),
],
);
}
}

View file

@ -0,0 +1,402 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
class DesktopContactOptionsMenuPopup extends ConsumerStatefulWidget {
const DesktopContactOptionsMenuPopup({Key? key, required this.contactId})
: super(key: key);
final String contactId;
@override
ConsumerState<DesktopContactOptionsMenuPopup> createState() =>
_DesktopContactOptionsMenuPopupState();
}
class _DesktopContactOptionsMenuPopupState
extends ConsumerState<DesktopContactOptionsMenuPopup> {
bool hoveredOnStar = false;
bool hoveredOnPencil = false;
bool hoveredOnTrash = false;
void editContact() {
// pop context menu
Navigator.of(context).pop();
showDialog<dynamic>(
context: context,
useSafeArea: true,
barrierDismissible: true,
builder: (_) => DesktopDialog(
maxWidth: 580,
maxHeight: 400,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Edit contact",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
top: 16,
left: 32,
right: 32,
bottom: 32,
),
child: EditContactNameEmojiView(
contactId: widget.contactId,
),
),
),
],
),
),
);
}
void attemptDeleteContact() {
final contact =
ref.read(addressBookServiceProvider).getContactById(widget.contactId);
// pop context menu
Navigator.of(context).pop();
showDialog<dynamic>(
context: context,
useSafeArea: true,
barrierDismissible: true,
builder: (_) => DesktopDialog(
maxWidth: 500,
maxHeight: 400,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Delete ${contact.name}?",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Spacer(
flex: 1,
),
Text(
"Contact will be deleted permanently!",
style: STextStyles.desktopTextSmall(context),
),
const Spacer(
flex: 2,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
onPressed: Navigator.of(context).pop,
buttonHeight: ButtonHeight.l,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Consumer(
builder: (context, ref, __) => PrimaryButton(
label: "Delete",
buttonHeight: ButtonHeight.l,
onPressed: () {
ref
.read(addressBookServiceProvider)
.removeContact(contact.id);
Navigator.of(context).pop();
showFloatingFlushBar(
type: FlushBarType.success,
message: "${contact.name} deleted",
context: context,
);
},
),
),
),
],
)
],
),
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: 210,
left: MediaQuery.of(context).size.width - 280,
child: Container(
width: 270,
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: BorderRadius.circular(
20,
),
boxShadow: [
Theme.of(context).extension<StackColors>()!.standardBoxShadow,
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
MouseRegion(
onEnter: (_) {
setState(() {
hoveredOnStar = true;
});
},
onExit: (_) {
setState(() {
hoveredOnStar = false;
});
},
child: RawMaterialButton(
hoverColor: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
1000,
),
),
onPressed: () {
final contact =
ref.read(addressBookServiceProvider).getContactById(
widget.contactId,
);
ref.read(addressBookServiceProvider).editContact(
contact.copyWith(
isFavorite: !contact.isFavorite,
),
);
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 16,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.star,
width: 24,
height: 22,
color: hoveredOnStar
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultSearchIconLeft,
),
const SizedBox(
width: 12,
),
Text(
ref.watch(addressBookServiceProvider.select(
(value) => value
.getContactById(widget.contactId)
.isFavorite))
? "Remove from favorites"
: "Add to favorites",
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
)
],
),
),
),
),
if (widget.contactId != "default")
const SizedBox(
height: 2,
),
if (widget.contactId != "default")
MouseRegion(
onEnter: (_) {
setState(() {
hoveredOnPencil = true;
});
},
onExit: (_) {
setState(() {
hoveredOnPencil = false;
});
},
child: RawMaterialButton(
hoverColor: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
1000,
),
),
onPressed: editContact,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 16,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.pencil,
width: 24,
height: 22,
color: hoveredOnPencil
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultSearchIconLeft,
),
const SizedBox(
width: 12,
),
Text(
"Edit contact",
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
)
],
),
),
),
),
if (widget.contactId != "default")
const SizedBox(
height: 2,
),
if (widget.contactId != "default")
MouseRegion(
onEnter: (_) {
setState(() {
hoveredOnTrash = true;
});
},
onExit: (_) {
setState(() {
hoveredOnTrash = false;
});
},
child: RawMaterialButton(
hoverColor: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
1000,
),
),
onPressed: attemptDeleteContact,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 16,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.trash,
width: 24,
height: 22,
color: hoveredOnTrash
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultSearchIconLeft,
),
const SizedBox(
width: 12,
),
Text(
"Delete contact",
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
)
],
),
),
),
),
],
),
),
),
),
],
);
}
}

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/desktop_address_book.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_settings_view.dart';
@ -29,10 +30,11 @@ class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: MyStackView.routeName,
),
// Container(
// // todo: exchange
// color: Colors.green,
// ),
DesktopMenuItemId.exchange: const Navigator(
key: Key("desktopExchangeHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: DesktopExchangeView.routeName,
),
DesktopMenuItemId.notifications: const Navigator(
key: Key("desktopNotificationsHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,

View file

@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu_item.dart';
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -104,10 +105,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
.withOpacity(0.8),
),
label: "My Stack",
@ -120,46 +121,57 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
const SizedBox(
height: 2,
),
// DesktopMenuItem(
// icon: SvgPicture.asset(
// Assets.svg.exchangeDesktop,
// width: 20,
// height: 20,
// color: DesktopMenuItemId.exchange == ref.watch(currentDesktopMenuItemProvider.state).state
// ? Theme.of(context)
// .extension<StackColors>()!
// .textDark
// : Theme.of(context)
// .extension<StackColors>()!
// .textDark
// .withOpacity(0.8),
// ),
// label: "Exchange",
// value: DesktopMenuItemId.exchange,
// group: ref.watch(currentDesktopMenuItemProvider.state).state,
// onChanged: updateSelectedMenuItem,
// iconOnly: _width == minimizedWidth,
// ),
// const SizedBox(
// height: 2,
// ),
DesktopMenuItem(
icon: SvgPicture.asset(
Assets.svg.bell,
Assets.svg.exchangeDesktop,
width: 20,
height: 20,
color: DesktopMenuItemId.notifications ==
color: DesktopMenuItemId.exchange ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
.withOpacity(0.8),
),
label: "Exchange",
value: DesktopMenuItemId.exchange,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
const SizedBox(
height: 2,
),
DesktopMenuItem(
icon: SvgPicture.asset(
ref.watch(notificationsProvider.select(
(value) => value.hasUnreadNotifications))
? Assets.svg.bellNew(context)
: Assets.svg.bell,
width: 20,
height: 20,
color: ref.watch(notificationsProvider.select(
(value) => value.hasUnreadNotifications))
? null
: DesktopMenuItemId.notifications ==
ref
.watch(currentDesktopMenuItemProvider
.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.8),
),
label: "Notifications",
value: DesktopMenuItemId.notifications,
group:
@ -181,10 +193,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
.withOpacity(0.8),
),
label: "Address Book",
@ -208,10 +220,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
.withOpacity(0.8),
),
label: "Settings",
@ -235,10 +247,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
.withOpacity(0.8),
),
label: "Support",
@ -262,10 +274,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
: Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
.withOpacity(0.8),
),
label: "About",
@ -283,7 +295,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.textDark
.accentColorDark
.withOpacity(0.8),
),
label: "Exit",

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -7,6 +8,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/delete_wallet_button.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/my_wallet.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/network_info_button.dart';
@ -15,6 +17,7 @@ import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_vie
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
@ -27,8 +30,11 @@ import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/hover_text_field.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -165,6 +171,116 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
}
}
Future<void> attemptAnonymize() async {
final managerProvider =
ref.read(walletsChangeNotifierProvider).getManagerProvider(walletId);
bool shouldPop = false;
unawaited(
showDialog(
context: context,
builder: (context) => WillPopScope(
child: const CustomLoadingOverlay(
message: "Anonymizing balance",
eventBus: null,
),
onWillPop: () async => shouldPop,
),
),
);
final firoWallet = ref.read(managerProvider).wallet as FiroWallet;
final publicBalance = await firoWallet.availablePublicBalance();
if (publicBalance <= Decimal.zero) {
shouldPop = true;
if (mounted) {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).popUntil(
ModalRoute.withName(DesktopWalletView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "No funds available to anonymize!",
context: context,
),
);
}
return;
}
try {
await firoWallet.anonymizeAllPublicFunds();
shouldPop = true;
if (mounted) {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).popUntil(
ModalRoute.withName(DesktopWalletView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "Anonymize transaction submitted",
context: context,
),
);
}
} catch (e) {
shouldPop = true;
if (mounted) {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).popUntil(
ModalRoute.withName(DesktopWalletView.routeName),
);
await showDialog<dynamic>(
context: context,
builder: (_) => DesktopDialog(
maxWidth: 400,
maxHeight: 300,
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Anonymize all failed",
style: STextStyles.desktopH3(context),
),
const Spacer(
flex: 1,
),
Text(
"Reason: $e",
style: STextStyles.desktopTextSmall(context),
),
const Spacer(
flex: 2,
),
Row(
children: [
const Spacer(),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
label: "Ok",
buttonHeight: ButtonHeight.l,
onPressed:
Navigator.of(context, rootNavigator: true).pop,
),
),
],
)
],
),
),
),
);
}
}
}
@override
void initState() {
controller = TextEditingController();
@ -297,6 +413,12 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
WalletKeysButton(
walletId: walletId,
),
const SizedBox(
width: 2,
),
DeleteWalletButton(
walletId: walletId,
),
const SizedBox(
width: 12,
),
@ -333,35 +455,96 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
: WalletSyncStatus.synced,
),
const Spacer(),
SecondaryButton(
width: 180,
desktopMed: true,
onPressed: () {
_onExchangePressed(context);
},
label: "Exchange",
icon: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackPrimary
.withOpacity(0.2),
),
child: Center(
child: SvgPicture.asset(
Assets.svg.arrowRotate2,
width: 14,
height: 14,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
),
if (coin == Coin.firo) const SizedBox(width: 10),
if (coin == Coin.firo)
SecondaryButton(
width: 180,
buttonHeight: ButtonHeight.l,
label: "Anonymize funds",
onPressed: () async {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => DesktopDialog(
maxWidth: 500,
maxHeight: 210,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32, vertical: 20),
child: Column(
children: [
Text(
"Attention!",
style: STextStyles.desktopH2(context),
),
const SizedBox(height: 16),
Text(
"You're about to anonymize all of your public funds.",
style:
STextStyles.desktopTextSmall(context),
),
const SizedBox(height: 32),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SecondaryButton(
width: 200,
buttonHeight: ButtonHeight.l,
label: "Cancel",
onPressed: () {
Navigator.of(context).pop();
},
),
const SizedBox(width: 20),
PrimaryButton(
width: 200,
buttonHeight: ButtonHeight.l,
label: "Continue",
onPressed: () {
Navigator.of(context).pop();
unawaited(attemptAnonymize());
},
)
],
),
],
),
),
),
);
},
),
),
// if (coin == Coin.firo) const SizedBox(width: 16),
// SecondaryButton(
// width: 180,
// buttonHeight: ButtonHeight.l,
// onPressed: () {
// _onExchangePressed(context);
// },
// label: "Exchange",
// icon: Container(
// width: 24,
// height: 24,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(24),
// color: Theme.of(context)
// .extension<StackColors>()!
// .buttonBackPrimary
// .withOpacity(0.2),
// ),
// child: Center(
// child: SvgPicture.asset(
// Assets.svg.arrowRotate2,
// width: 14,
// height: 14,
// color: Theme.of(context)
// .extension<StackColors>()!
// .buttonTextSecondary,
// ),
// ),
// ),
// ),
],
),
),

View file

@ -200,12 +200,19 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
final favorites = pullOutFavorites(contacts);
return ListView.builder(
final totalLength = favorites.length +
contacts.length +
2; // +2 for "fav" and "all" headers
return ListView.separated(
primary: false,
shrinkWrap: true,
itemCount: favorites.length +
contacts.length +
2, // +2 for "fav" and "all" headers
itemCount: totalLength,
separatorBuilder: (context, index) {
return const SizedBox(
height: 10,
);
},
itemBuilder: (context, index) {
if (index == 0) {
return Padding(
@ -220,7 +227,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
STextStyles.desktopTextExtraExtraSmall(context),
),
);
} else if (index <= favorites.length) {
} else if (index < favorites.length + 1) {
final id = favorites[index - 1].id;
return ContactListItem(
key: Key("contactContactListItem_${id}_key"),
@ -241,7 +248,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
),
);
} else {
final id = contacts[index - favorites.length - 1].id;
final id = contacts[index - favorites.length - 2].id;
return ContactListItem(
key: Key("contactContactListItem_${id}_key"),
contactId: id,

View file

@ -58,7 +58,7 @@ class _ContactListItemState extends ConsumerState<ContactListItem> {
),
child: AddressBookCard(
contactId: contactId,
indicatorDown: _state == ExpandableState.expanded,
indicatorDown: _state,
),
),
body: Column(
@ -87,35 +87,47 @@ class _ContactListItemState extends ConsumerState<ContactListItem> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
WalletInfoCoinIcon(coin: e.coin),
const SizedBox(
width: 12,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${contactId == "default" ? e.other! : e.label} (${e.coin.ticker})",
style: STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
Flexible(
child: Row(
children: [
WalletInfoCoinIcon(coin: e.coin),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"${contactId == "default" ? e.other! : e.label} (${e.coin.ticker})",
style: STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
Row(
children: [
Flexible(
child: Text(
e.address,
style: STextStyles
.desktopTextExtraExtraSmall(
context),
),
),
],
),
],
),
Text(
e.address,
style: STextStyles
.desktopTextExtraExtraSmall(context),
),
],
),
],
),
],
),
),
BlueTextButton(
text: "Select wallet",

View file

@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class DeleteWalletButton extends ConsumerStatefulWidget {
const DeleteWalletButton({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<DeleteWalletButton> createState() => _DeleteWalletButton();
}
class _DeleteWalletButton extends ConsumerState<DeleteWalletButton> {
late final String walletId;
@override
void initState() {
walletId = widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
return RawMaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(1000),
),
onPressed: () async {
final shouldOpenDeleteDialog = await showDialog<bool?>(
context: context,
barrierColor: Colors.transparent,
builder: (context) {
return DeletePopupButton(
onTap: () async {
Navigator.of(context).pop(true);
},
);
},
);
if (shouldOpenDeleteDialog == true) {
final result = await showDialog<bool?>(
context: context,
barrierDismissible: false,
builder: (context) => Navigator(
initialRoute: DesktopDeleteWalletDialog.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
RouteGenerator.generateRoute(
RouteSettings(
name: DesktopDeleteWalletDialog.routeName,
arguments: walletId,
),
),
];
},
),
);
if (result == true) {
if (mounted) {
Navigator.of(context).pop();
}
}
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 19,
horizontal: 32,
),
child: Row(
children: [
SvgPicture.asset(
Assets.svg.ellipsis,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
],
),
),
);
}
}
class DeletePopupButton extends StatefulWidget {
const DeletePopupButton({
Key? key,
this.onTap,
}) : super(key: key);
final VoidCallback? onTap;
@override
State<DeletePopupButton> createState() => _DeletePopupButtonState();
}
class _DeletePopupButtonState extends State<DeletePopupButton> {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: 24,
left: MediaQuery.of(context).size.width - 234,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: widget.onTap,
child: Container(
width: 210,
height: 70,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius * 2,
),
color: Theme.of(context).extension<StackColors>()!.popupBG,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow,
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(width: 24),
SvgPicture.asset(
Assets.svg.trash,
),
const SizedBox(width: 14),
Text(
"Delete wallet",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark),
),
],
),
),
),
),
),
],
);
}
}

View file

@ -0,0 +1,256 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/global/wallets_service_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
class DeleteWalletKeysPopup extends ConsumerStatefulWidget {
const DeleteWalletKeysPopup({
Key? key,
required this.walletId,
required this.words,
this.clipboardInterface = const ClipboardWrapper(),
}) : super(key: key);
final String walletId;
final List<String> words;
final ClipboardInterface clipboardInterface;
static const String routeName = "/desktopDeleteWalletKeysPopup";
@override
ConsumerState<DeleteWalletKeysPopup> createState() =>
_DeleteWalletKeysPopup();
}
class _DeleteWalletKeysPopup extends ConsumerState<DeleteWalletKeysPopup> {
late final String _walletId;
late final List<String> _words;
late final ClipboardInterface _clipboardInterface;
@override
void initState() {
_walletId = widget.walletId;
_words = widget.words;
_clipboardInterface = widget.clipboardInterface;
super.initState();
}
@override
Widget build(BuildContext context) {
return DesktopDialog(
maxWidth: 614,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Wallet keys",
style: STextStyles.desktopH3(context),
),
),
DesktopDialogCloseButton(
onPressedOverride: () {
Navigator.of(
context,
rootNavigator: true,
).pop();
},
),
],
),
const SizedBox(
height: 28,
),
Text(
"Recovery phrase",
style: STextStyles.desktopTextMedium(context),
),
const SizedBox(
height: 8,
),
Center(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32,
),
child: Text(
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
style: STextStyles.desktopTextExtraExtraSmall(context),
textAlign: TextAlign.center,
),
),
),
const SizedBox(
height: 24,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32,
),
child: RawMaterialButton(
hoverColor: Colors.transparent,
onPressed: () async {
await _clipboardInterface.setData(
ClipboardData(text: _words.join(" ")),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
),
);
},
child: MnemonicTable(
words: widget.words,
isDesktop: true,
itemBorderColor: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
),
),
),
const SizedBox(
height: 24,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32,
),
child: Row(
children: [
Expanded(
child: PrimaryButton(
label: "Continue",
onPressed: () async {
await Navigator.of(context).push(
RouteGenerator.getRoute(
builder: (context) {
return ConfirmDelete(
walletId: _walletId,
);
},
settings: const RouteSettings(
name: "/desktopConfirmDelete",
),
),
);
},
),
),
],
),
),
const SizedBox(
height: 32,
),
],
),
);
}
}
class ConfirmDelete extends ConsumerStatefulWidget {
const ConfirmDelete({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<ConfirmDelete> createState() => _ConfirmDeleteState();
}
class _ConfirmDeleteState extends ConsumerState<ConfirmDelete> {
@override
Widget build(BuildContext context) {
return DesktopDialog(
maxHeight: 350,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
DesktopDialogCloseButton(),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Thanks! "
"\n\nYour wallet will be deleted.",
style: STextStyles.desktopH2(context),
textAlign: TextAlign.center,
),
const SizedBox(height: 50),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SecondaryButton(
width: 250,
buttonHeight: ButtonHeight.xl,
label: "Cancel",
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
const SizedBox(width: 16),
PrimaryButton(
width: 250,
buttonHeight: ButtonHeight.xl,
label: "Continue",
onPressed: () async {
final walletsInstance =
ref.read(walletsChangeNotifierProvider);
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId);
final _managerWalletId = manager.walletId;
//
await ref
.read(walletsServiceChangeNotifierProvider)
.deleteWallet(manager.walletName, true);
if (mounted) {
Navigator.of(context, rootNavigator: true).pop(true);
}
// wait for widget tree to dispose of any widgets watching the manager
await Future<void>.delayed(const Duration(seconds: 1));
walletsInstance.removeWallet(walletId: _managerWalletId);
},
),
],
),
],
),
],
),
);
}
}

View file

@ -0,0 +1,128 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:tuple/tuple.dart';
class DesktopAttentionDeleteWallet extends ConsumerStatefulWidget {
const DesktopAttentionDeleteWallet({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
static const String routeName = "/desktopAttentionDeleteWallet";
@override
ConsumerState<DesktopAttentionDeleteWallet> createState() =>
_DesktopAttentionDeleteWallet();
}
class _DesktopAttentionDeleteWallet
extends ConsumerState<DesktopAttentionDeleteWallet> {
@override
Widget build(BuildContext context) {
return DesktopDialog(
maxWidth: 610,
maxHeight: 530,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
DesktopDialogCloseButton(
onPressedOverride: () {
Navigator.of(
context,
rootNavigator: true,
).pop();
},
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 26),
child: Column(
children: [
Text(
"Attention!",
style: STextStyles.desktopH2(context),
),
const SizedBox(
height: 16,
),
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.snackBarBackError,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"You are going to permanently delete you wallet.\n\nIf you delete your wallet, "
"the only way you can have access to your funds is by using your backup key."
"\n\nStack Wallet does not keep nor is able to restore your backup key or your wallet."
"\n\nPLEASE SAVE YOUR BACKUP KEY.",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
),
),
),
),
const SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SecondaryButton(
width: 250,
buttonHeight: ButtonHeight.xl,
label: "Cancel",
onPressed: () {
Navigator.of(
context,
rootNavigator: true,
).pop();
},
),
const SizedBox(width: 16),
PrimaryButton(
width: 250,
buttonHeight: ButtonHeight.xl,
label: "View Backup Key",
onPressed: () async {
final words = await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.mnemonic;
if (mounted) {
await Navigator.of(context).pushNamed(
DeleteWalletKeysPopup.routeName,
arguments: Tuple2(
widget.walletId,
words,
),
);
}
},
),
],
)
],
),
),
],
),
);
}
}

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -10,6 +12,9 @@ import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import '../../../../../notifications/show_flush_bar.dart';
import '../../../../../widgets/loading_indicator.dart';
class DesktopAuthSend extends ConsumerStatefulWidget {
const DesktopAuthSend({Key? key}) : super(key: key);
@ -142,7 +147,7 @@ class _DesktopAuthSendState extends ConsumerState<DesktopAuthSend> {
Expanded(
child: SecondaryButton(
label: "Cancel",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
@ -153,14 +158,37 @@ class _DesktopAuthSendState extends ConsumerState<DesktopAuthSend> {
child: PrimaryButton(
enabled: _confirmEnabled,
label: "Confirm",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: () async {
// TODO show spinner while verifying passphrase
unawaited(
showDialog<void>(
context: context,
builder: (context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
LoadingIndicator(
width: 200,
height: 200,
),
],
),
),
);
await Future<void>.delayed(const Duration(seconds: 1));
final passwordIsValid = await verifyPassphrase();
if (mounted) {
Navigator.of(context).pop(passwordIsValid);
Navigator.of(context).pop();
Navigator.of(
context,
rootNavigator: true,
).pop(passwordIsValid);
await Future<void>.delayed(const Duration(
milliseconds: 100,
));
}
},
),

View file

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class DesktopBalanceToggleButton extends ConsumerWidget {
const DesktopBalanceToggleButton({
Key? key,
this.onPressed,
}) : super(key: key);
final VoidCallback? onPressed;
@override
Widget build(BuildContext context, WidgetRef ref) {
return SizedBox(
height: 22,
width: 22,
child: MaterialButton(
color: Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
onPressed: () {
if (ref.read(walletBalanceToggleStateProvider.state).state ==
WalletBalanceToggleState.available) {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
} else {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
}
onPressed?.call();
},
elevation: 0,
highlightElevation: 0,
hoverElevation: 0,
padding: EdgeInsets.zero,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Center(
child: Image(
image: AssetImage(
ref.watch(walletBalanceToggleStateProvider.state).state ==
WalletBalanceToggleState.available
? Assets.png.glassesHidden
: Assets.png.glasses,
),
width: 16,
),
),
),
);
}
}

View file

@ -0,0 +1,256 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
class DesktopDeleteWalletDialog extends ConsumerStatefulWidget {
const DesktopDeleteWalletDialog({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
static const String routeName = "/desktopDeleteWalletDialog";
@override
ConsumerState<DesktopDeleteWalletDialog> createState() =>
_DesktopDeleteWalletDialog();
}
class _DesktopDeleteWalletDialog
extends ConsumerState<DesktopDeleteWalletDialog> {
late final TextEditingController passwordController;
late final FocusNode passwordFocusNode;
bool hidePassword = true;
bool _continueEnabled = false;
Future<void> enterPassphrase() async {
unawaited(
showDialog(
context: context,
builder: (context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
LoadingIndicator(
width: 200,
height: 200,
),
],
),
),
);
await Future<void>.delayed(const Duration(seconds: 1));
final verified = await ref
.read(storageCryptoHandlerProvider)
.verifyPassphrase(passwordController.text);
if (verified) {
Navigator.of(context, rootNavigator: true).pop();
final words = await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.mnemonic;
if (mounted) {
Navigator.of(context).pop();
unawaited(
Navigator.of(context).pushNamed(
DesktopAttentionDeleteWallet.routeName,
arguments: widget.walletId,
),
);
}
} else {
Navigator.of(context, rootNavigator: true).pop();
await Future<void>.delayed(const Duration(milliseconds: 300));
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Invalid passphrase!",
context: context,
),
);
}
}
@override
void initState() {
passwordController = TextEditingController();
passwordFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
passwordController.dispose();
passwordFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
DesktopDialogCloseButton(
onPressedOverride: Navigator.of(
context,
rootNavigator: true,
).pop,
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 26),
child: Column(
children: [
const SizedBox(height: 16),
Text(
"Delete wallet",
style: STextStyles.desktopH2(context),
),
const SizedBox(height: 16),
Text(
"Enter your password",
style: STextStyles.desktopTextMedium(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textDark3,
),
),
const SizedBox(height: 24),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("desktopDeleteWalletPasswordFieldKey"),
focusNode: passwordFocusNode,
controller: passwordController,
style: STextStyles.field(context),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
autofocus: true,
onSubmitted: (_) {
if (_continueEnabled) {
enterPassphrase();
}
},
decoration: standardInputDecoration(
"Enter password",
passwordFocusNode,
context,
).copyWith(
labelStyle: STextStyles.fieldLabel(context),
suffixIcon: UnconstrainedBox(
child: SizedBox(
height: 70,
child: Row(
children: [
const SizedBox(
width: 24,
),
GestureDetector(
key: const Key(
"desktopDeleteWalletShowPasswordButtonKey"),
onTap: () async {
setState(() {
hidePassword = !hidePassword;
});
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: SvgPicture.asset(
hidePassword
? Assets.svg.eye
: Assets.svg.eyeSlash,
color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
width: 24,
height: 24,
),
),
),
const SizedBox(
width: 12,
),
],
),
),
),
),
onChanged: (newValue) {
setState(() {
_continueEnabled = passwordController.text.isNotEmpty;
});
},
),
),
const SizedBox(height: 50),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SecondaryButton(
width: 250,
buttonHeight: ButtonHeight.xl,
label: "Cancel",
onPressed: Navigator.of(
context,
rootNavigator: true,
).pop,
),
const SizedBox(width: 16),
PrimaryButton(
width: 250,
buttonHeight: ButtonHeight.xl,
enabled: _continueEnabled,
label: "Continue",
onPressed: _continueEnabled
? () async {
// add loading indicator
enterPassphrase();
}
: null,
),
],
)
],
),
),
],
),
);
}
}

View file

@ -117,78 +117,82 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
GestureDetector(
onTap: () {
clipboard.setData(
ClipboardData(text: receivingAddress),
);
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).extension<StackColors>()!.background,
width: 2,
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
clipboard.setData(
ClipboardData(text: receivingAddress),
);
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).extension<StackColors>()!.background,
width: 2,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: RoundedWhiteContainer(
child: Column(
children: [
Row(
children: [
Text(
"Your ${coin.ticker} address",
style: STextStyles.itemSubtitle(context),
),
const Spacer(),
Row(
children: [
SvgPicture.asset(
Assets.svg.copy,
width: 15,
height: 15,
color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
),
const SizedBox(
width: 4,
),
Text(
"Copy",
style: STextStyles.link2(context),
),
],
),
],
),
const SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: Text(
receivingAddress,
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
child: RoundedWhiteContainer(
child: Column(
children: [
Row(
children: [
Text(
"Your ${coin.ticker} address",
style: STextStyles.itemSubtitle(context),
),
const Spacer(),
Row(
children: [
SvgPicture.asset(
Assets.svg.copy,
width: 15,
height: 15,
color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
),
const SizedBox(
width: 4,
),
Text(
"Copy",
style: STextStyles.link2(context),
),
],
),
],
),
const SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: Text(
receivingAddress,
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
),
),
],
),
],
],
),
],
),
),
),
),
@ -199,7 +203,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
),
if (coin != Coin.epicCash)
SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: generateNewAddress,
label: "Generate new address",
),

View file

@ -145,7 +145,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
right: 32,
),
child: SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Ok",
onPressed: () {
Navigator.of(context).pop();
@ -232,7 +232,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
children: [
Expanded(
child: SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Cancel",
onPressed: () {
Navigator.of(context).pop(false);
@ -244,7 +244,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
),
Expanded(
child: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Yes",
onPressed: () {
Navigator.of(context).pop(true);
@ -399,7 +399,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
),
child: Expanded(
child: SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Yes",
onPressed: () {
Navigator.of(
@ -1385,7 +1385,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
height: 36,
),
PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Preview send",
enabled: ref.watch(previewTxButtonStateProvider.state).state,
onPressed: ref.watch(previewTxButtonStateProvider.state).state

View file

@ -1,13 +1,15 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_balance_toggle_button.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -33,19 +35,6 @@ class _WDesktopWalletSummaryState extends State<DesktopWalletSummary> {
late final String walletId;
late final ChangeNotifierProvider<Manager> managerProvider;
void showSheet() {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => WalletBalanceToggleSheet(walletId: walletId),
);
}
Decimal? _balanceTotalCached;
Decimal? _balanceCached;
@ -59,225 +48,161 @@ class _WDesktopWalletSummaryState extends State<DesktopWalletSummary> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
return Consumer(
builder: (context, ref, __) {
final Coin coin =
ref.watch(managerProvider.select((value) => value.coin));
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer(
builder: (_, ref, __) {
final Coin coin =
ref.watch(managerProvider.select((value) => value.coin));
final externalCalls = ref.watch(prefsChangeNotifierProvider
.select((value) => value.externalCalls));
Column(
children: [
Consumer(
builder: (_, ref, __) {
final externalCalls = ref.watch(prefsChangeNotifierProvider
.select((value) => value.externalCalls));
Future<Decimal>? totalBalanceFuture;
Future<Decimal>? availableBalanceFuture;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
final firoWallet =
ref.watch(managerProvider.select((value) => value.wallet))
Future<Decimal>? totalBalanceFuture;
Future<Decimal>? availableBalanceFuture;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
final firoWallet = ref.watch(
managerProvider.select((value) => value.wallet))
as FiroWallet;
totalBalanceFuture = firoWallet.availablePublicBalance();
availableBalanceFuture = firoWallet.availablePrivateBalance();
} else {
totalBalanceFuture = ref.watch(
managerProvider.select((value) => value.totalBalance));
availableBalanceFuture = ref.watch(managerProvider
.select((value) => value.availableBalance));
}
final locale = ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale));
final baseCurrency = ref.watch(prefsChangeNotifierProvider
.select((value) => value.currency));
final priceTuple = ref.watch(priceAnd24hChangeNotifierProvider
.select((value) => value.getPrice(coin)));
final _showAvailable = false;
// ref.watch(walletBalanceToggleStateProvider.state).state ==
// WalletBalanceToggleState.available;
return FutureBuilder(
future: _showAvailable
? availableBalanceFuture
: totalBalanceFuture,
builder: (fbContext, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData &&
snapshot.data != null) {
if (_showAvailable) {
_balanceCached = snapshot.data!;
} else {
_balanceTotalCached = snapshot.data!;
}
}
Decimal? balanceToShow =
_showAvailable ? _balanceCached : _balanceTotalCached;
if (balanceToShow != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// GestureDetector(
// onTap: showSheet,
// child: Row(
// children: [
// if (coin == Coin.firo ||
// coin == Coin.firoTestNet)
// Text(
// "${_showAvailable ? "Private" : "Public"} Balance",
// style: STextStyles.subtitle500(context)
// .copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFavoriteCard,
// ),
// ),
// if (coin != Coin.firo &&
// coin != Coin.firoTestNet)
// Text(
// "${_showAvailable ? "Available" : "Full"} Balance",
// style: STextStyles.subtitle500(context)
// .copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFavoriteCard,
// ),
// ),
// const SizedBox(
// width: 4,
// ),
// SvgPicture.asset(
// Assets.svg.chevronDown,
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFavoriteCard,
// width: 8,
// height: 4,
// ),
// ],
// ),
// ),
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"${Format.localizedStringAsFixed(
value: balanceToShow,
locale: locale,
decimalPlaces: 8,
)} ${coin.ticker}",
style: STextStyles.desktopH3(context),
),
),
if (externalCalls)
Text(
"${Format.localizedStringAsFixed(
value: priceTuple.item1 * balanceToShow,
locale: locale,
decimalPlaces: 2,
)} $baseCurrency",
style: STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
);
totalBalanceFuture = firoWallet.availablePublicBalance();
availableBalanceFuture =
firoWallet.availablePrivateBalance();
} else {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// GestureDetector(
// onTap: showSheet,
// child: Row(
// children: [
// if (coin == Coin.firo ||
// coin == Coin.firoTestNet)
// Text(
// "${_showAvailable ? "Private" : "Public"} Balance",
// style: STextStyles.subtitle500(context)
// .copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFavoriteCard,
// ),
// ),
// if (coin != Coin.firo &&
// coin != Coin.firoTestNet)
// Text(
// "${_showAvailable ? "Available" : "Full"} Balance",
// style: STextStyles.subtitle500(context)
// .copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFavoriteCard,
// ),
// ),
// const SizedBox(
// width: 4,
// ),
// SvgPicture.asset(
// Assets.svg.chevronDown,
// width: 8,
// height: 4,
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFavoriteCard,
// ),
// ],
// ),
// ),
AnimatedText(
stringsToLoopThrough: const [
"Loading balance ",
"Loading balance. ",
"Loading balance.. ",
"Loading balance..."
],
style: STextStyles.desktopH3(context).copyWith(
fontSize: 24,
color: Theme.of(context)
.extension<StackColors>()!
.textFavoriteCard,
),
),
if (externalCalls)
AnimatedText(
stringsToLoopThrough: const [
"Loading balance ",
"Loading balance. ",
"Loading balance.. ",
"Loading balance..."
],
style: STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
);
totalBalanceFuture = ref.watch(managerProvider
.select((value) => value.totalBalance));
availableBalanceFuture = ref.watch(managerProvider
.select((value) => value.availableBalance));
}
final locale = ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale));
final baseCurrency = ref.watch(prefsChangeNotifierProvider
.select((value) => value.currency));
final priceTuple = ref.watch(
priceAnd24hChangeNotifierProvider
.select((value) => value.getPrice(coin)));
final _showAvailable = ref
.watch(walletBalanceToggleStateProvider.state)
.state ==
WalletBalanceToggleState.available;
return FutureBuilder(
future: _showAvailable
? availableBalanceFuture
: totalBalanceFuture,
builder: (fbContext, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData &&
snapshot.data != null) {
if (_showAvailable) {
_balanceCached = snapshot.data!;
} else {
_balanceTotalCached = snapshot.data!;
}
}
Decimal? balanceToShow = _showAvailable
? _balanceCached
: _balanceTotalCached;
if (balanceToShow != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"${Format.localizedStringAsFixed(
value: balanceToShow,
locale: locale,
decimalPlaces: 8,
)} ${coin.ticker}",
style: STextStyles.desktopH3(context),
),
),
if (externalCalls)
Text(
"${Format.localizedStringAsFixed(
value: priceTuple.item1 * balanceToShow,
locale: locale,
decimalPlaces: 2,
)} $baseCurrency",
style:
STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
);
} else {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AnimatedText(
stringsToLoopThrough: const [
"Loading balance ",
"Loading balance. ",
"Loading balance.. ",
"Loading balance..."
],
style: STextStyles.desktopH3(context).copyWith(
fontSize: 24,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
if (externalCalls)
AnimatedText(
stringsToLoopThrough: const [
"Loading balance ",
"Loading balance. ",
"Loading balance.. ",
"Loading balance..."
],
style:
STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
);
}
},
);
},
);
},
),
],
),
if (coin == Coin.firo || coin == Coin.firoTestNet)
const SizedBox(
width: 8,
),
if (coin == Coin.firo || coin == Coin.firoTestNet)
const DesktopBalanceToggleButton(),
const SizedBox(
width: 8,
),
WalletRefreshButton(
walletId: walletId,
initialSyncStatus: widget.initialSyncStatus,
)
],
),
const SizedBox(
width: 8,
),
WalletRefreshButton(
walletId: walletId,
initialSyncStatus: widget.initialSyncStatus,
)
],
);
},
);
}
}

View file

@ -16,6 +16,7 @@ import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
class UnlockWalletKeysDesktop extends ConsumerStatefulWidget {
@ -201,11 +202,32 @@ class _UnlockWalletKeysDesktopState
enabled: continueEnabled,
onPressed: continueEnabled
? () async {
unawaited(
showDialog(
context: context,
builder: (context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
LoadingIndicator(
width: 200,
height: 200,
),
],
),
),
);
await Future<void>.delayed(
const Duration(seconds: 1));
final verified = await ref
.read(storageCryptoHandlerProvider)
.verifyPassphrase(passwordController.text);
if (verified) {
Navigator.of(context, rootNavigator: true).pop();
final words = await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
@ -219,6 +241,11 @@ class _UnlockWalletKeysDesktopState
);
}
} else {
Navigator.of(context, rootNavigator: true).pop();
await Future<void>.delayed(
const Duration(milliseconds: 300));
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,

View file

@ -8,6 +8,7 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'debug_info_dialog.dart';
@ -143,7 +144,21 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
),
],
),
const StackPrivacyButton(),
PrimaryButton(
label: "Change",
buttonHeight: ButtonHeight.xs,
width: 86,
onPressed: () async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return const StackPrivacyDialog();
},
);
},
)
],
),
);
@ -172,7 +187,21 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
.textDark),
textAlign: TextAlign.left,
),
ShowLogsButton(),
PrimaryButton(
buttonHeight: ButtonHeight.xs,
label: "Show logs",
width: 101,
onPressed: () async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return const DebugInfoDialog();
},
);
},
),
],
),
),
@ -184,81 +213,3 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
);
}
}
class StackPrivacyButton extends ConsumerWidget {
const StackPrivacyButton({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
Future<void> changePrivacySettings() async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return StackPrivacyDialog();
},
);
}
return SizedBox(
width: 84,
height: 37,
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
onPressed: () {
// Navigator.of(context).pushNamed(
// StackPrivacyCalls.routeName,
// arguments: false,
// );
changePrivacySettings();
},
child: Text(
"Change",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(color: Colors.white),
),
),
);
}
}
class ShowLogsButton extends ConsumerWidget {
const ShowLogsButton({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
Future<void> viewDebugLogs() async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return const DebugInfoDialog();
},
);
}
return SizedBox(
width: 101,
height: 37,
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
onPressed: () {
viewDebugLogs();
},
child: Text(
"Show logs",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(color: Colors.white),
),
),
);
}
}

View file

@ -10,6 +10,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/color_theme.dart';
import 'package:stackwallet/utilities/theme/dark_colors.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/ocean_breeze_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -291,7 +292,109 @@ class _ThemeToggle extends ConsumerState<ThemeToggle> {
),
),
const SizedBox(
width: 20,
width: 10,
),
MaterialButton(
splashColor: Colors.transparent,
hoverColor: Colors.transparent,
padding: const EdgeInsets.all(0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.oceanBreeze.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
OceanBreezeColors(),
);
setState(() {
_selectedTheme = "oceanBreeze";
});
},
child: SizedBox(
width: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
border: Border.all(
width: 2.5,
color: _selectedTheme == "oceanBreeze"
? Theme.of(context)
.extension<StackColors>()!
.infoItemIcons
: Theme.of(context).extension<StackColors>()!.popupBG,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: SvgPicture.asset(
Assets.svg.themeOcean,
),
),
const SizedBox(
height: 12,
),
Row(
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: Theme.of(context)
.extension<StackColors>()!
.radioButtonIconEnabled,
value: "oceanBreeze",
groupValue: _selectedTheme,
onChanged: (newValue) {
if (newValue is String && newValue == "oceanBreeze") {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.oceanBreeze.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
OceanBreezeColors(),
);
setState(() {
_selectedTheme = "oceanBreeze";
});
}
},
),
),
const SizedBox(
width: 14,
),
Text(
"Ocean Breeze",
style:
STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
],
),
),
),
const SizedBox(
width: 10,
),
MaterialButton(
splashColor: Colors.transparent,

View file

@ -240,7 +240,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
children: [
Expanded(
child: SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Cancel",
onPressed: Navigator.of(context).pop,
),
@ -248,7 +248,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
const SizedBox(width: 16),
Expanded(
child: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Disable",
onPressed: () {
ref
@ -422,7 +422,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
padding: const EdgeInsets.all(10),
child: !isEnabledAutoBackup
? PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.m,
width: 200,
label: "Enable auto backup",
onPressed: () {
@ -467,7 +467,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
Row(
children: [
PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.m,
width: 190,
label: "Disable auto backup",
onPressed: () {
@ -476,7 +476,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
),
const SizedBox(width: 16),
SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.m,
width: 190,
label: "Edit auto backup",
onPressed: () {
@ -560,7 +560,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
child: CreateBackupView(),
)
: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.m,
width: 200,
label: "Create manual backup",
onPressed: () {
@ -642,7 +642,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
child: RestoreFromFileView(),
)
: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.m,
width: 200,
label: "Restore backup",
onPressed: () {

View file

@ -556,7 +556,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
Expanded(
child: SecondaryButton(
label: "Cancel",
desktopMed: true,
buttonHeight: ButtonHeight.l,
onPressed: Navigator.of(context).pop,
),
),
@ -565,7 +565,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
),
Expanded(
child: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Enable Auto Backup",
enabled: shouldEnableCreate,
onPressed: !shouldEnableCreate
@ -792,7 +792,8 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
Expanded(
child: PrimaryButton(
label: "Ok",
desktopMed: true,
buttonHeight:
ButtonHeight.l,
onPressed: () {
Navigator.of(context)
.pop();

View file

@ -59,7 +59,7 @@ class EnableBackupDialog extends StatelessWidget {
children: [
Expanded(
child: SecondaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Cancel",
onPressed: () {
Navigator.of(context).pop();
@ -71,7 +71,7 @@ class EnableBackupDialog extends StatelessWidget {
),
Expanded(
child: PrimaryButton(
desktopMed: true,
buttonHeight: ButtonHeight.l,
label: "Continue",
onPressed: () {
Navigator.of(context).pop();

View file

@ -107,8 +107,8 @@ class _CurrencySettings extends ConsumerState<CurrencySettings> {
10,
),
child: PrimaryButton(
width: 210,
desktopMed: true,
width: 200,
buttonHeight: ButtonHeight.m,
enabled: true,
label: "Change currency",
onPressed: () {

View file

@ -80,12 +80,12 @@ class _LanguageOptionSettings extends ConsumerState<LanguageOptionSettings> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(
padding: const EdgeInsets.all(
10,
),
child: PrimaryButton(
width: 210,
desktopMed: true,
width: 200,
buttonHeight: ButtonHeight.m,
enabled: true,
label: "Change language",
onPressed: () {

View file

@ -61,6 +61,8 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
if (verified) {
if (pwNew != pwNewRepeat) {
await Future<void>.delayed(const Duration(seconds: 1));
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
@ -77,6 +79,8 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
);
if (success) {
await Future<void>.delayed(const Duration(seconds: 1));
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
@ -86,6 +90,8 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
);
return true;
} else {
await Future<void>.delayed(const Duration(seconds: 1));
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
@ -97,6 +103,8 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
}
}
} else {
await Future<void>.delayed(const Duration(seconds: 1));
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
@ -485,7 +493,7 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
const SizedBox(height: 20),
PrimaryButton(
width: 160,
desktopMed: true,
buttonHeight: ButtonHeight.l,
enabled: shouldEnableSave,
label: "Save changes",
onPressed: () async {
@ -503,7 +511,7 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
)
: PrimaryButton(
width: 210,
desktopMed: true,
buttonHeight: ButtonHeight.m,
enabled: true,
label: "Set up new password",
onPressed: () {

View file

@ -3,9 +3,13 @@ import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/sync_type_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class SyncingPreferencesSettings extends ConsumerStatefulWidget {
@ -20,6 +24,19 @@ class SyncingPreferencesSettings extends ConsumerStatefulWidget {
class _SyncingPreferencesSettings
extends ConsumerState<SyncingPreferencesSettings> {
String _currentTypeDescription(SyncingType type) {
switch (type) {
case SyncingType.currentWalletOnly:
return "Sync only currently open wallet";
case SyncingType.selectedWalletsAtStartup:
return "Sync only selected wallets at startup";
case SyncingType.allWalletsOnStartup:
return "Sync all wallets at startup";
}
}
late bool changePrefs = false;
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
@ -34,13 +51,40 @@ class _SyncingPreferencesSettings
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset(
Assets.svg.circleArrowRotate,
width: 48,
height: 48,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset(
Assets.svg.circleArrowRotate,
width: 48,
height: 48,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondaryDisabled,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_currentTypeDescription(ref.watch(
prefsChangeNotifierProvider
.select((value) => value.syncType))),
style: STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2),
textAlign: TextAlign.left,
),
),
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
@ -67,28 +111,50 @@ class _SyncingPreferencesSettings
),
],
),
///TODO: ONLY SHOW SYNC OPTIONS ON BUTTON PRESS
Column(
children: const [
SyncingOptionsView(),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(
10,
),
child: PrimaryButton(
width: 210,
desktopMed: true,
enabled: true,
label: "Change preferences",
onPressed: () {},
),
),
padding: const EdgeInsets.all(
10,
),
child: changePrefs
? SizedBox(
width: 512,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SyncingOptionsView(),
PrimaryButton(
width: 200,
buttonHeight: ButtonHeight.m,
enabled: true,
label: "Save",
onPressed: () {
setState(() {
changePrefs = false;
});
},
),
],
),
)
: Column(
children: [
const SizedBox(height: 10),
PrimaryButton(
width: 200,
buttonHeight: ButtonHeight.m,
enabled: true,
label: "Change preferences",
onPressed: () {
setState(() {
changePrefs = true;
});
},
),
],
)),
],
),
],

View file

@ -85,12 +85,16 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_sear
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
import 'package:stackwallet/pages_desktop_specific/create_password/create_password_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
import 'package:stackwallet/pages_desktop_specific/forgot_password_desktop_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/desktop_address_book.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_settings_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_notifications_view.dart';
@ -1019,6 +1023,12 @@ class RouteGenerator {
builder: (_) => const DesktopNotificationsView(),
settings: RouteSettings(name: settings.name));
case DesktopExchangeView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const DesktopExchangeView(),
settings: RouteSettings(name: settings.name));
case DesktopSettingsView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
@ -1163,6 +1173,73 @@ class RouteGenerator {
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case DesktopDeleteWalletDialog.routeName:
if (args is String) {
return FadePageRoute(
DesktopDeleteWalletDialog(
walletId: args,
),
RouteSettings(
name: settings.name,
),
);
// return getRoute(
// shouldUseMaterialRoute: useMaterialPageRoute,
// builder: (_) => WalletKeysDesktopPopup(
// words: args,
// ),
// settings: RouteSettings(
// name: settings.name,
// ),
// );
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case DesktopAttentionDeleteWallet.routeName:
if (args is String) {
return FadePageRoute(
DesktopAttentionDeleteWallet(
walletId: args,
),
RouteSettings(
name: settings.name,
),
);
// return getRoute(
// shouldUseMaterialRoute: useMaterialPageRoute,
// builder: (_) => WalletKeysDesktopPopup(
// words: args,
// ),
// settings: RouteSettings(
// name: settings.name,
// ),
// );
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case DeleteWalletKeysPopup.routeName:
if (args is Tuple2<String, List<String>>) {
return FadePageRoute(
DeleteWalletKeysPopup(
walletId: args.item1,
words: args.item2,
),
RouteSettings(
name: settings.name,
),
);
// return getRoute(
// shouldUseMaterialRoute: useMaterialPageRoute,
// builder: (_) => WalletKeysDesktopPopup(
// words: args,
// ),
// settings: RouteSettings(
// name: settings.name,
// ),
// );
}
return _routeError("${settings.name} invalid args: ${args.toString()}");
case QRCodeDesktopPopupContent.routeName:
if (args is String) {
return FadePageRoute(

View file

@ -185,8 +185,8 @@ class MoneroWallet extends CoinServiceAPI {
try {
if (walletBase!.syncStatus! is SyncedSyncStatus &&
walletBase!.syncStatus!.progress() == 1.0) {
Logging.instance
.log("currentSyncingHeight lol", level: LogLevel.Warning);
// Logging.instance
// .log("currentSyncingHeight lol", level: LogLevel.Warning);
return getSyncingHeight();
}
} catch (e, s) {}

Some files were not shown because too many files have changed in this diff Show more