Merge remote-tracking branch 'cypherstack/ui-fixes' into simplex

This commit is contained in:
sneurlax 2023-01-24 12:29:13 -06:00
commit 32ca83673a
239 changed files with 39842 additions and 27121 deletions

1
.gitignore vendored
View file

@ -55,3 +55,4 @@ libcw_monero.dll
libcw_wownero.dll
libepic_cash_wallet.dll
libmobileliblelantus.dll
/libisar.so

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 10C0 4.47656 4.47656 0 10 0C15.5234 0 20 4.47656 20 10C20 15.5234 15.5234 20 10 20C4.47656 20 0 15.5234 0 10ZM10 14.375C10.5195 14.375 10.9375 13.957 10.9375 13.4375V10.9375H13.4375C13.957 10.9375 14.375 10.5195 14.375 10C14.375 9.48047 13.957 9.0625 13.4375 9.0625H10.9375V6.5625C10.9375 6.04297 10.5195 5.625 10 5.625C9.48047 5.625 9.0625 6.04297 9.0625 6.5625V9.0625H6.5625C6.04297 9.0625 5.625 9.48047 5.625 10C5.625 10.5195 6.04297 10.9375 6.5625 10.9375H9.0625V13.4375C9.0625 13.957 9.48047 14.375 10 14.375Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 648 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 76 KiB

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 d="M15 21C15 21.5031 14.6859 22.0406 14.1234 22.4156C13.5609 22.7906 12.7547 23 12 23C11.2031 23 10.4391 22.7906 9.87656 22.4156C9.31406 22.0406 9 21.5031 9 21H15Z" fill="#232323"/>
<path d="M13.4279 2.38462V3.21538C16.6867 3.85707 19.1419 6.65096 19.1419 10V10.8135C19.1419 12.8514 19.9142 14.8115 21.307 16.3346L21.6373 16.6938C22.0123 17.1048 22.106 17.6846 21.8739 18.1822C21.6418 18.6798 21.1329 19 20.5704 19H3.42848C2.86601 19 2.35582 18.6798 2.12538 18.1822C1.89495 17.6846 1.98712 17.1048 2.36086 16.6938L2.69192 16.3346C4.08648 14.8115 4.85697 12.8514 4.85697 10.8135V10C4.85697 6.65096 7.27202 3.85707 10.5709 3.21538V2.38462C10.5709 1.62005 11.2093 1 11.9994 1C12.7896 1 13.4279 1.62005 13.4279 2.38462Z" fill="#232323"/>
<circle cx="20.5" cy="3.5" r="2.5" fill="#C00205"/>
</svg>

After

Width:  |  Height:  |  Size: 895 B

View file

@ -0,0 +1,97 @@
<svg width="360" height="640" viewBox="0 0 360 640" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="paint0_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(445.5 482.5) rotate(90) scale(127.5 399.264)">
<stop stop-color="#FFEEBC"/>
<stop offset="1" stop-color="#FFE377" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint1_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(158.5 258.5) rotate(48.43) scale(263.308)">
<stop stop-color="#FE7160"/>
<stop offset="1" stop-color="#FBAF4A" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint2_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(11.5 568.5) rotate(-38.0039) scale(136.427)">
<stop stop-color="#F95369"/>
<stop offset="1" stop-color="#F95369" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint3_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(149.5 200.5) rotate(90) scale(205.5)">
<stop stop-color="#FED393"/>
<stop offset="1" stop-color="#FED393" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint4_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(116 536) rotate(90) scale(242)">
<stop stop-color="#F95369"/>
<stop offset="1" stop-color="#F95369" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint5_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(195.5 241.5) rotate(90) scale(127.5)">
<stop stop-color="#FED393"/>
<stop offset="1" stop-color="#FED393" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint6_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-36.5 -11.5) rotate(48.43) scale(336.833)">
<stop stop-color="#FE7160"/>
<stop offset="1" stop-color="#FBAF4A" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint7_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(360 -12) rotate(152.509) scale(340.105)">
<stop stop-color="#F95369"/>
<stop offset="1" stop-color="#F95369" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint8_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(11 465) rotate(-56.3099) scale(251.858 235.919)">
<stop stop-color="#FED393"/>
<stop offset="1" stop-color="#FED393" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint9_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(393 430) rotate(90) scale(200)">
<stop stop-color="#FEAC02"/>
<stop offset="1" stop-color="#FEAC02" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint10_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(415.5 658.5) rotate(90) scale(253.5)">
<stop stop-color="#FE7160"/>
<stop offset="1" stop-color="#F95369" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint11_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(421 -52) rotate(90) scale(221)">
<stop stop-color="#FEAC02"/>
<stop offset="1" stop-color="#FEAC02" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint12_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-125 501) rotate(90) scale(207)">
<stop stop-color="#FFE9A0"/>
<stop offset="1" stop-color="#FFE9A0" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint13_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(145 -74) rotate(-56.3099) scale(191.698 179.566)">
<stop stop-color="#FED393"/>
<stop offset="1" stop-color="#FED393" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint14_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(154 678) rotate(90) scale(139)">
<stop stop-color="#FBB44A"/>
<stop offset="1" stop-color="#FDC800" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint15_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(222 546) rotate(90) scale(110)">
<stop stop-color="#FBBF4A" stop-opacity="0.5"/>
<stop offset="1" stop-color="#FDC800" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint16_radial_815_28509" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(401 629) rotate(90) scale(223)">
<stop stop-color="#FF6545"/>
<stop offset="1" stop-color="#FDC800" stop-opacity="0"/>
</radialGradient>
<clipPath id="clip0_815_28509">
<rect width="360" height="640" fill="white"/>
</clipPath>
</defs>
<g clip-path="url(#clip0_815_28509)">
<rect width="360" height="640" fill="#FFF9F2"/>
<g opacity="0.25">
<ellipse cx="445.5" cy="482.5" rx="419.5" ry="127.5" fill="url(#paint0_radial_815_28509)"/>
<circle cx="158.5" cy="258.5" r="286.5" fill="url(#paint1_radial_815_28509)"/>
<circle cx="11.5" cy="568.5" r="154.5" fill="url(#paint2_radial_815_28509)"/>
<circle cx="149.5" cy="200.5" r="205.5" fill="url(#paint3_radial_815_28509)"/>
<circle cx="116" cy="536" r="242" fill="url(#paint4_radial_815_28509)"/>
<circle cx="195.5" cy="241.5" r="127.5" fill="url(#paint5_radial_815_28509)"/>
<circle cx="-36.5" cy="-11.5" r="366.5" fill="url(#paint6_radial_815_28509)"/>
<circle cx="360" cy="-12" r="486" fill="url(#paint7_radial_815_28509)"/>
<circle cx="11" cy="465" r="247" fill="url(#paint8_radial_815_28509)"/>
<circle cx="393" cy="430" r="200" fill="url(#paint9_radial_815_28509)"/>
<circle cx="415.5" cy="658.5" r="253.5" fill="url(#paint10_radial_815_28509)"/>
<circle cx="421" cy="-52" r="221" fill="url(#paint11_radial_815_28509)"/>
<circle cx="-125" cy="501" r="207" fill="url(#paint12_radial_815_28509)"/>
<circle cx="145" cy="-74" r="188" fill="url(#paint13_radial_815_28509)"/>
<circle cx="154" cy="678" r="139" fill="url(#paint14_radial_815_28509)"/>
<circle cx="222" cy="546" r="110" fill="url(#paint15_radial_815_28509)"/>
<circle cx="401" cy="629" r="223" fill="url(#paint16_radial_815_28509)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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="#232323"/>
<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="#232323"/>
<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="#232323"/>
</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="#232323"/>
<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="#232323"/>
<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="#232323"/>
<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="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 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 fill-rule="evenodd" clip-rule="evenodd" d="M14.6695 1.06575C15.1855 0.607104 15.9756 0.65358 16.4343 1.16956L20.4343 5.66956C20.8552 6.14317 20.8552 6.85686 20.4343 7.33047L16.4343 11.8305C15.9756 12.3464 15.1855 12.3929 14.6695 11.9343C14.1536 11.4756 14.1071 10.6855 14.5657 10.1696L16.7164 7.75001H6C4.48122 7.75001 3.25 8.98123 3.25 10.5C3.25 11.1904 2.69036 11.75 2 11.75C1.30964 11.75 0.75 11.1904 0.75 10.5C0.75 7.60052 3.10051 5.25001 6 5.25001H16.7164L14.5657 2.83047C14.1071 2.31449 14.1536 1.5244 14.6695 1.06575Z" fill="#232323"/>
<path opacity="0.4" fill-rule="evenodd" clip-rule="evenodd" d="M9.33045 23.4342C8.81448 23.8929 8.02439 23.8464 7.56574 23.3304L3.56574 18.8304C3.14475 18.3568 3.14475 17.6431 3.56574 17.1695L7.56574 12.6695C8.02439 12.1536 8.81448 12.1071 9.33046 12.5657C9.84643 13.0244 9.89291 13.8145 9.43426 14.3304L7.28355 16.75L18 16.75C19.5188 16.75 20.75 15.5188 20.75 14C20.75 13.3096 21.3096 12.75 22 12.75C22.6904 12.75 23.25 13.3096 23.25 14C23.25 16.8995 20.8995 19.25 18 19.25L7.28355 19.25L9.43426 21.6695C9.89291 22.1855 9.84643 22.9756 9.33045 23.4342Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 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

@ -0,0 +1,24 @@
<svg width="200" height="162" viewBox="0 0 200 162" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_489_21263)">
<rect width="200" height="162" rx="8" fill="black"/>
<rect x="10" y="10" width="180" height="20" rx="2" fill="#77A7F9"/>
<rect x="16" y="16" width="106" height="8" rx="1" fill="black"/>
<rect x="10" y="40" width="180" height="20" rx="2" fill="#212F46"/>
<rect x="16" y="46" width="106" height="8" rx="1" fill="black"/>
<rect x="10" y="62" width="180" height="20" rx="2" fill="#212F46"/>
<rect x="16" y="68" width="106" height="8" rx="1" fill="black"/>
<rect x="10" y="84" width="180" height="20" rx="2" fill="#212F46"/>
<rect x="16" y="90" width="106" height="8" rx="1" fill="black"/>
<rect x="10" y="106" width="180" height="20" rx="2" fill="#212F46"/>
<rect x="16" y="112" width="106" height="8" rx="1" fill="black"/>
<rect x="10" y="128" width="180" height="20" rx="2" fill="#212F46"/>
<rect x="16" y="134" width="106" height="8" rx="1" fill="black"/>
<rect x="10" y="150" width="180" height="20" rx="2" fill="#212F46"/>
<rect x="16" y="156" width="106" height="8" rx="1" fill="black"/>
</g>
<defs>
<clipPath id="clip0_489_21263">
<rect width="200" height="162" rx="8" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

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 d="M15 21C15 21.5031 14.6859 22.0406 14.1234 22.4156C13.5609 22.7906 12.7547 23 12 23C11.2031 23 10.4391 22.7906 9.87656 22.4156C9.31406 22.0406 9 21.5031 9 21H15Z" fill="#E0E3E3"/>
<path d="M13.4279 2.38462V3.21538C16.6867 3.85707 19.1419 6.65096 19.1419 10V10.8135C19.1419 12.8514 19.9142 14.8115 21.307 16.3346L21.6373 16.6938C22.0123 17.1048 22.106 17.6846 21.8739 18.1822C21.6418 18.6798 21.1329 19 20.5704 19H3.42848C2.86601 19 2.35582 18.6798 2.12538 18.1822C1.89495 17.6846 1.98712 17.1048 2.36086 16.6938L2.69192 16.3346C4.08648 14.8115 4.85697 12.8514 4.85697 10.8135V10C4.85697 6.65096 7.27202 3.85707 10.5709 3.21538V2.38462C10.5709 1.62005 11.2093 1 11.9994 1C12.7896 1 13.4279 1.62005 13.4279 2.38462Z" fill="#E0E3E3"/>
<circle cx="20.5" cy="3.5" r="2.5" fill="#C00205"/>
</svg>

After

Width:  |  Height:  |  Size: 895 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_5101_18544)">
<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="white"/>
<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="white"/>
<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="white"/>
</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="white"/>
<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="white"/>
<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="white"/>
<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="white"/>
</g>
<defs>
<clipPath id="clip0_5101_18544">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5101_7226)">
<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="white"/>
<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="white"/>
</g>
<defs>
<clipPath id="clip0_5101_7226">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,5 @@
<svg width="60" height="47" viewBox="0 0 60 47" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M35.4652 0.708774C31.9724 -1.30683 27.6074 1.21691 27.6074 5.24811C27.6074 9.27931 29.5549 12.7727 32.7471 14.9281L46.2653 22.7661L46.2992 22.7534L59.9995 14.8349L35.4652 0.708774Z" fill="white"/>
<path d="M32.7438 14.9285C29.5515 12.7731 27.604 9.14421 27.604 5.24851C27.604 3.3303 28.6074 1.72968 30.0342 0.823501C29.9664 0.861612 30.1019 0.785391 30.0342 0.823501L8.64977 12.8409L5.21624 14.9158L2.875 16.2623C4.34833 15.5128 6.19422 15.4535 7.84113 16.4105L13.7302 19.7642L18.8911 22.7495L32.5913 30.6552L46.2577 22.758L32.7395 14.92L32.7438 14.9285Z" fill="#999999"/>
<path d="M60 30.6636L32.5953 46.5005L27.4217 43.5152L27.4005 43.494L5.19475 30.6636C3.67485 29.6728 2.43861 28.322 1.56647 26.8018C0.550381 25.0487 0 23.0331 0 20.9455C0 19.0654 0.944115 17.5114 2.31583 16.5883C2.50212 16.4739 2.68417 16.3681 2.87892 16.2665C4.35224 15.517 6.19814 15.4577 7.84505 16.4147L13.7003 19.7938L18.8611 22.7664L18.895 22.7536L32.5953 30.6594L46.2616 22.7621L60 30.6594V30.6636Z" fill="#4C4C4C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,14 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29952)">
<path 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="#B4C4FF"/>
<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="#00297A"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#D34E50"/>
<path d="M19.4395 19.4395L20.5001 20.5001L21.5608 21.5608" stroke="#2A2D34" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.5 21.5605L20.5607 20.4999L21.6213 19.4392" stroke="#2A2D34" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5094_29952">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,13 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29944)">
<path 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="#B4C4FF"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#F7D65D"/>
<path d="M20.5 19V20.5H21.5" stroke="#2A2D34" 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="#00297A"/>
</g>
<defs>
<clipPath id="clip0_5094_29944">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29949)">
<circle cx="12" cy="12" r="12" fill="#B4C4FF"/>
<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="#00297A"/>
</g>
<defs>
<clipPath id="clip0_5094_29949">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,14 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29925)">
<path 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="#8EF5C3"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#D34E50"/>
<path d="M16 8L8 16M8 16H14M8 16V10" stroke="#003921" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.4395 19.4395L20.5001 20.5001L21.5608 21.5608" stroke="#2A2D34" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.5 21.5605L20.5607 20.4999L21.6213 19.4392" stroke="#2A2D34" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5094_29925">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1,020 B

View file

@ -0,0 +1,12 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29921)">
<path 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="#8EF5C3"/>
<path d="M16 8L8 16M8 16H14M8 16V10" stroke="#003921" 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="#F7D65D"/>
</g>
<defs>
<clipPath id="clip0_5094_29921">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29958)">
<circle cx="12" cy="12" r="12" fill="#8EF5C3"/>
<path d="M16 8L8 16M8 16H14M8 16V10" stroke="#003921" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5094_29958">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 421 B

View file

@ -0,0 +1,14 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29938)">
<path 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="#FFB4A9"/>
<path d="M8 16L16 8M16 8L10 8M16 8L16 14" stroke="#690001" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#D34E50"/>
<path d="M19.4395 19.4395L20.5001 20.5001L21.5608 21.5608" stroke="#2A2D34" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.5 21.5605L20.5607 20.4999L21.6213 19.4392" stroke="#2A2D34" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5094_29938">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 985 B

View file

@ -0,0 +1,13 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29933)">
<path 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="#FFB4A9"/>
<path d="M8 16L16 8M16 8L10 8M16 8L16 14" stroke="#690001" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#F7D65D"/>
<path d="M20.5 19V20.5H21.5" stroke="#2A2D34" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5094_29933">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 833 B

View file

@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5094_29961)">
<circle cx="12" cy="12" r="12" fill="#FFB4A9"/>
<path d="M8 16L16 8M16 8L10 8M16 8L16 14" stroke="#690001" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5094_29961">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 426 B

View file

@ -0,0 +1,3 @@
<svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.29219 6.25098H2.70781C1.21266 6.25098 0 7.46348 0 8.95879C0 9.25879 0.2425 9.50098 0.541562 9.50098H6.45875C6.75781 9.50098 7 9.25879 7 8.95879C7 7.46348 5.7875 6.25098 4.29219 6.25098ZM3.5 5.50098C4.60469 5.50098 5.5 4.60551 5.5 3.50098C5.5 2.39645 4.60469 1.50098 3.5 1.50098C2.39531 1.50098 1.5 2.39645 1.5 3.50098C1.5 4.60551 2.39531 5.50098 3.5 5.50098ZM9.625 4.62598H7.375C7.16875 4.62598 7 4.79473 7 5.00098C7 5.20723 7.16797 5.37598 7.375 5.37598H9.625C9.83281 5.37598 10 5.20879 10 5.00098C10 4.79316 9.83281 4.62598 9.625 4.62598Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 674 B

3
assets/svg/user-plus.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.4 12.0004C11.0513 12.0004 13.2 9.85127 13.2 7.20039C13.2 4.54952 11.0513 2.40039 8.4 2.40039C5.74875 2.40039 3.6 4.54952 3.6 7.20039C3.6 9.85127 5.74875 12.0004 8.4 12.0004ZM10.3013 13.8004H6.49875C2.91038 13.8004 0 16.7104 0 20.2991C0 21.0191 0.582 21.6004 1.29975 21.6004H15.501C16.2188 21.6004 16.8 21.0191 16.8 20.2991C16.8 16.7104 13.89 13.8004 10.3013 13.8004ZM23.1 9.90039H21.3V8.10039C21.3 7.60539 20.8988 7.20039 20.4 7.20039C19.9013 7.20039 19.5 7.60352 19.5 8.10039V9.90039H17.7C17.205 9.90039 16.8 10.3054 16.8 10.8004C16.8 11.2954 17.2031 11.7004 17.7 11.7004H19.5V13.5004C19.5 13.9991 19.905 14.4004 20.4 14.4004C20.895 14.4004 21.3 13.9973 21.3 13.5004V11.7004H23.1C23.5988 11.7004 24 11.2991 24 10.8004C24 10.3016 23.5988 9.90039 23.1 9.90039Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 893 B

View file

@ -456,7 +456,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 83;
CURRENT_PROJECT_VERSION = 102;
DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -510,7 +510,7 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
);
MARKETING_VERSION = 1.5.11;
MARKETING_VERSION = 1.5.28;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -643,7 +643,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 83;
CURRENT_PROJECT_VERSION = 102;
DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -697,7 +697,7 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
);
MARKETING_VERSION = 1.5.11;
MARKETING_VERSION = 1.5.28;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -722,7 +722,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 83;
CURRENT_PROJECT_VERSION = 102;
DEVELOPMENT_TEAM = 4DQKUWSG6C;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@ -776,7 +776,7 @@
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
);
MARKETING_VERSION = 1.5.11;
MARKETING_VERSION = 1.5.28;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
PRODUCT_NAME = "$(TARGET_NAME)";

236
lib/db/main_db.dart Normal file
View file

@ -0,0 +1,236 @@
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:tuple/tuple.dart';
class MainDB {
MainDB._();
static MainDB? _instance;
static MainDB get instance => _instance ??= MainDB._();
Isar? _isar;
Isar get isar => _isar!;
Future<bool> initMainDB({Isar? mock}) async {
if (mock != null) {
_isar = mock;
return true;
}
if (_isar != null && isar.isOpen) return false;
_isar = await Isar.open(
[
TransactionSchema,
TransactionNoteSchema,
InputSchema,
OutputSchema,
UTXOSchema,
AddressSchema,
],
directory: (await StackFileSystem.applicationIsarDirectory()).path,
inspector: false,
name: "wallet_data",
);
return true;
}
// addresses
QueryBuilder<Address, Address, QAfterWhereClause> getAddresses(
String walletId) =>
isar.addresses.where().walletIdEqualTo(walletId);
Future<void> putAddress(Address address) => isar.writeTxn(() async {
await isar.addresses.put(address);
});
Future<void> putAddresses(List<Address> addresses) => isar.writeTxn(() async {
await isar.addresses.putAll(addresses);
});
Future<void> updateAddress(Address oldAddress, Address newAddress) =>
isar.writeTxn(() async {
newAddress.id = oldAddress.id;
await oldAddress.transactions.load();
final txns = oldAddress.transactions.toList();
await isar.addresses.delete(oldAddress.id);
await isar.addresses.put(newAddress);
newAddress.transactions.addAll(txns);
await newAddress.transactions.save();
});
// transactions
QueryBuilder<Transaction, Transaction, QAfterWhereClause> getTransactions(
String walletId) =>
isar.transactions.where().walletIdEqualTo(walletId);
Future<void> putTransaction(Transaction transaction) =>
isar.writeTxn(() async {
await isar.transactions.put(transaction);
});
Future<void> putTransactions(List<Transaction> transactions) =>
isar.writeTxn(() async {
await isar.transactions.putAll(transactions);
});
// utxos
QueryBuilder<UTXO, UTXO, QAfterWhereClause> getUTXOs(String walletId) =>
isar.utxos.where().walletIdEqualTo(walletId);
Future<void> putUTXO(UTXO utxo) => isar.writeTxn(() async {
await isar.utxos.put(utxo);
});
Future<void> putUTXOs(List<UTXO> utxos) => isar.writeTxn(() async {
await isar.utxos.putAll(utxos);
});
// inputs
QueryBuilder<Input, Input, QAfterWhereClause> getInputs(String walletId) =>
isar.inputs.where().walletIdEqualTo(walletId);
Future<void> putInput(Input input) => isar.writeTxn(() async {
await isar.inputs.put(input);
});
Future<void> putInputs(List<Input> inputs) => isar.writeTxn(() async {
await isar.inputs.putAll(inputs);
});
// outputs
QueryBuilder<Output, Output, QAfterWhereClause> getOutputs(String walletId) =>
isar.outputs.where().walletIdEqualTo(walletId);
Future<void> putOutput(Output output) => isar.writeTxn(() async {
await isar.outputs.put(output);
});
Future<void> putOutputs(List<Output> outputs) => isar.writeTxn(() async {
await isar.outputs.putAll(outputs);
});
// transaction notes
QueryBuilder<TransactionNote, TransactionNote, QAfterWhereClause>
getTransactionNotes(String walletId) =>
isar.transactionNotes.where().walletIdEqualTo(walletId);
Future<void> putTransactionNote(TransactionNote transactionNote) =>
isar.writeTxn(() async {
await isar.transactionNotes.put(transactionNote);
});
Future<void> putTransactionNotes(List<TransactionNote> transactionNotes) =>
isar.writeTxn(() async {
await isar.transactionNotes.putAll(transactionNotes);
});
//
Future<void> deleteWalletBlockchainData(String walletId) async {
final transactionCount = await getTransactions(walletId).count();
final addressCount = await getAddresses(walletId).count();
final utxoCount = await getUTXOs(walletId).count();
final inputCount = await getInputs(walletId).count();
final outputCount = await getOutputs(walletId).count();
await isar.writeTxn(() async {
const paginateLimit = 50;
// transactions
for (int i = 0; i < transactionCount; i += paginateLimit) {
final txns = await getTransactions(walletId)
.offset(i)
.limit(paginateLimit)
.findAll();
await isar.transactions
.deleteAll(txns.map((e) => e.id).toList(growable: false));
}
// addresses
for (int i = 0; i < addressCount; i += paginateLimit) {
final addresses = await getAddresses(walletId)
.offset(i)
.limit(paginateLimit)
.findAll();
await isar.addresses
.deleteAll(addresses.map((e) => e.id).toList(growable: false));
}
// utxos
for (int i = 0; i < utxoCount; i += paginateLimit) {
final utxos =
await getUTXOs(walletId).offset(i).limit(paginateLimit).findAll();
await isar.utxos
.deleteAll(utxos.map((e) => e.id).toList(growable: false));
}
// inputs
for (int i = 0; i < inputCount; i += paginateLimit) {
final inputs =
await getInputs(walletId).offset(i).limit(paginateLimit).findAll();
await isar.inputs
.deleteAll(inputs.map((e) => e.id).toList(growable: false));
}
// outputs
for (int i = 0; i < outputCount; i += paginateLimit) {
final outputs =
await getOutputs(walletId).offset(i).limit(paginateLimit).findAll();
await isar.outputs
.deleteAll(outputs.map((e) => e.id).toList(growable: false));
}
});
}
Future<void> addNewTransactionData(
List<Tuple4<Transaction, List<Output>, List<Input>, Address?>>
transactionsData,
String walletId) async {
await isar.writeTxn(() async {
for (final data in transactionsData) {
final tx = data.item1;
final potentiallyUnconfirmedTx = await getTransactions(walletId)
.filter()
.txidEqualTo(tx.txid)
.findFirst();
if (potentiallyUnconfirmedTx != null) {
// update use id to replace tx
tx.id = potentiallyUnconfirmedTx.id;
await isar.transactions.delete(potentiallyUnconfirmedTx.id);
}
// save transaction
await isar.transactions.put(tx);
// link and save outputs
if (data.item2.isNotEmpty) {
await isar.outputs.putAll(data.item2);
tx.outputs.addAll(data.item2);
await tx.outputs.save();
}
// link and save inputs
if (data.item3.isNotEmpty) {
await isar.inputs.putAll(data.item3);
tx.inputs.addAll(data.item3);
await tx.inputs.save();
}
if (data.item4 != null) {
final address = await getAddresses(walletId)
.filter()
.valueEqualTo(data.item4!.value)
.findFirst();
// check if address exists in db and add if it does not
if (address == null) {
await isar.addresses.put(data.item4!);
}
// link and save address
tx.address.value = address ?? data.item4!;
await tx.address.save();
}
}
});
}
}

View file

@ -246,3 +246,11 @@ class DB {
Future<void> deleteBoxFromDisk({required String boxName}) async =>
await mutex.protect(() async => await Hive.deleteBoxFromDisk(boxName));
}
abstract class DBKeys {
static const String cachedBalance = "cachedBalance";
static const String cachedBalanceSecondary = "cachedBalanceSecondary";
static const String isFavorite = "isFavorite";
static const String id = "id";
static const String storedChainHeight = "storedChainHeight";
}

View file

@ -60,10 +60,13 @@ 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/oled_black_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:window_size/window_size.dart';
import 'db/main_db.dart';
final openedFromSWBFileStringStateProvider =
StateProvider<String?>((ref) => null);
@ -76,7 +79,6 @@ void main() async {
if (Platform.isIOS) {
Util.libraryPath = await getLibraryDirectory();
}
Screen? screen;
if (Platform.isLinux || (Util.isDesktop && !Platform.isIOS)) {
screen = await getCurrentScreen();
@ -156,7 +158,7 @@ void main() async {
await Hive.openBox<dynamic>(DB.boxNameDBInfo);
// todo: db migrate stuff for desktop needs to be handled eventually
// Desktop migrate handled elsewhere (currently desktop_login_view.dart)
if (!Util.isDesktop) {
int dbVersion = DB.instance.get<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
@ -171,7 +173,7 @@ void main() async {
),
);
} catch (e, s) {
Logging.instance.log("Cannot migrate database\n$e $s",
Logging.instance.log("Cannot migrate mobile database\n$e $s",
level: LogLevel.Error, printFullLength: true);
}
}
@ -264,6 +266,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
await loadShared();
}
await MainDB.instance.initMainDB();
_notificationsService = ref.read(notificationsProvider);
_nodeService = ref.read(nodeServiceChangeNotifierProvider);
_tradesService = ref.read(tradesServiceProvider);
@ -328,6 +332,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
case "dark":
colorTheme = DarkColors();
break;
case "oledBlack":
colorTheme = OledBlackColors();
break;
case "oceanBreeze":
colorTheme = OceanBreezeColors();
break;

59
lib/models/balance.dart Normal file
View file

@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:decimal/decimal.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
class Balance {
final Coin coin;
final int total;
final int spendable;
final int blockedTotal;
final int pendingSpendable;
Balance({
required this.coin,
required this.total,
required this.spendable,
required this.blockedTotal,
required this.pendingSpendable,
});
Decimal getTotal({bool includeBlocked = false}) => Format.satoshisToAmount(
includeBlocked ? total : total - blockedTotal,
coin: coin,
);
Decimal getSpendable() => Format.satoshisToAmount(
spendable,
coin: coin,
);
Decimal getPending() => Format.satoshisToAmount(
pendingSpendable,
coin: coin,
);
Decimal getBlocked() => Format.satoshisToAmount(
blockedTotal,
coin: coin,
);
String toJsonIgnoreCoin() => jsonEncode({
"total": total,
"spendable": spendable,
"blockedTotal": blockedTotal,
"pendingSpendable": pendingSpendable,
});
factory Balance.fromJson(String json, Coin coin) {
final decoded = jsonDecode(json);
return Balance(
coin: coin,
total: decoded["total"] as int,
spendable: decoded["spendable"] as int,
blockedTotal: decoded["blockedTotal"] as int,
pendingSpendable: decoded["pendingSpendable"] as int,
);
}
}

View file

@ -0,0 +1,94 @@
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/address/crypto_currency_address.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
part 'address.g.dart';
class AddressException extends SWException {
AddressException(super.message);
}
@Collection(accessor: "addresses")
class Address extends CryptoCurrencyAddress {
Address({
required this.walletId,
required this.value,
required this.publicKey,
required this.derivationIndex,
required this.type,
required this.subType,
this.otherData,
});
Id id = Isar.autoIncrement;
@Index()
late final String walletId;
@Index(unique: true, composite: [CompositeIndex("walletId")])
late final String value;
late final List<byte> publicKey;
@Index()
late final int derivationIndex; // -1 generally means unknown
@enumerated
late final AddressType type;
@enumerated
late final AddressSubType subType;
late final String? otherData;
final transactions = IsarLinks<Transaction>();
int derivationChain() {
if (subType == AddressSubType.receiving) {
return 0; // 0 for receiving (external)
} else if (subType == AddressSubType.change) {
return 1; // 1 for change (internal)
} else {
throw AddressException("Could not imply derivation chain value");
}
}
bool isPaynymAddress() =>
subType == AddressSubType.paynymNotification ||
subType == AddressSubType.paynymSend ||
subType == AddressSubType.paynymReceive;
@override
String toString() => "{ "
"id: $id, "
"walletId: $walletId, "
"value: $value, "
"publicKey: $publicKey, "
"derivationIndex: $derivationIndex, "
"type: ${type.name}, "
"subType: ${subType.name}, "
"transactionsLength: ${transactions.length} "
"otherData: $otherData, "
"}";
}
enum AddressType {
p2pkh,
p2sh,
p2wpkh,
cryptonote,
mimbleWimble,
unknown,
nonWallet,
}
enum AddressSubType {
receiving,
change,
paynymNotification,
paynymSend,
paynymReceive,
unknown,
nonWallet,
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
abstract class CryptoCurrencyAddress {
// future use?
}

View file

@ -0,0 +1,46 @@
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/output.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
part 'input.g.dart';
@Collection()
class Input {
Input({
required this.walletId,
required this.txid,
required this.vout,
required this.scriptSig,
required this.scriptSigAsm,
required this.isCoinbase,
required this.sequence,
required this.innerRedeemScriptAsm,
});
Id id = Isar.autoIncrement;
@Index()
late final String walletId;
late final String txid;
late final int vout;
late final String? scriptSig;
late final String? scriptSigAsm;
// TODO: find witness type // is it even used?
// late List<dynamic>? witness;
late final bool? isCoinbase;
late final int? sequence;
late final String? innerRedeemScriptAsm;
final prevOut = IsarLink<Output>();
@Backlink(to: 'inputs')
final transaction = IsarLink<Transaction>();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
part 'output.g.dart';
@Collection()
class Output {
Output({
required this.walletId,
required this.scriptPubKey,
required this.scriptPubKeyAsm,
required this.scriptPubKeyType,
required this.scriptPubKeyAddress,
required this.value,
});
Id id = Isar.autoIncrement;
@Index()
late final String walletId;
late final String? scriptPubKey;
late final String? scriptPubKeyAsm;
late final String? scriptPubKeyType;
late final String scriptPubKeyAddress;
late final int value;
@Backlink(to: 'outputs')
final transaction = IsarLink<Transaction>();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,114 @@
import 'dart:math';
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/address/address.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/input.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/output.dart';
part 'transaction.g.dart';
@Collection()
class Transaction {
Transaction({
required this.walletId,
required this.txid,
required this.timestamp,
required this.type,
required this.subType,
required this.amount,
required this.fee,
required this.height,
required this.isCancelled,
required this.isLelantus,
required this.slateId,
required this.otherData,
});
Id id = Isar.autoIncrement;
@Index()
late final String walletId;
@Index(unique: true, composite: [CompositeIndex("walletId")])
late final String txid;
@Index()
late final int timestamp;
@enumerated
late final TransactionType type;
@enumerated
late final TransactionSubType subType;
late final int amount;
late final int fee;
late final int? height;
late final bool isCancelled;
late bool? isLelantus;
late final String? slateId;
late final String? otherData;
@Backlink(to: "transactions")
final address = IsarLink<Address>();
final inputs = IsarLinks<Input>();
final outputs = IsarLinks<Output>();
int getConfirmations(int currentChainHeight) {
if (height == null || height! <= 0) return 0;
return max(0, currentChainHeight - (height! - 1));
}
bool isConfirmed(int currentChainHeight, int minimumConfirms) {
final confirmations = getConfirmations(currentChainHeight);
return confirmations >= minimumConfirms;
}
@override
toString() => "{ "
"id: $id, "
"walletId: $walletId, "
"txid: $txid, "
"timestamp: $timestamp, "
"type: ${type.name}, "
"subType: ${subType.name}, "
"amount: $amount, "
"fee: $fee, "
"height: $height, "
"isCancelled: $isCancelled, "
"isLelantus: $isLelantus, "
"slateId: $slateId, "
"otherData: $otherData, "
"address: ${address.value}, "
"inputsLength: ${inputs.length}, "
"outputsLength: ${outputs.length}, "
"}";
}
// Used in Isar db and stored there as int indexes so adding/removing values
// in this definition should be done extremely carefully in production
enum TransactionType {
// TODO: add more types before prod release?
outgoing,
incoming,
sentToSelf, // should we keep this?
unknown;
}
// Used in Isar db and stored there as int indexes so adding/removing values
// in this definition should be done extremely carefully in production
enum TransactionSubType {
// TODO: add more types before prod release?
none,
bip47Notification, // bip47 payment code notification transaction flag
mint, // firo specific
join; // firo specific
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,79 @@
import 'dart:math';
import 'package:isar/isar.dart';
part 'utxo.g.dart';
@Collection(accessor: "utxos")
class UTXO {
UTXO({
required this.walletId,
required this.txid,
required this.vout,
required this.value,
required this.name,
required this.isBlocked,
required this.blockedReason,
required this.isCoinbase,
required this.blockHash,
required this.blockHeight,
required this.blockTime,
this.otherData,
});
Id id = Isar.autoIncrement;
@Index()
late final String walletId;
@Index(unique: true, replace: true, composite: [CompositeIndex("walletId")])
late final String txid;
late final int vout;
late final int value;
late final String name;
@Index()
late final bool isBlocked;
late final String? blockedReason;
late final bool isCoinbase;
late final String? blockHash;
late final int? blockHeight;
late final int? blockTime;
late final String? otherData;
int getConfirmations(int currentChainHeight) {
if (blockTime == null || blockHash == null) return 0;
if (blockHeight == null || blockHeight! <= 0) return 0;
return max(0, currentChainHeight - (blockHeight! - 1));
}
bool isConfirmed(int currentChainHeight, int minimumConfirms) {
final confirmations = getConfirmations(currentChainHeight);
return confirmations >= minimumConfirms;
}
@override
String toString() => "{ "
"id: $id, "
"walletId: $walletId, "
"txid: $txid, "
"vout: $vout, "
"value: $value, "
"name: $name, "
"isBlocked: $isBlocked, "
"blockedReason: $blockedReason, "
"isCoinbase: $isCoinbase, "
"blockHash: $blockHash, "
"blockHeight: $blockHeight, "
"blockTime: $blockTime, "
"}";
}

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ part of 'encrypted_string_value.dart';
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, join_return_with_assignment, avoid_js_rounded_ints, prefer_final_locals
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters
extension GetEncryptedStringValueCollection on Isar {
IsarCollection<EncryptedStringValue> get encryptedStringValues =>
@ -30,12 +30,9 @@ const EncryptedStringValueSchema = CollectionSchema(
)
},
estimateSize: _encryptedStringValueEstimateSize,
serializeNative: _encryptedStringValueSerializeNative,
deserializeNative: _encryptedStringValueDeserializeNative,
deserializePropNative: _encryptedStringValueDeserializePropNative,
serializeWeb: _encryptedStringValueSerializeWeb,
deserializeWeb: _encryptedStringValueDeserializeWeb,
deserializePropWeb: _encryptedStringValueDeserializePropWeb,
serialize: _encryptedStringValueSerialize,
deserialize: _encryptedStringValueDeserialize,
deserializeProp: _encryptedStringValueDeserializeProp,
idName: r'id',
indexes: {
r'key': IndexSchema(
@ -57,7 +54,7 @@ const EncryptedStringValueSchema = CollectionSchema(
getId: _encryptedStringValueGetId,
getLinks: _encryptedStringValueGetLinks,
attach: _encryptedStringValueAttach,
version: 5,
version: '3.0.5',
);
int _encryptedStringValueEstimateSize(
@ -71,20 +68,19 @@ int _encryptedStringValueEstimateSize(
return bytesCount;
}
int _encryptedStringValueSerializeNative(
void _encryptedStringValueSerialize(
EncryptedStringValue object,
IsarBinaryWriter writer,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeString(offsets[0], object.key);
writer.writeString(offsets[1], object.value);
return writer.usedBytes;
}
EncryptedStringValue _encryptedStringValueDeserializeNative(
int id,
IsarBinaryReader reader,
EncryptedStringValue _encryptedStringValueDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
@ -95,9 +91,8 @@ EncryptedStringValue _encryptedStringValueDeserializeNative(
return object;
}
P _encryptedStringValueDeserializePropNative<P>(
Id id,
IsarBinaryReader reader,
P _encryptedStringValueDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
@ -112,33 +107,8 @@ P _encryptedStringValueDeserializePropNative<P>(
}
}
Object _encryptedStringValueSerializeWeb(
IsarCollection<EncryptedStringValue> collection,
EncryptedStringValue object) {
/*final jsObj = IsarNative.newJsObject();*/ throw UnimplementedError();
}
EncryptedStringValue _encryptedStringValueDeserializeWeb(
IsarCollection<EncryptedStringValue> collection, Object jsObj) {
/*final object = EncryptedStringValue();object.id = IsarNative.jsObjectGet(jsObj, r'id') ?? (double.negativeInfinity as int);object.key = IsarNative.jsObjectGet(jsObj, r'key') ?? '';object.value = IsarNative.jsObjectGet(jsObj, r'value') ?? '';*/
//return object;
throw UnimplementedError();
}
P _encryptedStringValueDeserializePropWeb<P>(
Object jsObj, String propertyName) {
switch (propertyName) {
default:
throw IsarError('Illegal propertyName');
}
}
int? _encryptedStringValueGetId(EncryptedStringValue object) {
if (object.id == Isar.autoIncrement) {
return null;
} else {
return object.id;
}
Id _encryptedStringValueGetId(EncryptedStringValue object) {
return object.id;
}
List<IsarLinkBase<dynamic>> _encryptedStringValueGetLinks(
@ -188,19 +158,19 @@ extension EncryptedStringValueByIndex on IsarCollection<EncryptedStringValue> {
return deleteAllByIndexSync(r'key', values);
}
Future<int> putByKey(EncryptedStringValue object) {
Future<Id> putByKey(EncryptedStringValue object) {
return putByIndex(r'key', object);
}
int putByKeySync(EncryptedStringValue object, {bool saveLinks = true}) {
Id putByKeySync(EncryptedStringValue object, {bool saveLinks = true}) {
return putByIndexSync(r'key', object, saveLinks: saveLinks);
}
Future<List<int>> putAllByKey(List<EncryptedStringValue> objects) {
Future<List<Id>> putAllByKey(List<EncryptedStringValue> objects) {
return putAllByIndex(r'key', objects);
}
List<int> putAllByKeySync(List<EncryptedStringValue> objects,
List<Id> putAllByKeySync(List<EncryptedStringValue> objects,
{bool saveLinks = true}) {
return putAllByIndexSync(r'key', objects, saveLinks: saveLinks);
}
@ -219,7 +189,7 @@ extension EncryptedStringValueQueryWhereSort
extension EncryptedStringValueQueryWhere
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QWhereClause> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idEqualTo(int id) {
idEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: id,
@ -229,7 +199,7 @@ extension EncryptedStringValueQueryWhere
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idNotEqualTo(int id) {
idNotEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
@ -252,7 +222,7 @@ extension EncryptedStringValueQueryWhere
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idGreaterThan(int id, {bool include = false}) {
idGreaterThan(Id id, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: include),
@ -261,7 +231,7 @@ extension EncryptedStringValueQueryWhere
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idLessThan(int id, {bool include = false}) {
idLessThan(Id id, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: include),
@ -271,8 +241,8 @@ extension EncryptedStringValueQueryWhere
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idBetween(
int lowerId,
int upperId, {
Id lowerId,
Id upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
@ -335,7 +305,7 @@ extension EncryptedStringValueQueryWhere
extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
EncryptedStringValue, QFilterCondition> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idEqualTo(int value) {
QAfterFilterCondition> idEqualTo(Id value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'id',
@ -346,7 +316,7 @@ extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idGreaterThan(
int value, {
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
@ -360,7 +330,7 @@ extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idLessThan(
int value, {
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
@ -374,8 +344,8 @@ extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idBetween(
int lower,
int upper, {
Id lower,
Id upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
@ -508,6 +478,26 @@ extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'key',
value: '',
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'key',
value: '',
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueEqualTo(
String value, {
@ -625,6 +615,26 @@ extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'value',
value: '',
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'value',
value: '',
));
});
}
}
extension EncryptedStringValueQueryObject on QueryBuilder<EncryptedStringValue,

View file

@ -0,0 +1,7 @@
export 'address/address.dart';
export 'blockchain_data/input.dart';
export 'blockchain_data/output.dart';
export 'blockchain_data/transaction.dart';
export 'blockchain_data/utxo.dart';
export 'log.dart';
export 'transaction_note.dart';

View file

@ -13,6 +13,7 @@ class Log {
@Index()
late int timestampInMillisUTC;
@Enumerated(EnumType.name)
late LogLevel logLevel;
@override

View file

@ -7,7 +7,7 @@ part of 'log.dart';
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, join_return_with_assignment, avoid_js_rounded_ints, prefer_final_locals
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters
extension GetLogCollection on Isar {
IsarCollection<Log> get logs => this.collection();
@ -21,6 +21,7 @@ const LogSchema = CollectionSchema(
id: 0,
name: r'logLevel',
type: IsarType.string,
enumMap: _LoglogLevelEnumValueMap,
),
r'message': PropertySchema(
id: 1,
@ -34,12 +35,9 @@ const LogSchema = CollectionSchema(
)
},
estimateSize: _logEstimateSize,
serializeNative: _logSerializeNative,
deserializeNative: _logDeserializeNative,
deserializePropNative: _logDeserializePropNative,
serializeWeb: _logSerializeWeb,
deserializeWeb: _logDeserializeWeb,
deserializePropWeb: _logDeserializePropWeb,
serialize: _logSerialize,
deserialize: _logDeserialize,
deserializeProp: _logDeserializeProp,
idName: r'id',
indexes: {
r'timestampInMillisUTC': IndexSchema(
@ -61,7 +59,7 @@ const LogSchema = CollectionSchema(
getId: _logGetId,
getLinks: _logGetLinks,
attach: _logAttach,
version: 5,
version: '3.0.5',
);
int _logEstimateSize(
@ -70,49 +68,48 @@ int _logEstimateSize(
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
bytesCount += 3 + object.logLevel.value.length * 3;
bytesCount += 3 + object.logLevel.name.length * 3;
bytesCount += 3 + object.message.length * 3;
return bytesCount;
}
int _logSerializeNative(
void _logSerialize(
Log object,
IsarBinaryWriter writer,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeString(offsets[0], object.logLevel.value);
writer.writeString(offsets[0], object.logLevel.name);
writer.writeString(offsets[1], object.message);
writer.writeLong(offsets[2], object.timestampInMillisUTC);
return writer.usedBytes;
}
Log _logDeserializeNative(
int id,
IsarBinaryReader reader,
Log _logDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = Log();
object.id = id;
object.logLevel =
_LogLogLevelMap[reader.readStringOrNull(offsets[0])] ?? LogLevel.Info;
_LoglogLevelValueEnumMap[reader.readStringOrNull(offsets[0])] ??
LogLevel.Info;
object.message = reader.readString(offsets[1]);
object.timestampInMillisUTC = reader.readLong(offsets[2]);
return object;
}
P _logDeserializePropNative<P>(
Id id,
IsarBinaryReader reader,
P _logDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (_LogLogLevelMap[reader.readStringOrNull(offset)] ?? LogLevel.Info)
as P;
return (_LoglogLevelValueEnumMap[reader.readStringOrNull(offset)] ??
LogLevel.Info) as P;
case 1:
return (reader.readString(offset)) as P;
case 2:
@ -122,36 +119,21 @@ P _logDeserializePropNative<P>(
}
}
Object _logSerializeWeb(IsarCollection<Log> collection, Log object) {
/*final jsObj = IsarNative.newJsObject();*/ throw UnimplementedError();
}
Log _logDeserializeWeb(IsarCollection<Log> collection, Object jsObj) {
/*final object = Log();object.id = IsarNative.jsObjectGet(jsObj, r'id') ?? (double.negativeInfinity as int);object.logLevel = IsarNative.jsObjectGet(jsObj, r'logLevel') ?? LogLevel.Info;object.message = IsarNative.jsObjectGet(jsObj, r'message') ?? '';object.timestampInMillisUTC = IsarNative.jsObjectGet(jsObj, r'timestampInMillisUTC') ?? (double.negativeInfinity as int);*/
//return object;
throw UnimplementedError();
}
P _logDeserializePropWeb<P>(Object jsObj, String propertyName) {
switch (propertyName) {
default:
throw IsarError('Illegal propertyName');
}
}
final _LogLogLevelMap = {
LogLevel.Info.value: LogLevel.Info,
LogLevel.Warning.value: LogLevel.Warning,
LogLevel.Error.value: LogLevel.Error,
LogLevel.Fatal.value: LogLevel.Fatal,
const _LoglogLevelEnumValueMap = {
r'Info': r'Info',
r'Warning': r'Warning',
r'Error': r'Error',
r'Fatal': r'Fatal',
};
const _LoglogLevelValueEnumMap = {
r'Info': LogLevel.Info,
r'Warning': LogLevel.Warning,
r'Error': LogLevel.Error,
r'Fatal': LogLevel.Fatal,
};
int? _logGetId(Log object) {
if (object.id == Isar.autoIncrement) {
return null;
} else {
return object.id;
}
Id _logGetId(Log object) {
return object.id;
}
List<IsarLinkBase<dynamic>> _logGetLinks(Log object) {
@ -179,7 +161,7 @@ extension LogQueryWhereSort on QueryBuilder<Log, Log, QWhere> {
}
extension LogQueryWhere on QueryBuilder<Log, Log, QWhereClause> {
QueryBuilder<Log, Log, QAfterWhereClause> idEqualTo(int id) {
QueryBuilder<Log, Log, QAfterWhereClause> idEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: id,
@ -188,7 +170,7 @@ extension LogQueryWhere on QueryBuilder<Log, Log, QWhereClause> {
});
}
QueryBuilder<Log, Log, QAfterWhereClause> idNotEqualTo(int id) {
QueryBuilder<Log, Log, QAfterWhereClause> idNotEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
@ -210,7 +192,7 @@ extension LogQueryWhere on QueryBuilder<Log, Log, QWhereClause> {
});
}
QueryBuilder<Log, Log, QAfterWhereClause> idGreaterThan(int id,
QueryBuilder<Log, Log, QAfterWhereClause> idGreaterThan(Id id,
{bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
@ -219,7 +201,7 @@ extension LogQueryWhere on QueryBuilder<Log, Log, QWhereClause> {
});
}
QueryBuilder<Log, Log, QAfterWhereClause> idLessThan(int id,
QueryBuilder<Log, Log, QAfterWhereClause> idLessThan(Id id,
{bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
@ -229,8 +211,8 @@ extension LogQueryWhere on QueryBuilder<Log, Log, QWhereClause> {
}
QueryBuilder<Log, Log, QAfterWhereClause> idBetween(
int lowerId,
int upperId, {
Id lowerId,
Id upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
@ -336,7 +318,7 @@ extension LogQueryWhere on QueryBuilder<Log, Log, QWhereClause> {
}
extension LogQueryFilter on QueryBuilder<Log, Log, QFilterCondition> {
QueryBuilder<Log, Log, QAfterFilterCondition> idEqualTo(int value) {
QueryBuilder<Log, Log, QAfterFilterCondition> idEqualTo(Id value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'id',
@ -346,7 +328,7 @@ extension LogQueryFilter on QueryBuilder<Log, Log, QFilterCondition> {
}
QueryBuilder<Log, Log, QAfterFilterCondition> idGreaterThan(
int value, {
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
@ -359,7 +341,7 @@ extension LogQueryFilter on QueryBuilder<Log, Log, QFilterCondition> {
}
QueryBuilder<Log, Log, QAfterFilterCondition> idLessThan(
int value, {
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
@ -372,8 +354,8 @@ extension LogQueryFilter on QueryBuilder<Log, Log, QFilterCondition> {
}
QueryBuilder<Log, Log, QAfterFilterCondition> idBetween(
int lower,
int upper, {
Id lower,
Id upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
@ -498,6 +480,24 @@ extension LogQueryFilter on QueryBuilder<Log, Log, QFilterCondition> {
});
}
QueryBuilder<Log, Log, QAfterFilterCondition> logLevelIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'logLevel',
value: '',
));
});
}
QueryBuilder<Log, Log, QAfterFilterCondition> logLevelIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'logLevel',
value: '',
));
});
}
QueryBuilder<Log, Log, QAfterFilterCondition> messageEqualTo(
String value, {
bool caseSensitive = true,
@ -608,6 +608,24 @@ extension LogQueryFilter on QueryBuilder<Log, Log, QFilterCondition> {
});
}
QueryBuilder<Log, Log, QAfterFilterCondition> messageIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'message',
value: '',
));
});
}
QueryBuilder<Log, Log, QAfterFilterCondition> messageIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'message',
value: '',
));
});
}
QueryBuilder<Log, Log, QAfterFilterCondition> timestampInMillisUTCEqualTo(
int value) {
return QueryBuilder.apply(this, (query) {

View file

@ -0,0 +1,22 @@
import 'package:isar/isar.dart';
part 'transaction_note.g.dart';
@Collection()
class TransactionNote {
TransactionNote({
required this.walletId,
required this.txid,
required this.value,
});
Id id = Isar.autoIncrement;
@Index()
late String walletId;
@Index(unique: true, composite: [CompositeIndex("walletId")])
late String txid;
late String value;
}

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,8 @@ class NodeModel {
final bool isFailover;
// @HiveField(9)
final bool isDown;
// @HiveField(10)
final bool? trusted;
NodeModel({
required this.host,
@ -38,6 +40,7 @@ class NodeModel {
required this.isFailover,
required this.isDown,
this.loginName,
this.trusted,
});
NodeModel copyWith({
@ -50,6 +53,7 @@ class NodeModel {
String? coinName,
bool? isFailover,
bool? isDown,
bool? trusted,
}) {
return NodeModel(
host: host ?? this.host,
@ -62,6 +66,7 @@ class NodeModel {
coinName: coinName ?? this.coinName,
isFailover: isFailover ?? this.isFailover,
isDown: isDown ?? this.isDown,
trusted: trusted ?? this.trusted,
);
}
@ -82,6 +87,7 @@ class NodeModel {
map['coinName'] = coinName;
map['isFailover'] = isFailover;
map['isDown'] = isDown;
map['trusted'] = trusted;
return map;
}

View file

@ -362,12 +362,16 @@ class Input {
class Output {
// @HiveField(0)
final String? scriptpubkey;
// @HiveField(1)
final String? scriptpubkeyAsm;
// @HiveField(2)
final String? scriptpubkeyType;
// @HiveField(3)
final String scriptpubkeyAddress;
// @HiveField(4)
final int value;
@ -381,9 +385,6 @@ class Output {
factory Output.fromJson(Map<String, dynamic> json) {
// TODO determine if any of this code is needed.
try {
// Particl has different tx types that need to be detected and handled here
// if (json.containsKey('scriptPubKey') as bool) {
// output is transparent
final address = json["scriptPubKey"]["addresses"] == null
? json['scriptPubKey']['type'] as String
: json["scriptPubKey"]["addresses"][0] as String;
@ -392,35 +393,13 @@ class Output {
scriptpubkeyAsm: json['scriptPubKey']['asm'] as String?,
scriptpubkeyType: json['scriptPubKey']['type'] as String?,
scriptpubkeyAddress: address,
value: (Decimal.parse(json["value"].toString()) *
value: (Decimal.parse(
(json["value"] ?? 0).toString()) *
Decimal.fromInt(Constants.satsPerCoin(Coin
.firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure
.toBigInt()
.toInt(),
);
// } /* else if (json.containsKey('ct_fee') as bool) {
// // or type: data
// // output is blinded (CT)
// } else if (json.containsKey('rangeproof') as bool) {
// // or valueCommitment or type: anon
// // output is private (RingCT)
// } */
// else {
// // TODO detect staking
// // TODO handle CT, RingCT, and staking accordingly
// // print("transaction not supported: ${json}");
// return Output(
// // Return output object with null values; allows wallet history to be built
// scriptpubkey: "",
// scriptpubkeyAsm: "",
// scriptpubkeyType: "",
// scriptpubkeyAddress: "",
// value: (Decimal.parse(0.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(Coin
// .firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure
// .toBigInt()
// .toInt());
// }
} catch (s, e) {
return Output(
// Return output object with null values; allows wallet history to be built

View file

@ -0,0 +1,35 @@
class CreatedPaynym {
final bool claimed;
final String? nymAvatar;
final String? nymId;
final String? nymName;
final String? token;
CreatedPaynym(
this.claimed,
this.nymAvatar,
this.nymId,
this.nymName,
this.token,
);
CreatedPaynym.fromMap(Map<String, dynamic> map)
: claimed = map["claimed"] as bool,
nymAvatar = map["nymAvatar"] as String?,
nymId = map["nymID"] as String?,
nymName = map["nymName"] as String?,
token = map["token"] as String?;
Map<String, dynamic> toMap() => {
"claimed": claimed,
"nymAvatar": nymAvatar,
"nymId": nymId,
"nymName": nymName,
"token": token,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,67 @@
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/models/paynym/paynym_code.dart';
class PaynymAccount {
final String nymID;
final String nymName;
final List<PaynymCode> codes;
/// list of nymId
final List<PaynymAccountLite> followers;
/// list of nymId
final List<PaynymAccountLite> following;
PaynymAccount(
this.nymID,
this.nymName,
this.codes,
this.followers,
this.following,
);
PaynymAccount.fromMap(Map<String, dynamic> map)
: nymID = map["nymID"] as String,
nymName = map["nymName"] as String,
codes = (map["codes"] as List<dynamic>)
.map((e) => PaynymCode.fromMap(Map<String, dynamic>.from(e as Map)))
.toList(),
followers = (map["followers"] as List<dynamic>)
.map((e) =>
PaynymAccountLite.fromMap(Map<String, dynamic>.from(e as Map)))
.toList(),
following = (map["following"] as List<dynamic>)
.map((e) =>
PaynymAccountLite.fromMap(Map<String, dynamic>.from(e as Map)))
.toList();
PaynymAccount copyWith({
String? nymID,
String? nymName,
List<PaynymCode>? codes,
List<PaynymAccountLite>? followers,
List<PaynymAccountLite>? following,
}) {
return PaynymAccount(
nymID ?? this.nymID,
nymName ?? this.nymName,
codes ?? this.codes,
followers ?? this.followers,
following ?? this.following,
);
}
Map<String, dynamic> toMap() => {
"nymID": nymID,
"nymName": nymName,
"codes": codes.map((e) => e.toMap()),
"followers": followers.map((e) => e.toMap()),
"following": followers.map((e) => e.toMap()),
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,31 @@
class PaynymAccountLite {
final String nymId;
final String nymName;
final String code;
final bool segwit;
PaynymAccountLite(
this.nymId,
this.nymName,
this.code,
this.segwit,
);
PaynymAccountLite.fromMap(Map<String, dynamic> map)
: nymId = map["nymId"] as String,
nymName = map["nymName"] as String,
code = map["code"] as String,
segwit = map["segwit"] as bool;
Map<String, dynamic> toMap() => {
"nymId": nymId,
"nymName": nymName,
"code": code,
"segwit": segwit,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,20 @@
class PaynymClaim {
final String claimed;
final String token;
PaynymClaim(this.claimed, this.token);
PaynymClaim.fromMap(Map<String, dynamic> map)
: claimed = map["claimed"] as String,
token = map["token"] as String;
Map<String, dynamic> toMap() => {
"claimed": claimed,
"token": token,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,27 @@
class PaynymCode {
final bool claimed;
final bool segwit;
final String code;
PaynymCode(
this.claimed,
this.segwit,
this.code,
);
PaynymCode.fromMap(Map<String, dynamic> map)
: claimed = map["claimed"] as bool,
segwit = map["segwit"] as bool,
code = map["code"] as String;
Map<String, dynamic> toMap() => {
"claimed": claimed,
"segwit": segwit,
"code": code,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,23 @@
class PaynymFollow {
final String follower;
final String following;
final String token;
PaynymFollow(this.follower, this.following, this.token);
PaynymFollow.fromMap(Map<String, dynamic> map)
: follower = map["follower"] as String,
following = map["following"] as String,
token = map["token"] as String;
Map<String, dynamic> toMap() => {
"follower": follower,
"following": following,
"token": token,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,12 @@
class PaynymResponse<T> {
final T? value;
final int statusCode;
final String message;
PaynymResponse(this.value, this.statusCode, this.message);
@override
String toString() {
return "PaynymResponse: value=$value, statusCode=$statusCode, message=$message";
}
}

View file

@ -0,0 +1,23 @@
class PaynymUnfollow {
final String follower;
final String unfollowing;
final String token;
PaynymUnfollow(this.follower, this.unfollowing, this.token);
PaynymUnfollow.fromMap(Map<String, dynamic> map)
: follower = map["follower"] as String,
unfollowing = map["unfollowing"] as String,
token = map["token"] as String;
Map<String, dynamic> toMap() => {
"follower": follower,
"unfollowing": unfollowing,
"token": token,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -26,14 +26,15 @@ class NodeModelAdapter extends TypeAdapter<NodeModel> {
loginName: fields[5] as String?,
coinName: fields[7] as String,
isFailover: fields[8] as bool,
isDown: fields[8] as bool,
isDown: fields[9] as bool,
trusted: fields[10] as bool?,
);
}
@override
void write(BinaryWriter writer, NodeModel obj) {
writer
..writeByte(10)
..writeByte(11)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@ -53,7 +54,9 @@ class NodeModelAdapter extends TypeAdapter<NodeModel> {
..writeByte(8)
..write(obj.isFailover)
..writeByte(9)
..write(obj.isDown);
..write(obj.isDown)
..writeByte(10)
..write(obj.trusted);
}
@override

View file

@ -373,17 +373,22 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
FormInputStatus status, String prefix) {
Color color;
Color prefixColor;
Color borderColor;
Widget? suffixIcon;
switch (status) {
case FormInputStatus.empty:
color = Theme.of(context).extension<StackColors>()!.textFieldDefaultBG;
prefixColor = Theme.of(context).extension<StackColors>()!.textSubtitle2;
borderColor =
Theme.of(context).extension<StackColors>()!.textFieldDefaultBG;
break;
case FormInputStatus.invalid:
color = Theme.of(context).extension<StackColors>()!.textFieldErrorBG;
prefixColor = Theme.of(context)
.extension<StackColors>()!
.textFieldErrorSearchIconLeft;
borderColor =
Theme.of(context).extension<StackColors>()!.textFieldErrorBorder;
suffixIcon = SvgPicture.asset(
Assets.svg.alertCircle,
width: 16,
@ -398,6 +403,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
prefixColor = Theme.of(context)
.extension<StackColors>()!
.textFieldSuccessSearchIconLeft;
borderColor =
Theme.of(context).extension<StackColors>()!.textFieldSuccessBorder;
suffixIcon = SvgPicture.asset(
Assets.svg.checkCircle,
width: 16,
@ -449,11 +456,11 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
child: suffixIcon,
),
),
enabledBorder: _buildOutlineInputBorder(color),
focusedBorder: _buildOutlineInputBorder(color),
errorBorder: _buildOutlineInputBorder(color),
disabledBorder: _buildOutlineInputBorder(color),
focusedErrorBorder: _buildOutlineInputBorder(color),
enabledBorder: _buildOutlineInputBorder(borderColor),
focusedBorder: _buildOutlineInputBorder(borderColor),
errorBorder: _buildOutlineInputBorder(borderColor),
disabledBorder: _buildOutlineInputBorder(borderColor),
focusedErrorBorder: _buildOutlineInputBorder(borderColor),
);
}
@ -786,7 +793,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.overlay,
.textRestore,
fontSize: isDesktop ? 16 : 14,
),
),
@ -993,7 +1000,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
STextStyles.field(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.overlay,
.textRestore,
fontSize: isDesktop ? 16 : 14,
),
),

View file

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
@ -160,8 +161,9 @@ class _VerifyRecoveryPhraseViewState
result.insert(random.nextInt(wordsToShow), chosenWord);
//todo: this prints sensitive info
debugPrint("Mnemonic game correct word: $chosenWord");
if (kDebugMode) {
print("Mnemonic game correct word: $chosenWord");
}
return Tuple2(result, chosenWord);
}

View file

@ -2,8 +2,8 @@ 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/models/contact.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_new_contact_address_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
@ -15,7 +15,6 @@ import 'package:stackwallet/services/coins/manager.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/background.dart';
@ -28,6 +27,8 @@ import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
import 'package:tuple/tuple.dart';
import '../../../db/main_db.dart';
class ContactDetailsView extends ConsumerStatefulWidget {
const ContactDetailsView({
Key? key,
@ -50,15 +51,6 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
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 {
@ -69,18 +61,18 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
List<Tuple2<String, Transaction>> result = [];
for (final manager in managers) {
final transactions = (await manager.transactionData)
.getAllTransactions()
.values
.toList()
.where((e) => _contactHasAddress(e.address, contact));
final transactions = await MainDB.instance
.getTransactions(manager.walletId)
.filter()
.anyOf(contact.addresses.map((e) => e.address),
(q, String e) => q.address((q) => q.valueEqualTo(e)))
.sortByTimestampDesc()
.findAll();
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;
}

View file

@ -248,6 +248,29 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
fromTicker: ref.read(exchangeFormStateProvider).fromTicker ?? "",
onSelected: (to) =>
ref.read(exchangeFormStateProvider).updateTo(to, true));
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: "Updating exchange rate",
eventBus: null,
),
),
),
),
);
await Future<void>.delayed(const Duration(milliseconds: 300));
Navigator.of(context).pop();
} else {
final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "";
final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "";

View file

@ -2,6 +2,8 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/pages/exchange_view/exchange_form.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
@ -12,6 +14,8 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/trade_card.dart';
import 'package:tuple/tuple.dart';
import '../../db/main_db.dart';
class ExchangeView extends ConsumerStatefulWidget {
const ExchangeView({Key? key}) : super(key: key);
@ -129,10 +133,11 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
//todo: check if print needed
// 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];
final tx = await MainDB.instance
.getTransactions(walletIds.first)
.filter()
.txidEqualTo(txid)
.findFirst();
if (mounted) {
unawaited(Navigator.of(context).pushNamed(

View file

@ -438,8 +438,10 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
style: STextStyles.itemSubtitle(context),
),
FutureBuilder(
future: (manager.wallet as FiroWallet)
.availablePrivateBalance(),
// TODO redo this widget now that its not actually a future
future: Future(() =>
(manager.wallet as FiroWallet)
.availablePrivateBalance()),
builder: (builderContext,
AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
@ -524,8 +526,10 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
style: STextStyles.itemSubtitle(context),
),
FutureBuilder(
future: (manager.wallet as FiroWallet)
.availablePublicBalance(),
// TODO redo this widget now that its not actually a future
future: Future(() =>
(manager.wallet as FiroWallet)
.availablePublicBalance()),
builder: (builderContext,
AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
@ -634,7 +638,8 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
),
if (!isFiro)
FutureBuilder(
future: manager.totalBalance,
// TODO redo this widget now that its not actually a future
future: Future(() => manager.balance.getTotal()),
builder:
(builderContext, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==

View file

@ -7,7 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
import 'package:stackwallet/pages/exchange_view/send_from_view.dart';
@ -23,7 +23,6 @@ import 'package:stackwallet/utilities/assets.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/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

@ -0,0 +1,458 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paynym/paynym_account.dart';
import 'package:stackwallet/pages/paynym/subwidgets/featured_paynyms_widget.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_card.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.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/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/paynym_search_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/loading_indicator.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';
class AddNewPaynymFollowView extends ConsumerStatefulWidget {
const AddNewPaynymFollowView({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
static const String routeName = "/addNewPaynymFollow";
@override
ConsumerState<AddNewPaynymFollowView> createState() =>
_AddNewPaynymFollowViewState();
}
class _AddNewPaynymFollowViewState
extends ConsumerState<AddNewPaynymFollowView> {
late final TextEditingController _searchController;
late final FocusNode searchFieldFocusNode;
String _searchString = "";
bool _didSearch = false;
PaynymAccount? _searchResult;
final isDesktop = Util.isDesktop;
Future<void> _search() async {
_didSearch = true;
bool didPopLoading = false;
unawaited(
showDialog<void>(
barrierDismissible: false,
context: context,
builder: (context) => const LoadingIndicator(
width: 200,
),
).then((_) => didPopLoading = true),
);
final paynymAccount = await ref.read(paynymAPIProvider).nym(_searchString);
if (mounted) {
if (!didPopLoading) {
Navigator.of(context).pop();
}
setState(() {
_searchResult = paynymAccount.value;
});
}
}
Future<void> _clear() async {
_searchString = "";
setState(() {
_searchController.text = "";
});
}
Future<void> _paste() async {
final ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
if (data?.text != null && data!.text!.isNotEmpty) {
String content = data.text!.trim();
if (content.contains("\n")) {
content = content.substring(
0,
content.indexOf(
"\n",
),
);
}
_searchString = content;
setState(() {
_searchController.text = content;
_searchController.selection = TextSelection.collapsed(
offset: content.length,
);
});
}
}
Future<void> _scanQr() async {
try {
if (!isDesktop && FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
final qrResult = await const BarcodeScannerWrapper().scan();
final pCodeString = qrResult.rawContent;
_searchString = pCodeString;
setState(() {
_searchController.text = pCodeString;
_searchController.selection = TextSelection.collapsed(
offset: pCodeString.length,
);
});
} catch (_) {
// scan failed
}
}
@override
void initState() {
_searchController = TextEditingController();
searchFieldFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
_searchController.dispose();
searchFieldFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
return ConditionalParent(
condition: !isDesktop,
builder: (child) => MasterScaffold(
isDesktop: isDesktop,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
),
titleSpacing: 0,
title: Text(
"New follow",
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) => SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
),
),
),
),
),
child: ConditionalParent(
condition: isDesktop,
builder: (child) => DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
"New follow",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: child,
),
],
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 10,
),
Text(
"Featured PayNyms",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.sectionLabelMedium12(context),
),
const SizedBox(
height: 12,
),
FeaturedPaynymsWidget(
walletId: widget.walletId,
),
const SizedBox(
height: 24,
),
Text(
"Add new",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.sectionLabelMedium12(context),
),
const SizedBox(
height: 12,
),
if (isDesktop)
Row(
children: [
Expanded(
child: Stack(
children: [
RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
height: 56,
child: Center(
child: TextField(
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
controller: _searchController,
focusNode: searchFieldFocusNode,
onChanged: (value) {
setState(() {
_searchString = value;
});
},
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
// height: 1.8,
),
decoration: InputDecoration(
hintText: "Paste payment code",
hoverColor: Colors.transparent,
fillColor: Colors.transparent,
contentPadding: const EdgeInsets.all(16),
hintStyle:
STextStyles.desktopTextFieldLabel(context)
.copyWith(
fontSize: 14,
),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 8),
child: UnconstrainedBox(
child: Row(
children: [
_searchController.text.isNotEmpty
? TextFieldIconButton(
onTap: _clear,
child: RoundedContainer(
padding:
const EdgeInsets.all(8),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: const XIcon(),
),
)
: TextFieldIconButton(
key: const Key(
"paynymPasteAddressFieldButtonKey"),
onTap: _paste,
child: RoundedContainer(
padding:
const EdgeInsets.all(8),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: const ClipboardIcon(),
),
),
TextFieldIconButton(
key: const Key(
"paynymScanQrButtonKey"),
onTap: _scanQr,
child: RoundedContainer(
padding: const EdgeInsets.all(8),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: const QrCodeIcon(),
),
)
],
),
),
),
),
),
),
),
],
),
),
const SizedBox(
width: 10,
),
PaynymSearchButton(onPressed: _search),
],
),
if (!isDesktop)
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
controller: _searchController,
focusNode: searchFieldFocusNode,
onChanged: (value) {
setState(() {
_searchString = value;
});
},
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Paste payment code",
searchFieldFocusNode,
context,
desktopMed: isDesktop,
).copyWith(
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 8),
child: UnconstrainedBox(
child: Row(
children: [
_searchController.text.isNotEmpty
? TextFieldIconButton(
onTap: _clear,
child: const XIcon(),
)
: TextFieldIconButton(
key: const Key(
"paynymPasteAddressFieldButtonKey"),
onTap: _paste,
child: const ClipboardIcon(),
),
TextFieldIconButton(
key: const Key("paynymScanQrButtonKey"),
onTap: _scanQr,
child: const QrCodeIcon(),
)
],
),
),
),
),
),
),
if (!isDesktop)
const SizedBox(
height: 12,
),
if (!isDesktop)
SecondaryButton(
label: "Search",
onPressed: _search,
),
if (_didSearch)
const SizedBox(
height: 20,
),
if (_didSearch && _searchResult == null)
RoundedWhiteContainer(
borderColor: isDesktop
? Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar
: null,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Nothing found. Please check the payment code.",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.label(context),
),
],
),
),
if (_didSearch && _searchResult != null)
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
borderColor: isDesktop
? Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar
: null,
child: PaynymCard(
label: _searchResult!.nymName,
paymentCodeString: _searchResult!.codes.first.code,
walletId: widget.walletId,
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.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/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class ClaimingPaynymDialog extends StatefulWidget {
const ClaimingPaynymDialog({
Key? key,
}) : super(key: key);
@override
State<ClaimingPaynymDialog> createState() => _RestoringDialogState();
}
class _RestoringDialogState extends State<ClaimingPaynymDialog>
with TickerProviderStateMixin {
late AnimationController? _spinController;
late Animation<double> _spinAnimation;
@override
void initState() {
_spinController = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat();
_spinAnimation = CurvedAnimation(
parent: _spinController!,
curve: Curves.linear,
);
super.initState();
}
@override
void dispose() {
_spinController?.dispose();
_spinController = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
if (Util.isDesktop) {
return DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
DesktopDialogCloseButton(
onPressedOverride: () => Navigator.of(context).pop(true),
),
],
),
RotationTransition(
turns: _spinAnimation,
child: SvgPicture.asset(
Assets.svg.arrowRotate,
color:
Theme.of(context).extension<StackColors>()!.accentColorDark,
width: 40,
height: 40,
),
),
Padding(
padding: const EdgeInsets.all(40),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Claiming PayNym",
style: STextStyles.desktopH2(context),
),
const SizedBox(
height: 20,
),
Text(
"We are generating your PayNym",
style: STextStyles.desktopTextMedium(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textDark3,
),
),
const SizedBox(
height: 40,
),
SecondaryButton(
label: "Cancel",
width: 272,
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
),
),
],
),
);
} else {
return WillPopScope(
onWillPop: () async {
return false;
},
child: StackDialog(
title: "Claiming PayNym",
message: "We are generating your PayNym",
icon: RotationTransition(
turns: _spinAnimation,
child: SvgPicture.asset(
Assets.svg.arrowRotate,
color:
Theme.of(context).extension<StackColors>()!.accentColorDark,
width: 24,
height: 24,
),
),
rightButton: SecondaryButton(
label: "Cancel",
onPressed: () {
Navigator.of(context).pop(true);
},
),
),
);
}
}
}

View file

@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/utilities/assets.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/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/stack_dialog.dart';
class ConfirmPaynymConnectDialog extends StatelessWidget {
const ConfirmPaynymConnectDialog({
Key? key,
required this.nymName,
required this.onConfirmPressed,
required this.amount,
required this.coin,
}) : super(key: key);
final String nymName;
final VoidCallback onConfirmPressed;
final int amount;
final Coin coin;
String get title => "Connect to $nymName";
String get message => "A one-time connection fee of "
"${Format.satoshisToAmount(amount, coin: coin)} ${coin.ticker} "
"will be charged to connect to this PayNym.\n\nThis fee "
"covers the cost of creating a one-time transaction to create a "
"record on the blockchain. This keeps PayNyms decentralized.";
@override
Widget build(BuildContext context) {
if (Util.isDesktop) {
return DesktopDialog(
maxHeight: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 40),
child: SvgPicture.asset(
Assets.svg.userPlus,
color: Theme.of(context).extension<StackColors>()!.textDark,
width: 32,
height: 32,
),
),
const DesktopDialogCloseButton(),
],
),
Padding(
padding: const EdgeInsets.only(
left: 40,
bottom: 32,
right: 40,
),
child: Text(
title,
style: STextStyles.desktopH3(context),
),
),
Padding(
padding: const EdgeInsets.only(
left: 40,
right: 40,
),
child: Text(
message,
style: STextStyles.desktopTextMedium(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark3,
),
),
),
Padding(
padding: const EdgeInsets.only(
left: 40,
bottom: 40,
right: 40,
top: 32,
),
child: Row(
children: [
Expanded(
child: SecondaryButton(
buttonHeight: ButtonHeight.l,
label: "Cancel",
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
buttonHeight: ButtonHeight.l,
label: "Connect",
onPressed: onConfirmPressed,
),
),
],
),
)
],
),
);
} else {
return StackDialog(
title: title,
icon: SvgPicture.asset(
Assets.svg.userPlus,
color: Theme.of(context).extension<StackColors>()!.textDark,
width: 24,
height: 24,
),
message: message,
leftButton: SecondaryButton(
buttonHeight: ButtonHeight.xl,
label: "Cancel",
onPressed: Navigator.of(context).pop,
),
rightButton: PrimaryButton(
buttonHeight: ButtonHeight.xl,
label: "Connect",
onPressed: onConfirmPressed,
),
);
}
}
}

View file

@ -0,0 +1,332 @@
import 'dart:async';
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:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart';
import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/utilities/assets.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/custom_buttons/paynym_follow_toggle_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/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
class PaynymDetailsPopup extends ConsumerStatefulWidget {
const PaynymDetailsPopup({
Key? key,
required this.walletId,
required this.accountLite,
}) : super(key: key);
final String walletId;
final PaynymAccountLite accountLite;
@override
ConsumerState<PaynymDetailsPopup> createState() => _PaynymDetailsPopupState();
}
class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
bool _showInsufficientFundsInfo = false;
Future<void> _onConnectPressed() async {
bool canPop = false;
unawaited(
showDialog<void>(
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => canPop,
child: const LoadingIndicator(
width: 200,
),
),
),
);
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet;
// sanity check to prevent second notifcation tx
if (wallet.hasConnectedConfirmed(widget.accountLite.code)) {
canPop = true;
Navigator.of(context).pop();
// TODO show info popup
return;
} else if (wallet.hasConnected(widget.accountLite.code)) {
canPop = true;
Navigator.of(context).pop();
// TODO show info popup
return;
}
final rates = await wallet.fees;
Map<String, dynamic> preparedTx;
try {
preparedTx = await wallet.buildNotificationTx(
selectedTxFeeRate: rates.medium,
targetPaymentCodeString: widget.accountLite.code,
);
} on InsufficientBalanceException catch (_) {
if (mounted) {
canPop = true;
Navigator.of(context).pop();
}
setState(() {
_showInsufficientFundsInfo = true;
});
return;
}
if (mounted) {
// We have enough balance and prepared tx should be good to go.
canPop = true;
// close loading
Navigator.of(context).pop();
// Close details
Navigator.of(context).pop();
// show info pop up
await showDialog<void>(
context: context,
builder: (context) => ConfirmPaynymConnectDialog(
nymName: widget.accountLite.nymName,
onConfirmPressed: () {
//
print("CONFIRM NOTIF TX: $preparedTx");
Navigator.of(context).push(
RouteGenerator.getRoute(
builder: (_) => ConfirmTransactionView(
walletId: wallet.walletId,
routeOnSuccessName: PaynymHomeView.routeName,
isPaynymNotificationTransaction: true,
transactionInfo: {
"hex": preparedTx["hex"],
"address": preparedTx["recipientPaynym"],
"recipientAmt": preparedTx["amount"],
"fee": preparedTx["fee"],
"vSize": preparedTx["vSize"],
"note": "PayNym connect"
},
),
),
);
},
amount: (preparedTx["amount"] as int) + (preparedTx["fee"] as int),
coin: wallet.coin,
),
);
}
}
@override
Widget build(BuildContext context) {
return DesktopDialog(
maxWidth: MediaQuery.of(context).size.width - 32,
maxHeight: double.infinity,
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(
left: 24,
top: 24,
right: 24,
bottom: 16,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
PayNymBot(
paymentCodeString: widget.accountLite.code,
size: 32,
),
const SizedBox(
width: 12,
),
Text(
widget.accountLite.nymName,
style: STextStyles.w600_12(context),
),
],
),
PrimaryButton(
label: "Connect",
buttonHeight: ButtonHeight.l,
icon: SvgPicture.asset(
Assets.svg.circlePlusFilled,
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
iconSpacing: 4,
width: 86,
onPressed: _onConnectPressed,
),
],
),
if (_showInsufficientFundsInfo)
Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 24,
),
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.warningBackground,
child: Text(
"Adding a PayNym to your contacts requires a one-time "
"transaction fee for creating the record on the "
"blockchain. Please deposit more "
"${ref.read(walletsChangeNotifierProvider).getManager(widget.walletId).wallet.coin.ticker} "
"into your wallet and try again.",
style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
),
),
),
],
),
],
),
),
Container(
color: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
height: 1,
),
Padding(
padding: const EdgeInsets.only(
left: 24,
top: 16,
right: 24,
bottom: 16,
),
child: Row(
children: [
Expanded(
child: ConstrainedBox(
constraints: const BoxConstraints(minHeight: 86),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"PayNym address",
style: STextStyles.infoSmall(context),
),
const SizedBox(
height: 6,
),
Text(
widget.accountLite.code,
style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
const SizedBox(
height: 6,
),
],
),
),
),
const SizedBox(
width: 20,
),
QrImage(
padding: const EdgeInsets.all(0),
size: 86,
data: widget.accountLite.code,
foregroundColor:
Theme.of(context).extension<StackColors>()!.textDark,
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 24,
right: 24,
bottom: 24,
),
child: Row(
children: [
Expanded(
child: PaynymFollowToggleButton(
walletId: widget.walletId,
paymentCodeStringToFollow: widget.accountLite.code,
style: PaynymFollowToggleButtonStyle.detailsPopup,
),
),
const SizedBox(
width: 12,
),
Expanded(
child: SecondaryButton(
label: "Copy",
buttonHeight: ButtonHeight.l,
icon: SvgPicture.asset(
Assets.svg.copy,
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
iconSpacing: 4,
onPressed: () async {
await Clipboard.setData(
ClipboardData(
text: widget.accountLite.code,
),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
),
);
},
),
),
],
),
),
],
),
);
}
}

View file

@ -0,0 +1,161 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/paynym/paynym_account.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.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/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
class PaynymQrPopup extends StatelessWidget {
const PaynymQrPopup({
Key? key,
required this.paynymAccount,
}) : super(key: key);
final PaynymAccount paynymAccount;
@override
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
return DesktopDialog(
maxWidth: isDesktop ? 580 : MediaQuery.of(context).size.width - 32,
maxHeight: double.infinity,
child: Column(
children: [
if (isDesktop)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
"Address details",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Padding(
padding: EdgeInsets.only(
left: isDesktop ? 32 : 24,
top: isDesktop ? 16 : 24,
right: 24,
bottom: 16,
),
child: Row(
children: [
PayNymBot(
paymentCodeString: paynymAccount.codes.first.code,
size: isDesktop ? 56 : 32,
),
const SizedBox(
width: 12,
),
Text(
paynymAccount.nymName,
style: isDesktop
? STextStyles.w500_24(context)
: STextStyles.w600_12(context),
),
],
),
),
if (!isDesktop)
Container(
color:
Theme.of(context).extension<StackColors>()!.backgroundAppBar,
height: 1,
),
Padding(
padding: const EdgeInsets.only(
left: 24,
top: 16,
right: 24,
bottom: 24,
),
child: Row(
children: [
Expanded(
child: ConstrainedBox(
constraints: const BoxConstraints(minHeight: 107),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isDesktop ? "PayNym address" : "Your PayNym address",
style: isDesktop
? STextStyles.desktopTextSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.infoSmall(context),
),
const SizedBox(
height: 6,
),
Text(
paynymAccount.codes.first.code,
style: isDesktop
? STextStyles.desktopTextSmall(context)
: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
const SizedBox(
height: 6,
),
BlueTextButton(
text: "Copy",
textSize: isDesktop ? 18 : 10,
onTap: () async {
await Clipboard.setData(
ClipboardData(
text: paynymAccount.codes.first.code,
),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
),
);
},
),
],
),
),
),
const SizedBox(
width: 20,
),
QrImage(
padding: const EdgeInsets.all(0),
size: 107,
data: paynymAccount.codes.first.code,
foregroundColor:
Theme.of(context).extension<StackColors>()!.textDark,
),
],
),
)
],
),
);
}
}

View file

@ -0,0 +1,258 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/paynym/dialogs/claiming_paynym_dialog.dart';
import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.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/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
class PaynymClaimView extends ConsumerStatefulWidget {
const PaynymClaimView({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
static const String routeName = "/claimPaynym";
@override
ConsumerState<PaynymClaimView> createState() => _PaynymClaimViewState();
}
class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final isDesktop = Util.isDesktop;
return MasterScaffold(
isDesktop: isDesktop,
appBar: isDesktop
? DesktopAppBar(
isCompactHeight: true,
background: Theme.of(context).extension<StackColors>()!.popupBG,
leading: Row(
children: [
Padding(
padding: const EdgeInsets.only(
left: 24,
right: 20,
),
child: AppBarIconButton(
size: 32,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
shadows: const [],
icon: SvgPicture.asset(
Assets.svg.arrowLeft,
width: 18,
height: 18,
color: Theme.of(context)
.extension<StackColors>()!
.topNavIconPrimary,
),
onPressed: Navigator.of(context).pop,
),
),
SvgPicture.asset(
Assets.svg.user,
width: 42,
height: 42,
color: Theme.of(context).extension<StackColors>()!.textDark,
),
const SizedBox(
width: 10,
),
Text(
"PayNym",
style: STextStyles.desktopH3(context),
)
],
),
)
: AppBar(
leading: const AppBarBackButton(),
titleSpacing: 0,
title: Text(
"PayNym",
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
),
body: ConditionalParent(
condition: !isDesktop,
builder: (child) => SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
child: ConditionalParent(
condition: isDesktop,
builder: (child) => SizedBox(
width: 328,
child: child,
),
child: Column(
children: [
const Spacer(
flex: 1,
),
Image(
image: AssetImage(
Assets.png.stack,
),
width: MediaQuery.of(context).size.width / 2,
),
const SizedBox(
height: 20,
),
Text(
"You do not have a PayNym yet.\nClaim yours now!",
style: isDesktop
? STextStyles.desktopSubtitleH2(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.baseXS(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
textAlign: TextAlign.center,
),
if (isDesktop)
const SizedBox(
height: 30,
),
if (!isDesktop)
const Spacer(
flex: 2,
),
PrimaryButton(
label: "Claim",
onPressed: () async {
bool shouldCancel = false;
unawaited(
showDialog<bool?>(
context: context,
barrierDismissible: false,
builder: (context) => const ClaimingPaynymDialog(),
).then((value) => shouldCancel = value == true),
);
// get wallet to access paynym calls
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet;
if (shouldCancel) return;
// get payment code
final pCode = await wallet.getPaymentCode();
if (shouldCancel) return;
// attempt to create new entry in paynym.is db
final created = await ref
.read(paynymAPIProvider)
.create(pCode.toString());
debugPrint("created:$created");
if (shouldCancel) return;
if (created.value!.claimed) {
// payment code already claimed
debugPrint("pcode already claimed!!");
if (mounted) {
if (isDesktop) {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).pop();
} else {
Navigator.of(context).popUntil(
ModalRoute.withName(
WalletView.routeName,
),
);
}
}
return;
}
if (shouldCancel) return;
final token =
await ref.read(paynymAPIProvider).token(pCode.toString());
if (shouldCancel) return;
// sign token with notification private key
final signature =
await wallet.signStringWithNotificationKey(token.value!);
if (shouldCancel) return;
// claim paynym account
final claim = await ref
.read(paynymAPIProvider)
.claim(token.value!, signature);
if (shouldCancel) return;
if (claim.value?.claimed == pCode.toString()) {
final account =
await ref.read(paynymAPIProvider).nym(pCode.toString());
ref.read(myPaynymAccountStateProvider.state).state =
account.value!;
if (mounted) {
if (isDesktop) {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).pop();
} else {
Navigator.of(context).popUntil(
ModalRoute.withName(
WalletView.routeName,
),
);
}
await Navigator.of(context).pushNamed(
PaynymHomeView.routeName,
arguments: widget.walletId,
);
}
} else if (mounted && !shouldCancel) {
Navigator.of(context, rootNavigator: isDesktop).pop();
}
},
),
if (isDesktop)
const Spacer(
flex: 2,
),
],
),
),
),
);
}
}

View file

@ -0,0 +1,636 @@
import 'dart:async';
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/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/paynym/add_new_paynym_follow_view.dart';
import 'package:stackwallet/pages/paynym/dialogs/paynym_qr_popup.dart';
import 'package:stackwallet/pages/paynym/subwidgets/desktop_paynym_details.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_followers_list.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_following_list.dart';
import 'package:stackwallet/providers/ui/selected_paynym_details_item_Provider.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.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_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/copy_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/share_icon.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/toggle.dart';
class PaynymHomeView extends ConsumerStatefulWidget {
const PaynymHomeView({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
static const String routeName = "/paynymHome";
@override
ConsumerState<PaynymHomeView> createState() => _PaynymHomeViewState();
}
class _PaynymHomeViewState extends ConsumerState<PaynymHomeView> {
bool showFollowers = false;
int secretCount = 0;
Timer? timer;
bool _followButtonHoverState = false;
@override
void dispose() {
timer?.cancel();
timer = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final isDesktop = Util.isDesktop;
return MasterScaffold(
isDesktop: isDesktop,
appBar: isDesktop
? DesktopAppBar(
isCompactHeight: true,
background: Theme.of(context).extension<StackColors>()!.popupBG,
leading: Row(
children: [
Padding(
padding: const EdgeInsets.only(
left: 24,
right: 20,
),
child: AppBarIconButton(
size: 32,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
shadows: const [],
icon: SvgPicture.asset(
Assets.svg.arrowLeft,
width: 18,
height: 18,
color: Theme.of(context)
.extension<StackColors>()!
.topNavIconPrimary,
),
onPressed: Navigator.of(context).pop,
),
),
SvgPicture.asset(
Assets.svg.user,
width: 32,
height: 32,
color: Theme.of(context).extension<StackColors>()!.textDark,
),
const SizedBox(
width: 10,
),
Text(
"PayNym",
style: STextStyles.desktopH3(context),
)
],
),
trailing: Padding(
padding: const EdgeInsets.only(right: 12),
child: SizedBox(
height: 56,
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (_) => setState(() {
_followButtonHoverState = true;
}),
onExit: (_) => setState(() {
_followButtonHoverState = false;
}),
child: GestureDetector(
onTap: () {
showDialog<void>(
context: context,
builder: (context) => AddNewPaynymFollowView(
walletId: widget.walletId,
),
);
},
child: RoundedContainer(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
color: _followButtonHoverState
? Theme.of(context)
.extension<StackColors>()!
.highlight
: Colors.transparent,
radiusMultiplier: 100,
child: Row(
children: [
SvgPicture.asset(
Assets.svg.plus,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
const SizedBox(
width: 8,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Follow",
style:
STextStyles.desktopButtonSecondaryEnabled(
context)
.copyWith(
fontSize: 16,
),
),
const SizedBox(
height: 2,
),
],
),
],
),
),
),
),
),
),
)
: AppBar(
leading: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop();
},
),
titleSpacing: 0,
title: Text(
"PayNym",
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
actions: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
icon: SvgPicture.asset(
Assets.svg.circlePlusFilled,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
Navigator.of(context).pushNamed(
AddNewPaynymFollowView.routeName,
arguments: widget.walletId,
);
},
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
icon: SvgPicture.asset(
Assets.svg.circleQuestion,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
// todo info ?
},
),
),
),
const SizedBox(
width: 4,
),
],
),
body: ConditionalParent(
condition: !isDesktop,
builder: (child) => SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: child,
),
),
child: Column(
crossAxisAlignment:
isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.center,
children: [
if (!isDesktop)
Column(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
secretCount++;
if (secretCount > 5) {
debugPrint(
"My Account: ${ref.read(myPaynymAccountStateProvider.state).state}");
debugPrint(
"My Account: ${ref.read(myPaynymAccountStateProvider.state).state!.following}");
secretCount = 0;
}
timer ??= Timer(
const Duration(milliseconds: 1500),
() {
secretCount = 0;
timer = null;
},
);
},
child: PayNymBot(
paymentCodeString: ref
.watch(myPaynymAccountStateProvider.state)
.state!
.codes
.first
.code,
),
),
const SizedBox(
height: 10,
),
Text(
ref
.watch(myPaynymAccountStateProvider.state)
.state!
.nymName,
style: STextStyles.desktopMenuItemSelected(context),
),
const SizedBox(
height: 4,
),
Text(
Format.shorten(
ref
.watch(myPaynymAccountStateProvider.state)
.state!
.codes
.first
.code,
12,
5),
style: STextStyles.label(context),
),
const SizedBox(
height: 11,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Copy",
buttonHeight: ButtonHeight.l,
iconSpacing: 4,
icon: CopyIcon(
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () async {
await Clipboard.setData(
ClipboardData(
text: ref
.read(myPaynymAccountStateProvider.state)
.state!
.codes
.first
.code,
),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
),
);
},
),
),
const SizedBox(
width: 13,
),
Expanded(
child: SecondaryButton(
label: "Share",
buttonHeight: ButtonHeight.l,
iconSpacing: 4,
icon: ShareIcon(
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
// copy to clipboard
},
),
),
const SizedBox(
width: 13,
),
Expanded(
child: SecondaryButton(
label: "Address",
buttonHeight: ButtonHeight.l,
iconSpacing: 4,
icon: QrCodeIcon(
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
showDialog<void>(
context: context,
builder: (context) => PaynymQrPopup(
paynymAccount: ref
.read(myPaynymAccountStateProvider.state)
.state!,
),
);
},
),
),
],
),
],
),
if (isDesktop)
Padding(
padding: const EdgeInsets.all(24),
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const SizedBox(
width: 4,
),
GestureDetector(
onTap: () {
secretCount++;
if (secretCount > 5) {
debugPrint(
"My Account: ${ref.read(myPaynymAccountStateProvider.state).state}");
debugPrint(
"My Account: ${ref.read(myPaynymAccountStateProvider.state).state!.following}");
secretCount = 0;
}
timer ??= Timer(
const Duration(milliseconds: 1500),
() {
secretCount = 0;
timer = null;
},
);
},
child: PayNymBot(
paymentCodeString: ref
.watch(myPaynymAccountStateProvider.state)
.state!
.codes
.first
.code,
),
),
const SizedBox(
width: 16,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ref
.watch(myPaynymAccountStateProvider.state)
.state!
.nymName,
style: STextStyles.desktopH3(context),
),
const SizedBox(
height: 4,
),
Text(
Format.shorten(
ref
.watch(myPaynymAccountStateProvider.state)
.state!
.codes
.first
.code,
12,
5),
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
],
),
const Spacer(),
SecondaryButton(
label: "Copy",
buttonHeight: ButtonHeight.l,
width: 160,
icon: CopyIcon(
width: 18,
height: 18,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () async {
await Clipboard.setData(
ClipboardData(
text: ref
.read(myPaynymAccountStateProvider.state)
.state!
.codes
.first
.code,
),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
),
);
},
),
const SizedBox(
width: 16,
),
SecondaryButton(
label: "Address",
width: 160,
buttonHeight: ButtonHeight.l,
icon: QrCodeIcon(
width: 18,
height: 18,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
showDialog<void>(
context: context,
builder: (context) => PaynymQrPopup(
paynymAccount: ref
.read(myPaynymAccountStateProvider.state)
.state!,
),
);
},
),
],
),
),
),
if (!isDesktop)
const SizedBox(
height: 24,
),
ConditionalParent(
condition: isDesktop,
builder: (child) => Padding(
padding: const EdgeInsets.only(left: 24),
child: child,
),
child: SizedBox(
height: isDesktop ? 56 : 40,
width: isDesktop ? 490 : null,
child: Toggle(
onColor: Theme.of(context).extension<StackColors>()!.popupBG,
onText:
"Following (${ref.watch(myPaynymAccountStateProvider.state).state?.following.length ?? 0})",
offColor: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
offText:
"Followers (${ref.watch(myPaynymAccountStateProvider.state).state?.followers.length ?? 0})",
isOn: showFollowers,
onValueChanged: (value) {
setState(() {
showFollowers = value;
});
},
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
),
),
SizedBox(
height: isDesktop ? 20 : 16,
),
Expanded(
child: ConditionalParent(
condition: isDesktop,
builder: (child) => Padding(
padding: const EdgeInsets.only(left: 24),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 490,
child: child,
),
const SizedBox(
width: 24,
),
if (ref
.watch(selectedPaynymDetailsItemProvider.state)
.state !=
null)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 600,
),
child: DesktopPaynymDetails(
walletId: widget.walletId,
accountLite: ref
.watch(selectedPaynymDetailsItemProvider
.state)
.state!,
),
),
],
),
),
if (ref
.watch(selectedPaynymDetailsItemProvider.state)
.state !=
null)
const SizedBox(
width: 24,
),
],
),
),
child: ConditionalParent(
condition: !isDesktop,
builder: (child) => Container(
child: child,
),
child: !showFollowers
? PaynymFollowingList(
walletId: widget.walletId,
)
: PaynymFollowersList(
walletId: widget.walletId,
),
),
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,344 @@
import 'dart:async';
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:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/utilities/assets.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/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_buttons/paynym_follow_toggle_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopPaynymDetails extends ConsumerStatefulWidget {
const DesktopPaynymDetails({
Key? key,
required this.walletId,
required this.accountLite,
}) : super(key: key);
final String walletId;
final PaynymAccountLite accountLite;
@override
ConsumerState<DesktopPaynymDetails> createState() =>
_PaynymDetailsPopupState();
}
class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
bool _showInsufficientFundsInfo = false;
Future<void> _onConnectPressed() async {
bool canPop = false;
unawaited(
showDialog<void>(
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => canPop,
child: const LoadingIndicator(
width: 200,
),
),
),
);
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet;
// sanity check to prevent second notification tx
if (wallet.hasConnectedConfirmed(widget.accountLite.code)) {
canPop = true;
Navigator.of(context, rootNavigator: true).pop();
// TODO show info popup
return;
} else if (wallet.hasConnected(widget.accountLite.code)) {
canPop = true;
Navigator.of(context, rootNavigator: true).pop();
// TODO show info popup
return;
}
final rates = await wallet.fees;
Map<String, dynamic> preparedTx;
try {
preparedTx = await wallet.buildNotificationTx(
selectedTxFeeRate: rates.medium,
targetPaymentCodeString: widget.accountLite.code,
);
} on InsufficientBalanceException catch (e) {
if (mounted) {
canPop = true;
Navigator.of(context, rootNavigator: true).pop();
}
setState(() {
_showInsufficientFundsInfo = true;
});
return;
}
if (mounted) {
// We have enough balance and prepared tx should be good to go.
canPop = true;
// close loading
Navigator.of(context, rootNavigator: true).pop();
// show info pop up
await showDialog<void>(
context: context,
builder: (context) => ConfirmPaynymConnectDialog(
nymName: widget.accountLite.nymName,
onConfirmPressed: () {
//
print("CONFIRM NOTIF TX: $preparedTx");
Navigator.of(context, rootNavigator: true).pop();
unawaited(
showDialog(
context: context,
builder: (context) => DesktopDialog(
maxHeight: double.infinity,
maxWidth: 580,
child: ConfirmTransactionView(
walletId: wallet.walletId,
isPaynymNotificationTransaction: true,
transactionInfo: {
"hex": preparedTx["hex"],
"address": preparedTx["recipientPaynym"],
"recipientAmt": preparedTx["amount"],
"fee": preparedTx["fee"],
"vSize": preparedTx["vSize"],
"note": "PayNym connect"
},
onSuccessInsteadOfRouteOnSuccess: () {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context, rootNavigator: true).pop();
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message:
"Connection initiated to ${widget.accountLite.nymName}",
iconAsset: Assets.svg.copy,
context: context,
),
);
},
),
),
),
);
},
amount: (preparedTx["amount"] as int) + (preparedTx["fee"] as int),
coin: wallet.coin,
),
);
}
}
Future<void> _onSend() async {
print("sned");
}
@override
Widget build(BuildContext context) {
final wallet = ref
.watch(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet;
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
PayNymBot(
paymentCodeString: widget.accountLite.code,
size: 32,
),
const SizedBox(
width: 12,
),
Text(
widget.accountLite.nymName,
style: STextStyles.desktopTextSmall(context),
),
],
),
const SizedBox(
height: 20,
),
Row(
children: [
if (!wallet.hasConnected(widget.accountLite.code))
Expanded(
child: PrimaryButton(
label: "Connect",
buttonHeight: ButtonHeight.s,
icon: SvgPicture.asset(
Assets.svg.circlePlusFilled,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
iconSpacing: 6,
onPressed: _onConnectPressed,
),
),
if (wallet.hasConnected(widget.accountLite.code))
Expanded(
child: PrimaryButton(
label: "Send",
buttonHeight: ButtonHeight.s,
icon: SvgPicture.asset(
Assets.svg.circleArrowUpRight,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
iconSpacing: 6,
onPressed: _onSend,
),
),
const SizedBox(
width: 20,
),
Expanded(
child: PaynymFollowToggleButton(
walletId: widget.walletId,
paymentCodeStringToFollow: widget.accountLite.code,
style: PaynymFollowToggleButtonStyle.detailsDesktop,
),
),
],
),
if (_showInsufficientFundsInfo)
Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 24,
),
RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.warningBackground,
child: Text(
"Adding a PayNym to your contacts requires a one-time "
"transaction fee for creating the record on the "
"blockchain. Please deposit more "
"${ref.read(walletsChangeNotifierProvider).getManager(widget.walletId).wallet.coin.ticker} "
"into your wallet and try again.",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.warningForeground,
),
),
),
],
),
],
),
),
Container(
color: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
height: 1,
),
Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"PayNym address",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
const SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: ConstrainedBox(
constraints: const BoxConstraints(minHeight: 100),
child: Text(
widget.accountLite.code,
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
),
),
const SizedBox(
width: 20,
),
QrImage(
padding: const EdgeInsets.all(0),
size: 100,
data: widget.accountLite.code,
foregroundColor:
Theme.of(context).extension<StackColors>()!.textDark,
),
],
),
const SizedBox(
height: 8,
),
BlueTextButton(
text: "Copy",
onTap: () async {
await Clipboard.setData(
ClipboardData(
text: widget.accountLite.code,
),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
),
);
},
),
],
),
),
],
),
);
}
}

View file

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_card.dart';
import 'package:stackwallet/utilities/featured_paynyms.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_white_container.dart';
class FeaturedPaynymsWidget extends StatelessWidget {
const FeaturedPaynymsWidget({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
Widget build(BuildContext context) {
final entries = FeaturedPaynyms.featured.entries.toList(growable: false);
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) => RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: child,
),
child: Column(
children: [
for (int i = 0; i < entries.length; i++)
Column(
children: [
if (i > 0)
isDesktop
? const SizedBox(
height: 10,
)
: Container(
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
height: 1,
),
ConditionalParent(
condition: isDesktop,
builder: (child) => RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
borderColor: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
child: child,
),
child: PaynymCard(
walletId: walletId,
label: entries[i].key,
paymentCodeString: entries[i].value,
),
),
],
),
],
),
);
}
}

View file

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
class PayNymBot extends StatelessWidget {
const PayNymBot({
Key? key,
required this.paymentCodeString,
this.size = 60.0,
}) : super(key: key);
final String paymentCodeString;
final double size;
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(size / 2),
child: SizedBox(
width: size,
height: size,
child: Image.network(
"https://paynym.is/$paymentCodeString/avatar",
loadingBuilder: (context, child, loadingProgress) =>
loadingProgress == null
? child
: const Center(
child: LoadingIndicator(),
),
),
),
);
}
}

View file

@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.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/custom_buttons/paynym_follow_toggle_button.dart';
class PaynymCard extends StatefulWidget {
const PaynymCard({
Key? key,
required this.walletId,
required this.label,
required this.paymentCodeString,
}) : super(key: key);
final String walletId;
final String label;
final String paymentCodeString;
@override
State<PaynymCard> createState() => _PaynymCardState();
}
class _PaynymCardState extends State<PaynymCard> {
final isDesktop = Util.isDesktop;
@override
Widget build(BuildContext context) {
return Padding(
padding: isDesktop
? const EdgeInsets.symmetric(
vertical: 16,
horizontal: 20,
)
: const EdgeInsets.all(12),
child: Row(
children: [
PayNymBot(
size: 32,
paymentCodeString: widget.paymentCodeString,
),
const SizedBox(
width: 12,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.label,
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
)
: STextStyles.w500_12(context),
),
const SizedBox(
height: 2,
),
Text(
Format.shorten(widget.paymentCodeString, 12, 5),
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
PaynymFollowToggleButton(
walletId: widget.walletId,
paymentCodeStringToFollow: widget.paymentCodeString,
),
],
),
);
}
}

View file

@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/pages/paynym/dialogs/paynym_details_popup.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
import 'package:stackwallet/providers/ui/selected_paynym_details_item_Provider.dart';
import 'package:stackwallet/utilities/constants.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/rounded_container.dart';
class PaynymCardButton extends ConsumerStatefulWidget {
const PaynymCardButton({
Key? key,
required this.walletId,
required this.accountLite,
}) : super(key: key);
final String walletId;
final PaynymAccountLite accountLite;
@override
ConsumerState<PaynymCardButton> createState() => _PaynymCardButtonState();
}
class _PaynymCardButtonState extends ConsumerState<PaynymCardButton> {
final isDesktop = Util.isDesktop;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(4),
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: isDesktop &&
ref
.watch(selectedPaynymDetailsItemProvider.state)
.state
?.nymId ==
widget.accountLite.nymId
? Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.08)
: Colors.transparent,
child: RawMaterialButton(
padding: const EdgeInsets.all(0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
if (isDesktop) {
ref.read(selectedPaynymDetailsItemProvider.state).state =
widget.accountLite;
} else {
showDialog<void>(
context: context,
builder: (context) => PaynymDetailsPopup(
accountLite: widget.accountLite,
walletId: widget.walletId,
),
);
}
},
child: Padding(
padding: isDesktop
? const EdgeInsets.symmetric(
vertical: 8,
horizontal: 12,
)
: const EdgeInsets.all(8.0),
child: Row(
children: [
PayNymBot(
size: 32,
paymentCodeString: widget.accountLite.code,
),
const SizedBox(
width: 12,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.accountLite.nymName,
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
)
: STextStyles.w500_12(context),
),
const SizedBox(
height: 2,
),
Text(
Format.shorten(widget.accountLite.code, 12, 5),
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
],
),
),
),
),
);
}
}

View file

@ -0,0 +1,114 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_card_button.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_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/utilities/util.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class PaynymFollowersList extends ConsumerStatefulWidget {
const PaynymFollowersList({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<PaynymFollowersList> createState() =>
_PaynymFollowersListState();
}
class _PaynymFollowersListState extends ConsumerState<PaynymFollowersList> {
final isDesktop = Util.isDesktop;
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 followers =
ref.watch(myPaynymAccountStateProvider.state).state?.followers;
final count = followers?.length ?? 0;
return ListView.separated(
itemCount: max(count, 1),
separatorBuilder: (BuildContext context, int index) => Container(
height: 1.5,
color: Colors.transparent,
),
itemBuilder: (BuildContext context, int index) {
if (count == 0) {
return RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Your PayNym followers will appear here",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.label(context),
),
],
),
);
} else if (count == 1) {
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: PaynymCardButton(
walletId: widget.walletId,
accountLite: followers![0],
),
);
} else {
BorderRadius? borderRadius;
if (index == 0) {
borderRadius = _borderRadiusFirst;
} else if (index == count - 1) {
borderRadius = _borderRadiusLast;
}
return Container(
key: Key("paynymCardKey_${followers![index].nymId}"),
decoration: BoxDecoration(
borderRadius: borderRadius,
color: Theme.of(context).extension<StackColors>()!.popupBG,
),
child: PaynymCardButton(
walletId: widget.walletId,
accountLite: followers[index],
),
);
}
},
);
}
}

View file

@ -0,0 +1,114 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_card_button.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_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/utilities/util.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class PaynymFollowingList extends ConsumerStatefulWidget {
const PaynymFollowingList({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<PaynymFollowingList> createState() =>
_PaynymFollowingListState();
}
class _PaynymFollowingListState extends ConsumerState<PaynymFollowingList> {
final isDesktop = Util.isDesktop;
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 following =
ref.watch(myPaynymAccountStateProvider.state).state?.following;
final count = following?.length ?? 0;
return ListView.separated(
itemCount: max(count, 1),
separatorBuilder: (BuildContext context, int index) => Container(
height: 1.5,
color: Colors.transparent,
),
itemBuilder: (BuildContext context, int index) {
if (count == 0) {
return RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Your PayNym contacts will appear here",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.label(context),
),
],
),
);
} else if (count == 1) {
return RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: PaynymCardButton(
walletId: widget.walletId,
accountLite: following![0],
),
);
} else {
BorderRadius? borderRadius;
if (index == 0) {
borderRadius = _borderRadiusFirst;
} else if (index == count - 1) {
borderRadius = _borderRadiusLast;
}
return Container(
key: Key("paynymCardKey_${following![index].nymId}"),
decoration: BoxDecoration(
borderRadius: borderRadius,
color: Theme.of(context).extension<StackColors>()!.popupBG,
),
child: PaynymCardButton(
walletId: widget.walletId,
accountLite: following[index],
),
);
}
},
);
}
}

View file

@ -12,6 +12,8 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -41,6 +43,9 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
required this.walletId,
this.routeOnSuccessName = WalletView.routeName,
this.isTradeTransaction = false,
this.isPaynymTransaction = false,
this.isPaynymNotificationTransaction = false,
this.onSuccessInsteadOfRouteOnSuccess,
}) : super(key: key);
static const String routeName = "/confirmTransactionView";
@ -49,6 +54,9 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
final String walletId;
final String routeOnSuccessName;
final bool isTradeTransaction;
final bool isPaynymTransaction;
final bool isPaynymNotificationTransaction;
final VoidCallback? onSuccessInsteadOfRouteOnSuccess;
@override
ConsumerState<ConfirmTransactionView> createState() =>
@ -83,14 +91,22 @@ class _ConfirmTransactionViewState
try {
String txid;
final coin = manager.coin;
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
txid = await (manager.wallet as FiroWallet)
.confirmSendPublic(txData: transactionInfo);
if (widget.isPaynymNotificationTransaction) {
txid = await (manager.wallet as DogecoinWallet)
.confirmNotificationTx(preparedTx: transactionInfo);
} else if (widget.isPaynymTransaction) {
//
throw UnimplementedError("paynym send not implemented yet");
} else {
txid = await manager.confirmSend(txData: transactionInfo);
final coin = manager.coin;
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
txid = await (manager.wallet as FiroWallet)
.confirmSendPublic(txData: transactionInfo);
} else {
txid = await manager.confirmSend(txData: transactionInfo);
}
}
// save note
@ -102,7 +118,12 @@ class _ConfirmTransactionViewState
// pop back to wallet
if (mounted) {
Navigator.of(context).popUntil(ModalRoute.withName(routeOnSuccessName));
if (widget.onSuccessInsteadOfRouteOnSuccess == null) {
Navigator.of(context)
.popUntil(ModalRoute.withName(routeOnSuccessName));
} else {
widget.onSuccessInsteadOfRouteOnSuccess!.call();
}
}
} on BadEpicHttpAddressException catch (_) {
if (mounted) {

View file

@ -485,22 +485,22 @@ class _SendViewState extends ConsumerState<SendView> {
coin == Coin.firoTestNet)
const Spacer(),
FutureBuilder(
// TODO redo this widget now that its not actually a future
future: (coin != Coin.firo &&
coin != Coin.firoTestNet)
? ref.watch(provider.select(
(value) => value.availableBalance))
: ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
? Future(() => ref.watch(
provider.select((value) =>
value.balance.getSpendable())))
: ref.watch(publicPrivateBalanceStateProvider.state).state ==
"Private"
? (ref.watch(provider).wallet
as FiroWallet)
.availablePrivateBalance()
: (ref.watch(provider).wallet
as FiroWallet)
.availablePublicBalance(),
? Future(() => (ref
.watch(provider)
.wallet as FiroWallet)
.availablePrivateBalance())
: Future(() => (ref
.watch(provider)
.wallet as FiroWallet)
.availablePublicBalance()),
builder:
(_, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
@ -1085,9 +1085,10 @@ class _SendViewState extends ConsumerState<SendView> {
.decimalPlacesForCoin(coin));
}
} else {
cryptoAmountController.text = (await ref
cryptoAmountController.text = (ref
.read(provider)
.availableBalance)
.balance
.getSpendable())
.toStringAsFixed(
Constants.decimalPlacesForCoin(
coin));
@ -1523,43 +1524,43 @@ class _SendViewState extends ConsumerState<SendView> {
.read(walletsChangeNotifierProvider)
.getManager(walletId);
// TODO: remove the need for this!!
final bool isOwnAddress =
await manager.isOwnAddress(_address!);
if (isOwnAddress) {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return StackDialog(
title: "Transaction failed",
message:
"Sending to self is currently disabled",
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(
context),
child: Text(
"Ok",
style: STextStyles.button(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.accentColorDark),
),
onPressed: () {
Navigator.of(context).pop();
},
),
);
},
);
return;
}
// // TODO: remove the need for this!!
// final bool isOwnAddress =
// await manager.isOwnAddress(_address!);
// if (isOwnAddress && coin != Coin.dogecoinTestNet) {
// await showDialog<dynamic>(
// context: context,
// useSafeArea: false,
// barrierDismissible: true,
// builder: (context) {
// return StackDialog(
// title: "Transaction failed",
// message:
// "Sending to self is currently disabled",
// rightButton: TextButton(
// style: Theme.of(context)
// .extension<StackColors>()!
// .getSecondaryEnabledButtonColor(
// context),
// child: Text(
// "Ok",
// style: STextStyles.button(
// context)
// .copyWith(
// color: Theme.of(context)
// .extension<
// StackColors>()!
// .accentColorDark),
// ),
// onPressed: () {
// Navigator.of(context).pop();
// },
// ),
// );
// },
// );
// return;
// }
final amount =
Format.decimalAmountToSatoshis(
@ -1575,22 +1576,20 @@ class _SendViewState extends ConsumerState<SendView> {
"Private") {
availableBalance =
Format.decimalAmountToSatoshis(
await (manager.wallet
as FiroWallet)
(manager.wallet as FiroWallet)
.availablePrivateBalance(),
coin);
} else {
availableBalance =
Format.decimalAmountToSatoshis(
await (manager.wallet
as FiroWallet)
(manager.wallet as FiroWallet)
.availablePublicBalance(),
coin);
}
} else {
availableBalance =
Format.decimalAmountToSatoshis(
await manager.availableBalance,
manager.balance.getSpendable(),
coin);
}

View file

@ -154,7 +154,9 @@ class _FiroBalanceSelectionSheetState
width: 2,
),
FutureBuilder(
future: firoWallet.availablePrivateBalance(),
// TODO redo this widget now that its not actually a future
future: Future(
() => firoWallet.availablePrivateBalance()),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
@ -244,7 +246,9 @@ class _FiroBalanceSelectionSheetState
width: 2,
),
FutureBuilder(
future: firoWallet.availablePublicBalance(),
// TODO redo this widget now that its not actually a future
future: Future(
() => firoWallet.availablePublicBalance()),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==

View file

@ -7,8 +7,10 @@ import 'package:stackwallet/utilities/constants.dart';
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/fruit_sorbet_colors.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
import 'package:stackwallet/utilities/theme/ocean_breeze_colors.dart';
import 'package:stackwallet/utilities/theme/oled_black_colors.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -28,6 +30,10 @@ class AppearanceSettingsView extends ConsumerWidget {
return "Ocean theme";
case ThemeType.dark:
return "Dark theme";
case ThemeType.oledBlack:
return "Oled Black theme";
case ThemeType.fruitSorbet:
return "Fruit Sorbet theme";
}
}
@ -430,6 +436,168 @@ class _ThemeOptionsView extends ConsumerState<ThemeOptionsView> {
),
),
),
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.oledBlack.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
OledBlackColors(),
);
setState(() {
_selectedTheme = "oledBlack";
});
},
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: "oledBlack",
groupValue: _selectedTheme,
onChanged: (newValue) {
if (newValue is String && newValue == "oledBlack") {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.oledBlack.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
OledBlackColors(),
);
setState(() {
_selectedTheme = "oledBlack";
});
}
},
),
),
const SizedBox(
width: 14,
),
Text(
"OLED Black",
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.fruitSorbet.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
FruitSorbetColors(),
);
setState(() {
_selectedTheme = "fruitSorbet";
});
},
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: "fruitSorbet",
groupValue: _selectedTheme,
onChanged: (newValue) {
if (newValue is String && newValue == "fruitSorbet") {
DB.instance.put<dynamic>(
boxName: DB.boxNameTheme,
key: "colorScheme",
value: ThemeType.fruitSorbet.name,
);
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
FruitSorbetColors(),
);
setState(() {
_selectedTheme = "fruitSorbet";
});
}
},
),
),
const SizedBox(
width: 14,
),
Text(
"Fruit Sorbet",
style:
STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark2,
),
),
],
),
],
),
),
),
],
);
}

View file

@ -328,6 +328,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
enabled: true,
coinName: coin.name,
isFailover: formData.isFailover!,
trusted: formData.trusted!,
isDown: false,
);
@ -352,6 +353,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
enabled: true,
coinName: coin.name,
isFailover: formData.isFailover!,
trusted: formData.trusted!,
isDown: false,
);
@ -621,11 +623,11 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
class NodeFormData {
String? name, host, login, password;
int? port;
bool? useSSL, isFailover;
bool? useSSL, isFailover, trusted;
@override
String toString() {
return "{ name: $name, host: $host, port: $port, useSSL: $useSSL }";
return "{ name: $name, host: $host, port: $port, useSSL: $useSSL, trusted: $trusted }";
}
}
@ -666,6 +668,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
bool _useSSL = false;
bool _isFailover = false;
bool _trusted = false;
int? port;
late bool enableSSLCheckbox;
@ -718,6 +721,9 @@ class _NodeFormState extends ConsumerState<NodeForm> {
return enable;
}
bool get shouldBeReadOnly =>
widget.readOnly || widget.node?.isDefault == true;
void _updateState() {
port = int.tryParse(_portController.text);
onChanged?.call(canSave, canTestConnection);
@ -733,6 +739,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
ref.read(nodeFormDataProvider).port = port;
ref.read(nodeFormDataProvider).useSSL = _useSSL;
ref.read(nodeFormDataProvider).isFailover = _isFailover;
ref.read(nodeFormDataProvider).trusted = _trusted;
}
@override
@ -764,12 +771,12 @@ class _NodeFormState extends ConsumerState<NodeForm> {
_usernameController.text = node.loginName ?? "";
_useSSL = node.useSSL;
_isFailover = node.isFailover;
_trusted = node.trusted ?? false;
if (widget.coin == Coin.epicCash) {
enableSSLCheckbox = !node.host.startsWith("http");
} else {
enableSSLCheckbox = true;
}
print("enableSSLCheckbox: $enableSSLCheckbox");
WidgetsBinding.instance.addPostFrameCallback((_) {
// update provider state object so test connection works without having to modify a field in the ui first
@ -812,7 +819,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
key: const Key("addCustomNodeNodeNameFieldKey"),
readOnly: widget.readOnly,
readOnly: shouldBeReadOnly,
enabled: enableField(_nameController),
controller: _nameController,
focusNode: _nameFocusNode,
@ -822,7 +829,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
_nameFocusNode,
context,
).copyWith(
suffixIcon: !widget.readOnly && _nameController.text.isNotEmpty
suffixIcon: !shouldBeReadOnly && _nameController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
@ -858,7 +865,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
key: const Key("addCustomNodeNodeAddressFieldKey"),
readOnly: widget.readOnly,
readOnly: shouldBeReadOnly,
enabled: enableField(_hostController),
controller: _hostController,
focusNode: _hostFocusNode,
@ -870,7 +877,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
_hostFocusNode,
context,
).copyWith(
suffixIcon: !widget.readOnly && _hostController.text.isNotEmpty
suffixIcon: !shouldBeReadOnly && _hostController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
@ -917,7 +924,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
key: const Key("addCustomNodeNodePortFieldKey"),
readOnly: widget.readOnly,
readOnly: shouldBeReadOnly,
enabled: enableField(_portController),
controller: _portController,
focusNode: _portFocusNode,
@ -929,7 +936,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
_portFocusNode,
context,
).copyWith(
suffixIcon: !widget.readOnly && _portController.text.isNotEmpty
suffixIcon: !shouldBeReadOnly && _portController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
@ -966,7 +973,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _usernameController,
readOnly: widget.readOnly,
readOnly: shouldBeReadOnly,
enabled: enableField(_usernameController),
keyboardType: TextInputType.number,
focusNode: _usernameFocusNode,
@ -977,7 +984,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
context,
).copyWith(
suffixIcon:
!widget.readOnly && _usernameController.text.isNotEmpty
!shouldBeReadOnly && _usernameController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
@ -1015,7 +1022,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: _passwordController,
readOnly: widget.readOnly,
readOnly: shouldBeReadOnly,
enabled: enableField(_passwordController),
keyboardType: TextInputType.number,
focusNode: _passwordFocusNode,
@ -1026,7 +1033,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
context,
).copyWith(
suffixIcon:
!widget.readOnly && _passwordController.text.isNotEmpty
!shouldBeReadOnly && _passwordController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
@ -1059,7 +1066,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
Row(
children: [
GestureDetector(
onTap: !widget.readOnly && enableSSLCheckbox
onTap: !shouldBeReadOnly && enableSSLCheckbox
? () {
setState(() {
_useSSL = !_useSSL;
@ -1075,7 +1082,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
width: 20,
height: 20,
child: Checkbox(
fillColor: !widget.readOnly && enableSSLCheckbox
fillColor: !shouldBeReadOnly && enableSSLCheckbox
? null
: MaterialStateProperty.all(Theme.of(context)
.extension<StackColors>()!
@ -1083,7 +1090,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
value: _useSSL,
onChanged: !widget.readOnly && enableSSLCheckbox
onChanged: !shouldBeReadOnly && enableSSLCheckbox
? (newValue) {
setState(() {
_useSSL = newValue!;
@ -1106,6 +1113,57 @@ class _NodeFormState extends ConsumerState<NodeForm> {
),
],
),
if (widget.coin == Coin.monero || widget.coin == Coin.wownero)
Row(
children: [
GestureDetector(
onTap: !widget.readOnly /*&& trustedCheckbox*/
? () {
setState(() {
_trusted = !_trusted;
});
_updateState();
}
: null,
child: Container(
color: Colors.transparent,
child: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: Checkbox(
fillColor: !widget.readOnly
? null
: MaterialStateProperty.all(Theme.of(context)
.extension<StackColors>()!
.checkboxBGDisabled),
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
value: _trusted,
onChanged: !widget.readOnly
? (newValue) {
setState(() {
_trusted = newValue!;
});
_updateState();
}
: null,
),
),
const SizedBox(
width: 12,
),
Text(
"Trusted",
style: STextStyles.itemSubtitle12(context),
)
],
),
),
),
],
),
if (widget.coin != Coin.monero &&
widget.coin != Coin.wownero &&
widget.coin != Coin.epicCash)

View file

@ -70,15 +70,13 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
switch (coin) {
case Coin.epicCash:
try {
testPassed = await testEpicNodeConnection(
NodeFormData()
..host = node!.host
..useSSL = node.useSSL
..port = node.port,
) !=
null;
testPassed = await testEpicNodeConnection(
NodeFormData()
..host = node!.host
..useSSL = node.useSSL
..port = node.port,
) !=
null;
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
testPassed = false;
@ -388,6 +386,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
port: ref.read(nodeFormDataProvider).port,
name: ref.read(nodeFormDataProvider).name,
useSSL: ref.read(nodeFormDataProvider).useSSL,
trusted: ref.read(nodeFormDataProvider).trusted,
loginName: ref.read(nodeFormDataProvider).login,
isFailover:
ref.read(nodeFormDataProvider).isFailover,

View file

@ -596,7 +596,7 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
fileToSave,
adkString,
jsonEncode(backup),
adkVersion: adkVersion,
adkVersion,
);
// this future should already be complete unless there was an error encrypting

View file

@ -187,7 +187,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
fileToSave,
adkString,
jsonEncode(backup),
adkVersion: adkVersion,
adkVersion,
);
// this future should already be complete unless there was an error encrypting

View file

@ -133,9 +133,9 @@ abstract class SWB {
static Future<bool> encryptStackWalletWithADK(
String fileToSave,
String adk,
String plaintext, {
int? adkVersion,
}) async {
String plaintext,
int adkVersion,
) async {
try {
File backupFile = File(fileToSave);
if (!backupFile.existsSync()) {

View file

@ -145,7 +145,8 @@ class WalletSyncingOptionsView extends ConsumerWidget {
height: 2,
),
FutureBuilder(
future: manager.totalBalance,
future: Future(
() => manager.balance.getTotal()),
builder: (builderContext,
AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==

View file

@ -507,7 +507,7 @@ class _WalletNetworkSettingsViewState
children: [
Text(
"Synchronized",
style: STextStyles.w600_10(context),
style: STextStyles.w600_12(context),
),
Text(
"100%",
@ -581,7 +581,7 @@ class _WalletNetworkSettingsViewState
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AnimatedText(
style: STextStyles.w600_10(context),
style: STextStyles.w600_12(context),
stringsToLoopThrough: const [
"Synchronizing",
"Synchronizing.",
@ -679,7 +679,7 @@ class _WalletNetworkSettingsViewState
children: [
Text(
"Unable to synchronize",
style: STextStyles.w600_10(context).copyWith(
style: STextStyles.w600_12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorRed,

View file

@ -2,9 +2,10 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
import 'package:stackwallet/providers/blockchain/dogecoin/current_height_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/route_generator.dart';
@ -37,19 +38,10 @@ class TransactionsList extends ConsumerStatefulWidget {
class _TransactionsListState extends ConsumerState<TransactionsList> {
//
bool _hasLoaded = false;
Map<String, Transaction> _transactions = {};
List<Transaction> _transactions2 = [];
late final ChangeNotifierProvider<Manager> managerProvider;
void updateTransactions(TransactionData newData) {
_transactions = {};
final newTransactions =
newData.txChunks.expand((element) => element.transactions);
for (final tx in newTransactions) {
_transactions[tx.txid] = tx;
}
}
BorderRadius get _borderRadiusFirst {
return BorderRadius.only(
topLeft: Radius.circular(
@ -73,12 +65,15 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
}
Widget itemBuilder(
BuildContext context, Transaction tx, BorderRadius? radius) {
BuildContext context,
Transaction tx,
BorderRadius? radius,
) {
final matchingTrades = ref
.read(tradesServiceProvider)
.trades
.where((e) => e.payInTxid == tx.txid || e.payOutTxid == tx.txid);
if (tx.txType == "Sent" && matchingTrades.isNotEmpty) {
if (tx.type == TransactionType.outgoing && matchingTrades.isNotEmpty) {
final trade = matchingTrades.first;
return Container(
decoration: BoxDecoration(
@ -90,13 +85,16 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
children: [
TransactionCard(
// this may mess with combined firo transactions
key: Key(tx.toString()), //
key: Key(tx.txid + tx.type.name + tx.address.value.toString()), //
transaction: tx,
walletId: widget.walletId,
),
TradeCard(
// this may mess with combined firo transactions
key: Key(tx.toString() + trade.uuid), //
key: Key(tx.txid +
tx.type.name +
tx.address.value.toString() +
trade.uuid), //
trade: trade,
onTap: () async {
if (Util.isDesktop) {
@ -182,7 +180,7 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
),
child: TransactionCard(
// this may mess with combined firo transactions
key: Key(tx.toString()), //
key: Key(tx.txid + tx.type.name + tx.address.value.toString()), //
transaction: tx,
walletId: widget.walletId,
),
@ -190,6 +188,13 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
}
}
void updateHeightProvider(Manager manager) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
ref.read(currentHeightProvider(manager.coin).state).state =
manager.currentHeight;
});
}
@override
void initState() {
managerProvider = widget.managerProvider;
@ -202,13 +207,16 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
// .watch(walletsChangeNotifierProvider)
// .getManagerProvider(widget.walletId);
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId)));
updateHeightProvider(manager);
return FutureBuilder(
future:
ref.watch(managerProvider.select((value) => value.transactionData)),
builder: (fbContext, AsyncSnapshot<TransactionData> snapshot) {
future: manager.transactions,
builder: (fbContext, AsyncSnapshot<List<Transaction>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
updateTransactions(snapshot.data!);
_transactions2 = snapshot.data!;
_hasLoaded = true;
}
if (!_hasLoaded) {
@ -227,11 +235,10 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
],
);
}
if (_transactions.isEmpty) {
if (_transactions2.isEmpty) {
return const NoTransActionsFound();
} else {
final list = _transactions.values.toList(growable: false);
list.sort((a, b) => b.timestamp - a.timestamp);
_transactions2.sort((a, b) => b.timestamp - a.timestamp);
return RefreshIndicator(
onRefresh: () async {
//todo: check if print needed
@ -247,12 +254,16 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
? ListView.separated(
itemBuilder: (context, index) {
BorderRadius? radius;
if (index == list.length - 1) {
if (_transactions2.length == 1) {
radius = BorderRadius.circular(
Constants.size.circularBorderRadius,
);
} else if (index == _transactions2.length - 1) {
radius = _borderRadiusLast;
} else if (index == 0) {
radius = _borderRadiusFirst;
}
final tx = list[index];
final tx = _transactions2[index];
return itemBuilder(context, tx, radius);
},
separatorBuilder: (context, index) {
@ -264,18 +275,22 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
.background,
);
},
itemCount: list.length,
itemCount: _transactions2.length,
)
: ListView.builder(
itemCount: list.length,
itemCount: _transactions2.length,
itemBuilder: (context, index) {
BorderRadius? radius;
if (index == list.length - 1) {
if (_transactions2.length == 1) {
radius = BorderRadius.circular(
Constants.size.circularBorderRadius,
);
} else if (index == _transactions2.length - 1) {
radius = _borderRadiusLast;
} else if (index == 0) {
radius = _borderRadiusFirst;
}
final tx = list[index];
final tx = _transactions2[index];
return itemBuilder(context, tx, radius);
},
),

View file

@ -1,17 +1,26 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
class TxIcon extends StatelessWidget {
const TxIcon({Key? key, required this.transaction}) : super(key: key);
const TxIcon({
Key? key,
required this.transaction,
required this.currentHeight,
required this.coin,
}) : super(key: key);
final Transaction transaction;
final int currentHeight;
final Coin coin;
static const Size size = Size(32, 32);
String _getAssetName(
bool isCancelled, bool isReceived, bool isPending, BuildContext context) {
if (!isReceived && transaction.subType == "mint") {
if (!isReceived && transaction.subType == TransactionSubType.mint) {
if (isCancelled) {
return Assets.svg.anonymizeFailed;
}
@ -42,7 +51,7 @@ class TxIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
final txIsReceived = transaction.txType == "Received";
final txIsReceived = transaction.type == TransactionType.incoming;
return SizedBox(
width: size.width,
@ -52,7 +61,10 @@ class TxIcon extends StatelessWidget {
_getAssetName(
transaction.isCancelled,
txIsReceived,
!transaction.confirmedStatus,
!transaction.isConfirmed(
currentHeight,
coin.requiredConfirmations,
),
context,
),
width: size.width,

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