BIN
assets/images/glasses-hidden.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
assets/images/glasses.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
11
assets/svg/configuration.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="48" height="48" rx="24" fill="#E0E3E3"/>
|
||||
<g clip-path="url(#clip0_5813_29086)">
|
||||
<path d="M13.5 30.5625C13.5 29.8365 14.0878 29.25 14.8125 29.25H17.0544C17.5605 28.0893 18.7172 27.2812 20.0625 27.2812C21.4078 27.2812 22.5275 28.0893 23.0689 29.25H33.1875C33.9135 29.25 34.5 29.8365 34.5 30.5625C34.5 31.2885 33.9135 31.875 33.1875 31.875H23.0689C22.5275 33.0357 21.4078 33.8438 20.0625 33.8438C18.7172 33.8438 17.5605 33.0357 17.0544 31.875H14.8125C14.0878 31.875 13.5 31.2885 13.5 30.5625ZM21.375 30.5625C21.375 29.8365 20.7885 29.25 20.0625 29.25C19.3365 29.25 18.75 29.8365 18.75 30.5625C18.75 31.2885 19.3365 31.875 20.0625 31.875C20.7885 31.875 21.375 31.2885 21.375 30.5625ZM27.9375 20.7188C29.2828 20.7188 30.4025 21.5268 30.9439 22.6875H33.1875C33.9135 22.6875 34.5 23.274 34.5 24C34.5 24.726 33.9135 25.3125 33.1875 25.3125H30.9439C30.4025 26.4732 29.2828 27.2812 27.9375 27.2812C26.5922 27.2812 25.4355 26.4732 24.9311 25.3125H14.8125C14.0878 25.3125 13.5 24.726 13.5 24C13.5 23.274 14.0878 22.6875 14.8125 22.6875H24.9311C25.4355 21.5268 26.5922 20.7188 27.9375 20.7188ZM29.25 24C29.25 23.274 28.6635 22.6875 27.9375 22.6875C27.2115 22.6875 26.625 23.274 26.625 24C26.625 24.726 27.2115 25.3125 27.9375 25.3125C28.6635 25.3125 29.25 24.726 29.25 24ZM33.1875 16.125C33.9135 16.125 34.5 16.7128 34.5 17.4375C34.5 18.1635 33.9135 18.75 33.1875 18.75H24.3814C23.84 19.9107 22.7203 20.7188 21.375 20.7188C20.0297 20.7188 18.873 19.9107 18.3686 18.75H14.8125C14.0878 18.75 13.5 18.1635 13.5 17.4375C13.5 16.7128 14.0878 16.125 14.8125 16.125H18.3686C18.873 14.9663 20.0297 14.1562 21.375 14.1562C22.7203 14.1562 23.84 14.9663 24.3814 16.125H33.1875ZM20.0625 17.4375C20.0625 18.1635 20.649 18.75 21.375 18.75C22.101 18.75 22.6875 18.1635 22.6875 17.4375C22.6875 16.7128 22.101 16.125 21.375 16.125C20.649 16.125 20.0625 16.7128 20.0625 17.4375Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5813_29086">
|
||||
<rect width="21" height="21" fill="white" transform="translate(13.5 13.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
3
assets/svg/lock-open.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 1.75C5.79141 1.75 4.8125 2.72945 4.8125 3.9375V5.25H11.375C12.3402 5.25 13.125 6.03477 13.125 7V12.25C13.125 13.2152 12.3402 14 11.375 14H2.625C1.6584 14 0.875 13.2152 0.875 12.25V7C0.875 6.03477 1.6584 5.25 2.625 5.25H3.0625V3.9375C3.0625 1.76285 4.82617 0 7 0C8.57227 0 9.92578 0.921211 10.5574 2.24957C10.7652 2.68598 10.5793 3.20742 10.1199 3.41523C9.68242 3.62305 9.18476 3.43711 8.97695 2.99961C8.62422 2.25941 7.87227 1.75 7 1.75ZM7.875 10.5C8.35898 10.5 8.75 10.109 8.75 9.625C8.75 9.14102 8.35898 8.75 7.875 8.75H6.125C5.64102 8.75 5.25 9.14102 5.25 9.625C5.25 10.109 5.64102 10.5 6.125 10.5H7.875Z" fill="#0056D2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 741 B |
28
assets/svg/ocean-breeze-theme.svg
Normal file
|
@ -0,0 +1,28 @@
|
|||
<svg width="200" height="162" viewBox="0 0 200 162" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_518_22068)">
|
||||
<rect width="200" height="162" rx="8" fill="url(#paint0_linear_518_22068)"/>
|
||||
<rect x="10" y="10" width="180" height="20" rx="2" fill="#C2DAE2"/>
|
||||
<rect x="16" y="16" width="106" height="8" rx="1" fill="#227386"/>
|
||||
<rect x="10" y="40" width="180" height="20" rx="2" fill="#FEFEFE"/>
|
||||
<rect x="16" y="46" width="106" height="8" rx="1" fill="#BDD5DB"/>
|
||||
<rect x="10" y="62" width="180" height="20" rx="2" fill="#FEFEFE"/>
|
||||
<rect x="16" y="68" width="106" height="8" rx="1" fill="#BDD5DB"/>
|
||||
<rect x="10" y="84" width="180" height="20" rx="2" fill="#FEFEFE"/>
|
||||
<rect x="16" y="90" width="106" height="8" rx="1" fill="#BDD5DB"/>
|
||||
<rect x="10" y="106" width="180" height="20" rx="2" fill="#FEFEFE"/>
|
||||
<rect x="16" y="112" width="106" height="8" rx="1" fill="#BDD5DB"/>
|
||||
<rect x="10" y="128" width="180" height="20" rx="2" fill="#FEFEFE"/>
|
||||
<rect x="16" y="134" width="106" height="8" rx="1" fill="#BDD5DB"/>
|
||||
<rect x="10" y="150" width="180" height="20" rx="2" fill="#FEFEFE"/>
|
||||
<rect x="16" y="156" width="106" height="8" rx="1" fill="#BDD5DB"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_518_22068" x1="100" y1="0" x2="100" y2="162" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F3F7FA"/>
|
||||
<stop offset="1" stop-color="#E8F2F9"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_518_22068">
|
||||
<rect width="200" height="162" rx="8" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
5
assets/svg/oceanBreeze/bell-new.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5 17.5C12.5 17.9193 12.2383 18.3672 11.7695 18.6797C11.3008 18.9922 10.6289 19.1667 10 19.1667C9.33594 19.1667 8.69922 18.9922 8.23047 18.6797C7.76172 18.3672 7.5 17.9193 7.5 17.5H12.5Z" fill="#227386"/>
|
||||
<path d="M11.1903 1.98716V2.67947C13.9059 3.2142 15.9519 5.54245 15.9519 8.33331V9.0112C15.9519 10.7095 16.5955 12.3429 17.7561 13.6122L18.0314 13.9114C18.3439 14.254 18.422 14.7372 18.2286 15.1518C18.0351 15.5665 17.611 15.8333 17.1423 15.8333H2.85739C2.38867 15.8333 1.96351 15.5665 1.77148 15.1518C1.57945 14.7372 1.65626 14.254 1.96771 13.9114L2.24359 13.6122C3.40573 12.3429 4.0478 10.7095 4.0478 9.0112V8.33331C4.0478 5.54245 6.06034 3.2142 8.80945 2.67947V1.98716C8.80945 1.35002 9.34141 0.833313 9.99986 0.833313C10.6583 0.833313 11.1903 1.35002 11.1903 1.98716Z" fill="#227386"/>
|
||||
<ellipse cx="17.0833" cy="2.91665" rx="2.08333" ry="2.08333" fill="#D34E50"/>
|
||||
</svg>
|
After Width: | Height: | Size: 987 B |
11
assets/svg/oceanBreeze/bg.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<svg width="360" height="480" viewBox="0 0 360 480" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M48 174.503C45.4098 171.437 39.6642 169.267 34.578 176.625C29.4917 183.983 22.3192 180.197 19.7431 176.625C16.682 172.38 8.86422 166.438 2.08257 176.625C-0.507645 179.926 -7.2422 184.549 -13.4587 176.625C-19.6752 168.702 -26.4098 171.909 -29 174.503" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M48 188.503C45.4098 185.437 39.6642 183.267 34.578 190.625C29.4917 197.983 22.3192 194.197 19.7431 190.625C16.682 186.38 8.86422 180.438 2.08257 190.625C-0.507645 193.926 -7.2422 198.549 -13.4587 190.625C-19.6752 182.702 -26.4098 185.909 -29 188.503" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M48 202.503C45.4098 199.437 39.6642 197.267 34.578 204.625C29.4917 211.983 22.3192 208.197 19.7431 204.625C16.682 200.38 8.86422 194.438 2.08257 204.625C-0.507645 207.926 -7.2422 212.549 -13.4587 204.625C-19.6752 196.702 -26.4098 199.909 -29 202.503" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M389 444.503C386.41 441.437 380.664 439.267 375.578 446.625C370.492 453.983 363.319 450.197 360.743 446.625C357.682 442.38 349.864 436.438 343.083 446.625C340.492 449.926 333.758 454.549 327.541 446.625C321.325 438.702 314.59 441.909 312 444.503" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M389 458.503C386.41 455.437 380.664 453.267 375.578 460.625C370.492 467.983 363.319 464.197 360.743 460.625C357.682 456.38 349.864 450.438 343.083 460.625C340.492 463.926 333.758 468.549 327.541 460.625C321.325 452.702 314.59 455.909 312 458.503" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M389 472.503C386.41 469.437 380.664 467.267 375.578 474.625C370.492 481.983 363.319 478.197 360.743 474.625C357.682 470.38 349.864 464.438 343.083 474.625C340.492 477.926 333.758 482.549 327.541 474.625C321.325 466.702 314.59 469.909 312 472.503" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M389 4.5028C386.41 1.4371 380.664 -0.732664 375.578 6.62502C370.492 13.9827 363.319 10.1971 360.743 6.62502C357.682 2.38025 349.864 -3.56244 343.083 6.62502C340.492 9.92648 333.758 14.5485 327.541 6.62502C321.325 -1.29849 314.59 1.90875 312 4.5028" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M389 18.5028C386.41 15.4371 380.664 13.2673 375.578 20.625C370.492 27.9827 363.319 24.1971 360.743 20.625C357.682 16.3802 349.864 10.4376 343.083 20.625C340.492 23.9265 333.758 28.5485 327.541 20.625C321.325 12.7015 314.59 15.9087 312 18.5028" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M389 32.5028C386.41 29.4371 380.664 27.2673 375.578 34.625C370.492 41.9827 363.319 38.1971 360.743 34.625C357.682 30.3802 349.864 24.4376 343.083 34.625C340.492 37.9265 333.758 42.5485 327.541 34.625C321.325 26.7015 314.59 29.9087 312 32.5028" stroke="#D3EAF3" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
18
assets/svg/oceanBreeze/buy-coins-icon.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_519_18707)">
|
||||
<g opacity="0.4">
|
||||
<path d="M22.2 6C23.3297 5.37187 24 4.59422 24 3.75C24 1.67906 19.9688 0 15 0C9.98906 0 6 1.67906 6 3.75C6 4.59422 6.67031 5.37187 7.8 6C7.80937 6.00469 7.81758 6.00937 7.82578 6.01406C7.83398 6.01875 7.84219 6.02344 7.85156 6.02813C8.23125 6.00938 8.61094 6 9 6C11.6344 6 14.0906 6.44062 15.9422 7.21406C16.1203 7.28906 16.2984 7.36875 16.4672 7.44844C18.8062 7.28906 20.8359 6.75469 22.2 6Z" fill="#227386"/>
|
||||
<path d="M19.9435 12.9151C19.7958 12.9551 19.6477 12.9951 19.5 13.0359V13.5C20.7602 13.5 21.9296 13.8885 22.8951 14.5522C23.5995 14.0172 24 13.4028 24 12.75V11.0906C23.4141 11.5734 22.7063 11.9672 21.9422 12.2859C21.3382 12.5376 20.6447 12.7253 19.9435 12.9151Z" fill="#227386"/>
|
||||
<path d="M18.3703 8.74688C19.0031 9.37969 19.5 10.2234 19.5 11.25V11.4984C20.4328 11.2734 21.2625 10.9781 21.9469 10.6359C21.9739 10.6209 22.0009 10.6021 22.0279 10.5833C22.0852 10.5432 22.1426 10.5032 22.2 10.5C23.3297 9.87187 24 9.09375 24 8.25V6.59063C23.4141 7.07344 22.7063 7.46719 21.9422 7.78594C20.9109 8.2125 19.6969 8.54063 18.3703 8.74688Z" fill="#227386"/>
|
||||
</g>
|
||||
<path d="M16.2 13.5C17.3297 12.8719 18 12.0938 18 11.25C18 9.17813 13.9688 7.5 9 7.5C4.02938 7.5 0 9.17813 0 11.25C0 12.0938 0.669375 12.8719 1.79953 13.5C1.85443 13.5031 1.91057 13.5415 1.96782 13.5807C1.9966 13.6004 2.02567 13.6203 2.055 13.6359C3.70594 14.4703 6.20625 15 9 15C11.9438 15 14.5594 14.4094 16.2 13.5Z" fill="#227386"/>
|
||||
<path d="M14.8788 15.6729C13.1948 16.2046 11.1571 16.5 9 16.5C6.36562 16.5 3.91125 16.0594 2.05922 15.2859C1.29469 14.9672 0.583594 14.5734 0 14.0906V15.75C0 16.5938 0.669375 17.3719 1.79953 18C3.44109 18.9094 6.05625 19.5 9 19.5C10.6471 19.5 12.1916 19.3159 13.5211 18.9937C13.6261 17.7367 14.1186 16.5898 14.8788 15.6729Z" fill="#227386"/>
|
||||
<path d="M13.5862 20.5191C13.7529 21.4936 14.1547 22.3879 14.731 23.1415C13.1742 23.6778 11.1771 24 9 24C4.02938 24 0 22.3219 0 20.25V18.5906C0.583594 19.0734 1.29469 19.4672 2.05922 19.7859C3.91125 20.5594 6.36562 21 9 21C10.6307 21 12.1932 20.8312 13.5862 20.5191Z" fill="#227386"/>
|
||||
<path d="M24 19.5C24 21.9844 21.9844 24 19.5 24C17.0156 24 15 21.9844 15 19.5C15 17.0156 17.0156 15 19.5 15C21.9844 15 24 17.0156 24 19.5ZM19 17.4719V18.9719H17.5C17.225 18.9719 17 19.225 17 19.4719C17 19.775 17.225 19.9719 17.5 19.9719H19V21.4719C19 21.775 19.225 21.9719 19.5 21.9719C19.775 21.9719 20 21.775 20 21.4719V19.9719H21.5C21.775 19.9719 22 19.775 22 19.4719C22 19.225 21.775 18.9719 21.5 18.9719H20V17.4719C20 17.225 19.775 16.9719 19.5 16.9719C19.225 16.9719 19 17.225 19 17.4719Z" fill="#227386"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_519_18707">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
4
assets/svg/oceanBreeze/exchange-2.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.5 6.5L20.4343 7.33045C20.8552 6.85685 20.8552 6.14315 20.4343 5.66955L19.5 6.5ZM16.4343 1.16955C15.9756 0.653567 15.1855 0.607091 14.6695 1.06574C14.1536 1.52439 14.1071 2.31448 14.5657 2.83045L16.4343 1.16955ZM14.5657 10.1695C14.1071 10.6855 14.1536 11.4756 14.6695 11.9343C15.1855 12.3929 15.9756 12.3464 16.4343 11.8305L14.5657 10.1695ZM0.75 10.5C0.75 11.1904 1.30964 11.75 2 11.75C2.69036 11.75 3.25 11.1904 3.25 10.5H0.75ZM6 7.75H19.5V5.25H6V7.75ZM14.5657 2.83045L18.5657 7.33045L20.4343 5.66955L16.4343 1.16955L14.5657 2.83045ZM16.4343 11.8305L20.4343 7.33045L18.5657 5.66955L14.5657 10.1695L16.4343 11.8305ZM3.25 10.5C3.25 8.98122 4.48122 7.75 6 7.75V5.25C3.10051 5.25 0.75 7.60051 0.75 10.5H3.25Z" fill="#227386"/>
|
||||
<path opacity="0.4" d="M4.5 18L3.56574 17.1695C3.14475 17.6432 3.14475 18.3568 3.56574 18.8305L4.5 18ZM7.56574 23.3305C8.02439 23.8464 8.81448 23.8929 9.33045 23.4343C9.84643 22.9756 9.89291 22.1855 9.43426 21.6695L7.56574 23.3305ZM9.43426 14.3305C9.89291 13.8145 9.84643 13.0244 9.33046 12.5657C8.81448 12.1071 8.02439 12.1536 7.56574 12.6695L9.43426 14.3305ZM23.25 14C23.25 13.3096 22.6904 12.75 22 12.75C21.3096 12.75 20.75 13.3096 20.75 14L23.25 14ZM18 16.75L4.5 16.75L4.5 19.25L18 19.25L18 16.75ZM9.43426 21.6695L5.43426 17.1695L3.56574 18.8305L7.56574 23.3305L9.43426 21.6695ZM7.56574 12.6695L3.56574 17.1695L5.43426 18.8305L9.43426 14.3305L7.56574 12.6695ZM20.75 14C20.75 15.5188 19.5188 16.75 18 16.75L18 19.25C20.8995 19.25 23.25 16.8995 23.25 14L20.75 14Z" fill="#227386"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
5
assets/svg/oceanBreeze/stack-icon1.svg
Normal 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 |
7
assets/svg/oceanBreeze/tx-exchange-icon-failed.svg
Normal 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 |
6
assets/svg/oceanBreeze/tx-exchange-icon-pending.svg
Normal 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 |
4
assets/svg/oceanBreeze/tx-exchange-icon.svg
Normal 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 |
7
assets/svg/oceanBreeze/tx-icon-receive-failed.svg
Normal 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 |
5
assets/svg/oceanBreeze/tx-icon-receive-pending.svg
Normal 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 |
4
assets/svg/oceanBreeze/tx-icon-receive.svg
Normal 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 |
7
assets/svg/oceanBreeze/tx-icon-send-failed.svg
Normal 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 |
6
assets/svg/oceanBreeze/tx-icon-send-pending.svg
Normal 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 |
4
assets/svg/oceanBreeze/tx-icon-send.svg
Normal 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 |
12
assets/svg/plus-circle.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_5370_82626)">
|
||||
<path d="M9.99935 18.3337C14.6017 18.3337 18.3327 14.6027 18.3327 10.0003C18.3327 5.39795 14.6017 1.66699 9.99935 1.66699C5.39698 1.66699 1.66602 5.39795 1.66602 10.0003C1.66602 14.6027 5.39698 18.3337 9.99935 18.3337Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 6.66699V13.3337" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.66602 10H13.3327" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5370_82626">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 780 B |
|
@ -1 +1 @@
|
|||
Subproject commit 2da77438527732dfaa5398aa391eab5253dabe19
|
||||
Subproject commit de29931dacc9aefaf42a9ca139a8754a42adc40d
|
|
@ -9,7 +9,6 @@ import 'package:stackwallet/models/node_model.dart';
|
|||
import 'package:stackwallet/models/notification_model.dart';
|
||||
import 'package:stackwallet/models/trade_wallet_lookup.dart';
|
||||
import 'package:stackwallet/services/wallets_service.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
|
||||
|
@ -33,6 +32,7 @@ class DB {
|
|||
static const String boxNamePriceCache = "priceAPIPrice24hCache";
|
||||
static const String boxNameDBInfo = "dbInfo";
|
||||
static const String boxNameTheme = "theme";
|
||||
static const String boxNameDesktopData = "desktopData";
|
||||
|
||||
String boxNameTxCache({required Coin coin}) => "${coin.name}_txCache";
|
||||
String boxNameSetCache({required Coin coin}) =>
|
||||
|
@ -42,22 +42,23 @@ class DB {
|
|||
|
||||
static bool _initialized = false;
|
||||
|
||||
late final Box<dynamic> _boxAddressBook;
|
||||
late final Box<String> _boxDebugInfo;
|
||||
late final Box<NodeModel> _boxNodeModels;
|
||||
late final Box<NodeModel> _boxPrimaryNodes;
|
||||
late final Box<dynamic> _boxAllWalletsData;
|
||||
late final Box<NotificationModel> _boxNotifications;
|
||||
late final Box<NotificationModel> _boxWatchedTransactions;
|
||||
late final Box<NotificationModel> _boxWatchedTrades;
|
||||
late final Box<ExchangeTransaction> _boxTrades;
|
||||
late final Box<Trade> _boxTradesV2;
|
||||
late final Box<String> _boxTradeNotes;
|
||||
late final Box<String> _boxFavoriteWallets;
|
||||
late final Box<xmr.WalletInfo> _walletInfoSource;
|
||||
late final Box<dynamic> _boxPrefs;
|
||||
late final Box<TradeWalletLookup> _boxTradeLookup;
|
||||
late final Box<dynamic> _boxDBInfo;
|
||||
Box<dynamic>? _boxAddressBook;
|
||||
Box<String>? _boxDebugInfo;
|
||||
Box<NodeModel>? _boxNodeModels;
|
||||
Box<NodeModel>? _boxPrimaryNodes;
|
||||
Box<dynamic>? _boxAllWalletsData;
|
||||
Box<NotificationModel>? _boxNotifications;
|
||||
Box<NotificationModel>? _boxWatchedTransactions;
|
||||
Box<NotificationModel>? _boxWatchedTrades;
|
||||
Box<ExchangeTransaction>? _boxTrades;
|
||||
Box<Trade>? _boxTradesV2;
|
||||
Box<String>? _boxTradeNotes;
|
||||
Box<String>? _boxFavoriteWallets;
|
||||
Box<xmr.WalletInfo>? _walletInfoSource;
|
||||
Box<dynamic>? _boxPrefs;
|
||||
Box<TradeWalletLookup>? _boxTradeLookup;
|
||||
Box<dynamic>? _boxDBInfo;
|
||||
Box<String>? _boxDesktopData;
|
||||
|
||||
final Map<String, Box<dynamic>> _walletBoxes = {};
|
||||
|
||||
|
@ -66,7 +67,7 @@ class DB {
|
|||
final Map<Coin, Box<dynamic>> _usedSerialsCacheBoxes = {};
|
||||
|
||||
// exposed for monero
|
||||
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource;
|
||||
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource!;
|
||||
|
||||
// mutex for stack backup
|
||||
final mutex = Mutex();
|
||||
|
@ -122,6 +123,12 @@ class DB {
|
|||
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNameDesktopData)) {
|
||||
_boxDesktopData = Hive.box<String>(boxNameDesktopData);
|
||||
} else {
|
||||
_boxDesktopData = await Hive.openBox<String>(boxNameDesktopData);
|
||||
}
|
||||
|
||||
_boxNotifications =
|
||||
await Hive.openBox<NotificationModel>(boxNameNotifications);
|
||||
_boxWatchedTransactions =
|
||||
|
@ -143,22 +150,11 @@ class DB {
|
|||
_loadSharedCoinCacheBoxes(),
|
||||
]);
|
||||
_initialized = true;
|
||||
|
||||
try {
|
||||
if (_boxPrefs.get("familiarity") == null) {
|
||||
await _boxPrefs.put("familiarity", 0);
|
||||
}
|
||||
int count = _boxPrefs.get("familiarity") as int;
|
||||
await _boxPrefs.put("familiarity", count + 1);
|
||||
Constants.exchangeForExperiencedUsers(count + 1);
|
||||
} catch (e, s) {
|
||||
print("$e $s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadWalletBoxes() async {
|
||||
final names = _boxAllWalletsData.get("names") as Map? ?? {};
|
||||
final names = _boxAllWalletsData!.get("names") as Map? ?? {};
|
||||
names.removeWhere((name, dyn) {
|
||||
final jsonObject = Map<String, dynamic>.from(dyn as Map);
|
||||
try {
|
||||
|
|
158
lib/main.dart
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
|
@ -10,6 +11,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_libmonero/monero/monero.dart';
|
||||
import 'package:flutter_libmonero/wownero/wownero.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
@ -47,15 +49,16 @@ import 'package:stackwallet/services/node_service.dart';
|
|||
import 'package:stackwallet/services/notifications_api.dart';
|
||||
import 'package:stackwallet/services/notifications_service.dart';
|
||||
import 'package:stackwallet/services/trade_service.dart';
|
||||
import 'package:stackwallet/services/wallets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/db_version_migration.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||
import 'package:stackwallet/utilities/theme/color_theme.dart';
|
||||
import 'package:stackwallet/utilities/theme/dark_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/ocean_breeze_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:window_size/window_size.dart';
|
||||
|
@ -73,25 +76,32 @@ void main() async {
|
|||
Util.libraryPath = await getLibraryDirectory();
|
||||
}
|
||||
|
||||
if (Util.isDesktop) {
|
||||
setWindowTitle('Stack Wallet');
|
||||
setWindowMinSize(const Size(1200, 900));
|
||||
setWindowMaxSize(Size.infinite);
|
||||
Screen? screen;
|
||||
if (Platform.isLinux || Util.isDesktop) {
|
||||
screen = await getCurrentScreen();
|
||||
Util.screenWidth = screen?.frame.width;
|
||||
}
|
||||
|
||||
Directory appDirectory = (await getApplicationDocumentsDirectory());
|
||||
if (Platform.isIOS) {
|
||||
appDirectory = (await getLibraryDirectory());
|
||||
if (Util.isDesktop) {
|
||||
setWindowTitle('Stack Wallet');
|
||||
setWindowMinSize(const Size(1220, 100));
|
||||
setWindowMaxSize(Size.infinite);
|
||||
|
||||
final screenHeight = screen?.frame.height;
|
||||
if (screenHeight != null) {
|
||||
// starting to height be 3/4 screen height or 900, whichever is smaller
|
||||
final height = min<double>(screenHeight * 0.75, 900);
|
||||
setWindowFrame(
|
||||
Rect.fromLTWH(0, 0, 1220, height),
|
||||
);
|
||||
}
|
||||
if (Platform.isLinux || Logging.isArmLinux) {
|
||||
appDirectory = Directory("${appDirectory.path}/.stackwallet");
|
||||
await appDirectory.create();
|
||||
}
|
||||
|
||||
// FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
if (!(Logging.isArmLinux || Logging.isTestEnv)) {
|
||||
final isar = await Isar.open(
|
||||
[LogSchema],
|
||||
directory: appDirectory.path,
|
||||
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
||||
inspector: false,
|
||||
);
|
||||
await Logging.instance.init(isar);
|
||||
|
@ -140,20 +150,31 @@ void main() async {
|
|||
Hive.registerAdapter(WalletTypeAdapter());
|
||||
|
||||
Hive.registerAdapter(UnspentCoinsInfoAdapter());
|
||||
await Hive.initFlutter(appDirectory.path);
|
||||
await Hive.initFlutter(
|
||||
(await StackFileSystem.applicationHiveDirectory()).path);
|
||||
|
||||
await Hive.openBox<dynamic>(DB.boxNameDBInfo);
|
||||
|
||||
// todo: db migrate stuff for desktop needs to be handled eventually
|
||||
if (!Util.isDesktop) {
|
||||
int dbVersion = DB.instance.get<dynamic>(
|
||||
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
|
||||
0;
|
||||
if (dbVersion < Constants.currentHiveDbVersion) {
|
||||
try {
|
||||
await DbVersionMigrator().migrate(dbVersion);
|
||||
await DbVersionMigrator().migrate(
|
||||
dbVersion,
|
||||
secureStore: const SecureStorageWrapper(
|
||||
store: FlutterSecureStorage(),
|
||||
isDesktop: false,
|
||||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("Cannot migrate database\n$e $s",
|
||||
level: LogLevel.Error, printFullLength: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monero.onStartup();
|
||||
wownero.onStartup();
|
||||
|
@ -199,8 +220,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
static const platform = MethodChannel("STACK_WALLET_RESTORE");
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
late final Wallets _wallets;
|
||||
late final Prefs _prefs;
|
||||
// late final Wallets _wallets;
|
||||
// late final Prefs _prefs;
|
||||
late final NotificationsService _notificationsService;
|
||||
late final NodeService _nodeService;
|
||||
late final TradesService _tradesService;
|
||||
|
@ -208,8 +229,29 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
late final Completer<void> loadingCompleter;
|
||||
|
||||
bool didLoad = false;
|
||||
bool didLoadShared = false;
|
||||
bool _desktopHasPassword = false;
|
||||
|
||||
Future<void> loadShared() async {
|
||||
if (didLoadShared) {
|
||||
return;
|
||||
}
|
||||
didLoadShared = true;
|
||||
|
||||
await DB.instance.init();
|
||||
await ref.read(prefsChangeNotifierProvider).init();
|
||||
|
||||
final familiarity = ref.read(prefsChangeNotifierProvider).familiarity + 1;
|
||||
ref.read(prefsChangeNotifierProvider).familiarity = familiarity;
|
||||
|
||||
Constants.exchangeForExperiencedUsers(familiarity);
|
||||
|
||||
if (Util.isDesktop) {
|
||||
_desktopHasPassword =
|
||||
await ref.read(storageCryptoHandlerProvider).hasPassword();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
try {
|
||||
if (didLoad) {
|
||||
|
@ -217,19 +259,15 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
}
|
||||
didLoad = true;
|
||||
|
||||
await DB.instance.init();
|
||||
await _prefs.init();
|
||||
|
||||
if (Util.isDesktop) {
|
||||
_desktopHasPassword =
|
||||
await ref.read(storageCryptoHandlerProvider).hasPassword();
|
||||
if (!Util.isDesktop) {
|
||||
await loadShared();
|
||||
}
|
||||
|
||||
_notificationsService = ref.read(notificationsProvider);
|
||||
_nodeService = ref.read(nodeServiceChangeNotifierProvider);
|
||||
_tradesService = ref.read(tradesServiceProvider);
|
||||
|
||||
NotificationApi.prefs = _prefs;
|
||||
NotificationApi.prefs = ref.read(prefsChangeNotifierProvider);
|
||||
NotificationApi.notificationsService = _notificationsService;
|
||||
|
||||
unawaited(ref.read(baseCurrenciesProvider).update());
|
||||
|
@ -238,23 +276,25 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
await _notificationsService.init(
|
||||
nodeService: _nodeService,
|
||||
tradesService: _tradesService,
|
||||
prefs: _prefs,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
);
|
||||
ref.read(priceAnd24hChangeNotifierProvider).start(true);
|
||||
await _wallets.load(_prefs);
|
||||
await ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.load(ref.read(prefsChangeNotifierProvider));
|
||||
loadingCompleter.complete();
|
||||
// TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet
|
||||
// unawaited(_nodeService.updateCommunityNodes());
|
||||
|
||||
// run without awaiting
|
||||
if (Constants.enableExchange &&
|
||||
_prefs.externalCalls &&
|
||||
await _prefs.isExternalCallsSet()) {
|
||||
ref.read(prefsChangeNotifierProvider).externalCalls &&
|
||||
await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) {
|
||||
unawaited(ExchangeDataLoadingService().loadAll(ref));
|
||||
}
|
||||
|
||||
if (_prefs.isAutoBackupEnabled) {
|
||||
switch (_prefs.backupFrequencyType) {
|
||||
if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled) {
|
||||
switch (ref.read(prefsChangeNotifierProvider).backupFrequencyType) {
|
||||
case BackupFrequencyType.everyTenMinutes:
|
||||
ref.read(autoSWBServiceProvider).startPeriodicBackupTimer(
|
||||
duration: const Duration(minutes: 10));
|
||||
|
@ -278,14 +318,17 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
final colorScheme = DB.instance
|
||||
.get<dynamic>(boxName: DB.boxNameTheme, key: "colorScheme") as String?;
|
||||
|
||||
ThemeType themeType;
|
||||
StackColorTheme colorTheme;
|
||||
switch (colorScheme) {
|
||||
case "dark":
|
||||
themeType = ThemeType.dark;
|
||||
colorTheme = DarkColors();
|
||||
break;
|
||||
case "oceanBreeze":
|
||||
colorTheme = OceanBreezeColors();
|
||||
break;
|
||||
case "light":
|
||||
default:
|
||||
themeType = ThemeType.light;
|
||||
colorTheme = LightColors();
|
||||
}
|
||||
loadingCompleter = Completer();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
@ -294,13 +337,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
.read(localeServiceChangeNotifierProvider.notifier)
|
||||
.loadLocale(notify: false);
|
||||
|
||||
_prefs = ref.read(prefsChangeNotifierProvider);
|
||||
_wallets = ref.read(walletsChangeNotifierProvider);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
themeType == ThemeType.dark ? DarkColors() : LightColors());
|
||||
StackColors.fromStackColorTheme(colorTheme);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
// fetch open file if it exists
|
||||
|
@ -401,7 +440,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
}
|
||||
|
||||
Future<void> goToRestoreSWB(String encrypted) async {
|
||||
if (!_prefs.hasPin) {
|
||||
if (!ref.read(prefsChangeNotifierProvider).hasPin) {
|
||||
await Navigator.of(navigatorKey.currentContext!)
|
||||
.pushNamed(CreatePinView.routeName, arguments: true)
|
||||
.then((value) {
|
||||
|
@ -547,26 +586,45 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
_buildOutlineInputBorder(colorScheme.textFieldDefaultBG),
|
||||
),
|
||||
),
|
||||
home: FutureBuilder(
|
||||
future: load(),
|
||||
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
||||
home: Util.isDesktop
|
||||
? FutureBuilder(
|
||||
future: loadShared(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
// FlutterNativeSplash.remove();
|
||||
if (Util.isDesktop &&
|
||||
(_wallets.hasWallets || _desktopHasPassword)) {
|
||||
if (_desktopHasPassword) {
|
||||
String? startupWalletId;
|
||||
if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) {
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.gotoWalletOnStartup) {
|
||||
startupWalletId =
|
||||
ref.read(prefsChangeNotifierProvider).startupWalletId;
|
||||
}
|
||||
|
||||
return DesktopLoginView(startupWalletId: startupWalletId);
|
||||
} else if (!Util.isDesktop &&
|
||||
(_wallets.hasWallets || _prefs.hasPin)) {
|
||||
return DesktopLoginView(
|
||||
startupWalletId: startupWalletId,
|
||||
load: load,
|
||||
);
|
||||
} else {
|
||||
return const IntroView();
|
||||
}
|
||||
} else {
|
||||
return const LoadingView();
|
||||
}
|
||||
},
|
||||
)
|
||||
: FutureBuilder(
|
||||
future: load(),
|
||||
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
// FlutterNativeSplash.remove();
|
||||
if (ref.read(walletsChangeNotifierProvider).hasWallets ||
|
||||
ref.read(prefsChangeNotifierProvider).hasPin) {
|
||||
// return HomeView();
|
||||
|
||||
String? startupWalletId;
|
||||
if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) {
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.gotoWalletOnStartup) {
|
||||
startupWalletId =
|
||||
ref.read(prefsChangeNotifierProvider).startupWalletId;
|
||||
}
|
||||
|
|
18
lib/models/isar/models/encrypted_string_value.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
import 'package:isar/isar.dart';
|
||||
|
||||
part 'encrypted_string_value.g.dart';
|
||||
|
||||
@Collection()
|
||||
class EncryptedStringValue {
|
||||
Id id = Isar.autoIncrement;
|
||||
|
||||
@Index(unique: true, replace: true)
|
||||
late String key;
|
||||
|
||||
late String value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "EncryptedStringValue {\n key=$key\n value=$value\n}";
|
||||
}
|
||||
}
|
748
lib/models/isar/models/encrypted_string_value.g.dart
Normal file
|
@ -0,0 +1,748 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'encrypted_string_value.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// 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
|
||||
|
||||
extension GetEncryptedStringValueCollection on Isar {
|
||||
IsarCollection<EncryptedStringValue> get encryptedStringValues =>
|
||||
this.collection();
|
||||
}
|
||||
|
||||
const EncryptedStringValueSchema = CollectionSchema(
|
||||
name: r'EncryptedStringValue',
|
||||
id: 4826543019451092626,
|
||||
properties: {
|
||||
r'key': PropertySchema(
|
||||
id: 0,
|
||||
name: r'key',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'value': PropertySchema(
|
||||
id: 1,
|
||||
name: r'value',
|
||||
type: IsarType.string,
|
||||
)
|
||||
},
|
||||
estimateSize: _encryptedStringValueEstimateSize,
|
||||
serializeNative: _encryptedStringValueSerializeNative,
|
||||
deserializeNative: _encryptedStringValueDeserializeNative,
|
||||
deserializePropNative: _encryptedStringValueDeserializePropNative,
|
||||
serializeWeb: _encryptedStringValueSerializeWeb,
|
||||
deserializeWeb: _encryptedStringValueDeserializeWeb,
|
||||
deserializePropWeb: _encryptedStringValueDeserializePropWeb,
|
||||
idName: r'id',
|
||||
indexes: {
|
||||
r'key': IndexSchema(
|
||||
id: -4906094122524121629,
|
||||
name: r'key',
|
||||
unique: true,
|
||||
replace: true,
|
||||
properties: [
|
||||
IndexPropertySchema(
|
||||
name: r'key',
|
||||
type: IndexType.hash,
|
||||
caseSensitive: true,
|
||||
)
|
||||
],
|
||||
)
|
||||
},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
getId: _encryptedStringValueGetId,
|
||||
getLinks: _encryptedStringValueGetLinks,
|
||||
attach: _encryptedStringValueAttach,
|
||||
version: 5,
|
||||
);
|
||||
|
||||
int _encryptedStringValueEstimateSize(
|
||||
EncryptedStringValue object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
bytesCount += 3 + object.key.length * 3;
|
||||
bytesCount += 3 + object.value.length * 3;
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
int _encryptedStringValueSerializeNative(
|
||||
EncryptedStringValue object,
|
||||
IsarBinaryWriter 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,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = EncryptedStringValue();
|
||||
object.id = id;
|
||||
object.key = reader.readString(offsets[0]);
|
||||
object.value = reader.readString(offsets[1]);
|
||||
return object;
|
||||
}
|
||||
|
||||
P _encryptedStringValueDeserializePropNative<P>(
|
||||
Id id,
|
||||
IsarBinaryReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readString(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _encryptedStringValueGetLinks(
|
||||
EncryptedStringValue object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _encryptedStringValueAttach(
|
||||
IsarCollection<dynamic> col, Id id, EncryptedStringValue object) {
|
||||
object.id = id;
|
||||
}
|
||||
|
||||
extension EncryptedStringValueByIndex on IsarCollection<EncryptedStringValue> {
|
||||
Future<EncryptedStringValue?> getByKey(String key) {
|
||||
return getByIndex(r'key', [key]);
|
||||
}
|
||||
|
||||
EncryptedStringValue? getByKeySync(String key) {
|
||||
return getByIndexSync(r'key', [key]);
|
||||
}
|
||||
|
||||
Future<bool> deleteByKey(String key) {
|
||||
return deleteByIndex(r'key', [key]);
|
||||
}
|
||||
|
||||
bool deleteByKeySync(String key) {
|
||||
return deleteByIndexSync(r'key', [key]);
|
||||
}
|
||||
|
||||
Future<List<EncryptedStringValue?>> getAllByKey(List<String> keyValues) {
|
||||
final values = keyValues.map((e) => [e]).toList();
|
||||
return getAllByIndex(r'key', values);
|
||||
}
|
||||
|
||||
List<EncryptedStringValue?> getAllByKeySync(List<String> keyValues) {
|
||||
final values = keyValues.map((e) => [e]).toList();
|
||||
return getAllByIndexSync(r'key', values);
|
||||
}
|
||||
|
||||
Future<int> deleteAllByKey(List<String> keyValues) {
|
||||
final values = keyValues.map((e) => [e]).toList();
|
||||
return deleteAllByIndex(r'key', values);
|
||||
}
|
||||
|
||||
int deleteAllByKeySync(List<String> keyValues) {
|
||||
final values = keyValues.map((e) => [e]).toList();
|
||||
return deleteAllByIndexSync(r'key', values);
|
||||
}
|
||||
|
||||
Future<int> putByKey(EncryptedStringValue object) {
|
||||
return putByIndex(r'key', object);
|
||||
}
|
||||
|
||||
int putByKeySync(EncryptedStringValue object, {bool saveLinks = true}) {
|
||||
return putByIndexSync(r'key', object, saveLinks: saveLinks);
|
||||
}
|
||||
|
||||
Future<List<int>> putAllByKey(List<EncryptedStringValue> objects) {
|
||||
return putAllByIndex(r'key', objects);
|
||||
}
|
||||
|
||||
List<int> putAllByKeySync(List<EncryptedStringValue> objects,
|
||||
{bool saveLinks = true}) {
|
||||
return putAllByIndexSync(r'key', objects, saveLinks: saveLinks);
|
||||
}
|
||||
}
|
||||
|
||||
extension EncryptedStringValueQueryWhereSort
|
||||
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QWhere> {
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhere>
|
||||
anyId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension EncryptedStringValueQueryWhere
|
||||
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QWhereClause> {
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
|
||||
idEqualTo(int id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(
|
||||
lower: id,
|
||||
upper: id,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
|
||||
idNotEqualTo(int id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
|
||||
idGreaterThan(int id, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
|
||||
idLessThan(int id, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
|
||||
idBetween(
|
||||
int lowerId,
|
||||
int upperId, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(
|
||||
lower: lowerId,
|
||||
includeLower: includeLower,
|
||||
upper: upperId,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
|
||||
keyEqualTo(String key) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.equalTo(
|
||||
indexName: r'key',
|
||||
value: [key],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
|
||||
keyNotEqualTo(String key) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'key',
|
||||
lower: [],
|
||||
upper: [key],
|
||||
includeUpper: false,
|
||||
))
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'key',
|
||||
lower: [key],
|
||||
includeLower: false,
|
||||
upper: [],
|
||||
));
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'key',
|
||||
lower: [key],
|
||||
includeLower: false,
|
||||
upper: [],
|
||||
))
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'key',
|
||||
lower: [],
|
||||
upper: [key],
|
||||
includeUpper: false,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
|
||||
EncryptedStringValue, QFilterCondition> {
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> idEqualTo(int value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'id',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> idGreaterThan(
|
||||
int value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> idLessThan(
|
||||
int value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> idBetween(
|
||||
int lower,
|
||||
int upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'id',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> keyEqualTo(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'key',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> keyGreaterThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'key',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> keyLessThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'key',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> keyBetween(
|
||||
String lower,
|
||||
String upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'key',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> keyStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'key',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> keyEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'key',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition>
|
||||
keyContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'key',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition>
|
||||
keyMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'key',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> valueEqualTo(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'value',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> valueGreaterThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'value',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> valueLessThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'value',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> valueBetween(
|
||||
String lower,
|
||||
String upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'value',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> valueStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'value',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition> valueEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'value',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition>
|
||||
valueContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'value',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
|
||||
QAfterFilterCondition>
|
||||
valueMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'value',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension EncryptedStringValueQueryObject on QueryBuilder<EncryptedStringValue,
|
||||
EncryptedStringValue, QFilterCondition> {}
|
||||
|
||||
extension EncryptedStringValueQueryLinks on QueryBuilder<EncryptedStringValue,
|
||||
EncryptedStringValue, QFilterCondition> {}
|
||||
|
||||
extension EncryptedStringValueQuerySortBy
|
||||
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QSortBy> {
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
sortByKey() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'key', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
sortByKeyDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'key', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
sortByValue() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'value', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
sortByValueDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'value', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension EncryptedStringValueQuerySortThenBy
|
||||
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QSortThenBy> {
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
thenById() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
thenByIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
thenByKey() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'key', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
thenByKeyDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'key', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
thenByValue() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'value', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
|
||||
thenByValueDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'value', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension EncryptedStringValueQueryWhereDistinct
|
||||
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QDistinct> {
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QDistinct>
|
||||
distinctByKey({bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'key', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QDistinct>
|
||||
distinctByValue({bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'value', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension EncryptedStringValueQueryProperty on QueryBuilder<
|
||||
EncryptedStringValue, EncryptedStringValue, QQueryProperty> {
|
||||
QueryBuilder<EncryptedStringValue, int, QQueryOperations> idProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'id');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, String, QQueryOperations> keyProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'key');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<EncryptedStringValue, String, QQueryOperations> valueProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'value');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
|
||||
part 'type_adaptors/node_model.g.dart';
|
||||
|
@ -65,8 +66,7 @@ class NodeModel {
|
|||
}
|
||||
|
||||
/// convenience getter to retrieve login password
|
||||
Future<String?> getPassword(
|
||||
FlutterSecureStorageInterface secureStorage) async {
|
||||
Future<String?> getPassword(SecureStorageInterface secureStorage) async {
|
||||
return await secureStorage.read(key: "${id}_nodePW");
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ class NodeModel {
|
|||
return map;
|
||||
}
|
||||
|
||||
bool get isDefault => id.startsWith("default_");
|
||||
bool get isDefault => id.startsWith(DefaultNodes.defaultNodeIdPrefix);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:dart_numerics/dart_numerics.dart';
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
||||
part '../type_adaptors/transactions_model.g.dart';
|
||||
|
||||
|
@ -220,14 +221,16 @@ class Transaction {
|
|||
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
txType: json['txType'] as String,
|
||||
amount: (Decimal.parse(json["amount"].toString()) *
|
||||
Decimal.fromInt(Constants.satsPerCoin))
|
||||
Decimal.fromInt(Constants.satsPerCoin(Coin
|
||||
.firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure
|
||||
.toBigInt()
|
||||
.toInt(),
|
||||
aliens: [],
|
||||
worthNow: json['worthNow'] as String,
|
||||
worthAtBlockTimestamp: json['worthAtBlockTimestamp'] as String? ?? "0",
|
||||
fees: (Decimal.parse(json["fees"].toString()) *
|
||||
Decimal.fromInt(Constants.satsPerCoin))
|
||||
Decimal.fromInt(Constants.satsPerCoin(Coin
|
||||
.firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure
|
||||
.toBigInt()
|
||||
.toInt(),
|
||||
inputSize: json['inputSize'] as int? ?? 0,
|
||||
|
@ -386,7 +389,8 @@ class Output {
|
|||
scriptpubkeyType: json['scriptPubKey']['type'] as String?,
|
||||
scriptpubkeyAddress: address,
|
||||
value: (Decimal.parse(json["value"].toString()) *
|
||||
Decimal.fromInt(Constants.satsPerCoin))
|
||||
Decimal.fromInt(Constants.satsPerCoin(Coin
|
||||
.firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure
|
||||
.toBigInt()
|
||||
.toInt(),
|
||||
);
|
||||
|
|
|
@ -4,6 +4,8 @@ import 'package:stackwallet/models/notification_model.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/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
|
@ -20,22 +22,33 @@ class NotificationCard extends StatelessWidget {
|
|||
return Format.extractDateFrom(date.millisecondsSinceEpoch ~/ 1000);
|
||||
}
|
||||
|
||||
static const double mobileIconSize = 24;
|
||||
static const double desktopIconSize = 30;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 10,
|
||||
)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
notification.changeNowId == null
|
||||
? SvgPicture.asset(
|
||||
notification.iconAssetName,
|
||||
width: 24,
|
||||
height: 24,
|
||||
width: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
height: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
)
|
||||
: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
width: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
height: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
|
@ -45,8 +58,8 @@ class NotificationCard extends StatelessWidget {
|
|||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
width: 24,
|
||||
height: 24,
|
||||
width: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
height: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -56,9 +69,35 @@ class NotificationCard extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ConditionalParent(
|
||||
condition: isDesktop && !notification.read,
|
||||
builder: (child) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
child,
|
||||
Text(
|
||||
"New",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
notification.title,
|
||||
style: STextStyles.titleBold12(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.titleBold12(context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
|
@ -68,11 +107,25 @@ class NotificationCard extends StatelessWidget {
|
|||
children: [
|
||||
Text(
|
||||
notification.description,
|
||||
style: STextStyles.label(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
)
|
||||
: STextStyles.label(context),
|
||||
),
|
||||
Text(
|
||||
extractPrettyDateString(notification.date),
|
||||
style: STextStyles.label(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
)
|
||||
: STextStyles.label(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -6,6 +6,8 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
export 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
|
||||
Future<dynamic> showFloatingFlushBar({
|
||||
required FlushBarType type,
|
||||
required String message,
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.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';
|
||||
|
@ -182,7 +183,10 @@ class _AddWalletViewState extends State<AddWalletView> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
|
@ -218,6 +222,7 @@ class _AddWalletViewState extends State<AddWalletView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,11 @@ class SearchableCoinList extends ConsumerWidget {
|
|||
// remove firo testnet regardless
|
||||
_coins.remove(Coin.firoTestNet);
|
||||
|
||||
// Kidgloves for Wownero on desktop
|
||||
if(isDesktop) {
|
||||
_coins.remove(Coin.wownero);
|
||||
}
|
||||
|
||||
return _coins;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.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';
|
||||
|
@ -77,7 +78,10 @@ class CreateOrRestoreWalletView extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
|
@ -123,6 +127,7 @@ class CreateOrRestoreWalletView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:stackwallet/utilities/name_generator.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/background.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';
|
||||
|
@ -108,7 +109,10 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
|
@ -143,6 +147,7 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,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/constants.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/utilities/util.dart';
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||
import 'package:stackwallet/services/coins/manager.dart';
|
||||
|
@ -241,6 +242,7 @@ class _NewWalletRecoveryPhraseWarningViewState
|
|||
coin,
|
||||
walletId,
|
||||
walletName,
|
||||
ref.read(secureStoreProvider),
|
||||
node,
|
||||
txTracker,
|
||||
ref.read(prefsChangeNotifierProvider),
|
||||
|
|
|
@ -23,6 +23,8 @@ import 'package:stackwallet/utilities/util.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/rounded_date_picker/flutter_rounded_date_picker_widget.dart'
|
||||
as datePicker;
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
|
@ -152,7 +154,7 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
await Future<void>.delayed(const Duration(milliseconds: 125));
|
||||
}
|
||||
|
||||
final date = await showRoundedDatePicker(
|
||||
final date = await datePicker.showRoundedDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
height: height * 0.5,
|
||||
|
|
|
@ -19,6 +19,7 @@ import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/sub_widge
|
|||
import 'package:stackwallet/pages/home_view/home_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||
import 'package:stackwallet/services/coins/manager.dart';
|
||||
|
@ -273,6 +274,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
widget.coin,
|
||||
walletId,
|
||||
widget.walletName,
|
||||
ref.read(secureStoreProvider),
|
||||
node,
|
||||
txTracker,
|
||||
ref.read(prefsChangeNotifierProvider),
|
||||
|
|
|
@ -13,22 +13,27 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/address_book_card.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class AddressBookView extends ConsumerStatefulWidget {
|
||||
const AddressBookView({Key? key, this.coin}) : super(key: key);
|
||||
const AddressBookView({
|
||||
Key? key,
|
||||
this.coin,
|
||||
this.filterTerm,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/addressBook";
|
||||
|
||||
final Coin? coin;
|
||||
final String? filterTerm;
|
||||
|
||||
@override
|
||||
ConsumerState<AddressBookView> createState() => _AddressBookViewState();
|
||||
|
@ -39,9 +44,6 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
|
||||
final _searchFocusNode = FocusNode();
|
||||
|
||||
List<Contact>? _cache;
|
||||
List<Contact>? _cacheFav;
|
||||
|
||||
String _searchTerm = "";
|
||||
|
||||
@override
|
||||
|
@ -50,8 +52,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
ref.refresh(addressBookFilterProvider);
|
||||
|
||||
if (widget.coin == null) {
|
||||
List<Coin> coins =
|
||||
Coin.values.where((e) => !(e == Coin.epicCash)).toList();
|
||||
List<Coin> coins = Coin.values.toList();
|
||||
coins.remove(Coin.firoTestNet);
|
||||
|
||||
bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins;
|
||||
|
@ -59,8 +60,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
if (showTestNet) {
|
||||
ref.read(addressBookFilterProvider).addAll(coins, false);
|
||||
} else {
|
||||
ref.read(addressBookFilterProvider).addAll(
|
||||
coins.getRange(0, coins.length - kTestNetCoinCount + 1), false);
|
||||
ref
|
||||
.read(addressBookFilterProvider)
|
||||
.addAll(coins.getRange(0, coins.length - kTestNetCoinCount), false);
|
||||
}
|
||||
} else {
|
||||
ref.read(addressBookFilterProvider).add(widget.coin!, false);
|
||||
|
@ -100,11 +102,17 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
final addressBookEntriesFuture = ref.watch(
|
||||
addressBookServiceProvider.select((value) => value.addressBookEntries));
|
||||
final contacts =
|
||||
ref.watch(addressBookServiceProvider.select((value) => value.contacts));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
|
@ -128,7 +136,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
key: const Key("addressBookFilterViewButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.filter,
|
||||
color: Theme.of(context)
|
||||
|
@ -157,7 +167,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
key: const Key("addressBookAddNewContactViewButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
|
@ -192,6 +204,23 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight:
|
||||
MediaQuery.of(context).size.height - 271,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
|
@ -199,7 +228,8 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
child: !isDesktop
|
||||
? TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: _searchController,
|
||||
|
@ -237,6 +267,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
_searchTerm = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
|
@ -246,11 +277,10 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
)
|
||||
: null,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
if (!isDesktop) const SizedBox(height: 16),
|
||||
Text(
|
||||
"Favorites",
|
||||
style: STextStyles.smallMed12(context),
|
||||
|
@ -258,61 +288,39 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: addressBookEntriesFuture,
|
||||
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_cacheFav = snapshot.data!;
|
||||
}
|
||||
if (_cacheFav == null) {
|
||||
// TODO proper loading animation
|
||||
return const LoadingIndicator();
|
||||
} else {
|
||||
if (_cacheFav!.isNotEmpty) {
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
if (contacts.isNotEmpty)
|
||||
RoundedWhiteContainer(
|
||||
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
|
||||
child: Column(
|
||||
children: [
|
||||
..._cacheFav!
|
||||
...contacts
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(
|
||||
addressBookFilterProvider
|
||||
.select((value) => value
|
||||
.coins
|
||||
.contains(e.coin))))
|
||||
.where((e) => ref.watch(addressBookFilterProvider
|
||||
.select((value) => value.coins.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((e) =>
|
||||
e.isFavorite &&
|
||||
ref
|
||||
.read(
|
||||
addressBookServiceProvider)
|
||||
.matches(_searchTerm, e))
|
||||
.where(
|
||||
(element) => element.isFavorite)
|
||||
.read(addressBookServiceProvider)
|
||||
.matches(widget.filterTerm ?? _searchTerm, e))
|
||||
.where((element) => element.isFavorite)
|
||||
.map(
|
||||
(e) => AddressBookCard(
|
||||
key: Key(
|
||||
"favContactCard_${e.id}_key"),
|
||||
key: Key("favContactCard_${e.id}_key"),
|
||||
contactId: e.id,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return RoundedWhiteContainer(
|
||||
),
|
||||
if (contacts.isEmpty)
|
||||
RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Your favorite contacts will appear here",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
|
@ -324,68 +332,47 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: addressBookEntriesFuture,
|
||||
builder: (_, AsyncSnapshot<List<Contact>> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_cache = snapshot.data!;
|
||||
}
|
||||
if (_cache == null) {
|
||||
// TODO proper loading animation
|
||||
return const LoadingIndicator();
|
||||
} else {
|
||||
if (_cache!.isNotEmpty) {
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
if (contacts.isNotEmpty)
|
||||
Column(
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
padding: EdgeInsets.all(!isDesktop ? 0 : 15),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
..._cache!
|
||||
...contacts
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(
|
||||
addressBookFilterProvider
|
||||
.select((value) => value
|
||||
.coins
|
||||
.contains(e.coin))))
|
||||
addressBookFilterProvider.select((value) =>
|
||||
value.coins.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((e) => ref
|
||||
.read(addressBookServiceProvider)
|
||||
.matches(_searchTerm, e))
|
||||
.where(
|
||||
(element) => !element.isFavorite)
|
||||
.matches(widget.filterTerm ?? _searchTerm, e))
|
||||
.map(
|
||||
(e) => AddressBookCard(
|
||||
key: Key(
|
||||
"contactCard_${e.id}_key"),
|
||||
key: Key("desktopContactCard_${e.id}_key"),
|
||||
contactId: e.id,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return RoundedWhiteContainer(
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (contacts.isEmpty)
|
||||
RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Your contacts will appear here",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,15 +15,20 @@ import 'package:stackwallet/utilities/clipboard_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/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/emoji_select_sheet.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class AddAddressBookEntryView extends ConsumerStatefulWidget {
|
||||
const AddAddressBookEntryView({
|
||||
Key? key,
|
||||
|
@ -108,14 +113,21 @@ class _AddAddressBookEntryViewState
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 75));
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -139,7 +151,9 @@ class _AddAddressBookEntryViewState
|
|||
key: const Key("addAddressBookEntryFavoriteButtonKey"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.star,
|
||||
color: _isFavorite
|
||||
|
@ -162,29 +176,243 @@ class _AddAddressBookEntryViewState
|
|||
),
|
||||
],
|
||||
),
|
||||
body: LayoutBuilder(
|
||||
body: child),
|
||||
);
|
||||
},
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"New contact",
|
||||
style: STextStyles.desktopH3(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10,
|
||||
right: 10,
|
||||
bottom: 32,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraint) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
padding: const EdgeInsets.only(
|
||||
padding: EdgeInsets.only(
|
||||
// top: 8,
|
||||
left: 4,
|
||||
right: 4,
|
||||
bottom: 16,
|
||||
bottom: isDesktop ? 0 : 16,
|
||||
),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
// subtract top and bottom padding set in parent
|
||||
minHeight: constraint.maxHeight - 16, // - 8,
|
||||
minHeight:
|
||||
constraint.maxHeight - (isDesktop ? 0 : 16), // - 8,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
if (!isDesktop) const SizedBox(height: 4),
|
||||
isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 56,
|
||||
width: 56,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedEmoji != null) {
|
||||
setState(() {
|
||||
_selectedEmoji = null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const DesktopDialog(
|
||||
maxHeight: 700,
|
||||
maxWidth: 600,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 20,
|
||||
top: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: EmojiSelectSheet(),
|
||||
),
|
||||
);
|
||||
}).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(100),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveBG,
|
||||
),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.user,
|
||||
height: 30,
|
||||
width: 30,
|
||||
)
|
||||
: Text(
|
||||
_selectedEmoji!.char,
|
||||
style: STextStyles
|
||||
.pageTitleH1(
|
||||
context),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
14),
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.accentColorDark),
|
||||
child: Center(
|
||||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(
|
||||
context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 12,
|
||||
height: 12,
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
Assets.svg.thickX,
|
||||
color: Theme.of(
|
||||
context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 8,
|
||||
height: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: isDesktop ? 450 : null,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect:
|
||||
Util.isDesktop ? false : true,
|
||||
enableSuggestions:
|
||||
Util.isDesktop ? false : true,
|
||||
controller: nameController,
|
||||
focusNode: nameFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Enter contact name",
|
||||
nameFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle:
|
||||
STextStyles.fieldLabel(context),
|
||||
suffixIcon: ref
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state
|
||||
? Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
nameController
|
||||
.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
ref
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state = newValue.isNotEmpty;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedEmoji != null) {
|
||||
|
@ -193,6 +421,7 @@ class _AddAddressBookEntryViewState
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
showModalBottomSheet<dynamic>(
|
||||
backgroundColor: Colors.transparent,
|
||||
context: context,
|
||||
|
@ -201,7 +430,8 @@ class _AddAddressBookEntryViewState
|
|||
top: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
builder: (_) => const EmojiSelectSheet(),
|
||||
builder: (_) =>
|
||||
const EmojiSelectSheet(),
|
||||
).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
|
@ -219,7 +449,8 @@ class _AddAddressBookEntryViewState
|
|||
height: 48,
|
||||
width: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
borderRadius:
|
||||
BorderRadius.circular(24),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveBG,
|
||||
|
@ -233,8 +464,8 @@ class _AddAddressBookEntryViewState
|
|||
)
|
||||
: Text(
|
||||
_selectedEmoji!.char,
|
||||
style:
|
||||
STextStyles.pageTitleH1(context),
|
||||
style: STextStyles
|
||||
.pageTitleH1(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -244,7 +475,8 @@ class _AddAddressBookEntryViewState
|
|||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
borderRadius:
|
||||
BorderRadius.circular(14),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
|
@ -253,7 +485,8 @@ class _AddAddressBookEntryViewState
|
|||
? SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 12,
|
||||
height: 12,
|
||||
|
@ -261,7 +494,8 @@ class _AddAddressBookEntryViewState
|
|||
: SvgPicture.asset(
|
||||
Assets.svg.thickX,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
width: 8,
|
||||
height: 8,
|
||||
|
@ -273,16 +507,16 @@ class _AddAddressBookEntryViewState
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
autocorrect:
|
||||
Util.isDesktop ? false : true,
|
||||
enableSuggestions:
|
||||
Util.isDesktop ? false : true,
|
||||
controller: nameController,
|
||||
focusNode: nameFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
|
@ -292,11 +526,13 @@ class _AddAddressBookEntryViewState
|
|||
context,
|
||||
).copyWith(
|
||||
suffixIcon: ref
|
||||
.read(contactNameIsNotEmptyStateProvider
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
padding: const EdgeInsets.only(
|
||||
right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -304,7 +540,8 @@ class _AddAddressBookEntryViewState
|
|||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
nameController.text = "";
|
||||
nameController
|
||||
.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
|
@ -316,11 +553,16 @@ class _AddAddressBookEntryViewState
|
|||
),
|
||||
onChanged: (newValue) {
|
||||
ref
|
||||
.read(contactNameIsNotEmptyStateProvider.state)
|
||||
.read(
|
||||
contactNameIsNotEmptyStateProvider
|
||||
.state)
|
||||
.state = newValue.isNotEmpty;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (forms.length <= 1)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
|
@ -384,22 +626,16 @@ class _AddAddressBookEntryViewState
|
|||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
buttonHeight: isDesktop ? ButtonHeight.m : null,
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
if (!isDesktop &&
|
||||
FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
const Duration(milliseconds: 75),
|
||||
);
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -426,21 +662,19 @@ class _AddAddressBookEntryViewState
|
|||
bool shouldEnableSave =
|
||||
validForms && nameExists;
|
||||
|
||||
return TextButton(
|
||||
style: shouldEnableSave
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context)
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryDisabledButtonColor(
|
||||
context),
|
||||
return PrimaryButton(
|
||||
label: "Save",
|
||||
buttonHeight:
|
||||
isDesktop ? ButtonHeight.m : null,
|
||||
enabled: shouldEnableSave,
|
||||
onPressed: shouldEnableSave
|
||||
? () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
if (FocusScope.of(context)
|
||||
.hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75),
|
||||
const Duration(
|
||||
milliseconds: 75),
|
||||
);
|
||||
}
|
||||
List<ContactAddressEntry> entries =
|
||||
|
@ -449,7 +683,8 @@ class _AddAddressBookEntryViewState
|
|||
i < forms.length;
|
||||
i++) {
|
||||
entries.add(ref
|
||||
.read(addressEntryDataProvider(
|
||||
.read(
|
||||
addressEntryDataProvider(
|
||||
forms[i].id))
|
||||
.buildAddressEntry());
|
||||
}
|
||||
|
@ -461,7 +696,8 @@ class _AddAddressBookEntryViewState
|
|||
);
|
||||
|
||||
if (await ref
|
||||
.read(addressBookServiceProvider)
|
||||
.read(
|
||||
addressBookServiceProvider)
|
||||
.addContact(contact)) {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -472,18 +708,6 @@ class _AddAddressBookEntryViewState
|
|||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
"Save",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: shouldEnableSave
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextPrimary
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextPrimaryDisabled,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -498,6 +722,7 @@ class _AddAddressBookEntryViewState
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
|||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
|
||||
class AddNewContactAddressView extends ConsumerStatefulWidget {
|
||||
const AddNewContactAddressView({
|
||||
|
@ -55,8 +60,14 @@ class _AddNewContactAddressViewState
|
|||
final contact = ref.watch(addressBookServiceProvider
|
||||
.select((value) => value.getContactById(contactId)));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
|
@ -90,6 +101,16 @@ class _AddNewContactAddressViewState
|
|||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
|
@ -119,6 +140,12 @@ class _AddNewContactAddressViewState
|
|||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
contact.name,
|
||||
style: STextStyles.pageTitleH2(context),
|
||||
),
|
||||
if (!isDesktop)
|
||||
Expanded(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
|
@ -145,19 +172,11 @@ class _AddNewContactAddressViewState
|
|||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
if (!isDesktop && FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
|
@ -172,45 +191,28 @@ class _AddNewContactAddressViewState
|
|||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
bool shouldEnableSave =
|
||||
ref.watch(validContactStateProvider([0]));
|
||||
|
||||
return TextButton(
|
||||
style: shouldEnableSave
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(
|
||||
context)
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryDisabledButtonColor(
|
||||
context),
|
||||
onPressed: shouldEnableSave
|
||||
? () async {
|
||||
if (FocusScope.of(context)
|
||||
.hasFocus) {
|
||||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
enabled: ref.watch(validContactStateProvider([0])),
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(
|
||||
milliseconds: 75),
|
||||
const Duration(milliseconds: 75),
|
||||
);
|
||||
}
|
||||
List<ContactAddressEntry> entries =
|
||||
contact.addresses;
|
||||
List<ContactAddressEntry> entries = contact.addresses;
|
||||
|
||||
entries.add(ref
|
||||
.read(
|
||||
addressEntryDataProvider(0))
|
||||
.read(addressEntryDataProvider(0))
|
||||
.buildAddressEntry());
|
||||
|
||||
Contact editedContact = contact
|
||||
.copyWith(addresses: entries);
|
||||
Contact editedContact =
|
||||
contact.copyWith(addresses: entries);
|
||||
|
||||
if (await ref
|
||||
.read(
|
||||
addressBookServiceProvider)
|
||||
.read(addressBookServiceProvider)
|
||||
.editContact(editedContact)) {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -219,13 +221,6 @@ class _AddNewContactAddressViewState
|
|||
} else {
|
||||
// TODO show error notification
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
"Save",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -233,13 +228,6 @@ class _AddNewContactAddressViewState
|
|||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,13 @@ import 'package:stackwallet/providers/ui/address_book_providers/address_book_fil
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.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_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class AddressBookFilterView extends ConsumerStatefulWidget {
|
||||
|
@ -33,7 +39,7 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
} else {
|
||||
_coins = coins
|
||||
.toList(growable: false)
|
||||
.getRange(0, coins.length - kTestNetCoinCount + 1)
|
||||
.getRange(0, coins.length - kTestNetCoinCount)
|
||||
.toList(growable: false);
|
||||
}
|
||||
super.initState();
|
||||
|
@ -41,10 +47,17 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -85,7 +98,94 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
child,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Text(
|
||||
"Select cryptocurrency",
|
||||
style: STextStyles.desktopH3(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: child,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 32, vertical: 32),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SecondaryButton(
|
||||
width: 248,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
enabled: true,
|
||||
label: "Cancel",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
// const SizedBox(width: 16),
|
||||
PrimaryButton(
|
||||
width: 248,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
enabled: true,
|
||||
label: "Apply",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: Wrap(
|
||||
children: [
|
||||
|
@ -102,9 +202,7 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
.read(addressBookFilterProvider)
|
||||
.remove(coin, true);
|
||||
} else {
|
||||
ref
|
||||
.read(addressBookFilterProvider)
|
||||
.add(coin, true);
|
||||
ref.read(addressBookFilterProvider).add(coin, true);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
|
@ -113,30 +211,25 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: Checkbox(
|
||||
value: ref
|
||||
.watch(
|
||||
addressBookFilterProvider
|
||||
.select((value) =>
|
||||
value.coins))
|
||||
.watch(addressBookFilterProvider
|
||||
.select((value) => value.coins))
|
||||
.contains(coin),
|
||||
onChanged: (value) {
|
||||
if (value is bool) {
|
||||
if (value) {
|
||||
ref
|
||||
.read(
|
||||
addressBookFilterProvider)
|
||||
.read(addressBookFilterProvider)
|
||||
.add(coin, true);
|
||||
} else {
|
||||
ref
|
||||
.read(
|
||||
addressBookFilterProvider)
|
||||
.read(addressBookFilterProvider)
|
||||
.remove(coin, true);
|
||||
}
|
||||
setState(() {});
|
||||
|
@ -148,23 +241,18 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
width: 12,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
coin.prettyName,
|
||||
style:
|
||||
STextStyles.largeMedium14(
|
||||
context),
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
coin.ticker,
|
||||
style:
|
||||
STextStyles.itemSubtitle(
|
||||
context),
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
@ -179,29 +267,6 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// Row(
|
||||
// children: [
|
||||
// TextButton(
|
||||
// onPressed: () {},
|
||||
// child: Text("Cancel"),
|
||||
// ),
|
||||
// SizedBox(
|
||||
// width: 16,
|
||||
// ),
|
||||
// TextButton(
|
||||
// onPressed: () {},
|
||||
// child: Text("Cancel"),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ 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';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
|
@ -104,7 +105,8 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
|||
final _contact = ref.watch(addressBookServiceProvider
|
||||
.select((value) => value.getContactById(_contactId)));
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -145,9 +147,8 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
|||
onPressed: () {
|
||||
bool isFavorite = _contact.isFavorite;
|
||||
|
||||
ref
|
||||
.read(addressBookServiceProvider)
|
||||
.editContact(_contact.copyWith(isFavorite: !isFavorite));
|
||||
ref.read(addressBookServiceProvider).editContact(
|
||||
_contact.copyWith(isFavorite: !isFavorite));
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -350,7 +351,8 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
|||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${e.label} (${e.coin.ticker})",
|
||||
|
@ -364,7 +366,8 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
|||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
e.address,
|
||||
style: STextStyles.itemSubtitle(context)
|
||||
style:
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
fontSize: 8,
|
||||
),
|
||||
|
@ -469,7 +472,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
|||
..._cachedTransactions.map(
|
||||
(e) => TransactionCard(
|
||||
key: Key(
|
||||
"contactDetailsTransaction_${e.item2.txid}_cardKey"),
|
||||
"contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"),
|
||||
transaction: e.item2,
|
||||
walletId: e.item1,
|
||||
),
|
||||
|
@ -499,7 +502,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
|||
..._cachedTransactions.map(
|
||||
(e) => TransactionCard(
|
||||
key: Key(
|
||||
"contactDetailsTransaction_${e.item2.txid}_cardKey"),
|
||||
"contactDetailsTransaction_${e.item1}_${e.item2.txid}_cardKey"),
|
||||
transaction: e.item2,
|
||||
walletId: e.item1,
|
||||
),
|
||||
|
@ -519,6 +522,7 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
|||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
|
||||
class EditContactAddressView extends ConsumerStatefulWidget {
|
||||
const EditContactAddressView({
|
||||
|
@ -44,6 +49,42 @@ class _EditContactAddressViewState
|
|||
late final BarcodeScannerInterface barcodeScanner;
|
||||
late final ClipboardInterface clipboard;
|
||||
|
||||
Future<void> save(Contact contact) async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75),
|
||||
);
|
||||
}
|
||||
List<ContactAddressEntry> entries = contact.addresses.toList();
|
||||
|
||||
final entry = entries.firstWhere(
|
||||
(e) =>
|
||||
e.label == addressEntry.label &&
|
||||
e.address == addressEntry.address &&
|
||||
e.coin == addressEntry.coin,
|
||||
);
|
||||
|
||||
final index = entries.indexOf(entry);
|
||||
entries.remove(entry);
|
||||
|
||||
ContactAddressEntry editedEntry =
|
||||
ref.read(addressEntryDataProvider(0)).buildAddressEntry();
|
||||
|
||||
entries.insert(index, editedEntry);
|
||||
|
||||
Contact editedContact = contact.copyWith(addresses: entries);
|
||||
|
||||
if (await ref.read(addressBookServiceProvider).editContact(editedContact)) {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
// TODO show success notification
|
||||
} else {
|
||||
// TODO show error notification
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
contactId = widget.contactId;
|
||||
|
@ -59,8 +100,14 @@ class _EditContactAddressViewState
|
|||
final contact = ref.watch(addressBookServiceProvider
|
||||
.select((value) => value.getContactById(contactId)));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final bool isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
|
@ -94,6 +141,16 @@ class _EditContactAddressViewState
|
|||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
|
@ -123,6 +180,12 @@ class _EditContactAddressViewState
|
|||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
contact.name,
|
||||
style: STextStyles.pageTitleH2(context),
|
||||
),
|
||||
if (!isDesktop)
|
||||
Expanded(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
|
@ -145,7 +208,13 @@ class _EditContactAddressViewState
|
|||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
GestureDetector(
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
// delete address
|
||||
final _addresses = contact.addresses;
|
||||
|
@ -157,8 +226,7 @@ class _EditContactAddressViewState
|
|||
);
|
||||
|
||||
_addresses.remove(entry);
|
||||
Contact editedContact =
|
||||
contact.copyWith(addresses: _addresses);
|
||||
Contact editedContact = contact.copyWith(addresses: _addresses);
|
||||
if (await ref
|
||||
.read(addressBookServiceProvider)
|
||||
.editContact(editedContact)) {
|
||||
|
@ -173,6 +241,7 @@ class _EditContactAddressViewState
|
|||
style: STextStyles.link(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
|
@ -180,19 +249,11 @@ class _EditContactAddressViewState
|
|||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
if (!isDesktop && FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
|
@ -207,89 +268,17 @@ class _EditContactAddressViewState
|
|||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
bool shouldEnableSave =
|
||||
ref.watch(validContactStateProvider([0]));
|
||||
|
||||
return TextButton(
|
||||
style: shouldEnableSave
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(
|
||||
context)
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryDisabledButtonColor(
|
||||
context),
|
||||
onPressed: shouldEnableSave
|
||||
? () async {
|
||||
if (FocusScope.of(context)
|
||||
.hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(
|
||||
milliseconds: 75),
|
||||
);
|
||||
}
|
||||
List<ContactAddressEntry> entries =
|
||||
contact.addresses.toList();
|
||||
|
||||
final entry = entries.firstWhere(
|
||||
(e) =>
|
||||
e.label ==
|
||||
addressEntry.label &&
|
||||
e.address ==
|
||||
addressEntry.address &&
|
||||
e.coin == addressEntry.coin,
|
||||
);
|
||||
|
||||
final index =
|
||||
entries.indexOf(entry);
|
||||
entries.remove(entry);
|
||||
|
||||
ContactAddressEntry editedEntry = ref
|
||||
.read(
|
||||
addressEntryDataProvider(0))
|
||||
.buildAddressEntry();
|
||||
|
||||
entries.insert(index, editedEntry);
|
||||
|
||||
Contact editedContact = contact
|
||||
.copyWith(addresses: entries);
|
||||
|
||||
if (await ref
|
||||
.read(
|
||||
addressBookServiceProvider)
|
||||
.editContact(editedContact)) {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
// TODO show success notification
|
||||
} else {
|
||||
// TODO show error notification
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
"Save",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
enabled: ref.watch(validContactStateProvider([0])),
|
||||
onPressed: () => save(contact),
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:emojis/emoji.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
@ -7,14 +9,18 @@ import 'package:stackwallet/utilities/assets.dart';
|
|||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/emoji_select_sheet.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class EditContactNameEmojiView extends ConsumerStatefulWidget {
|
||||
const EditContactNameEmojiView({
|
||||
Key? key,
|
||||
|
@ -69,8 +75,15 @@ class _EditContactNameEmojiViewState
|
|||
final contact = ref.watch(addressBookServiceProvider
|
||||
.select((value) => value.getContactById(contactId)));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
final double emojiSize = isDesktop ? 56 : 48;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
|
@ -104,7 +117,20 @@ class _EditContactNameEmojiViewState
|
|||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
|
@ -114,6 +140,32 @@ class _EditContactNameEmojiViewState
|
|||
});
|
||||
return;
|
||||
}
|
||||
if (isDesktop) {
|
||||
showDialog<dynamic>(
|
||||
barrierColor: Colors.transparent,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const DesktopDialog(
|
||||
maxHeight: 700,
|
||||
maxWidth: 600,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 20,
|
||||
top: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: EmojiSelectSheet(),
|
||||
),
|
||||
);
|
||||
}).then((value) {
|
||||
if (value is Emoji) {
|
||||
setState(() {
|
||||
_selectedEmoji = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showModalBottomSheet<dynamic>(
|
||||
backgroundColor: Colors.transparent,
|
||||
context: context,
|
||||
|
@ -130,17 +182,18 @@ class _EditContactNameEmojiViewState
|
|||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
width: 48,
|
||||
height: emojiSize,
|
||||
width: emojiSize,
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 48,
|
||||
width: 48,
|
||||
height: emojiSize,
|
||||
width: emojiSize,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
borderRadius: BorderRadius.circular(emojiSize / 2),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveBG,
|
||||
|
@ -149,13 +202,14 @@ class _EditContactNameEmojiViewState
|
|||
child: _selectedEmoji == null
|
||||
? SvgPicture.asset(
|
||||
Assets.svg.user,
|
||||
height: 24,
|
||||
width: 24,
|
||||
height: emojiSize / 2,
|
||||
width: emojiSize / 2,
|
||||
)
|
||||
: Text(
|
||||
_selectedEmoji!.char,
|
||||
style: STextStyles.pageTitleH1(
|
||||
context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopH3(context)
|
||||
: STextStyles.pageTitleH1(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -194,9 +248,58 @@ class _EditContactNameEmojiViewState
|
|||
),
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
if (isDesktop)
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
controller: nameController,
|
||||
focusNode: nameFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
onChanged: (_) => setState(() {}),
|
||||
decoration: standardInputDecoration(
|
||||
"Enter contact name",
|
||||
nameFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
suffixIcon: nameController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
nameController.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (!isDesktop)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -242,19 +345,11 @@ class _EditContactNameEmojiViewState
|
|||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
if (!isDesktop && FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
|
@ -269,55 +364,31 @@ class _EditContactNameEmojiViewState
|
|||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
bool shouldEnableSave =
|
||||
nameController.text.isNotEmpty;
|
||||
|
||||
return TextButton(
|
||||
style: shouldEnableSave
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(
|
||||
context)
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryDisabledButtonColor(
|
||||
context),
|
||||
onPressed: shouldEnableSave
|
||||
? () async {
|
||||
if (FocusScope.of(context)
|
||||
.hasFocus) {
|
||||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
enabled: nameController.text.isNotEmpty,
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
if (!isDesktop && FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(
|
||||
milliseconds: 75),
|
||||
const Duration(milliseconds: 75),
|
||||
);
|
||||
}
|
||||
final editedContact =
|
||||
contact.copyWith(
|
||||
final editedContact = contact.copyWith(
|
||||
shouldCopyEmojiWithNull: true,
|
||||
name: nameController.text,
|
||||
emojiChar: _selectedEmoji == null
|
||||
? null
|
||||
: _selectedEmoji!.char,
|
||||
emojiChar:
|
||||
_selectedEmoji == null ? null : _selectedEmoji!.char,
|
||||
);
|
||||
ref
|
||||
.read(
|
||||
addressBookServiceProvider)
|
||||
.editContact(
|
||||
unawaited(
|
||||
ref.read(addressBookServiceProvider).editContact(
|
||||
editedContact,
|
||||
),
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
"Save",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -325,13 +396,6 @@ class _EditContactNameEmojiViewState
|
|||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/subviews/coin_select_sheet.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
// import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/address_book_providers/address_entry_data_provider.dart';
|
||||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
|
@ -14,14 +16,13 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.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/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class NewContactAddressEntryForm extends ConsumerStatefulWidget {
|
||||
const NewContactAddressEntryForm({
|
||||
Key? key,
|
||||
|
@ -48,6 +49,8 @@ class _NewContactAddressEntryFormState
|
|||
late final FocusNode addressLabelFocusNode;
|
||||
late final FocusNode addressFocusNode;
|
||||
|
||||
List<Coin> coins = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
addressLabelController = TextEditingController()
|
||||
|
@ -56,6 +59,7 @@ class _NewContactAddressEntryFormState
|
|||
..text = ref.read(addressEntryDataProvider(widget.id)).address ?? "";
|
||||
addressLabelFocusNode = FocusNode();
|
||||
addressFocusNode = FocusNode();
|
||||
coins = [...Coin.values];
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -70,8 +74,99 @@ class _NewContactAddressEntryFormState
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
bool showTestNet = ref.watch(
|
||||
prefsChangeNotifierProvider.select((value) => value.showTestNetCoins),
|
||||
);
|
||||
if (isDesktop) {
|
||||
coins = [...Coin.values];
|
||||
|
||||
coins.remove(Coin.firoTestNet);
|
||||
if (showTestNet) {
|
||||
coins = coins.sublist(0, coins.length - kTestNetCoinCount);
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
if (isDesktop)
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<Coin>(
|
||||
hint: Text(
|
||||
"Select cryptocurrency",
|
||||
style: STextStyles.fieldLabel(context),
|
||||
),
|
||||
offset: const Offset(0, -10),
|
||||
isExpanded: true,
|
||||
dropdownElevation: 0,
|
||||
value: ref.watch(addressEntryDataProvider(widget.id)
|
||||
.select((value) => value.coin)),
|
||||
onChanged: (value) {
|
||||
if (value is Coin) {
|
||||
ref.read(addressEntryDataProvider(widget.id)).coin = value;
|
||||
}
|
||||
},
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 10,
|
||||
height: 5,
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
buttonPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
buttonDecoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
dropdownDecoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
items: [
|
||||
...coins.map(
|
||||
(coin) => DropdownMenuItem<Coin>(
|
||||
value: coin,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.iconFor(coin: coin),
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
coin.prettyName,
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
|
@ -119,7 +214,8 @@ class _NewContactAddressEntryFormState
|
|||
Assets.svg.iconFor(
|
||||
coin: ref.watch(
|
||||
addressEntryDataProvider(widget.id)
|
||||
.select((value) => value.coin))!),
|
||||
.select(
|
||||
(value) => value.coin))!),
|
||||
height: 20,
|
||||
width: 20,
|
||||
),
|
||||
|
@ -128,13 +224,15 @@ class _NewContactAddressEntryFormState
|
|||
),
|
||||
Text(
|
||||
ref
|
||||
.watch(addressEntryDataProvider(widget.id)
|
||||
.watch(
|
||||
addressEntryDataProvider(widget.id)
|
||||
.select((value) => value.coin))!
|
||||
.prettyName,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 8,
|
||||
|
@ -168,6 +266,7 @@ class _NewContactAddressEntryFormState
|
|||
addressLabelFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: addressLabelController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
|
@ -212,6 +311,7 @@ class _NewContactAddressEntryFormState
|
|||
addressFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -251,7 +351,8 @@ class _NewContactAddressEntryFormState
|
|||
},
|
||||
child: const ClipboardIcon(),
|
||||
),
|
||||
if (ref.watch(addressEntryDataProvider(widget.id)
|
||||
if (!Util.isDesktop &&
|
||||
ref.watch(addressEntryDataProvider(widget.id)
|
||||
.select((value) => value.address)) ==
|
||||
null)
|
||||
TextFieldIconButton(
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
|
||||
|
@ -39,7 +40,8 @@ class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
|
|||
final walletIds = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getWalletIdsFor(coin: coin)));
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: const AppBarBackButton(),
|
||||
|
@ -72,8 +74,9 @@ class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
|
|||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||
child: RawMaterialButton(
|
||||
splashColor:
|
||||
Theme.of(context).extension<StackColors>()!.highlight,
|
||||
splashColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
@ -123,6 +126,7 @@ class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,24 @@ import 'package:stackwallet/models/trade_wallet_lookup.dart';
|
|||
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
|
||||
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
@ -29,6 +38,7 @@ class ConfirmChangeNowSendView extends ConsumerStatefulWidget {
|
|||
this.routeOnSuccessName = WalletView.routeName,
|
||||
required this.trade,
|
||||
this.shouldSendPublicFiroFunds,
|
||||
this.fromDesktopStep4 = false,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/confirmChangeNowSend";
|
||||
|
@ -38,6 +48,7 @@ class ConfirmChangeNowSendView extends ConsumerStatefulWidget {
|
|||
final String routeOnSuccessName;
|
||||
final Trade trade;
|
||||
final bool? shouldSendPublicFiroFunds;
|
||||
final bool fromDesktopStep4;
|
||||
|
||||
@override
|
||||
ConsumerState<ConfirmChangeNowSendView> createState() =>
|
||||
|
@ -52,14 +63,16 @@ class _ConfirmChangeNowSendViewState
|
|||
late final Trade trade;
|
||||
|
||||
Future<void> _attemptSend(BuildContext context) async {
|
||||
unawaited(showDialog<void>(
|
||||
unawaited(
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return const SendingTransactionDialog();
|
||||
},
|
||||
));
|
||||
),
|
||||
);
|
||||
|
||||
final String note = transactionInfo["note"] as String? ?? "";
|
||||
final manager =
|
||||
|
@ -93,6 +106,19 @@ class _ConfirmChangeNowSendViewState
|
|||
|
||||
// pop back to wallet
|
||||
if (mounted) {
|
||||
if (Util.isDesktop) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
|
||||
// stupid hack
|
||||
if (widget.fromDesktopStep4) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
}
|
||||
}
|
||||
|
||||
Navigator.of(context).popUntil(ModalRoute.withName(routeOnSuccessName));
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -129,6 +155,65 @@ class _ConfirmChangeNowSendViewState
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _confirmSend() async {
|
||||
final dynamic unlocked;
|
||||
|
||||
final coin =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId).coin;
|
||||
|
||||
if (Util.isDesktop) {
|
||||
unlocked = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (context) => DesktopDialog(
|
||||
maxWidth: 580,
|
||||
maxHeight: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: const [
|
||||
DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: DesktopAuthSend(
|
||||
coin: coin,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unlocked = await Navigator.push(
|
||||
context,
|
||||
RouteGenerator.getRoute(
|
||||
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
|
||||
builder: (_) => const LockscreenView(
|
||||
showBackButton: true,
|
||||
popOnSuccess: true,
|
||||
routeOnSuccessArguments: true,
|
||||
routeOnSuccess: "",
|
||||
biometricsCancelButtonString: "CANCEL",
|
||||
biometricsLocalizedReason: "Authenticate to send transaction",
|
||||
biometricsAuthenticationTitle: "Confirm Transaction",
|
||||
),
|
||||
settings: const RouteSettings(name: "/confirmsendlockscreen"),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (unlocked is bool && unlocked && mounted) {
|
||||
await _attemptSend(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
transactionInfo = widget.transactionInfo;
|
||||
|
@ -142,10 +227,19 @@ class _ConfirmChangeNowSendViewState
|
|||
Widget build(BuildContext context) {
|
||||
final managerProvider = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManagerProvider(walletId)));
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
// if (FocusScope.of(context).hasFocus) {
|
||||
|
@ -176,14 +270,212 @@ class _ConfirmChangeNowSendViewState
|
|||
child: IntrinsicHeight(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
),
|
||||
const AppBarBackButton(
|
||||
isCompact: true,
|
||||
iconSize: 23,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
"Confirm ${ref.watch(managerProvider.select((value) => value.coin)).ticker} transaction",
|
||||
style: STextStyles.desktopH3(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
child: child,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"Transaction fee",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
RoundedContainer(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
(transactionInfo["fee"] as int),
|
||||
ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
RoundedContainer(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.snackBarBackSuccess,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Total amount",
|
||||
style: STextStyles.titleBold12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textConfirmTotalAmount,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
(transactionInfo["fee"] as int) +
|
||||
(transactionInfo["recipientAmt"] as int),
|
||||
ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textConfirmTotalAmount,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Send",
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: _confirmSend,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}",
|
||||
style: STextStyles.pageTitleH1(context),
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
const SizedBox(
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
child,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
"Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextMedium(context)
|
||||
: STextStyles.pageTitleH1(context),
|
||||
),
|
||||
),
|
||||
isDesktop
|
||||
? Container(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
height: 1,
|
||||
)
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
|
@ -207,10 +499,13 @@ class _ConfirmChangeNowSendViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? Container(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
height: 1,
|
||||
)
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
|
@ -231,7 +526,13 @@ class _ConfirmChangeNowSendViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? Container(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
height: 1,
|
||||
)
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
|
@ -242,24 +543,62 @@ class _ConfirmChangeNowSendViewState
|
|||
"Amount",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => Row(
|
||||
children: [
|
||||
child,
|
||||
Builder(builder: (context) {
|
||||
final coin = ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value.getManager(walletId).coin));
|
||||
final price = ref.watch(
|
||||
priceAnd24hChangeNotifierProvider
|
||||
.select((value) => value.getPrice(coin)));
|
||||
final amount = Format.satoshisToAmount(
|
||||
transactionInfo["recipientAmt"] as int,
|
||||
ref.watch(
|
||||
coin: coin,
|
||||
);
|
||||
final value = price.item1 * amount;
|
||||
final currency = ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.currency));
|
||||
|
||||
return Text(
|
||||
" | ${value.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} $currency",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle2,
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
"${Format.satoshiAmountToPrettyString(transactionInfo["recipientAmt"] as int, ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
managerProvider
|
||||
.select((value) => value.coin),
|
||||
), ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
))} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? Container(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
height: 1,
|
||||
)
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
|
@ -271,15 +610,13 @@ class _ConfirmChangeNowSendViewState
|
|||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
transactionInfo["fee"] as int,
|
||||
ref.watch(
|
||||
"${Format.satoshiAmountToPrettyString(transactionInfo["fee"] as int, ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
managerProvider
|
||||
.select((value) => value.coin),
|
||||
), ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
))} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
textAlign: TextAlign.right,
|
||||
|
@ -287,7 +624,13 @@ class _ConfirmChangeNowSendViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? Container(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
height: 1,
|
||||
)
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
|
@ -308,7 +651,13 @@ class _ConfirmChangeNowSendViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? Container(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
height: 1,
|
||||
)
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
|
@ -327,9 +676,11 @@ class _ConfirmChangeNowSendViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (!isDesktop)
|
||||
RoundedContainer(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -339,27 +690,22 @@ class _ConfirmChangeNowSendViewState
|
|||
children: [
|
||||
Text(
|
||||
"Total amount",
|
||||
style:
|
||||
STextStyles.titleBold12(context).copyWith(
|
||||
style: STextStyles.titleBold12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textConfirmTotalAmount,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
(transactionInfo["fee"] as int) +
|
||||
(transactionInfo["recipientAmt"] as int),
|
||||
ref.watch(
|
||||
"${Format.satoshiAmountToPrettyString((transactionInfo["fee"] as int) + (transactionInfo["recipientAmt"] as int), ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
managerProvider
|
||||
.select((value) => value.coin),
|
||||
), ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
))} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textConfirmTotalAmount,
|
||||
|
@ -369,54 +715,20 @@ class _ConfirmChangeNowSendViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
onPressed: () async {
|
||||
final unlocked = await Navigator.push(
|
||||
context,
|
||||
RouteGenerator.getRoute(
|
||||
shouldUseMaterialRoute:
|
||||
RouteGenerator.useMaterialPageRoute,
|
||||
builder: (_) => const LockscreenView(
|
||||
showBackButton: true,
|
||||
popOnSuccess: true,
|
||||
routeOnSuccessArguments: true,
|
||||
routeOnSuccess: "",
|
||||
biometricsCancelButtonString: "CANCEL",
|
||||
biometricsLocalizedReason:
|
||||
"Authenticate to send transaction",
|
||||
biometricsAuthenticationTitle:
|
||||
"Confirm Transaction",
|
||||
),
|
||||
settings: const RouteSettings(
|
||||
name: "/confirmsendlockscreen"),
|
||||
),
|
||||
);
|
||||
|
||||
if (unlocked is bool && unlocked && mounted) {
|
||||
await _attemptSend(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
"Send",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
if (!isDesktop) const Spacer(),
|
||||
if (!isDesktop)
|
||||
PrimaryButton(
|
||||
label: "Send",
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: _confirmSend,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@ import 'package:stackwallet/providers/exchange/trade_note_service_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/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class EditTradeNoteView extends ConsumerStatefulWidget {
|
||||
const EditTradeNoteView({
|
||||
Key? key,
|
||||
|
@ -47,10 +47,12 @@ class _EditNoteViewState extends ConsumerState<EditTradeNoteView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
|
@ -100,7 +102,8 @@ class _EditNoteViewState extends ConsumerState<EditTradeNoteView> {
|
|||
).copyWith(
|
||||
suffixIcon: _noteController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
padding:
|
||||
const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -148,6 +151,7 @@ class _EditNoteViewState extends ConsumerState<EditTradeNoteView> {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
|
@ -16,8 +19,6 @@ import 'package:stackwallet/widgets/stack_text_field.dart';
|
|||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class FixedRateMarketPairCoinSelectionView extends ConsumerStatefulWidget {
|
||||
const FixedRateMarketPairCoinSelectionView({
|
||||
Key? key,
|
||||
|
@ -120,14 +121,21 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 50));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -143,9 +151,15 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
|
@ -208,7 +222,8 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
Flexible(
|
||||
child: Builder(builder: (context) {
|
||||
final items = _markets
|
||||
.where((e) => Coin.values
|
||||
.where((coin) =>
|
||||
|
@ -221,6 +236,7 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
final String ticker =
|
||||
|
@ -282,6 +298,7 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
|
@ -297,6 +314,7 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: _markets.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
final String ticker =
|
||||
|
@ -360,7 +378,6 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
|
@ -13,8 +16,6 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
|
|||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class FloatingRateCurrencySelectionView extends StatefulWidget {
|
||||
const FloatingRateCurrencySelectionView({
|
||||
Key? key,
|
||||
|
@ -76,14 +77,21 @@ class _FloatingRateCurrencySelectionViewState
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 50));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -99,9 +107,16 @@ class _FloatingRateCurrencySelectionViewState
|
|||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
|
@ -110,8 +125,8 @@ class _FloatingRateCurrencySelectionViewState
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: filter,
|
||||
|
@ -120,6 +135,7 @@ class _FloatingRateCurrencySelectionViewState
|
|||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
desktopMed: isDesktop,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
@ -165,7 +181,8 @@ class _FloatingRateCurrencySelectionViewState
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
Flexible(
|
||||
child: Builder(builder: (context) {
|
||||
final items = _currencies
|
||||
.where((e) => Coin.values
|
||||
.where((coin) =>
|
||||
|
@ -177,6 +194,7 @@ class _FloatingRateCurrencySelectionViewState
|
|||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
return Padding(
|
||||
|
@ -234,6 +252,7 @@ class _FloatingRateCurrencySelectionViewState
|
|||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
|
@ -249,6 +268,7 @@ class _FloatingRateCurrencySelectionViewState
|
|||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: _currencies.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
return Padding(
|
||||
|
@ -308,7 +328,6 @@ class _FloatingRateCurrencySelectionViewState
|
|||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
|||
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
@ -18,19 +17,30 @@ import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.
|
|||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_provider_options.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/rate_type_toggle.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.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/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/simple_desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
import 'package:stackwallet/widgets/textfields/exchange_textfield.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class ExchangeForm extends ConsumerStatefulWidget {
|
||||
|
@ -54,6 +64,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
|
||||
late final TextEditingController _sendController;
|
||||
late final TextEditingController _receiveController;
|
||||
final isDesktop = Util.isDesktop;
|
||||
final FocusNode _sendFocusNode = FocusNode();
|
||||
final FocusNode _receiveFocusNode = FocusNode();
|
||||
|
||||
|
@ -135,14 +146,27 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
.read(exchangeFormStateProvider)
|
||||
.updateMarket(market, true);
|
||||
} catch (e) {
|
||||
unawaited(showDialog<dynamic>(
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
builder: (_) => const StackDialog(
|
||||
builder: (_) {
|
||||
if (isDesktop) {
|
||||
return const SimpleDesktopDialog(
|
||||
title: "Fixed rate market error",
|
||||
message:
|
||||
"Could not find the specified fixed rate trade pair",
|
||||
);
|
||||
} else {
|
||||
return const StackDialog(
|
||||
title: "Fixed rate market error",
|
||||
message:
|
||||
"Could not find the specified fixed rate trade pair",
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
));
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
@ -225,14 +249,26 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
.read(exchangeFormStateProvider)
|
||||
.updateMarket(market, true);
|
||||
} catch (e) {
|
||||
unawaited(showDialog<dynamic>(
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
builder: (_) => const StackDialog(
|
||||
builder: (_) {
|
||||
if (isDesktop) {
|
||||
return const SimpleDesktopDialog(
|
||||
title: "Fixed rate market error",
|
||||
message:
|
||||
"Could not find the specified fixed rate trade pair",
|
||||
);
|
||||
} else {
|
||||
return const StackDialog(
|
||||
title: "Fixed rate market error",
|
||||
message:
|
||||
"Could not find the specified fixed rate trade pair",
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
));
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
@ -320,7 +356,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
await ref.read(exchangeFormStateProvider).swap();
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||
}
|
||||
_swapLock = false;
|
||||
}
|
||||
|
@ -375,7 +411,59 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
}
|
||||
}).toList(growable: false);
|
||||
|
||||
final result = await Navigator.of(context).push(
|
||||
final result = isDesktop
|
||||
? await showDialog<Currency?>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxHeight: 700,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Choose a coin to exchange",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(16),
|
||||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
child: FloatingRateCurrencySelectionView(
|
||||
currencies: tickers,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
})
|
||||
: await Navigator.of(context).push(
|
||||
MaterialPageRoute<dynamic>(
|
||||
builder: (_) => FloatingRateCurrencySelectionView(
|
||||
currencies: tickers,
|
||||
|
@ -455,11 +543,69 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
.toList(growable: false);
|
||||
}
|
||||
|
||||
final result = await Navigator.of(context).push(
|
||||
final result = isDesktop
|
||||
? await showDialog<String?>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxHeight: 700,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Choose a coin to exchange",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(16),
|
||||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
child: FixedRateMarketPairCoinSelectionView(
|
||||
markets: marketsThatPairWithExcludedTicker,
|
||||
currencies: ref
|
||||
.read(
|
||||
availableChangeNowCurrenciesProvider)
|
||||
.currencies,
|
||||
isFrom: excludedTicker != fromTicker,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
})
|
||||
: await Navigator.of(context).push(
|
||||
MaterialPageRoute<dynamic>(
|
||||
builder: (_) => FixedRateMarketPairCoinSelectionView(
|
||||
markets: marketsThatPairWithExcludedTicker,
|
||||
currencies: ref.read(availableChangeNowCurrenciesProvider).currencies,
|
||||
currencies:
|
||||
ref.read(availableChangeNowCurrenciesProvider).currencies,
|
||||
isFrom: excludedTicker != fromTicker,
|
||||
),
|
||||
),
|
||||
|
@ -563,14 +709,14 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
? "-"
|
||||
: ref.read(exchangeFormStateProvider).toAmountString;
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||
}
|
||||
if (!(fromTicker == "-" || toTicker == "-")) {
|
||||
unawaited(
|
||||
|
@ -616,7 +762,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
true,
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||
}
|
||||
return;
|
||||
case SimpleSwapExchange.exchangeName:
|
||||
|
@ -653,7 +799,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
? "-"
|
||||
: ref.read(exchangeFormStateProvider).toAmountString;
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -665,7 +811,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
}
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||
}
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
|
@ -718,15 +864,27 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
}
|
||||
|
||||
if (!isAvailable) {
|
||||
unawaited(showDialog<dynamic>(
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (_) => StackDialog(
|
||||
builder: (_) {
|
||||
if (isDesktop) {
|
||||
return SimpleDesktopDialog(
|
||||
title: "Selected trade pair unavailable",
|
||||
message:
|
||||
"The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades",
|
||||
);
|
||||
} else {
|
||||
return StackDialog(
|
||||
title: "Selected trade pair unavailable",
|
||||
message:
|
||||
"The $fromTicker - $toTicker market is currently disabled for estimated/floating rate trades",
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
));
|
||||
);
|
||||
return;
|
||||
}
|
||||
rate =
|
||||
|
@ -740,7 +898,69 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
shouldCancel = await showDialog<bool?>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (_) => StackDialog(
|
||||
builder: (_) {
|
||||
if (isDesktop) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 500,
|
||||
maxHeight: 300,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Failed to update trade estimate",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
estimate.warningMessage!,
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
"Do you want to attempt trade anyways?",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
const Spacer(
|
||||
flex: 2,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () => Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop(true),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Attempt",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () => Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop(false),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return StackDialog(
|
||||
title: "Failed to update trade estimate",
|
||||
message:
|
||||
"${estimate.warningMessage!}\n\nDo you want to attempt trade anyways?",
|
||||
|
@ -770,7 +990,9 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
Navigator.of(context).pop(false);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -799,14 +1021,54 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
if (walletInitiated) {
|
||||
ref.read(exchangeSendFromWalletIdStateProvider.state).state =
|
||||
Tuple2(walletId!, coin!);
|
||||
if (isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 720,
|
||||
maxHeight: double.infinity,
|
||||
child: StepScaffold(
|
||||
step: 2,
|
||||
model: model,
|
||||
body: DesktopStep2(
|
||||
model: model,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
Step2View.routeName,
|
||||
arguments: model,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ref.read(exchangeSendFromWalletIdStateProvider.state).state = null;
|
||||
|
||||
if (isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 720,
|
||||
maxHeight: double.infinity,
|
||||
child: StepScaffold(
|
||||
step: 1,
|
||||
model: model,
|
||||
body: DesktopStep1(
|
||||
model: model,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
Step1View.routeName,
|
||||
|
@ -816,6 +1078,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isWalletCoin(Coin? coin, bool isSend) {
|
||||
if (coin == null) {
|
||||
|
@ -960,170 +1223,75 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
SizedBox(
|
||||
height: isDesktop ? 10 : 4,
|
||||
),
|
||||
TextFormField(
|
||||
style: STextStyles.smallMed14(context).copyWith(
|
||||
ExchangeTextField(
|
||||
controller: _sendController,
|
||||
focusNode: _sendFocusNode,
|
||||
textStyle: STextStyles.smallMed14(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
focusNode: _sendFocusNode,
|
||||
controller: _sendController,
|
||||
textAlign: TextAlign.right,
|
||||
buttonColor:
|
||||
Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||
borderRadius: Constants.size.circularBorderRadius,
|
||||
background:
|
||||
Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
|
||||
onTap: () {
|
||||
if (_sendController.text == "-") {
|
||||
_sendController.text = "";
|
||||
}
|
||||
},
|
||||
onChanged: sendFieldOnChanged,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: true,
|
||||
onButtonTap: selectSendCurrency,
|
||||
isWalletCoin: isWalletCoin(coin, true),
|
||||
image: _fetchIconUrlFromTicker(ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.fromTicker))),
|
||||
ticker: ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.fromTicker)),
|
||||
),
|
||||
inputFormatters: [
|
||||
// regex to validate a crypto amount with 8 decimal places
|
||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
right: 12,
|
||||
SizedBox(
|
||||
height: isDesktop ? 10 : 4,
|
||||
),
|
||||
hintText: "0",
|
||||
hintStyle: STextStyles.fieldLabel(context).copyWith(
|
||||
fontSize: 14,
|
||||
),
|
||||
prefixIcon: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: GestureDetector(
|
||||
onTap: selectSendCurrency,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 18,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final image = _fetchIconUrlFromTicker(ref.watch(
|
||||
exchangeFormStateProvider
|
||||
.select((value) => value.fromTicker)));
|
||||
|
||||
if (image != null && image.isNotEmpty) {
|
||||
return Center(
|
||||
child: SvgPicture.network(
|
||||
image,
|
||||
height: 18,
|
||||
placeholderBuilder: (_) => Container(
|
||||
width: 18,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
18,
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
18,
|
||||
),
|
||||
child: const LoadingIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
width: 18,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
// color: Theme.of(context).extension<StackColors>()!.accentColorDark
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleQuestion,
|
||||
width: 18,
|
||||
height: 18,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
SizedBox(
|
||||
height: isDesktop ? 10 : 4,
|
||||
),
|
||||
if (ref
|
||||
.watch(
|
||||
exchangeFormStateProvider.select((value) => value.warning))
|
||||
.isNotEmpty &&
|
||||
!ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.reversed)))
|
||||
Text(
|
||||
ref.watch(exchangeFormStateProvider.select((value) =>
|
||||
value.fromTicker?.toUpperCase())) ??
|
||||
"-",
|
||||
style: STextStyles.smallMed14(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.warning)),
|
||||
style: STextStyles.errorSmall(context),
|
||||
),
|
||||
),
|
||||
if (!isWalletCoin(coin, true))
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
),
|
||||
if (!isWalletCoin(coin, true))
|
||||
SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 5,
|
||||
height: 2.5,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Stack(
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: Text(
|
||||
Text(
|
||||
"You will receive",
|
||||
style: STextStyles.itemSubtitle(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
),
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
GestureDetector(
|
||||
child: RoundedContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(6)
|
||||
: const EdgeInsets.all(2),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
radiusMultiplier: 0.75,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
await _swap();
|
||||
},
|
||||
|
@ -1139,41 +1307,24 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Align(
|
||||
alignment: ref.watch(exchangeFormStateProvider
|
||||
.select((value) => value.reversed))
|
||||
? Alignment.bottomRight
|
||||
: Alignment.topRight,
|
||||
child: Text(
|
||||
ref.watch(exchangeFormStateProvider
|
||||
.select((value) => value.warning)),
|
||||
style: STextStyles.errorSmall(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
TextFormField(
|
||||
style: STextStyles.smallMed14(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||
SizedBox(
|
||||
height: isDesktop ? 10 : 7,
|
||||
),
|
||||
ExchangeTextField(
|
||||
focusNode: _receiveFocusNode,
|
||||
controller: _receiveController,
|
||||
readOnly: ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.estimated ||
|
||||
ref.watch(exchangeProvider).name ==
|
||||
SimpleSwapExchange.exchangeName,
|
||||
textStyle: STextStyles.smallMed14(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
buttonColor:
|
||||
Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||
borderRadius: Constants.size.circularBorderRadius,
|
||||
background:
|
||||
Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
|
||||
onTap: () {
|
||||
if (!(ref.read(prefsChangeNotifierProvider).exchangeRateType ==
|
||||
ExchangeRateType.estimated) &&
|
||||
|
@ -1182,138 +1333,39 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
}
|
||||
},
|
||||
onChanged: receiveFieldOnChanged,
|
||||
textAlign: TextAlign.right,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: true,
|
||||
),
|
||||
inputFormatters: [
|
||||
// regex to validate a crypto amount with 8 decimal places
|
||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||
.hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
right: 12,
|
||||
),
|
||||
hintText: "0",
|
||||
hintStyle: STextStyles.fieldLabel(context).copyWith(
|
||||
fontSize: 14,
|
||||
),
|
||||
prefixIcon: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: GestureDetector(
|
||||
onTap: selectReceiveCurrency,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 18,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final image = _fetchIconUrlFromTicker(ref.watch(
|
||||
exchangeFormStateProvider
|
||||
.select((value) => value.toTicker)));
|
||||
|
||||
if (image != null && image.isNotEmpty) {
|
||||
return Center(
|
||||
child: SvgPicture.network(
|
||||
image,
|
||||
height: 18,
|
||||
placeholderBuilder: (_) => Container(
|
||||
width: 18,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
18,
|
||||
),
|
||||
child: const LoadingIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
width: 18,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
// color: Theme.of(context).extension<StackColors>()!.accentColorDark
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.circleQuestion,
|
||||
width: 18,
|
||||
height: 18,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
onButtonTap: selectReceiveCurrency,
|
||||
isWalletCoin: isWalletCoin(coin, true),
|
||||
image: _fetchIconUrlFromTicker(ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.toTicker))),
|
||||
ticker: ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.toTicker)),
|
||||
readOnly: ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.estimated ||
|
||||
ref.watch(exchangeProvider).name ==
|
||||
SimpleSwapExchange.exchangeName,
|
||||
),
|
||||
if (ref
|
||||
.watch(
|
||||
exchangeFormStateProvider.select((value) => value.warning))
|
||||
.isNotEmpty &&
|
||||
ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.reversed)))
|
||||
Text(
|
||||
ref.watch(exchangeFormStateProvider.select(
|
||||
(value) => value.toTicker?.toUpperCase())) ??
|
||||
"-",
|
||||
style: STextStyles.smallMed14(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.warning)),
|
||||
style: STextStyles.errorSmall(context),
|
||||
),
|
||||
),
|
||||
if (!isWalletCoin(coin, false))
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
),
|
||||
if (!isWalletCoin(coin, false))
|
||||
SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 5,
|
||||
height: 2.5,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
SizedBox(
|
||||
height: isDesktop ? 20 : 12,
|
||||
),
|
||||
RateTypeToggle(
|
||||
onChanged: onRateTypeChanged,
|
||||
),
|
||||
if (ref.read(exchangeFormStateProvider).fromAmount != null &&
|
||||
ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
SizedBox(
|
||||
height: isDesktop ? 20 : 12,
|
||||
),
|
||||
if (ref.read(exchangeFormStateProvider).fromAmount != null &&
|
||||
ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)
|
||||
|
@ -1328,10 +1380,11 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
reversed: ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.reversed)),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
SizedBox(
|
||||
height: isDesktop ? 20 : 12,
|
||||
),
|
||||
PrimaryButton(
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
enabled: ref.watch(
|
||||
exchangeFormStateProvider.select((value) => value.canExchange)),
|
||||
onPressed: ref.watch(exchangeFormStateProvider
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
|
|||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
|
@ -39,7 +40,8 @@ class _Step1ViewState extends State<Step1View> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -156,8 +158,8 @@ class _Step1ViewState extends State<Step1View> {
|
|||
model.rateType == ExchangeRateType.estimated
|
||||
? "Estimated rate"
|
||||
: "Fixed rate",
|
||||
style:
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
style: STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.infoItemLabel,
|
||||
|
@ -180,7 +182,8 @@ class _Step1ViewState extends State<Step1View> {
|
|||
const Spacer(),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(Step2View.routeName,
|
||||
Navigator.of(context).pushNamed(
|
||||
Step2View.routeName,
|
||||
arguments: model);
|
||||
},
|
||||
style: Theme.of(context)
|
||||
|
@ -200,6 +203,7 @@ class _Step1ViewState extends State<Step1View> {
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ import 'package:stackwallet/pages/address_book_views/subviews/contact_popup.dart
|
|||
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
|
||||
import 'package:stackwallet/providers/exchange/exchange_flow_is_active_state_provider.dart';
|
||||
import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||
|
@ -18,8 +16,10 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
|
||||
|
@ -57,6 +57,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
late final FocusNode _toFocusNode;
|
||||
late final FocusNode _refundFocusNode;
|
||||
|
||||
bool enableNext = false;
|
||||
|
||||
bool isStackCoin(String ticker) {
|
||||
try {
|
||||
coinFromTickerCaseInsensitive(ticker);
|
||||
|
@ -121,7 +123,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -190,7 +193,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
text: "Choose from stack",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin = coinFromTickerCaseInsensitive(
|
||||
final coin =
|
||||
coinFromTickerCaseInsensitive(
|
||||
model.receiveTicker,
|
||||
);
|
||||
Navigator.of(context)
|
||||
|
@ -201,12 +205,21 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
.then((value) async {
|
||||
if (value is String) {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.read(
|
||||
walletsChangeNotifierProvider)
|
||||
.getManager(value);
|
||||
|
||||
_toController.text = manager.walletName;
|
||||
_toController.text =
|
||||
manager.walletName;
|
||||
model.recipientAddress = await manager
|
||||
.currentReceivingAddress;
|
||||
|
||||
setState(() {
|
||||
enableNext =
|
||||
_toController.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e, s) {
|
||||
|
@ -275,7 +288,12 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
},
|
||||
child: const XIcon(),
|
||||
)
|
||||
|
@ -291,14 +309,22 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
final content =
|
||||
data.text!.trim();
|
||||
|
||||
_toController.text = content;
|
||||
_toController.text =
|
||||
content;
|
||||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text
|
||||
.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: _toController.text.isEmpty
|
||||
child:
|
||||
_toController.text.isEmpty
|
||||
? const ClipboardIcon()
|
||||
: const XIcon(),
|
||||
),
|
||||
|
@ -338,6 +364,12 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
.state)
|
||||
.state = "";
|
||||
}
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
});
|
||||
},
|
||||
child: const AddressBookIcon(),
|
||||
|
@ -361,14 +393,24 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
} else {
|
||||
_toController.text =
|
||||
qrResult.rawContent;
|
||||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
}
|
||||
} on PlatformException catch (e, s) {
|
||||
Logging.instance.log(
|
||||
|
@ -410,7 +452,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
text: "Choose from stack",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin = coinFromTickerCaseInsensitive(
|
||||
final coin =
|
||||
coinFromTickerCaseInsensitive(
|
||||
model.sendTicker,
|
||||
);
|
||||
Navigator.of(context)
|
||||
|
@ -421,7 +464,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
.then((value) async {
|
||||
if (value is String) {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.read(
|
||||
walletsChangeNotifierProvider)
|
||||
.getManager(value);
|
||||
|
||||
_refundController.text =
|
||||
|
@ -429,6 +473,11 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
model.refundAddress = await manager
|
||||
.currentReceivingAddress;
|
||||
}
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController.text.isNotEmpty;
|
||||
});
|
||||
});
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
|
@ -495,7 +544,12 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
},
|
||||
child: const XIcon(),
|
||||
)
|
||||
|
@ -516,11 +570,17 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text
|
||||
.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
}
|
||||
},
|
||||
child:
|
||||
_refundController.text.isEmpty
|
||||
child: _refundController
|
||||
.text.isEmpty
|
||||
? const ClipboardIcon()
|
||||
: const XIcon(),
|
||||
),
|
||||
|
@ -555,6 +615,12 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
model.refundAddress =
|
||||
_refundController.text;
|
||||
}
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
});
|
||||
},
|
||||
child: const AddressBookIcon(),
|
||||
|
@ -578,14 +644,24 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
} else {
|
||||
_refundController.text =
|
||||
qrResult.rawContent;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
}
|
||||
} on PlatformException catch (e, s) {
|
||||
Logging.instance.log(
|
||||
|
@ -637,20 +713,15 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
child: PrimaryButton(
|
||||
label: "Next",
|
||||
enabled: enableNext,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
Step3View.routeName,
|
||||
arguments: model,
|
||||
);
|
||||
},
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context),
|
||||
child: Text(
|
||||
"Next",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -664,6 +735,7 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:stackwallet/utilities/assets.dart';
|
|||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -50,7 +51,8 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -254,9 +256,11 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
amount: model.reversed
|
||||
? model.receiveAmount
|
||||
: model.sendAmount,
|
||||
addressTo: model.recipientAddress!,
|
||||
addressTo:
|
||||
model.recipientAddress!,
|
||||
extraId: null,
|
||||
addressRefund: model.refundAddress!,
|
||||
addressRefund:
|
||||
model.refundAddress!,
|
||||
refundExtraId: "",
|
||||
rateId: model.rateId,
|
||||
reversed: model.reversed,
|
||||
|
@ -272,7 +276,8 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
barrierDismissible: true,
|
||||
builder: (_) => StackDialog(
|
||||
title: "Failed to create trade",
|
||||
message: response.exception?.toString(),
|
||||
message:
|
||||
response.exception?.toString(),
|
||||
),
|
||||
));
|
||||
return;
|
||||
|
@ -335,6 +340,7 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@ import 'package:stackwallet/utilities/assets.dart';
|
|||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -107,7 +107,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
Widget build(BuildContext context) {
|
||||
final bool isWalletCoin =
|
||||
_isWalletCoinAndHasWallet(model.trade!.payInCurrency, ref);
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -320,7 +321,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
children: [
|
||||
Text(
|
||||
model.trade!.tradeId,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
|
@ -362,8 +364,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
),
|
||||
Text(
|
||||
_statusString,
|
||||
style:
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
style: STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.colorForStatus(_statusString),
|
||||
|
@ -391,8 +393,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
Center(
|
||||
child: Text(
|
||||
"Send ${model.sendTicker} to this address",
|
||||
style:
|
||||
STextStyles.pageTitleH2(context),
|
||||
style: STextStyles.pageTitleH2(
|
||||
context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -428,11 +430,12 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style:
|
||||
STextStyles.button(context)
|
||||
style: STextStyles.button(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
),
|
||||
),
|
||||
|
@ -483,12 +486,14 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
tuple.item2.ticker.toLowerCase()
|
||||
? () async {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.read(
|
||||
walletsChangeNotifierProvider)
|
||||
.getManager(tuple.item1);
|
||||
|
||||
final amount =
|
||||
Format.decimalAmountToSatoshis(
|
||||
model.sendAmount);
|
||||
model.sendAmount,
|
||||
manager.coin);
|
||||
final address =
|
||||
model.trade!.payInAddress;
|
||||
|
||||
|
@ -546,7 +551,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
HomeView.routeName,
|
||||
trade: model.trade!,
|
||||
),
|
||||
settings: const RouteSettings(
|
||||
settings:
|
||||
const RouteSettings(
|
||||
name:
|
||||
ConfirmChangeNowSendView
|
||||
.routeName,
|
||||
|
@ -570,7 +576,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
message: e.toString(),
|
||||
rightButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.getSecondaryEnabledButtonColor(
|
||||
context),
|
||||
child: Text(
|
||||
|
@ -585,7 +592,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -641,6 +649,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,11 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
|
|||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: const SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
),
|
||||
child: ExchangeForm(),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
|||
import 'package:stackwallet/pages/exchange_view/confirm_change_now_send.dart';
|
||||
import 'package:stackwallet/pages/home_view/home_view.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||
|
@ -19,9 +20,13 @@ import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
|||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/expandable.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
@ -33,6 +38,8 @@ class SendFromView extends ConsumerStatefulWidget {
|
|||
required this.trade,
|
||||
required this.amount,
|
||||
required this.address,
|
||||
this.shouldPopRoot = false,
|
||||
this.fromDesktopStep4 = false,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/sendFrom";
|
||||
|
@ -41,6 +48,8 @@ class SendFromView extends ConsumerStatefulWidget {
|
|||
final Decimal amount;
|
||||
final String address;
|
||||
final Trade trade;
|
||||
final bool shouldPopRoot;
|
||||
final bool fromDesktopStep4;
|
||||
|
||||
@override
|
||||
ConsumerState<SendFromView> createState() => _SendFromViewState();
|
||||
|
@ -53,25 +62,7 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
late final Trade trade;
|
||||
|
||||
String formatAmount(Decimal amount, Coin coin) {
|
||||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.litecoin:
|
||||
case Coin.dogecoin:
|
||||
case Coin.epicCash:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.firoTestNet:
|
||||
return amount.toStringAsFixed(Constants.decimalPlaces);
|
||||
case Coin.monero:
|
||||
return amount.toStringAsFixed(Constants.decimalPlacesMonero);
|
||||
case Coin.wownero:
|
||||
return amount.toStringAsFixed(Constants.decimalPlacesWownero);
|
||||
}
|
||||
return amount.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -90,8 +81,15 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
final walletIds = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getWalletIdsFor(coin: coin)));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
|
@ -105,6 +103,48 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Send from Stack",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: Navigator.of(
|
||||
context,
|
||||
rootNavigator: widget.shouldPopRoot,
|
||||
).pop,
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -112,15 +152,23 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
children: [
|
||||
Text(
|
||||
"You need to send ${formatAmount(amount, coin)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Expanded(
|
||||
ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Expanded(
|
||||
child: child,
|
||||
),
|
||||
child: ListView.builder(
|
||||
primary: isDesktop ? false : null,
|
||||
shrinkWrap: isDesktop,
|
||||
itemCount: walletIds.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Padding(
|
||||
|
@ -130,6 +178,7 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
amount: amount,
|
||||
address: address,
|
||||
trade: trade,
|
||||
fromDesktopStep4: widget.fromDesktopStep4,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -149,12 +198,14 @@ class SendFromCard extends ConsumerStatefulWidget {
|
|||
required this.amount,
|
||||
required this.address,
|
||||
required this.trade,
|
||||
this.fromDesktopStep4 = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final Decimal amount;
|
||||
final String address;
|
||||
final Trade trade;
|
||||
final bool fromDesktopStep4;
|
||||
|
||||
@override
|
||||
ConsumerState<SendFromCard> createState() => _SendFromCardState();
|
||||
|
@ -167,7 +218,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
late final Trade trade;
|
||||
|
||||
Future<void> _send(Manager manager, {bool? shouldSendPublicFiroFunds}) async {
|
||||
final _amount = Format.decimalAmountToSatoshis(amount);
|
||||
final _amount = Format.decimalAmountToSatoshis(amount, manager.coin);
|
||||
|
||||
try {
|
||||
bool wasCancelled = false;
|
||||
|
@ -178,12 +229,23 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
useSafeArea: false,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return BuildingTransactionDialog(
|
||||
return ConditionalParent(
|
||||
condition: Util.isDesktop,
|
||||
builder: (child) => DesktopDialog(
|
||||
maxWidth: 400,
|
||||
maxHeight: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: BuildingTransactionDialog(
|
||||
onCancel: () {
|
||||
wasCancelled = true;
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -229,7 +291,10 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
// pop building dialog
|
||||
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(
|
||||
context,
|
||||
rootNavigator: Util.isDesktop,
|
||||
).pop();
|
||||
}
|
||||
|
||||
txData["note"] =
|
||||
|
@ -243,9 +308,12 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
builder: (_) => ConfirmChangeNowSendView(
|
||||
transactionInfo: txData,
|
||||
walletId: walletId,
|
||||
routeOnSuccessName: HomeView.routeName,
|
||||
routeOnSuccessName: Util.isDesktop
|
||||
? DesktopExchangeView.routeName
|
||||
: HomeView.routeName,
|
||||
trade: trade,
|
||||
shouldSendPublicFiroFunds: shouldSendPublicFiroFunds,
|
||||
fromDesktopStep4: widget.fromDesktopStep4,
|
||||
),
|
||||
settings: const RouteSettings(
|
||||
name: ConfirmChangeNowSendView.routeName,
|
||||
|
@ -339,10 +407,16 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () => _send(
|
||||
onPressed: () async {
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
_send(
|
||||
manager,
|
||||
shouldSendPublicFiroFunds: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
|
@ -375,7 +449,8 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
"${Format.localizedStringAsFixed(
|
||||
value: snapshot.data!,
|
||||
locale: locale,
|
||||
decimalPlaces: Constants.decimalPlaces,
|
||||
decimalPlaces:
|
||||
Constants.decimalPlacesForCoin(coin),
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
|
@ -418,10 +493,16 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () => _send(
|
||||
onPressed: () async {
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
_send(
|
||||
manager,
|
||||
shouldSendPublicFiroFunds: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
|
@ -454,7 +535,8 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
"${Format.localizedStringAsFixed(
|
||||
value: snapshot.data!,
|
||||
locale: locale,
|
||||
decimalPlaces: Constants.decimalPlaces,
|
||||
decimalPlaces:
|
||||
Constants.decimalPlacesForCoin(coin),
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
|
@ -504,7 +586,13 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () => _send(manager),
|
||||
onPressed: () async {
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
_send(manager),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: child,
|
||||
),
|
||||
child: Row(
|
||||
|
@ -556,11 +644,8 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
"${Format.localizedStringAsFixed(
|
||||
value: snapshot.data!,
|
||||
locale: locale,
|
||||
decimalPlaces: coin == Coin.monero
|
||||
? Constants.decimalPlacesMonero
|
||||
: coin == Coin.wownero
|
||||
? Constants.decimalPlacesWownero
|
||||
: Constants.decimalPlaces,
|
||||
decimalPlaces:
|
||||
Constants.decimalPlacesForCoin(coin),
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
|
|
|
@ -15,7 +15,9 @@ import 'package:stackwallet/utilities/format.dart';
|
|||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class ExchangeProviderOptions extends ConsumerWidget {
|
||||
|
@ -38,22 +40,38 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
return RoundedWhiteContainer(
|
||||
padding: isDesktop ? const EdgeInsets.all(0) : const EdgeInsets.all(12),
|
||||
borderColor: isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.background
|
||||
: null,
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (ref.read(currentExchangeNameStateProvider.state).state !=
|
||||
ChangeNowExchange.exchangeName) {
|
||||
ref.read(currentExchangeNameStateProvider.state).state =
|
||||
ChangeNowExchange.exchangeName;
|
||||
ref.read(exchangeFormStateProvider).exchange =
|
||||
Exchange.fromName(
|
||||
ref.read(currentExchangeNameStateProvider.state).state);
|
||||
Exchange.fromName(ref
|
||||
.read(currentExchangeNameStateProvider.state)
|
||||
.state);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -75,7 +93,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
.state = value;
|
||||
ref.read(exchangeFormStateProvider).exchange =
|
||||
Exchange.fromName(ref
|
||||
.read(currentExchangeNameStateProvider.state)
|
||||
.read(currentExchangeNameStateProvider
|
||||
.state)
|
||||
.state);
|
||||
}
|
||||
},
|
||||
|
@ -86,8 +105,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
),
|
||||
SvgPicture.asset(
|
||||
Assets.exchange.changeNow,
|
||||
width: 24,
|
||||
height: 24,
|
||||
width: isDesktop ? 32 : 24,
|
||||
height: isDesktop ? 32 : 24,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
|
@ -130,29 +149,38 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
if (estimate != null) {
|
||||
Decimal rate;
|
||||
if (estimate.reversed) {
|
||||
rate =
|
||||
(toAmount! / estimate.estimatedAmount)
|
||||
rate = (toAmount! /
|
||||
estimate.estimatedAmount)
|
||||
.toDecimal(
|
||||
scaleOnInfinitePrecision: 12);
|
||||
} else {
|
||||
rate =
|
||||
(estimate.estimatedAmount / fromAmount!)
|
||||
rate = (estimate.estimatedAmount /
|
||||
fromAmount!)
|
||||
.toDecimal(
|
||||
scaleOnInfinitePrecision: 12);
|
||||
}
|
||||
Coin coin;
|
||||
try {
|
||||
coin =
|
||||
coinFromTickerCaseInsensitive(to!);
|
||||
} catch (_) {
|
||||
coin = Coin.bitcoin;
|
||||
}
|
||||
|
||||
return Text(
|
||||
"1 ${from!.toUpperCase()} ~ ${Format.localizedStringAsFixed(
|
||||
value: rate,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
.select(
|
||||
(value) => value.locale),
|
||||
),
|
||||
decimalPlaces: to!.toUpperCase() ==
|
||||
Coin.monero.ticker.toUpperCase()
|
||||
? Constants.decimalPlacesMonero
|
||||
: Constants.decimalPlaces,
|
||||
decimalPlaces:
|
||||
Constants.decimalPlacesForCoin(
|
||||
coin),
|
||||
)} ${to!.toUpperCase()}",
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -166,7 +194,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
);
|
||||
return Text(
|
||||
"Failed to fetch rate",
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -200,7 +229,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
fromAmount! > Decimal.zero))
|
||||
Text(
|
||||
"n/a",
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
|
@ -213,22 +243,41 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
GestureDetector(
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (ref.read(currentExchangeNameStateProvider.state).state !=
|
||||
SimpleSwapExchange.exchangeName) {
|
||||
ref.read(currentExchangeNameStateProvider.state).state =
|
||||
SimpleSwapExchange.exchangeName;
|
||||
ref.read(exchangeFormStateProvider).exchange =
|
||||
Exchange.fromName(
|
||||
ref.read(currentExchangeNameStateProvider.state).state);
|
||||
Exchange.fromName(ref
|
||||
.read(currentExchangeNameStateProvider.state)
|
||||
.state);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -250,7 +299,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
.state = value;
|
||||
ref.read(exchangeFormStateProvider).exchange =
|
||||
Exchange.fromName(ref
|
||||
.read(currentExchangeNameStateProvider.state)
|
||||
.read(currentExchangeNameStateProvider
|
||||
.state)
|
||||
.state);
|
||||
}
|
||||
},
|
||||
|
@ -261,8 +311,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
),
|
||||
SvgPicture.asset(
|
||||
Assets.exchange.simpleSwap,
|
||||
width: 24,
|
||||
height: 24,
|
||||
width: isDesktop ? 32 : 24,
|
||||
height: isDesktop ? 32 : 24,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
|
@ -307,21 +357,30 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
if (estimate != null) {
|
||||
Decimal rate = (estimate.estimatedAmount /
|
||||
fromAmount!)
|
||||
.toDecimal(scaleOnInfinitePrecision: 12);
|
||||
.toDecimal(
|
||||
scaleOnInfinitePrecision: 12);
|
||||
|
||||
Coin coin;
|
||||
try {
|
||||
coin =
|
||||
coinFromTickerCaseInsensitive(to!);
|
||||
} catch (_) {
|
||||
coin = Coin.bitcoin;
|
||||
}
|
||||
return Text(
|
||||
"1 ${from!.toUpperCase()} ~ ${Format.localizedStringAsFixed(
|
||||
value: rate,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
.select(
|
||||
(value) => value.locale),
|
||||
),
|
||||
decimalPlaces: to!.toUpperCase() ==
|
||||
Coin.monero.ticker.toUpperCase()
|
||||
? Constants.decimalPlacesMonero
|
||||
: Constants.decimalPlaces,
|
||||
decimalPlaces:
|
||||
Constants.decimalPlacesForCoin(
|
||||
coin),
|
||||
)} ${to!.toUpperCase()}",
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -335,7 +394,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
);
|
||||
return Text(
|
||||
"Failed to fetch rate",
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -375,7 +435,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
fromAmount! > Decimal.zero))
|
||||
Text(
|
||||
"n/a",
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
|
@ -388,6 +449,8 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -7,8 +7,9 @@ import 'package:stackwallet/providers/providers.dart';
|
|||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class RateTypeToggle extends ConsumerWidget {
|
||||
const RateTypeToggle({
|
||||
|
@ -21,15 +22,28 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
final estimated = ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.estimated;
|
||||
|
||||
return RoundedWhiteContainer(
|
||||
return RoundedContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
color: isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
|
||||
: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: estimated
|
||||
? SystemMouseCursors.basic
|
||||
: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (!estimated) {
|
||||
|
@ -39,6 +53,9 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
}
|
||||
},
|
||||
child: RoundedContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(17)
|
||||
: const EdgeInsets.all(12),
|
||||
color: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -48,11 +65,21 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.lock,
|
||||
Assets.svg.lockOpen,
|
||||
width: 12,
|
||||
height: 14,
|
||||
color: estimated
|
||||
? Theme.of(context).extension<StackColors>()!.textDark
|
||||
color: isDesktop
|
||||
? estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary
|
||||
: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
|
@ -62,7 +89,18 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
),
|
||||
Text(
|
||||
"Estimate rate",
|
||||
style: STextStyles.smallMed12(context).copyWith(
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
)
|
||||
: STextStyles.smallMed12(context).copyWith(
|
||||
color: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -77,7 +115,16 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: !estimated
|
||||
? SystemMouseCursors.basic
|
||||
: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (estimated) {
|
||||
|
@ -87,6 +134,9 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
}
|
||||
},
|
||||
child: RoundedContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(17)
|
||||
: const EdgeInsets.all(12),
|
||||
color: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -99,8 +149,18 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
Assets.svg.lock,
|
||||
width: 12,
|
||||
height: 14,
|
||||
color: !estimated
|
||||
? Theme.of(context).extension<StackColors>()!.textDark
|
||||
color: isDesktop
|
||||
? !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary
|
||||
: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
|
@ -110,7 +170,18 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
),
|
||||
Text(
|
||||
"Fixed rate",
|
||||
style: STextStyles.smallMed12(context).copyWith(
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
)
|
||||
: STextStyles.smallMed12(context).copyWith(
|
||||
color: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -125,6 +196,7 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -15,17 +15,24 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.d
|
|||
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
|
||||
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
||||
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';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -157,10 +164,17 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
final sendAmount =
|
||||
Decimal.tryParse(trade.payInAmount) ?? Decimal.parse("-1");
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -176,15 +190,133 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.only(left: 32)
|
||||
: const EdgeInsets.all(0),
|
||||
child: BranchedParent(
|
||||
condition: isDesktop,
|
||||
conditionBranchBuilder: (children) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 20,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 12,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
borderColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView(
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
if (!hasTx &&
|
||||
isStackCoin(trade.payInCurrency) &&
|
||||
(trade.status == "New" ||
|
||||
trade.status == "new" ||
|
||||
trade.status == "waiting" ||
|
||||
trade.status == "Waiting"))
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
if (!hasTx &&
|
||||
isStackCoin(trade.payInCurrency) &&
|
||||
(trade.status == "New" ||
|
||||
trade.status == "new" ||
|
||||
trade.status == "waiting" ||
|
||||
trade.status == "Waiting"))
|
||||
SecondaryButton(
|
||||
label: "Send from Stack",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () {
|
||||
final amount = sendAmount;
|
||||
final address = trade.payInAddress;
|
||||
|
||||
final coin =
|
||||
coinFromTickerCaseInsensitive(trade.payInCurrency);
|
||||
|
||||
Navigator.of(context).pushNamed(
|
||||
SendFromView.routeName,
|
||||
arguments: Tuple4(
|
||||
coin,
|
||||
amount,
|
||||
address,
|
||||
trade,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
otherBranchBuilder: (children) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: children,
|
||||
),
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(0)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Container(
|
||||
decoration: isDesktop
|
||||
? BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(12)
|
||||
: const EdgeInsets.all(0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (isDesktop)
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
_fetchIconAssetForStatus(trade.status),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SelectableText(
|
||||
"Exchange",
|
||||
style: STextStyles.desktopTextMedium(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: isDesktop
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SelectableText(
|
||||
"${trade.payInCurrency.toUpperCase()} → ${trade.payOutCurrency.toUpperCase()}",
|
||||
|
@ -194,7 +326,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
height: 4,
|
||||
),
|
||||
SelectableText(
|
||||
"${Format.localizedStringAsFixed(value: sendAmount, locale: ref.watch(
|
||||
"-${Format.localizedStringAsFixed(value: sendAmount, locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
), decimalPlaces: trade.payInCurrency.toLowerCase() == "xmr" ? 12 : 8)} ${trade.payInCurrency.toUpperCase()}",
|
||||
|
@ -202,6 +334,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
|
@ -219,10 +352,17 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
),
|
||||
),
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
@ -247,21 +387,77 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
),
|
||||
),
|
||||
if (!sentFromStack && !hasTx)
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (!sentFromStack && !hasTx)
|
||||
RoundedContainer(
|
||||
color: Theme.of(context)
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
color: isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.popupBG
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.warningBackground,
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Amount",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
"${trade.payInAmount} ${trade.payInCurrency.toUpperCase()}",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
IconCopyButton(
|
||||
data: trade.payInAmount,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
child,
|
||||
],
|
||||
),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text:
|
||||
"You must send at least ${sendAmount.toStringAsFixed(
|
||||
trade.payInCurrency.toLowerCase() == "xmr" ? 12 : 8,
|
||||
)} ${trade.payInCurrency.toUpperCase()}. ",
|
||||
style: STextStyles.label700(context).copyWith(
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed)
|
||||
: STextStyles.label(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.warningForeground,
|
||||
|
@ -274,7 +470,14 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
? 12
|
||||
: 8,
|
||||
)} ${trade.payInCurrency.toUpperCase()}, your transaction may not be converted and it may not be refunded.",
|
||||
style: STextStyles.label(context).copyWith(
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed)
|
||||
: STextStyles.label(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.warningForeground,
|
||||
|
@ -283,12 +486,18 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
]),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (sentFromStack)
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (sentFromStack)
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -306,32 +515,58 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
GestureDetector(
|
||||
BlueTextButton(
|
||||
text: "View transaction",
|
||||
onTap: () {
|
||||
final Coin coin = coinFromTickerCaseInsensitive(
|
||||
trade.payInCurrency);
|
||||
final Coin coin =
|
||||
coinFromTickerCaseInsensitive(trade.payInCurrency);
|
||||
|
||||
if (isDesktop) {
|
||||
Navigator.of(context).push(
|
||||
FadePageRoute<void>(
|
||||
DesktopDialog(
|
||||
maxHeight:
|
||||
MediaQuery.of(context).size.height - 64,
|
||||
maxWidth: 580,
|
||||
child: TransactionDetailsView(
|
||||
coin: coin,
|
||||
transaction: transactionIfSentFromStack!,
|
||||
walletId: walletId!,
|
||||
),
|
||||
),
|
||||
const RouteSettings(
|
||||
name: TransactionDetailsView.routeName,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(
|
||||
TransactionDetailsView.routeName,
|
||||
arguments: Tuple3(
|
||||
transactionIfSentFromStack!, coin, walletId!),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
"View transaction",
|
||||
style: STextStyles.link2(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (sentFromStack)
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (sentFromStack)
|
||||
RoundedWhiteContainer(
|
||||
child: Column(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
|
@ -347,13 +582,24 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(
|
||||
data: trade.payInAddress,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!sentFromStack && !hasTx)
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (!sentFromStack && !hasTx)
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -364,7 +610,11 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
"Send ${trade.payInCurrency.toUpperCase()} to this address",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
GestureDetector(
|
||||
isDesktop
|
||||
? IconCopyButton(
|
||||
data: trade.payInAddress,
|
||||
)
|
||||
: GestureDetector(
|
||||
onTap: () async {
|
||||
final address = trade.payInAddress;
|
||||
await Clipboard.setData(
|
||||
|
@ -417,18 +667,15 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (_) {
|
||||
final width =
|
||||
MediaQuery.of(context).size.width / 2;
|
||||
final width = MediaQuery.of(context).size.width / 2;
|
||||
return StackDialogBase(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
"Send ${trade.payInCurrency.toUpperCase()} to this address",
|
||||
style:
|
||||
STextStyles.pageTitleH2(context),
|
||||
style: STextStyles.pageTitleH2(context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -443,12 +690,10 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
child: QrImage(
|
||||
data: trade.payInAddress,
|
||||
size: width,
|
||||
backgroundColor: Theme.of(
|
||||
context)
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.popupBG,
|
||||
foregroundColor: Theme.of(
|
||||
context)
|
||||
foregroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
|
@ -474,8 +719,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
style: STextStyles.button(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
),
|
||||
|
@ -510,10 +754,15 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -524,7 +773,25 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
"Trade note",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
GestureDetector(
|
||||
isDesktop
|
||||
? IconPencilButton(
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 580,
|
||||
maxHeight: 360,
|
||||
child: EditTradeNoteView(
|
||||
tradeId: tradeId,
|
||||
note: _note,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
EditTradeNoteView.routeName,
|
||||
|
@ -562,19 +829,24 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
height: 4,
|
||||
),
|
||||
SelectableText(
|
||||
ref.watch(tradeNoteServiceProvider.select(
|
||||
(value) => value.getNote(tradeId: tradeId))),
|
||||
ref.watch(tradeNoteServiceProvider
|
||||
.select((value) => value.getNote(tradeId: tradeId))),
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (sentFromStack)
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (sentFromStack)
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -585,7 +857,27 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
"Transaction note",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
GestureDetector(
|
||||
isDesktop
|
||||
? IconPencilButton(
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 580,
|
||||
maxHeight: 360,
|
||||
child: EditNoteView(
|
||||
txid:
|
||||
transactionIfSentFromStack!.txid,
|
||||
walletId: walletId!,
|
||||
note: _note,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
EditNoteView.routeName,
|
||||
|
@ -623,13 +915,12 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
),
|
||||
FutureBuilder(
|
||||
future: ref.watch(
|
||||
notesServiceChangeNotifierProvider(walletId!)
|
||||
.select((value) => value.getNoteFor(
|
||||
notesServiceChangeNotifierProvider(walletId!).select(
|
||||
(value) => value.getNoteFor(
|
||||
txid: transactionIfSentFromStack!.txid))),
|
||||
builder:
|
||||
(builderContext, AsyncSnapshot<String> snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
_note = snapshot.data ?? "";
|
||||
}
|
||||
|
@ -642,42 +933,94 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Date",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
// Flexible(
|
||||
// child: FittedBox(
|
||||
// fit: BoxFit.scaleDown,
|
||||
// child:
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (isDesktop)
|
||||
SelectableText(
|
||||
Format.extractDateFrom(
|
||||
trade.timestamp.millisecondsSinceEpoch ~/ 1000),
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
Format.extractDateFrom(
|
||||
trade.timestamp.millisecondsSinceEpoch ~/ 1000),
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
// ),
|
||||
// ),
|
||||
if (isDesktop)
|
||||
IconCopyButton(
|
||||
data: Format.extractDateFrom(
|
||||
trade.timestamp.millisecondsSinceEpoch ~/ 1000),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Exchange",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (isDesktop)
|
||||
SelectableText(
|
||||
trade.exchangeName,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(
|
||||
data: trade.exchangeName,
|
||||
),
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
trade.exchangeName,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
|
@ -685,17 +1028,42 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Trade ID",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
const Spacer(),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
trade.tradeId,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(
|
||||
data: trade.tradeId,
|
||||
),
|
||||
if (!isDesktop)
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
|
@ -724,14 +1092,19 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -770,10 +1143,13 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (isStackCoin(trade.payInCurrency) &&
|
||||
if (!isDesktop &&
|
||||
!hasTx &&
|
||||
isStackCoin(trade.payInCurrency) &&
|
||||
(trade.status == "New" ||
|
||||
trade.status == "new" ||
|
||||
trade.status == "waiting" ||
|
||||
|
@ -801,8 +1177,18 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Divider extends StatelessWidget {
|
||||
const _Divider({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.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/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
|
||||
class WalletInitiatedExchangeView extends ConsumerStatefulWidget {
|
||||
|
@ -47,7 +48,8 @@ class _WalletInitiatedExchangeViewState
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -117,6 +119,7 @@ class _WalletInitiatedExchangeViewState
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
||||
|
@ -141,10 +142,14 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
debugPrint("BUILD: $runtimeType");
|
||||
return WillPopScope(
|
||||
onWillPop: _onWillPop,
|
||||
child: Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
key: _key,
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.backgroundAppBar,
|
||||
title: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
|
@ -177,7 +182,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
key: const Key("walletsViewAlertsButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.backgroundAppBar,
|
||||
icon: SvgPicture.asset(
|
||||
ref.watch(notificationsProvider
|
||||
.select((value) => value.hasUnreadNotifications))
|
||||
|
@ -208,7 +215,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
for (int i = 0;
|
||||
i < unreadNotificationIds.length - 1;
|
||||
i++) {
|
||||
futures.add(ref.read(notificationsProvider).markAsRead(
|
||||
futures.add(ref
|
||||
.read(notificationsProvider)
|
||||
.markAsRead(
|
||||
unreadNotificationIds.elementAt(i), false));
|
||||
}
|
||||
|
||||
|
@ -236,7 +245,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
key: const Key("walletsViewSettingsButton"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.backgroundAppBar,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.gear,
|
||||
color: Theme.of(context)
|
||||
|
@ -255,15 +266,14 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
),
|
||||
],
|
||||
),
|
||||
body: Container(
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
child: Column(
|
||||
body: Column(
|
||||
children: [
|
||||
if (Constants.enableExchange)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.backgroundAppBar,
|
||||
boxShadow: [
|
||||
Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
|
|
@ -3,14 +3,13 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/stack_privacy_calls.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
|
||||
class IntroView extends StatefulWidget {
|
||||
const IntroView({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -32,7 +31,8 @@ class _IntroViewState extends State<IntroView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType ");
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
body: Center(
|
||||
child: !isDesktop
|
||||
|
@ -145,6 +145,7 @@ class _IntroViewState extends State<IntroView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:lottie/lottie.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
|
||||
class LoadingView extends StatelessWidget {
|
||||
const LoadingView({Key? key}) : super(key: key);
|
||||
|
@ -11,7 +12,8 @@ class LoadingView extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
body: Container(
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
|
@ -32,6 +34,7 @@ class LoadingView extends StatelessWidget {
|
|||
// ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:stackwallet/providers/providers.dart';
|
|||
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
|
@ -43,7 +44,8 @@ class _NotificationsViewState extends ConsumerState<NotificationsView> {
|
|||
.where((element) => element.walletId == widget.walletId)
|
||||
.toList(growable: false);
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
|
@ -104,6 +106,7 @@ class _NotificationsViewState extends ConsumerState<NotificationsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/home_view/home_view.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/biometrics.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
|
@ -13,6 +13,7 @@ import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
|||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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';
|
||||
import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
|
||||
|
||||
|
@ -20,15 +21,11 @@ class CreatePinView extends ConsumerStatefulWidget {
|
|||
const CreatePinView({
|
||||
Key? key,
|
||||
this.popOnSuccess = false,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
this.biometrics = const Biometrics(),
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/createPin";
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
final Biometrics biometrics;
|
||||
final bool popOnSuccess;
|
||||
|
||||
|
@ -58,12 +55,12 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
|
|||
final TextEditingController _pinPutController2 = TextEditingController();
|
||||
final FocusNode _pinPutFocusNode2 = FocusNode();
|
||||
|
||||
late FlutterSecureStorageInterface _secureStore;
|
||||
late SecureStorageInterface _secureStore;
|
||||
late Biometrics biometrics;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
_secureStore = widget.secureStore;
|
||||
_secureStore = ref.read(secureStoreProvider);
|
||||
biometrics = widget.biometrics;
|
||||
super.initState();
|
||||
}
|
||||
|
@ -80,7 +77,8 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -136,8 +134,9 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
|
|||
disabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
focusedErrorBorder: InputBorder.none,
|
||||
fillColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
fillColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
counterText: "",
|
||||
),
|
||||
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
||||
|
@ -204,8 +203,9 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
|
|||
disabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
focusedErrorBorder: InputBorder.none,
|
||||
fillColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
fillColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
counterText: "",
|
||||
),
|
||||
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
||||
|
@ -291,6 +291,7 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/home_view/home_view.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||
// import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
// import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
|
@ -17,6 +17,7 @@ import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
|||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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';
|
||||
import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
|
||||
import 'package:stackwallet/widgets/shake/shake.dart';
|
||||
|
@ -33,9 +34,6 @@ class LockscreenView extends ConsumerStatefulWidget {
|
|||
this.popOnSuccess = false,
|
||||
this.isInitialAppLogin = false,
|
||||
this.routeOnSuccessArguments,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
this.biometrics = const Biometrics(),
|
||||
this.onSuccess,
|
||||
}) : super(key: key);
|
||||
|
@ -50,7 +48,6 @@ class LockscreenView extends ConsumerStatefulWidget {
|
|||
final String biometricsAuthenticationTitle;
|
||||
final String biometricsLocalizedReason;
|
||||
final String biometricsCancelButtonString;
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
final Biometrics biometrics;
|
||||
final VoidCallback? onSuccess;
|
||||
|
||||
|
@ -134,7 +131,7 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
void initState() {
|
||||
_shakeController = ShakeController();
|
||||
|
||||
_secureStore = widget.secureStore;
|
||||
_secureStore = ref.read(secureStoreProvider);
|
||||
biometrics = widget.biometrics;
|
||||
_attempts = 0;
|
||||
_timeout = Duration.zero;
|
||||
|
@ -162,11 +159,13 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
final _pinTextController = TextEditingController();
|
||||
final FocusNode _pinFocusNode = FocusNode();
|
||||
|
||||
late FlutterSecureStorageInterface _secureStore;
|
||||
late SecureStorageInterface _secureStore;
|
||||
late Biometrics biometrics;
|
||||
|
||||
Scaffold get _body => Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
Widget get _body => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: widget.showBackButton
|
||||
? AppBarBackButton(
|
||||
|
@ -274,7 +273,8 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
_timeout = const Duration(minutes: 60);
|
||||
}
|
||||
|
||||
unawaited(Future<void>.delayed(_timeout).then((_) {
|
||||
unawaited(
|
||||
Future<void>.delayed(_timeout).then((_) {
|
||||
_attemptLock = false;
|
||||
_attempts = 0;
|
||||
}));
|
||||
|
@ -334,6 +334,7 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
// import 'package:document_file_save_plus/document_file_save_plus.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
@ -21,6 +23,7 @@ import 'package:stackwallet/utilities/logger.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
|
@ -71,19 +74,53 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
Uint8List pngBytes = byteData!.buffer.asUint8List();
|
||||
|
||||
// if (shouldSaveInsteadOfShare) {
|
||||
if (shouldSaveInsteadOfShare) {
|
||||
if (Util.isDesktop) {
|
||||
final dir = Directory("${Platform.environment['HOME']}");
|
||||
if (!dir.existsSync()) {
|
||||
throw Exception(
|
||||
"Home dir not found while trying to open filepicker on QR image save");
|
||||
}
|
||||
final path = await FilePicker.platform.saveFile(
|
||||
fileName: "qrcode.png",
|
||||
initialDirectory: dir.path,
|
||||
);
|
||||
|
||||
if (path != null) {
|
||||
final file = File(path);
|
||||
if (file.existsSync()) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "$path already exists!",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await file.writeAsBytes(pngBytes);
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.success,
|
||||
message: "$path saved!",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// await DocumentFileSavePlus.saveFile(
|
||||
// pngBytes,
|
||||
// "receive_qr_code_${DateTime.now().toLocal().toIso8601String()}.png",
|
||||
// "image/png");
|
||||
// } else {
|
||||
}
|
||||
} else {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final file = await File("${tempDir.path}/qrcode.png").create();
|
||||
await file.writeAsBytes(pngBytes);
|
||||
|
||||
await Share.shareFiles(["${tempDir.path}/qrcode.png"],
|
||||
text: "Receive URI QR Code");
|
||||
// }
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
|
@ -269,8 +306,10 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
|
@ -313,6 +352,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.only(
|
||||
|
@ -494,7 +534,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
});
|
||||
}
|
||||
: onGeneratePressed,
|
||||
desktopMed: true,
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
),
|
||||
if (isDesktop && didGenerate)
|
||||
Row(
|
||||
|
@ -511,6 +551,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
width: isDesktop ? 370 : null,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
|
@ -542,10 +583,15 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
height: 12,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: isDesktop
|
||||
? MainAxisAlignment.center
|
||||
: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
SecondaryButton(
|
||||
width: 170,
|
||||
desktopMed: true,
|
||||
buttonHeight:
|
||||
isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
await _capturePng(false);
|
||||
},
|
||||
|
@ -559,14 +605,17 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
|||
.buttonTextSecondary,
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
PrimaryButton(
|
||||
width: 170,
|
||||
desktopMed: true,
|
||||
buttonHeight:
|
||||
isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
// TODO: add save functionality instead of share
|
||||
// save works on linux at the moment
|
||||
await _capturePng(true);
|
||||
},
|
||||
label: "Save",
|
||||
|
|
|
@ -12,9 +12,9 @@ import 'package:stackwallet/route_generator.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';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||
|
@ -115,7 +115,8 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
|||
}
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -260,6 +261,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:stackwallet/notifications/show_flush_bar.dart';
|
|||
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
|
@ -21,8 +22,11 @@ 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/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -34,6 +38,7 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
|
|||
required this.transactionInfo,
|
||||
required this.walletId,
|
||||
this.routeOnSuccessName = WalletView.routeName,
|
||||
this.isTradeTransaction = false,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/confirmTransactionView";
|
||||
|
@ -41,6 +46,7 @@ class ConfirmTransactionView extends ConsumerStatefulWidget {
|
|||
final Map<String, dynamic> transactionInfo;
|
||||
final String walletId;
|
||||
final String routeOnSuccessName;
|
||||
final bool isTradeTransaction;
|
||||
|
||||
@override
|
||||
ConsumerState<ConfirmTransactionView> createState() =>
|
||||
|
@ -54,22 +60,17 @@ class _ConfirmTransactionViewState
|
|||
late final String routeOnSuccessName;
|
||||
late final bool isDesktop;
|
||||
|
||||
int _fee = 12;
|
||||
final List<int> _dropDownItems = [
|
||||
12,
|
||||
22,
|
||||
234,
|
||||
];
|
||||
|
||||
Future<void> _attemptSend(BuildContext context) async {
|
||||
unawaited(showDialog<dynamic>(
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
useSafeArea: false,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return const SendingTransactionDialog();
|
||||
},
|
||||
));
|
||||
),
|
||||
);
|
||||
|
||||
final note = transactionInfo["note"] as String? ?? "";
|
||||
final manager =
|
||||
|
@ -122,6 +123,46 @@ class _ConfirmTransactionViewState
|
|||
useSafeArea: false,
|
||||
barrierDismissible: true,
|
||||
builder: (context) {
|
||||
if (isDesktop) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 450,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Broadcast transaction failed",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Text(
|
||||
e.toString(),
|
||||
style: STextStyles.smallMed14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 56,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
buttonHeight: ButtonHeight.l,
|
||||
label: "Ok",
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return StackDialog(
|
||||
title: "Broadcast transaction failed",
|
||||
message: e.toString(),
|
||||
|
@ -141,6 +182,7 @@ class _ConfirmTransactionViewState
|
|||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -162,8 +204,10 @@ class _ConfirmTransactionViewState
|
|||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
|
@ -206,6 +250,7 @@ class _ConfirmTransactionViewState
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => Column(
|
||||
|
@ -276,13 +321,12 @@ class _ConfirmTransactionViewState
|
|||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
transactionInfo["recipientAmt"] as int,
|
||||
ref.watch(
|
||||
"${Format.satoshiAmountToPrettyString(transactionInfo["recipientAmt"] as int, ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
), ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
))} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
|
@ -303,13 +347,12 @@ class _ConfirmTransactionViewState
|
|||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
transactionInfo["fee"] as int,
|
||||
ref.watch(
|
||||
"${Format.satoshiAmountToPrettyString(transactionInfo["fee"] as int, ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
), ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
))} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
|
@ -453,6 +496,7 @@ class _ConfirmTransactionViewState
|
|||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
coin,
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
|
@ -573,26 +617,91 @@ class _ConfirmTransactionViewState
|
|||
left: 32,
|
||||
right: 32,
|
||||
),
|
||||
child: DropdownButtonFormField(
|
||||
value: _fee,
|
||||
items: _dropDownItems
|
||||
.map(
|
||||
(e) => DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(
|
||||
e.toString(),
|
||||
),
|
||||
child: RoundedContainer(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 18,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
child: Builder(builder: (context) {
|
||||
final coin = ref
|
||||
.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(walletId)))
|
||||
.coin;
|
||||
|
||||
final fee = Format.satoshisToAmount(
|
||||
transactionInfo["fee"] as int,
|
||||
coin: coin,
|
||||
);
|
||||
|
||||
return Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: fee,
|
||||
locale: ref.watch(localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale)),
|
||||
decimalPlaces: Constants.decimalPlacesForCoin(coin),
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
}),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
if (value is int) {
|
||||
setState(() {
|
||||
_fee = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
// DropdownButtonHideUnderline(
|
||||
// child: DropdownButton2(
|
||||
// offset: const Offset(0, -10),
|
||||
// isExpanded: true,
|
||||
//
|
||||
// dropdownElevation: 0,
|
||||
// value: _fee,
|
||||
// items: [
|
||||
// ..._dropDownItems.map(
|
||||
// (e) {
|
||||
// String message = _fee.toString();
|
||||
//
|
||||
// return DropdownMenuItem(
|
||||
// value: e,
|
||||
// child: Text(message),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// onChanged: (value) {
|
||||
// if (value is int) {
|
||||
// setState(() {
|
||||
// _fee = value;
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// icon: SvgPicture.asset(
|
||||
// Assets.svg.chevronDown,
|
||||
// width: 12,
|
||||
// height: 6,
|
||||
// color:
|
||||
// Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
// ),
|
||||
// buttonPadding: const EdgeInsets.symmetric(
|
||||
// horizontal: 16,
|
||||
// vertical: 8,
|
||||
// ),
|
||||
// buttonDecoration: BoxDecoration(
|
||||
// color: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldDefaultBG,
|
||||
// borderRadius: BorderRadius.circular(
|
||||
// Constants.size.circularBorderRadius,
|
||||
// ),
|
||||
// ),
|
||||
// dropdownDecoration: BoxDecoration(
|
||||
// color: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldDefaultBG,
|
||||
// borderRadius: BorderRadius.circular(
|
||||
// Constants.size.circularBorderRadius,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
if (!isDesktop) const Spacer(),
|
||||
SizedBox(
|
||||
|
@ -640,6 +749,9 @@ class _ConfirmTransactionViewState
|
|||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
),
|
||||
ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
),
|
||||
)} ${ref.watch(
|
||||
managerProvider.select((value) => value.coin),
|
||||
).ticker}",
|
||||
|
@ -672,9 +784,46 @@ class _ConfirmTransactionViewState
|
|||
: const EdgeInsets.all(0),
|
||||
child: PrimaryButton(
|
||||
label: "Send",
|
||||
desktopMed: true,
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
final unlocked = await Navigator.push(
|
||||
final dynamic unlocked;
|
||||
|
||||
final coin = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(walletId)
|
||||
.coin;
|
||||
|
||||
if (isDesktop) {
|
||||
unlocked = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (context) => DesktopDialog(
|
||||
maxWidth: 580,
|
||||
maxHeight: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: const [
|
||||
DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: DesktopAuthSend(
|
||||
coin: coin,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unlocked = await Navigator.push(
|
||||
context,
|
||||
RouteGenerator.getRoute(
|
||||
shouldUseMaterialRoute:
|
||||
|
@ -693,9 +842,21 @@ class _ConfirmTransactionViewState
|
|||
const RouteSettings(name: "/confirmsendlockscreen"),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (unlocked is bool && unlocked && mounted) {
|
||||
if (mounted) {
|
||||
if (unlocked == true) {
|
||||
unawaited(_attemptSend(context));
|
||||
} else {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: Util.isDesktop
|
||||
? "Invalid passphrase"
|
||||
: "Invalid PIN",
|
||||
context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -30,7 +31,9 @@ import 'package:stackwallet/utilities/logger.dart';
|
|||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
|
@ -41,8 +44,6 @@ import 'package:stackwallet/widgets/stack_dialog.dart';
|
|||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class SendView extends ConsumerStatefulWidget {
|
||||
const SendView({
|
||||
Key? key,
|
||||
|
@ -211,29 +212,47 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
}
|
||||
|
||||
int fee;
|
||||
if (coin == Coin.monero) {
|
||||
MoneroTransactionPriority specialMoneroId;
|
||||
switch (ref.read(feeRateTypeStateProvider.state).state) {
|
||||
case FeeRateType.fast:
|
||||
specialMoneroId = MoneroTransactionPriority.fast;
|
||||
break;
|
||||
case FeeRateType.average:
|
||||
specialMoneroId = MoneroTransactionPriority.regular;
|
||||
break;
|
||||
case FeeRateType.slow:
|
||||
specialMoneroId = MoneroTransactionPriority.slow;
|
||||
break;
|
||||
}
|
||||
|
||||
if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||
fee = await manager.estimateFeeFor(amount, specialMoneroId.raw!);
|
||||
cachedFees[amount] = Format.satoshisToAmount(fee, coin: coin)
|
||||
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
|
||||
|
||||
return cachedFees[amount]!;
|
||||
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
|
||||
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
|
||||
"Private") {
|
||||
fee = await manager.estimateFeeFor(amount, feeRate);
|
||||
|
||||
cachedFiroPrivateFees[amount] = Format.satoshisToAmount(fee)
|
||||
.toStringAsFixed(Constants.decimalPlaces);
|
||||
cachedFiroPrivateFees[amount] = Format.satoshisToAmount(fee, coin: coin)
|
||||
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
|
||||
|
||||
return cachedFiroPrivateFees[amount]!;
|
||||
} else {
|
||||
fee = await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
|
||||
cachedFiroPublicFees[amount] = Format.satoshisToAmount(fee)
|
||||
.toStringAsFixed(Constants.decimalPlaces);
|
||||
cachedFiroPublicFees[amount] = Format.satoshisToAmount(fee, coin: coin)
|
||||
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
|
||||
|
||||
return cachedFiroPublicFees[amount]!;
|
||||
}
|
||||
} else {
|
||||
fee = await manager.estimateFeeFor(amount, feeRate);
|
||||
cachedFees[amount] =
|
||||
Format.satoshisToAmount(fee).toStringAsFixed(Constants.decimalPlaces);
|
||||
cachedFees[amount] = Format.satoshisToAmount(fee, coin: coin)
|
||||
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
|
||||
|
||||
return cachedFees[amount]!;
|
||||
}
|
||||
|
@ -296,8 +315,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_calculateFeesFuture =
|
||||
calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
|
||||
_calculateFeesFuture = calculateFees(
|
||||
Format.decimalAmountToSatoshis(_amountToSend!, coin));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -311,8 +330,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_calculateFeesFuture =
|
||||
calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
|
||||
_calculateFeesFuture = calculateFees(
|
||||
Format.decimalAmountToSatoshis(_amountToSend!, coin));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -354,14 +373,15 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_calculateFeesFuture =
|
||||
calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
|
||||
_calculateFeesFuture = calculateFees(
|
||||
Format.decimalAmountToSatoshis(_amountToSend!, coin));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -425,8 +445,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
coin != Coin.firoTestNet)
|
||||
Expanded(
|
||||
child: Text(
|
||||
ref.watch(provider
|
||||
.select((value) => value.walletName)),
|
||||
ref.watch(provider.select(
|
||||
(value) => value.walletName)),
|
||||
style: STextStyles.titleBold12(context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
|
@ -441,7 +461,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
Text(
|
||||
ref.watch(provider.select(
|
||||
(value) => value.walletName)),
|
||||
style: STextStyles.titleBold12(context)
|
||||
style:
|
||||
STextStyles.titleBold12(context)
|
||||
.copyWith(fontSize: 14),
|
||||
),
|
||||
// const SizedBox(
|
||||
|
@ -492,7 +513,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
onTap: () {
|
||||
cryptoAmountController.text =
|
||||
_cachedBalance!.toStringAsFixed(
|
||||
Constants.decimalPlaces);
|
||||
Constants
|
||||
.decimalPlacesForCoin(
|
||||
coin));
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
|
@ -506,7 +529,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
locale: locale,
|
||||
decimalPlaces: 8,
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles.titleBold12(
|
||||
style:
|
||||
STextStyles.titleBold12(
|
||||
context)
|
||||
.copyWith(
|
||||
fontSize: 10,
|
||||
|
@ -516,8 +540,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: _cachedBalance! *
|
||||
ref.watch(
|
||||
priceAnd24hChangeNotifierProvider
|
||||
ref.watch(priceAnd24hChangeNotifierProvider
|
||||
.select((value) =>
|
||||
value
|
||||
.getPrice(
|
||||
|
@ -526,8 +549,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
locale: locale,
|
||||
decimalPlaces: 2,
|
||||
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
|
||||
style:
|
||||
STextStyles.titleBold12_400(
|
||||
style: STextStyles
|
||||
.titleBold12_400(
|
||||
context)
|
||||
.copyWith(
|
||||
fontSize: 8,
|
||||
|
@ -669,10 +692,13 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
data!.text!.isNotEmpty) {
|
||||
String content =
|
||||
data.text!.trim();
|
||||
if (content.contains("\n")) {
|
||||
content = content.substring(
|
||||
if (content
|
||||
.contains("\n")) {
|
||||
content =
|
||||
content.substring(
|
||||
0,
|
||||
content.indexOf("\n"));
|
||||
content.indexOf(
|
||||
"\n"));
|
||||
}
|
||||
|
||||
sendToController.text =
|
||||
|
@ -680,7 +706,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
_address = content;
|
||||
|
||||
_updatePreviewButtonState(
|
||||
_address, _amountToSend);
|
||||
_address,
|
||||
_amountToSend);
|
||||
setState(() {
|
||||
_addressToggleFlag =
|
||||
sendToController
|
||||
|
@ -688,8 +715,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
});
|
||||
}
|
||||
},
|
||||
child:
|
||||
sendToController.text.isEmpty
|
||||
child: sendToController
|
||||
.text.isEmpty
|
||||
? const ClipboardIcon()
|
||||
: const XIcon(),
|
||||
),
|
||||
|
@ -770,10 +797,13 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
}
|
||||
|
||||
// autofill amount field
|
||||
if (results["amount"] != null) {
|
||||
final amount = Decimal.parse(
|
||||
if (results["amount"] !=
|
||||
null) {
|
||||
final amount =
|
||||
Decimal.parse(
|
||||
results["amount"]!);
|
||||
cryptoAmountController.text =
|
||||
cryptoAmountController
|
||||
.text =
|
||||
Format
|
||||
.localizedStringAsFixed(
|
||||
value: amount,
|
||||
|
@ -781,8 +811,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.read(
|
||||
localeServiceChangeNotifierProvider)
|
||||
.locale,
|
||||
decimalPlaces:
|
||||
Constants.decimalPlaces,
|
||||
decimalPlaces: Constants
|
||||
.decimalPlacesForCoin(
|
||||
coin),
|
||||
);
|
||||
amount.toString();
|
||||
_amountToSend = amount;
|
||||
|
@ -803,7 +834,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.getManager(walletId)
|
||||
.validateAddress(
|
||||
qrResult.rawContent)) {
|
||||
_address = qrResult.rawContent;
|
||||
_address =
|
||||
qrResult.rawContent;
|
||||
sendToController.text =
|
||||
_address ?? "";
|
||||
|
||||
|
@ -859,7 +891,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
child: Text(
|
||||
error,
|
||||
textAlign: TextAlign.left,
|
||||
style: STextStyles.label(context).copyWith(
|
||||
style:
|
||||
STextStyles.label(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textError,
|
||||
|
@ -916,7 +949,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
top: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
builder: (_) => FiroBalanceSelectionSheet(
|
||||
builder: (_) =>
|
||||
FiroBalanceSelectionSheet(
|
||||
walletId: walletId,
|
||||
),
|
||||
);
|
||||
|
@ -967,9 +1001,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
null) {
|
||||
return Text(
|
||||
"$_privateBalanceString ${coin.ticker}",
|
||||
style:
|
||||
STextStyles.itemSubtitle(
|
||||
context),
|
||||
style: STextStyles
|
||||
.itemSubtitle(context),
|
||||
);
|
||||
} else if (ref
|
||||
.read(
|
||||
|
@ -981,9 +1014,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
null) {
|
||||
return Text(
|
||||
"$_publicBalanceString ${coin.ticker}",
|
||||
style:
|
||||
STextStyles.itemSubtitle(
|
||||
context),
|
||||
style: STextStyles
|
||||
.itemSubtitle(context),
|
||||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
|
@ -993,9 +1025,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
"Loading balance..",
|
||||
"Loading balance...",
|
||||
],
|
||||
style:
|
||||
STextStyles.itemSubtitle(
|
||||
context),
|
||||
style: STextStyles
|
||||
.itemSubtitle(context),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -1043,20 +1074,22 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
cryptoAmountController.text =
|
||||
(await firoWallet
|
||||
.availablePrivateBalance())
|
||||
.toStringAsFixed(
|
||||
Constants.decimalPlaces);
|
||||
.toStringAsFixed(Constants
|
||||
.decimalPlacesForCoin(coin));
|
||||
} else {
|
||||
cryptoAmountController.text =
|
||||
(await firoWallet
|
||||
.availablePublicBalance())
|
||||
.toStringAsFixed(
|
||||
Constants.decimalPlaces);
|
||||
.toStringAsFixed(Constants
|
||||
.decimalPlacesForCoin(coin));
|
||||
}
|
||||
} else {
|
||||
cryptoAmountController.text = (await ref
|
||||
.read(provider)
|
||||
.availableBalance)
|
||||
.toStringAsFixed(Constants.decimalPlaces);
|
||||
.toStringAsFixed(
|
||||
Constants.decimalPlacesForCoin(
|
||||
coin));
|
||||
}
|
||||
},
|
||||
),
|
||||
|
@ -1073,7 +1106,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
key: const Key("amountInputFieldCryptoTextFieldKey"),
|
||||
key:
|
||||
const Key("amountInputFieldCryptoTextFieldKey"),
|
||||
controller: cryptoAmountController,
|
||||
focusNode: _cryptoFocus,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
|
@ -1096,7 +1130,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
right: 12,
|
||||
),
|
||||
hintText: "0",
|
||||
hintStyle: STextStyles.fieldLabel(context).copyWith(
|
||||
hintStyle:
|
||||
STextStyles.fieldLabel(context).copyWith(
|
||||
fontSize: 14,
|
||||
),
|
||||
prefixIcon: FittedBox(
|
||||
|
@ -1128,10 +1163,12 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
key: const Key("amountInputFieldFiatTextFieldKey"),
|
||||
key:
|
||||
const Key("amountInputFieldFiatTextFieldKey"),
|
||||
controller: baseAmountController,
|
||||
focusNode: _baseFocus,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: true,
|
||||
),
|
||||
|
@ -1149,10 +1186,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
if (baseAmountString.isNotEmpty &&
|
||||
baseAmountString != "." &&
|
||||
baseAmountString != ",") {
|
||||
final baseAmount = baseAmountString
|
||||
.contains(",")
|
||||
? Decimal.parse(
|
||||
baseAmountString.replaceFirst(",", "."))
|
||||
final baseAmount =
|
||||
baseAmountString.contains(",")
|
||||
? Decimal.parse(baseAmountString
|
||||
.replaceFirst(",", "."))
|
||||
: Decimal.parse(baseAmountString);
|
||||
|
||||
var _price = ref
|
||||
|
@ -1167,7 +1204,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
? Decimal.zero
|
||||
: (baseAmount / _price).toDecimal(
|
||||
scaleOnInfinitePrecision:
|
||||
Constants.decimalPlaces);
|
||||
Constants.decimalPlacesForCoin(
|
||||
coin));
|
||||
}
|
||||
if (_cachedAmountToSend != null &&
|
||||
_cachedAmountToSend == _amountToSend) {
|
||||
|
@ -1182,9 +1220,11 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
Format.localizedStringAsFixed(
|
||||
value: _amountToSend!,
|
||||
locale: ref
|
||||
.read(localeServiceChangeNotifierProvider)
|
||||
.read(
|
||||
localeServiceChangeNotifierProvider)
|
||||
.locale,
|
||||
decimalPlaces: Constants.decimalPlaces,
|
||||
decimalPlaces:
|
||||
Constants.decimalPlacesForCoin(coin),
|
||||
);
|
||||
|
||||
_cryptoAmountChangeLock = true;
|
||||
|
@ -1260,7 +1300,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
).copyWith(
|
||||
suffixIcon: noteController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
padding:
|
||||
const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -1295,7 +1336,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
children: [
|
||||
TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
enableSuggestions:
|
||||
Util.isDesktop ? false : true,
|
||||
controller: feeController,
|
||||
readOnly: true,
|
||||
textInputAction: TextInputAction.none,
|
||||
|
@ -1327,7 +1369,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
backgroundColor: Colors.transparent,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
borderRadius:
|
||||
BorderRadius.vertical(
|
||||
top: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
|
@ -1365,9 +1408,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
snapshot.hasData) {
|
||||
return Text(
|
||||
"~${snapshot.data! as String} ${coin.ticker}",
|
||||
style:
|
||||
STextStyles.itemSubtitle(
|
||||
context),
|
||||
style: STextStyles
|
||||
.itemSubtitle(context),
|
||||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
|
@ -1377,9 +1419,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
"Calculating..",
|
||||
"Calculating...",
|
||||
],
|
||||
style:
|
||||
STextStyles.itemSubtitle(
|
||||
context),
|
||||
style: STextStyles
|
||||
.itemSubtitle(context),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -1399,9 +1440,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.state)
|
||||
.state
|
||||
.prettyName,
|
||||
style:
|
||||
STextStyles.itemSubtitle12(
|
||||
context),
|
||||
style: STextStyles
|
||||
.itemSubtitle12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
|
@ -1488,7 +1528,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
context),
|
||||
child: Text(
|
||||
"Ok",
|
||||
style: STextStyles.button(context)
|
||||
style: STextStyles.button(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
|
@ -1505,8 +1546,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
return;
|
||||
}
|
||||
|
||||
final amount = Format.decimalAmountToSatoshis(
|
||||
_amountToSend!);
|
||||
final amount =
|
||||
Format.decimalAmountToSatoshis(
|
||||
_amountToSend!, coin);
|
||||
int availableBalance;
|
||||
if ((coin == Coin.firo ||
|
||||
coin == Coin.firoTestNet)) {
|
||||
|
@ -1520,18 +1562,21 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
Format.decimalAmountToSatoshis(
|
||||
await (manager.wallet
|
||||
as FiroWallet)
|
||||
.availablePrivateBalance());
|
||||
.availablePrivateBalance(),
|
||||
coin);
|
||||
} else {
|
||||
availableBalance =
|
||||
Format.decimalAmountToSatoshis(
|
||||
await (manager.wallet
|
||||
as FiroWallet)
|
||||
.availablePublicBalance());
|
||||
.availablePublicBalance(),
|
||||
coin);
|
||||
}
|
||||
} else {
|
||||
availableBalance =
|
||||
Format.decimalAmountToSatoshis(
|
||||
await manager.availableBalance);
|
||||
await manager.availableBalance,
|
||||
coin);
|
||||
}
|
||||
|
||||
// confirm send all
|
||||
|
@ -1553,7 +1598,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: STextStyles.button(context)
|
||||
style: STextStyles.button(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
|
@ -1561,7 +1607,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.accentColorDark),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
Navigator.of(context)
|
||||
.pop(false);
|
||||
},
|
||||
),
|
||||
rightButton: TextButton(
|
||||
|
@ -1623,8 +1670,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
address: _address!,
|
||||
satoshiAmount: amount,
|
||||
args: {
|
||||
"feeRate":
|
||||
ref.read(feeRateTypeStateProvider)
|
||||
"feeRate": ref
|
||||
.read(feeRateTypeStateProvider)
|
||||
},
|
||||
);
|
||||
} else {
|
||||
|
@ -1632,8 +1679,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
address: _address!,
|
||||
satoshiAmount: amount,
|
||||
args: {
|
||||
"feeRate":
|
||||
ref.read(feeRateTypeStateProvider)
|
||||
"feeRate": ref
|
||||
.read(feeRateTypeStateProvider)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1646,7 +1693,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
|
||||
unawaited(Navigator.of(context).push(
|
||||
RouteGenerator.getRoute(
|
||||
shouldUseMaterialRoute: RouteGenerator
|
||||
shouldUseMaterialRoute:
|
||||
RouteGenerator
|
||||
.useMaterialPageRoute,
|
||||
builder: (_) =>
|
||||
ConfirmTransactionView(
|
||||
|
@ -1683,7 +1731,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
style: STextStyles.button(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
color: Theme.of(
|
||||
context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.accentColorDark),
|
||||
|
@ -1725,6 +1774,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ class _RestoringDialogState extends State<BuildingTransactionDialog>
|
|||
height: 40,
|
||||
),
|
||||
SecondaryButton(
|
||||
desktopMed: true,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
label: "Cancel",
|
||||
onPressed: () {
|
||||
onCancel.call();
|
||||
|
|
|
@ -161,7 +161,7 @@ class _FiroBalanceSelectionSheetState
|
|||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
return Text(
|
||||
"${snapshot.data!} ${manager.coin.ticker}",
|
||||
"${snapshot.data!.toStringAsFixed(8)} ${manager.coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
textAlign: TextAlign.left,
|
||||
);
|
||||
|
@ -251,7 +251,7 @@ class _FiroBalanceSelectionSheetState
|
|||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
return Text(
|
||||
"${snapshot.data!} ${manager.coin.ticker}",
|
||||
"${snapshot.data!.toStringAsFixed(8)} ${manager.coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
textAlign: TextAlign.left,
|
||||
);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
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/stack_dialog.dart';
|
||||
|
||||
class SendingTransactionDialog extends StatefulWidget {
|
||||
|
@ -43,6 +46,36 @@ class _RestoringDialogState extends State<SendingTransactionDialog>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Util.isDesktop) {
|
||||
return DesktopDialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(40),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Sending transaction",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
RotationTransition(
|
||||
turns: _spinAnimation,
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.arrowRotate,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
return false;
|
||||
|
@ -55,7 +88,8 @@ class _RestoringDialogState extends State<SendingTransactionDialog>
|
|||
turns: _spinAnimation,
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.arrowRotate,
|
||||
color: Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
|
@ -63,4 +97,5 @@ class _RestoringDialogState extends State<SendingTransactionDialog>
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
@ -70,16 +71,27 @@ class _TransactionFeeSelectionSheetState
|
|||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.fast.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
Format.satoshisToAmount(
|
||||
fee,
|
||||
coin: coin,
|
||||
);
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate));
|
||||
Format.satoshisToAmount(
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate),
|
||||
coin: coin);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
Format.satoshisToAmount(
|
||||
await manager.estimateFeeFor(amount, feeRate));
|
||||
await manager.estimateFeeFor(amount, feeRate),
|
||||
coin: coin);
|
||||
}
|
||||
}
|
||||
return ref.read(feeSheetSessionCacheProvider).fast[amount]!;
|
||||
|
@ -88,17 +100,27 @@ class _TransactionFeeSelectionSheetState
|
|||
if (ref.read(feeSheetSessionCacheProvider).average[amount] == null) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.regular.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
Format.satoshisToAmount(
|
||||
fee,
|
||||
coin: coin,
|
||||
);
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate));
|
||||
Format.satoshisToAmount(
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate),
|
||||
coin: coin);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
Format.satoshisToAmount(
|
||||
await manager.estimateFeeFor(amount, feeRate));
|
||||
await manager.estimateFeeFor(amount, feeRate),
|
||||
coin: coin);
|
||||
}
|
||||
}
|
||||
return ref.read(feeSheetSessionCacheProvider).average[amount]!;
|
||||
|
@ -107,17 +129,27 @@ class _TransactionFeeSelectionSheetState
|
|||
if (ref.read(feeSheetSessionCacheProvider).slow[amount] == null) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.slow.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
Format.satoshisToAmount(
|
||||
fee,
|
||||
coin: coin,
|
||||
);
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate));
|
||||
Format.satoshisToAmount(
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate),
|
||||
coin: coin);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
Format.satoshisToAmount(
|
||||
await manager.estimateFeeFor(amount, feeRate));
|
||||
await manager.estimateFeeFor(amount, feeRate),
|
||||
coin: coin);
|
||||
}
|
||||
}
|
||||
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
|
||||
|
@ -225,7 +257,7 @@ class _TransactionFeeSelectionSheetState
|
|||
ref.read(feeRateTypeStateProvider.state).state =
|
||||
FeeRateType.fast;
|
||||
}
|
||||
String? fee = getAmount(FeeRateType.fast);
|
||||
String? fee = getAmount(FeeRateType.fast, manager.coin);
|
||||
if (fee != null) {
|
||||
widget.updateChosen(fee);
|
||||
}
|
||||
|
@ -293,7 +325,7 @@ class _TransactionFeeSelectionSheetState
|
|||
feeRate: feeObject!.fast,
|
||||
amount: Format
|
||||
.decimalAmountToSatoshis(
|
||||
amount)),
|
||||
amount, manager.coin)),
|
||||
// future: manager.estimateFeeFor(
|
||||
// Format.decimalAmountToSatoshis(
|
||||
// amount),
|
||||
|
@ -358,7 +390,8 @@ class _TransactionFeeSelectionSheetState
|
|||
ref.read(feeRateTypeStateProvider.state).state =
|
||||
FeeRateType.average;
|
||||
}
|
||||
String? fee = getAmount(FeeRateType.average);
|
||||
String? fee =
|
||||
getAmount(FeeRateType.average, manager.coin);
|
||||
if (fee != null) {
|
||||
widget.updateChosen(fee);
|
||||
}
|
||||
|
@ -424,7 +457,7 @@ class _TransactionFeeSelectionSheetState
|
|||
feeRate: feeObject!.medium,
|
||||
amount: Format
|
||||
.decimalAmountToSatoshis(
|
||||
amount)),
|
||||
amount, manager.coin)),
|
||||
// future: manager.estimateFeeFor(
|
||||
// Format.decimalAmountToSatoshis(
|
||||
// amount),
|
||||
|
@ -489,7 +522,7 @@ class _TransactionFeeSelectionSheetState
|
|||
ref.read(feeRateTypeStateProvider.state).state =
|
||||
FeeRateType.slow;
|
||||
}
|
||||
String? fee = getAmount(FeeRateType.slow);
|
||||
String? fee = getAmount(FeeRateType.slow, manager.coin);
|
||||
print("fee $fee");
|
||||
if (fee != null) {
|
||||
widget.updateChosen(fee);
|
||||
|
@ -557,7 +590,7 @@ class _TransactionFeeSelectionSheetState
|
|||
feeRate: feeObject!.slow,
|
||||
amount: Format
|
||||
.decimalAmountToSatoshis(
|
||||
amount)),
|
||||
amount, manager.coin)),
|
||||
// future: manager.estimateFeeFor(
|
||||
// Format.decimalAmountToSatoshis(
|
||||
// amount),
|
||||
|
@ -624,10 +657,10 @@ class _TransactionFeeSelectionSheetState
|
|||
);
|
||||
}
|
||||
|
||||
String? getAmount(FeeRateType feeRateType) {
|
||||
String? getAmount(FeeRateType feeRateType, Coin coin) {
|
||||
try {
|
||||
print(feeRateType);
|
||||
var amount = Format.decimalAmountToSatoshis(this.amount);
|
||||
var amount = Format.decimalAmountToSatoshis(this.amount, coin);
|
||||
print(amount);
|
||||
print(ref.read(feeSheetSessionCacheProvider).fast);
|
||||
print(ref.read(feeSheetSessionCacheProvider).average);
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:package_info_plus/package_info_plus.dart';
|
|||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -117,7 +118,8 @@ class AboutView extends ConsumerWidget {
|
|||
];
|
||||
Future commitMoneroFuture = Future.wait(futureMoneroList);
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -187,7 +189,8 @@ class AboutView extends ConsumerWidget {
|
|||
),
|
||||
SelectableText(
|
||||
version,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
style:
|
||||
STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -209,7 +212,8 @@ class AboutView extends ConsumerWidget {
|
|||
),
|
||||
SelectableText(
|
||||
build,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
style:
|
||||
STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -231,7 +235,8 @@ class AboutView extends ConsumerWidget {
|
|||
),
|
||||
SelectableText(
|
||||
signature,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
style:
|
||||
STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -245,10 +250,12 @@ class AboutView extends ConsumerWidget {
|
|||
),
|
||||
FutureBuilder(
|
||||
future: commitFiroFuture,
|
||||
builder: (context, AsyncSnapshot<dynamic> snapshot) {
|
||||
builder:
|
||||
(context, AsyncSnapshot<dynamic> snapshot) {
|
||||
bool commitExists = false;
|
||||
bool isHead = false;
|
||||
CommitStatus stateOfCommit = CommitStatus.notLoaded;
|
||||
CommitStatus stateOfCommit =
|
||||
CommitStatus.notLoaded;
|
||||
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
|
@ -268,21 +275,24 @@ class AboutView extends ConsumerWidget {
|
|||
switch (stateOfCommit) {
|
||||
case CommitStatus.isHead:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen);
|
||||
break;
|
||||
case CommitStatus.isOldCommit:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorYellow);
|
||||
break;
|
||||
case CommitStatus.notACommit:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed);
|
||||
|
@ -292,7 +302,8 @@ class AboutView extends ConsumerWidget {
|
|||
}
|
||||
return RoundedWhiteContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Firo Build Commit",
|
||||
|
@ -314,10 +325,12 @@ class AboutView extends ConsumerWidget {
|
|||
),
|
||||
FutureBuilder(
|
||||
future: commitEpicFuture,
|
||||
builder: (context, AsyncSnapshot<dynamic> snapshot) {
|
||||
builder:
|
||||
(context, AsyncSnapshot<dynamic> snapshot) {
|
||||
bool commitExists = false;
|
||||
bool isHead = false;
|
||||
CommitStatus stateOfCommit = CommitStatus.notLoaded;
|
||||
CommitStatus stateOfCommit =
|
||||
CommitStatus.notLoaded;
|
||||
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
|
@ -337,21 +350,24 @@ class AboutView extends ConsumerWidget {
|
|||
switch (stateOfCommit) {
|
||||
case CommitStatus.isHead:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen);
|
||||
break;
|
||||
case CommitStatus.isOldCommit:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorYellow);
|
||||
break;
|
||||
case CommitStatus.notACommit:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed);
|
||||
|
@ -361,7 +377,8 @@ class AboutView extends ConsumerWidget {
|
|||
}
|
||||
return RoundedWhiteContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Epic Cash Build Commit",
|
||||
|
@ -383,10 +400,12 @@ class AboutView extends ConsumerWidget {
|
|||
),
|
||||
FutureBuilder(
|
||||
future: commitMoneroFuture,
|
||||
builder: (context, AsyncSnapshot<dynamic> snapshot) {
|
||||
builder:
|
||||
(context, AsyncSnapshot<dynamic> snapshot) {
|
||||
bool commitExists = false;
|
||||
bool isHead = false;
|
||||
CommitStatus stateOfCommit = CommitStatus.notLoaded;
|
||||
CommitStatus stateOfCommit =
|
||||
CommitStatus.notLoaded;
|
||||
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
|
@ -406,21 +425,24 @@ class AboutView extends ConsumerWidget {
|
|||
switch (stateOfCommit) {
|
||||
case CommitStatus.isHead:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen);
|
||||
break;
|
||||
case CommitStatus.isOldCommit:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorYellow);
|
||||
break;
|
||||
case CommitStatus.notACommit:
|
||||
indicationStyle =
|
||||
STextStyles.itemSubtitle(context).copyWith(
|
||||
STextStyles.itemSubtitle(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed);
|
||||
|
@ -430,7 +452,8 @@ class AboutView extends ConsumerWidget {
|
|||
}
|
||||
return RoundedWhiteContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Monero Build Commit",
|
||||
|
@ -521,6 +544,7 @@ class AboutView extends ConsumerWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/advanced_views/debug_view.dart';
|
||||
import 'package:stackwallet/pages/stack_privacy_calls.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'package:stackwallet/pages/stack_privacy_calls.dart';
|
||||
|
||||
class AdvancedSettingsView extends StatelessWidget {
|
||||
const AdvancedSettingsView({
|
||||
|
@ -23,7 +22,8 @@ class AdvancedSettingsView extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -101,8 +101,8 @@ class AdvancedSettingsView extends StatelessWidget {
|
|||
width: 40,
|
||||
child: DraggableSwitchButton(
|
||||
isOn: ref.watch(
|
||||
prefsChangeNotifierProvider
|
||||
.select((value) => value.showTestNetCoins),
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.showTestNetCoins),
|
||||
),
|
||||
onValueChanged: (newValue) {
|
||||
ref
|
||||
|
@ -177,6 +177,7 @@ class AdvancedSettingsView extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,19 +7,26 @@ import 'package:event_bus/event_bus.dart';
|
|||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
|
||||
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:stackwallet/models/isar/models/log.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
|
||||
import 'package:stackwallet/providers/global/debug_service_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/stack_file_system.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/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||
|
@ -28,15 +35,6 @@ import 'package:stackwallet/widgets/rounded_container.dart';
|
|||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
|
||||
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
|
||||
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
|
||||
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class DebugView extends ConsumerStatefulWidget {
|
||||
const DebugView({Key? key}) : super(key: key);
|
||||
|
@ -102,7 +100,8 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -256,7 +255,8 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
padding:
|
||||
const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -291,7 +291,8 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
await PackageInfo.fromPlatform();
|
||||
final version = packageInfo.version;
|
||||
final build = packageInfo.buildNumber;
|
||||
final signature = packageInfo.buildSignature;
|
||||
final signature =
|
||||
packageInfo.buildSignature;
|
||||
final appName = packageInfo.appName;
|
||||
String firoCommit =
|
||||
FIRO_VERSIONS.getPluginVersion();
|
||||
|
@ -307,8 +308,9 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
deviceInfoMap.remove("systemFeatures");
|
||||
|
||||
final logs = filtered(
|
||||
ref.watch(debugServiceProvider.select(
|
||||
(value) => value.recentLogs)),
|
||||
ref.watch(debugServiceProvider
|
||||
.select((value) =>
|
||||
value.recentLogs)),
|
||||
_searchTerm)
|
||||
.reversed
|
||||
.toList(growable: false);
|
||||
|
@ -352,13 +354,14 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
BlueTextButton(
|
||||
text: "Save logs to file",
|
||||
onTap: () async {
|
||||
final systemfile = StackFileSystem();
|
||||
final systemfile = SWBFileSystem();
|
||||
await systemfile.prepareStorage();
|
||||
Directory rootPath =
|
||||
(await getApplicationDocumentsDirectory());
|
||||
Directory rootPath = await StackFileSystem
|
||||
.applicationRootDirectory();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
rootPath = Directory("/storage/emulated/0/");
|
||||
rootPath =
|
||||
Directory("/storage/emulated/0/");
|
||||
}
|
||||
|
||||
Directory dir =
|
||||
|
@ -482,7 +485,8 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
final log = logs[index];
|
||||
|
||||
return Container(
|
||||
key: Key("log_${log.id}_${log.timestampInMillisUTC}"),
|
||||
key: Key(
|
||||
"log_${log.id}_${log.timestampInMillisUTC}"),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -506,14 +510,16 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
style: STextStyles.baseXS(context)
|
||||
.copyWith(
|
||||
fontSize: 8,
|
||||
color: (log.logLevel == LogLevel.Info
|
||||
color: (log.logLevel ==
|
||||
LogLevel.Info
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.topNavIconGreen
|
||||
: (log.logLevel ==
|
||||
LogLevel.Warning
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.topNavIconYellow
|
||||
: (log.logLevel ==
|
||||
LogLevel.Error
|
||||
|
@ -550,7 +556,8 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
children: [
|
||||
SelectableText(
|
||||
log.message,
|
||||
style: STextStyles.baseXS(context)
|
||||
style:
|
||||
STextStyles.baseXS(context)
|
||||
.copyWith(fontSize: 8),
|
||||
),
|
||||
],
|
||||
|
@ -573,6 +580,7 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ import 'package:stackwallet/utilities/text_styles.dart';
|
|||
import 'package:stackwallet/utilities/theme/color_theme.dart';
|
||||
import 'package:stackwallet/utilities/theme/dark_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/ocean_breeze_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -18,9 +20,21 @@ class AppearanceSettingsView extends ConsumerWidget {
|
|||
|
||||
static const String routeName = "/appearanceSettings";
|
||||
|
||||
String chooseThemeType(ThemeType type) {
|
||||
switch (type) {
|
||||
case ThemeType.light:
|
||||
return "Light theme";
|
||||
case ThemeType.oceanBreeze:
|
||||
return "Ocean theme";
|
||||
case ThemeType.dark:
|
||||
return "Dark theme";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -84,7 +98,8 @@ class AppearanceSettingsView extends ConsumerWidget {
|
|||
),
|
||||
onValueChanged: (newValue) {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.read(
|
||||
prefsChangeNotifierProvider)
|
||||
.showFavoriteWallets = newValue;
|
||||
},
|
||||
),
|
||||
|
@ -100,12 +115,10 @@ class AppearanceSettingsView extends ConsumerWidget {
|
|||
height: 10,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
return RawMaterialButton(
|
||||
splashColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.highlight,
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
padding: const EdgeInsets.all(0),
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
|
@ -115,50 +128,27 @@ class AppearanceSettingsView extends ConsumerWidget {
|
|||
),
|
||||
onPressed: null,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Enable dark mode",
|
||||
"Choose Theme",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
width: 40,
|
||||
child: DraggableSwitchButton(
|
||||
isOn: (DB.instance.get<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme")
|
||||
as String?) ==
|
||||
"dark",
|
||||
onValueChanged: (newValue) {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme",
|
||||
value: (newValue
|
||||
? ThemeType.dark
|
||||
: ThemeType.light)
|
||||
.name,
|
||||
);
|
||||
ref
|
||||
.read(colorThemeProvider.state)
|
||||
.state =
|
||||
StackColors.fromStackColorTheme(
|
||||
newValue
|
||||
? DarkColors()
|
||||
: LightColors());
|
||||
},
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: ThemeOptionsView(),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -169,6 +159,278 @@ class AppearanceSettingsView extends ConsumerWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeOptionsView extends ConsumerStatefulWidget {
|
||||
const ThemeOptionsView({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
ConsumerState<ThemeOptionsView> createState() => _ThemeOptionsView();
|
||||
}
|
||||
|
||||
class _ThemeOptionsView extends ConsumerState<ThemeOptionsView> {
|
||||
late String _selectedTheme;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_selectedTheme =
|
||||
DB.instance.get<dynamic>(boxName: DB.boxNameTheme, key: "colorScheme")
|
||||
as String? ??
|
||||
"light";
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
MaterialButton(
|
||||
splashColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
padding: const EdgeInsets.all(0),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme",
|
||||
value: ThemeType.light.name,
|
||||
);
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
LightColors(),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTheme = "light";
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
child: Radio(
|
||||
activeColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.radioButtonIconEnabled,
|
||||
value: "light",
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (newValue) {
|
||||
if (newValue is String && newValue == "light") {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme",
|
||||
value: ThemeType.light.name,
|
||||
);
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
LightColors(),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTheme = "light";
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
Text(
|
||||
"Light",
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
MaterialButton(
|
||||
splashColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
padding: const EdgeInsets.all(0),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme",
|
||||
value: ThemeType.oceanBreeze.name,
|
||||
);
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
OceanBreezeColors(),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTheme = "oceanBreeze";
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
child: Radio(
|
||||
activeColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.radioButtonIconEnabled,
|
||||
value: "oceanBreeze",
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (newValue) {
|
||||
if (newValue is String && newValue == "oceanBreeze") {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme",
|
||||
value: ThemeType.oceanBreeze.name,
|
||||
);
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
OceanBreezeColors(),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTheme = "oceanBreeze";
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
Text(
|
||||
"Ocean Breeze",
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
MaterialButton(
|
||||
splashColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
padding: const EdgeInsets.all(0),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme",
|
||||
value: ThemeType.dark.name,
|
||||
);
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
DarkColors(),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTheme = "dark";
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
child: Radio(
|
||||
activeColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.radioButtonIconEnabled,
|
||||
value: "dark",
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (newValue) {
|
||||
if (newValue is String && newValue == "dark") {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameTheme,
|
||||
key: "colorScheme",
|
||||
value: ThemeType.dark.name,
|
||||
);
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
DarkColors(),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTheme = "dark";
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
Text(
|
||||
"Dark",
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,17 @@ 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/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
|
||||
class BaseCurrencySettingsView extends ConsumerStatefulWidget {
|
||||
const BaseCurrencySettingsView({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -37,6 +37,11 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
final _searchFocusNode = FocusNode();
|
||||
|
||||
void onTap(int index) {
|
||||
if (Util.isDesktop) {
|
||||
setState(() {
|
||||
current = currenciesWithoutSelected[index];
|
||||
});
|
||||
} else {
|
||||
if (currenciesWithoutSelected[index] == current || current.isEmpty) {
|
||||
// ignore if already selected currency
|
||||
return;
|
||||
|
@ -46,6 +51,7 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
currenciesWithoutSelected.insert(0, current);
|
||||
ref.read(prefsChangeNotifierProvider).currency = current;
|
||||
}
|
||||
}
|
||||
|
||||
BorderRadius? _borderRadius(int index) {
|
||||
if (index == 0 && currenciesWithoutSelected.length == 1) {
|
||||
|
@ -82,6 +88,15 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
if (Util.isDesktop) {
|
||||
currenciesWithoutSelected =
|
||||
ref.read(baseCurrenciesProvider).map.keys.toList();
|
||||
current = ref.read(prefsChangeNotifierProvider).currency;
|
||||
if (current.isNotEmpty) {
|
||||
currenciesWithoutSelected.remove(current);
|
||||
currenciesWithoutSelected.insert(0, current);
|
||||
}
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -94,6 +109,9 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
if (!isDesktop) {
|
||||
current = ref
|
||||
.watch(prefsChangeNotifierProvider.select((value) => value.currency));
|
||||
|
||||
|
@ -101,17 +119,20 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
.watch(baseCurrenciesProvider.select((value) => value.map))
|
||||
.keys
|
||||
.toList();
|
||||
|
||||
if (current.isNotEmpty) {
|
||||
currenciesWithoutSelected.remove(current);
|
||||
currenciesWithoutSelected.insert(0, current);
|
||||
}
|
||||
}
|
||||
|
||||
currenciesWithoutSelected = _filtered();
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
|
@ -119,7 +140,8 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 75));
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -139,6 +161,7 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ConditionalParent(
|
||||
|
@ -170,7 +193,7 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
desktopMed: true,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
|
@ -180,8 +203,21 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
|
|||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Save changes",
|
||||
desktopMed: true,
|
||||
onPressed: Navigator.of(context).pop,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () {
|
||||
ref.read(prefsChangeNotifierProvider).currency =
|
||||
current;
|
||||
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.externalCalls) {
|
||||
ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.updatePrice();
|
||||
}
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -20,11 +20,10 @@ import 'package:stackwallet/route_generator.dart';
|
|||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/delete_everything.dart';
|
||||
|
||||
class GlobalSettingsView extends StatelessWidget {
|
||||
const GlobalSettingsView({
|
||||
Key? key,
|
||||
|
@ -35,7 +34,8 @@ class GlobalSettingsView extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -97,7 +97,8 @@ class GlobalSettingsView extends StatelessWidget {
|
|||
showBackButton: true,
|
||||
routeOnSuccess:
|
||||
StackBackupView.routeName,
|
||||
biometricsCancelButtonString: "CANCEL",
|
||||
biometricsCancelButtonString:
|
||||
"CANCEL",
|
||||
biometricsLocalizedReason:
|
||||
"Authenticate to access Stack backup & restore settings",
|
||||
biometricsAuthenticationTitle:
|
||||
|
@ -203,8 +204,8 @@ class GlobalSettingsView extends StatelessWidget {
|
|||
iconSize: 16,
|
||||
title: "Delete account",
|
||||
onPressed: () async {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(DeleteAccountView.routeName);
|
||||
await Navigator.of(context).pushNamed(
|
||||
DeleteAccountView.routeName);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -284,6 +285,7 @@ class GlobalSettingsView extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/providers/global/debug_service_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class HiddenSettings extends StatelessWidget {
|
||||
|
@ -17,7 +18,8 @@ class HiddenSettings extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: Container(),
|
||||
|
@ -126,6 +128,42 @@ class HiddenSettings extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Consumer(
|
||||
builder: (_, ref, __) {
|
||||
if (ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.familiarity)) <
|
||||
6) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
final familiarity = ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.familiarity;
|
||||
if (familiarity < 6) {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.familiarity = 6;
|
||||
|
||||
Constants.exchangeForExperiencedUsers(6);
|
||||
}
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
child: Text(
|
||||
"Enable exchange",
|
||||
style: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
),
|
||||
// const SizedBox(
|
||||
// height: 12,
|
||||
// ),
|
||||
|
@ -162,6 +200,7 @@ class HiddenSettings extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,14 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/languages_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class LanguageSettingsView extends ConsumerStatefulWidget {
|
||||
const LanguageSettingsView({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -100,7 +100,8 @@ class _LanguageViewState extends ConsumerState<LanguageSettingsView> {
|
|||
listWithoutSelected.insert(0, current);
|
||||
}
|
||||
listWithoutSelected = _filtered();
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -300,6 +301,7 @@ class _LanguageViewState extends ConsumerState<LanguageSettingsView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,15 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||
import 'package:stackwallet/models/node_model.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/test_epic_box_connection.dart';
|
||||
|
@ -20,6 +19,7 @@ import 'package:stackwallet/utilities/test_monero_node_connection.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/background.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';
|
||||
|
@ -40,9 +40,6 @@ class AddEditNodeView extends ConsumerStatefulWidget {
|
|||
required this.coin,
|
||||
required this.nodeId,
|
||||
required this.routeOnSuccessOrDelete,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/addEditNode";
|
||||
|
@ -51,7 +48,6 @@ class AddEditNodeView extends ConsumerStatefulWidget {
|
|||
final Coin coin;
|
||||
final String routeOnSuccessOrDelete;
|
||||
final String? nodeId;
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
@override
|
||||
ConsumerState<AddEditNodeView> createState() => _AddEditNodeViewState();
|
||||
|
@ -242,7 +238,8 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
desktopMed: true,
|
||||
buttonHeight:
|
||||
isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () => Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
|
@ -255,7 +252,8 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
desktopMed: true,
|
||||
buttonHeight:
|
||||
isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () => Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
|
@ -413,8 +411,10 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
|
@ -432,7 +432,12 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
actions: [
|
||||
if (viewType == AddEditNodeViewType.edit)
|
||||
if (viewType == AddEditNodeViewType.edit &&
|
||||
ref
|
||||
.watch(nodeServiceChangeNotifierProvider
|
||||
.select((value) => value.getNodesFor(coin)))
|
||||
.length >
|
||||
1)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
|
@ -445,8 +450,9 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
key: const Key("deleteNodeAppBarButtonKey"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.trash,
|
||||
color: Theme.of(context)
|
||||
|
@ -459,7 +465,9 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
Navigator.popUntil(context,
|
||||
ModalRoute.withName(widget.routeOnSuccessOrDelete));
|
||||
|
||||
await ref.read(nodeServiceChangeNotifierProvider).delete(
|
||||
await ref
|
||||
.read(nodeServiceChangeNotifierProvider)
|
||||
.delete(
|
||||
nodeId!,
|
||||
true,
|
||||
);
|
||||
|
@ -494,10 +502,12 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => DesktopDialog(
|
||||
maxWidth: 580,
|
||||
maxHeight: 500,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
@ -533,7 +543,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
children: [
|
||||
NodeForm(
|
||||
node: node,
|
||||
secureStore: widget.secureStore,
|
||||
secureStore: ref.read(secureStoreProvider),
|
||||
readOnly: false,
|
||||
coin: widget.coin,
|
||||
onChanged: (canSave, canTest) {
|
||||
|
@ -565,7 +575,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
child: SecondaryButton(
|
||||
label: "Test connection",
|
||||
enabled: testConnectionEnabled,
|
||||
desktopMed: true,
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: testConnectionEnabled
|
||||
? () async {
|
||||
await _testConnection();
|
||||
|
@ -582,7 +592,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
enabled: saveEnabled,
|
||||
desktopMed: true,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: saveEnabled ? attemptSave : null,
|
||||
),
|
||||
),
|
||||
|
@ -638,7 +648,7 @@ class NodeForm extends ConsumerStatefulWidget {
|
|||
}) : super(key: key);
|
||||
|
||||
final NodeModel? node;
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
final SecureStorageInterface secureStore;
|
||||
final bool readOnly;
|
||||
final Coin coin;
|
||||
final void Function(bool canSave, bool canTestConnection)? onChanged;
|
||||
|
@ -834,10 +844,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
|
@ -859,8 +866,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
_hostFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
suffixIcon:
|
||||
!widget.readOnly && _hostController.text.isNotEmpty
|
||||
suffixIcon: !widget.readOnly && _hostController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
|
@ -885,12 +891,10 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
height: 8,
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
|
@ -910,8 +914,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
_portFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
suffixIcon:
|
||||
!widget.readOnly && _portController.text.isNotEmpty
|
||||
suffixIcon: !widget.readOnly && _portController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
|
@ -936,9 +939,6 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
|
@ -122,8 +123,10 @@ class _CoinNodesViewState extends ConsumerState<CoinNodesView> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
|
@ -147,7 +150,8 @@ class _CoinNodesViewState extends ConsumerState<CoinNodesView> {
|
|||
key: const Key("manageNodesAddNewNodeButtonKey"),
|
||||
size: 36,
|
||||
shadows: const [],
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.plus,
|
||||
color: Theme.of(context)
|
||||
|
@ -185,6 +189,7 @@ class _CoinNodesViewState extends ConsumerState<CoinNodesView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
|
@ -47,7 +48,8 @@ class _ManageNodesViewState extends ConsumerState<ManageNodesView> {
|
|||
? _coins
|
||||
: _coins.sublist(0, _coins.length - kTestNetCoinCount);
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -88,7 +90,8 @@ class _ManageNodesViewState extends ConsumerState<ManageNodesView> {
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
CoinNodesView.routeName,
|
||||
|
@ -132,6 +135,7 @@ class _ManageNodesViewState extends ConsumerState<ManageNodesView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,14 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/test_epic_box_connection.dart';
|
||||
|
@ -18,6 +17,7 @@ import 'package:stackwallet/utilities/test_monero_node_connection.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/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/delete_button.dart';
|
||||
|
@ -32,14 +32,10 @@ class NodeDetailsView extends ConsumerStatefulWidget {
|
|||
required this.coin,
|
||||
required this.nodeId,
|
||||
required this.popRouteName,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/nodeDetails";
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
final Coin coin;
|
||||
final String nodeId;
|
||||
final String popRouteName;
|
||||
|
@ -49,7 +45,7 @@ class NodeDetailsView extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
||||
late final FlutterSecureStorageInterface secureStore;
|
||||
late final SecureStorageInterface secureStore;
|
||||
late final Coin coin;
|
||||
late final String nodeId;
|
||||
late final String popRouteName;
|
||||
|
@ -58,7 +54,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
|
||||
@override
|
||||
initState() {
|
||||
secureStore = widget.secureStore;
|
||||
secureStore = ref.read(secureStoreProvider);
|
||||
coin = widget.coin;
|
||||
nodeId = widget.nodeId;
|
||||
popRouteName = widget.popRouteName;
|
||||
|
@ -181,10 +177,17 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
final node = ref.watch(nodeServiceChangeNotifierProvider
|
||||
.select((value) => value.getNodeById(id: nodeId)));
|
||||
|
||||
final nodesForCoin = ref.watch(nodeServiceChangeNotifierProvider
|
||||
.select((value) => value.getNodesFor(coin)));
|
||||
|
||||
final canDelete = nodesForCoin.length > 1;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
|
@ -202,7 +205,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
actions: [
|
||||
if (!nodeId.startsWith("default"))
|
||||
// if (!nodeId.startsWith(DefaultNodes.defaultNodeIdPrefix))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
|
@ -265,6 +268,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => DesktopDialog(
|
||||
|
@ -314,7 +318,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
const SizedBox(
|
||||
height: 22,
|
||||
),
|
||||
if (isDesktop)
|
||||
if (isDesktop && canDelete)
|
||||
SizedBox(
|
||||
height: 56,
|
||||
child: _desktopReadOnly
|
||||
|
@ -344,7 +348,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
if (isDesktop && !_desktopReadOnly)
|
||||
if (isDesktop && !_desktopReadOnly && canDelete)
|
||||
const SizedBox(
|
||||
height: 45,
|
||||
),
|
||||
|
@ -353,7 +357,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Test connection",
|
||||
desktopMed: true,
|
||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||
onPressed: () async {
|
||||
await _testConnection(ref, context);
|
||||
},
|
||||
|
@ -365,10 +369,12 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
),
|
||||
if (isDesktop)
|
||||
Expanded(
|
||||
child: !nodeId.startsWith("default")
|
||||
? PrimaryButton(
|
||||
child:
|
||||
// !nodeId.startsWith(DefaultNodes.defaultNodeIdPrefix)
|
||||
// ?
|
||||
PrimaryButton(
|
||||
label: _desktopReadOnly ? "Edit" : "Save",
|
||||
desktopMed: true,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () async {
|
||||
final shouldSave = _desktopReadOnly == false;
|
||||
setState(() {
|
||||
|
@ -376,11 +382,28 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
});
|
||||
|
||||
if (shouldSave) {
|
||||
// todo save node
|
||||
final editedNode = node!.copyWith(
|
||||
host: ref.read(nodeFormDataProvider).host,
|
||||
port: ref.read(nodeFormDataProvider).port,
|
||||
name: ref.read(nodeFormDataProvider).name,
|
||||
useSSL: ref.read(nodeFormDataProvider).useSSL,
|
||||
loginName: ref.read(nodeFormDataProvider).login,
|
||||
isFailover:
|
||||
ref.read(nodeFormDataProvider).isFailover,
|
||||
);
|
||||
|
||||
await ref
|
||||
.read(nodeServiceChangeNotifierProvider)
|
||||
.edit(
|
||||
editedNode,
|
||||
ref.read(nodeFormDataProvider).password,
|
||||
true,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
: Container(),
|
||||
// : Container()
|
||||
,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,33 +1,30 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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';
|
||||
import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
|
||||
|
||||
class ChangePinView extends StatefulWidget {
|
||||
class ChangePinView extends ConsumerStatefulWidget {
|
||||
const ChangePinView({
|
||||
Key? key,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/changePin";
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
@override
|
||||
State<ChangePinView> createState() => _ChangePinViewState();
|
||||
ConsumerState<ChangePinView> createState() => _ChangePinViewState();
|
||||
}
|
||||
|
||||
class _ChangePinViewState extends State<ChangePinView> {
|
||||
class _ChangePinViewState extends ConsumerState<ChangePinView> {
|
||||
BoxDecoration get _pinPutDecoration {
|
||||
return BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.textSubtitle2,
|
||||
|
@ -49,11 +46,11 @@ class _ChangePinViewState extends State<ChangePinView> {
|
|||
final TextEditingController _pinPutController2 = TextEditingController();
|
||||
final FocusNode _pinPutFocusNode2 = FocusNode();
|
||||
|
||||
late final FlutterSecureStorageInterface _secureStore;
|
||||
late final SecureStorageInterface _secureStore;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_secureStore = widget.secureStore;
|
||||
_secureStore = ref.read(secureStoreProvider);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -69,7 +66,8 @@ class _ChangePinViewState extends State<ChangePinView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -120,8 +118,9 @@ class _ChangePinViewState extends State<ChangePinView> {
|
|||
disabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
focusedErrorBorder: InputBorder.none,
|
||||
fillColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
fillColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
counterText: "",
|
||||
),
|
||||
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
||||
|
@ -184,8 +183,9 @@ class _ChangePinViewState extends State<ChangePinView> {
|
|||
disabledBorder: InputBorder.none,
|
||||
errorBorder: InputBorder.none,
|
||||
focusedErrorBorder: InputBorder.none,
|
||||
fillColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
fillColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
counterText: "",
|
||||
),
|
||||
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
||||
|
@ -204,8 +204,8 @@ class _ChangePinViewState extends State<ChangePinView> {
|
|||
onSubmit: (String pin) async {
|
||||
if (_pinPutController1.text == _pinPutController2.text) {
|
||||
// This should never fail as we are overwriting the existing pin
|
||||
assert(
|
||||
(await _secureStore.read(key: "stack_pin")) != null);
|
||||
assert((await _secureStore.read(key: "stack_pin")) !=
|
||||
null);
|
||||
await _secureStore.write(key: "stack_pin", value: pin);
|
||||
|
||||
showFloatingFlushBar(
|
||||
|
@ -247,6 +247,7 @@ class _ChangePinViewState extends State<ChangePinView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:stackwallet/route_generator.dart';
|
|||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
@ -22,7 +23,8 @@ class SecurityView extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -60,7 +62,8 @@ class SecurityView extends StatelessWidget {
|
|||
showBackButton: true,
|
||||
routeOnSuccess: ChangePinView.routeName,
|
||||
biometricsCancelButtonString: "CANCEL",
|
||||
biometricsLocalizedReason: "Authenticate to change PIN",
|
||||
biometricsLocalizedReason:
|
||||
"Authenticate to change PIN",
|
||||
biometricsAuthenticationTitle: "Change PIN",
|
||||
),
|
||||
settings:
|
||||
|
@ -146,6 +149,7 @@ class SecurityView extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import 'package:stackwallet/utilities/enums/backup_frequency_type.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/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
|
||||
|
@ -19,8 +21,6 @@ import 'package:stackwallet/widgets/stack_dialog.dart';
|
|||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class AutoBackupView extends ConsumerStatefulWidget {
|
||||
const AutoBackupView({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -225,7 +225,8 @@ class _AutoBackupViewState extends ConsumerState<AutoBackupView> {
|
|||
frequencyController.text = Format.prettyFrequencyType(next);
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -460,6 +461,7 @@ class _AutoBackupViewState extends ConsumerState<AutoBackupView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,15 @@ import 'dart:io';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/auto_backup_view.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_views/backup_frequency_type_select_sheet.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
|
@ -21,33 +21,28 @@ import 'package:stackwallet/utilities/format.dart';
|
|||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/progress_bar.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:zxcvbn/zxcvbn.dart';
|
||||
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class CreateAutoBackupView extends ConsumerStatefulWidget {
|
||||
const CreateAutoBackupView({
|
||||
Key? key,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/createAutoBackup";
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
@override
|
||||
ConsumerState<CreateAutoBackupView> createState() =>
|
||||
_EnableAutoBackupViewState();
|
||||
}
|
||||
|
||||
class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
||||
late final FlutterSecureStorageInterface secureStore;
|
||||
late final SecureStorageInterface secureStore;
|
||||
|
||||
late final TextEditingController fileLocationController;
|
||||
late final TextEditingController passwordController;
|
||||
|
@ -55,7 +50,7 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
|
||||
late final FocusNode passwordFocusNode;
|
||||
late final FocusNode passwordRepeatFocusNode;
|
||||
late final StackFileSystem stackFileSystem;
|
||||
late final SWBFileSystem stackFileSystem;
|
||||
final zxcvbn = Zxcvbn();
|
||||
|
||||
String passwordFeedback =
|
||||
|
@ -75,8 +70,8 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
secureStore = widget.secureStore;
|
||||
stackFileSystem = StackFileSystem();
|
||||
secureStore = ref.read(secureStoreProvider);
|
||||
stackFileSystem = SWBFileSystem();
|
||||
fileLocationController = TextEditingController();
|
||||
passwordController = TextEditingController();
|
||||
passwordRepeatController = TextEditingController();
|
||||
|
@ -114,7 +109,8 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -341,8 +337,9 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
percent:
|
||||
passwordStrength < 0.25 ? 0.03 : passwordStrength,
|
||||
percent: passwordStrength < 0.25
|
||||
? 0.03
|
||||
: passwordStrength,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -445,8 +442,8 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
|
@ -456,10 +453,12 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
prefsChangeNotifierProvider.select(
|
||||
(value) =>
|
||||
value.backupFrequencyType))),
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 4.0),
|
||||
padding:
|
||||
const EdgeInsets.only(right: 4.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
color: Theme.of(context)
|
||||
|
@ -493,7 +492,8 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
: () async {
|
||||
final String pathToSave =
|
||||
fileLocationController.text;
|
||||
final String passphrase = passwordController.text;
|
||||
final String passphrase =
|
||||
passwordController.text;
|
||||
final String repeatPassphrase =
|
||||
passwordRepeatController.text;
|
||||
|
||||
|
@ -548,10 +548,12 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
try {
|
||||
final adk =
|
||||
await compute(generateAdk, passphrase);
|
||||
adkString = Format.uint8listToString(adk.item2);
|
||||
adkString =
|
||||
Format.uint8listToString(adk.item2);
|
||||
adkVersion = adk.item1;
|
||||
} on Exception catch (e, s) {
|
||||
String err = getErrorMessageFromSWBException(e);
|
||||
String err =
|
||||
getErrorMessageFromSWBException(e);
|
||||
Logging.instance
|
||||
.log("$err\n$s", level: LogLevel.Error);
|
||||
// pop encryption progress dialog
|
||||
|
@ -585,9 +587,12 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
final String fileToSave =
|
||||
createAutoBackupFilename(pathToSave, now);
|
||||
|
||||
final backup = await SWB.createStackWalletJSON();
|
||||
final backup = await SWB.createStackWalletJSON(
|
||||
secureStorage: secureStore,
|
||||
);
|
||||
|
||||
bool result = await SWB.encryptStackWalletWithADK(
|
||||
bool result =
|
||||
await SWB.encryptStackWalletWithADK(
|
||||
fileToSave,
|
||||
adkString,
|
||||
jsonEncode(backup),
|
||||
|
@ -639,7 +644,8 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackOkDialog(
|
||||
title: "Failed to enable Auto Backup"),
|
||||
title:
|
||||
"Failed to enable Auto Backup"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -656,6 +662,7 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
|
@ -12,7 +13,8 @@ class CreateBackupInfoView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
@ -83,6 +85,7 @@ class CreateBackupInfoView extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ 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/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
|
@ -15,8 +16,10 @@ import 'package:stackwallet/utilities/logger.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/progress_bar.dart';
|
||||
|
@ -40,7 +43,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
|
||||
late final FocusNode passwordFocusNode;
|
||||
late final FocusNode passwordRepeatFocusNode;
|
||||
late final StackFileSystem stackFileSystem;
|
||||
late final SWBFileSystem stackFileSystem;
|
||||
final zxcvbn = Zxcvbn();
|
||||
|
||||
String passwordFeedback =
|
||||
|
@ -60,7 +63,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
stackFileSystem = StackFileSystem();
|
||||
stackFileSystem = SWBFileSystem();
|
||||
fileLocationController = TextEditingController();
|
||||
passwordController = TextEditingController();
|
||||
passwordRepeatController = TextEditingController();
|
||||
|
@ -101,7 +104,8 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Scaffold(
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
|
@ -109,7 +113,8 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 75));
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 75));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -138,6 +143,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ConditionalParent(
|
||||
|
@ -443,7 +449,8 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
),
|
||||
if (!isDesktop) const Spacer(),
|
||||
!isDesktop
|
||||
? TextButton(
|
||||
? Consumer(builder: (context, ref, __) {
|
||||
return TextButton(
|
||||
style: shouldEnableCreate
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -509,7 +516,8 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
final String fileToSave =
|
||||
"$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb";
|
||||
|
||||
final backup = await SWB.createStackWalletJSON();
|
||||
final backup = await SWB.createStackWalletJSON(
|
||||
secureStorage: ref.read(secureStoreProvider));
|
||||
|
||||
bool result =
|
||||
await SWB.encryptStackWalletWithPassphrase(
|
||||
|
@ -520,7 +528,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
|
||||
if (mounted) {
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
if (!isDesktop) Navigator.of(context).pop();
|
||||
|
||||
if (result) {
|
||||
await showDialog<dynamic>(
|
||||
|
@ -551,12 +559,14 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
"Create backup",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
)
|
||||
);
|
||||
})
|
||||
: Row(
|
||||
children: [
|
||||
PrimaryButton(
|
||||
Consumer(builder: (context, ref, __) {
|
||||
return PrimaryButton(
|
||||
width: 183,
|
||||
desktopMed: true,
|
||||
buttonHeight: ButtonHeight.m,
|
||||
label: "Create backup",
|
||||
enabled: shouldEnableCreate,
|
||||
onPressed: !shouldEnableCreate
|
||||
|
@ -602,49 +612,158 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
return;
|
||||
}
|
||||
|
||||
unawaited(showDialog<dynamic>(
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackDialog(
|
||||
title: "Encrypting backup",
|
||||
message: "This shouldn't take long",
|
||||
builder: (_) {
|
||||
if (Util.isDesktop) {
|
||||
return DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
maxWidth: 450,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
32,
|
||||
),
|
||||
));
|
||||
// make sure the dialog is able to be displayed for at least 1 second
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Encrypting initial backup",
|
||||
style:
|
||||
STextStyles.desktopH3(
|
||||
context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
Text(
|
||||
"This shouldn't take long",
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const StackDialog(
|
||||
title: "Encrypting initial backup",
|
||||
message: "This shouldn't take long",
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await Future<void>.delayed(
|
||||
const Duration(seconds: 1));
|
||||
|
||||
// make sure the dialog is able to be displayed for at least 1 second
|
||||
final fut = Future<void>.delayed(
|
||||
const Duration(seconds: 1));
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final String fileToSave =
|
||||
"$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb";
|
||||
|
||||
final backup =
|
||||
await SWB.createStackWalletJSON();
|
||||
await SWB.createStackWalletJSON(
|
||||
secureStorage:
|
||||
ref.read(secureStoreProvider));
|
||||
|
||||
bool result =
|
||||
await SWB.encryptStackWalletWithPassphrase(
|
||||
bool result = await SWB
|
||||
.encryptStackWalletWithPassphrase(
|
||||
fileToSave,
|
||||
passphrase,
|
||||
jsonEncode(backup),
|
||||
);
|
||||
|
||||
await Future.wait([fut]);
|
||||
|
||||
if (mounted) {
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
if (!isDesktop) Navigator.of(context).pop();
|
||||
|
||||
if (result) {
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
builder: (context) {
|
||||
if (Platform.isAndroid) {
|
||||
return StackOkDialog(
|
||||
title: "Backup saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title:
|
||||
"Backup creation succeeded"),
|
||||
);
|
||||
} else if (isDesktop) {
|
||||
return DesktopDialog(
|
||||
maxHeight: double.infinity,
|
||||
maxWidth: 500,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment
|
||||
.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 26),
|
||||
Text(
|
||||
"Stack backup saved to: \n",
|
||||
style: STextStyles
|
||||
.desktopH3(context),
|
||||
),
|
||||
Text(
|
||||
fileToSave,
|
||||
style: STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
// const Spacer(),
|
||||
Expanded(
|
||||
child:
|
||||
PrimaryButton(
|
||||
label: "Ok",
|
||||
buttonHeight:
|
||||
ButtonHeight
|
||||
.l,
|
||||
onPressed: () {
|
||||
int count = 0;
|
||||
Navigator.of(
|
||||
context)
|
||||
.popUntil((_) =>
|
||||
count++ >=
|
||||
2);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const StackOkDialog(
|
||||
title:
|
||||
"Backup creation succeeded");
|
||||
}
|
||||
});
|
||||
passwordController.text = "";
|
||||
passwordRepeatController.text = "";
|
||||
setState(() {});
|
||||
|
@ -658,13 +777,14 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SecondaryButton(
|
||||
width: 183,
|
||||
desktopMed: true,
|
||||
buttonHeight: ButtonHeight.m,
|
||||
label: "Cancel",
|
||||
onPressed: () {},
|
||||
),
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import 'package:flutter/material.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/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
||||
class CancelStackRestoreDialog extends StatelessWidget {
|
||||
|
@ -14,7 +19,8 @@ class CancelStackRestoreDialog extends StatelessWidget {
|
|||
onWillPop: () async {
|
||||
return false;
|
||||
},
|
||||
child: StackDialog(
|
||||
child: !Util.isDesktop
|
||||
? StackDialog(
|
||||
title: "Cancel restore process",
|
||||
message:
|
||||
"Cancelling will revert any changes that may have been applied",
|
||||
|
@ -37,14 +43,70 @@ class CancelStackRestoreDialog extends StatelessWidget {
|
|||
child: Text(
|
||||
"Yes, cancel",
|
||||
style: STextStyles.itemSubtitle12(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.buttonTextPrimary,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextPrimary,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
)
|
||||
: DesktopDialog(
|
||||
maxHeight: 250,
|
||||
maxWidth: 600,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 20, left: 32, right: 32, bottom: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Cancel Restore Process",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: 500,
|
||||
child: RoundedContainer(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.snackBarBackError,
|
||||
child: Text(
|
||||
"If you cancel, the restore will not complete, and "
|
||||
"the wallets will not appear in your Stack.",
|
||||
style: STextStyles.desktopTextMedium(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SecondaryButton(
|
||||
width: 248,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
enabled: true,
|
||||
label: "Keep restoring",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
PrimaryButton(
|
||||
width: 248,
|
||||
buttonHeight: ButtonHeight.l,
|
||||
enabled: true,
|
||||
label: "Cancel anyway",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,52 +1,53 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/auto_backup_view.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_views/backup_frequency_type_select_sheet.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/progress_bar.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:zxcvbn/zxcvbn.dart';
|
||||
|
||||
import '../../../../utilities/util.dart';
|
||||
|
||||
class EditAutoBackupView extends ConsumerStatefulWidget {
|
||||
const EditAutoBackupView({
|
||||
Key? key,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/editAutoBackup";
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
@override
|
||||
ConsumerState<EditAutoBackupView> createState() => _EditAutoBackupViewState();
|
||||
}
|
||||
|
||||
class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
||||
late final FlutterSecureStorageInterface secureStore;
|
||||
late final SecureStorageInterface secureStore;
|
||||
|
||||
late final TextEditingController fileLocationController;
|
||||
late final TextEditingController passwordController;
|
||||
|
@ -54,9 +55,17 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
|
||||
late final FocusNode passwordFocusNode;
|
||||
late final FocusNode passwordRepeatFocusNode;
|
||||
late final StackFileSystem stackFileSystem;
|
||||
late final SWBFileSystem stackFileSystem;
|
||||
final zxcvbn = Zxcvbn();
|
||||
|
||||
late BackupFrequencyType _currentDropDownValue;
|
||||
|
||||
final List<BackupFrequencyType> _dropDownItems = [
|
||||
BackupFrequencyType.everyTenMinutes,
|
||||
BackupFrequencyType.everyAppStart,
|
||||
BackupFrequencyType.afterClosingAWallet,
|
||||
];
|
||||
|
||||
String passwordFeedback =
|
||||
"Add another word or two. Uncommon words are better. Use a few words, avoid common phrases. No need for symbols, digits, or uppercase letters.";
|
||||
|
||||
|
@ -72,10 +81,161 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
passwordRepeatController.text.isNotEmpty;
|
||||
}
|
||||
|
||||
void onSavePressed() async {
|
||||
final String pathToSave = fileLocationController.text;
|
||||
final String passphrase = passwordController.text;
|
||||
final String repeatPassphrase = passwordRepeatController.text;
|
||||
|
||||
if (pathToSave.isEmpty) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Directory not chosen",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!(await Directory(pathToSave).exists())) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Directory does not exist",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (passphrase.isEmpty) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "A passphrase is required",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (passphrase != repeatPassphrase) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Passphrase does not match",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackDialog(
|
||||
title: "Updating Auto Backup",
|
||||
message: "This shouldn't take long",
|
||||
),
|
||||
),
|
||||
);
|
||||
// make sure the dialog is able to be displayed for at least 1 second
|
||||
final fut = Future<void>.delayed(const Duration(seconds: 1));
|
||||
|
||||
String adkString;
|
||||
int adkVersion;
|
||||
try {
|
||||
final adk = await compute(generateAdk, passphrase);
|
||||
adkString = Format.uint8listToString(adk.item2);
|
||||
adkVersion = adk.item1;
|
||||
} on Exception catch (e, s) {
|
||||
String err = getErrorMessageFromSWBException(e);
|
||||
Logging.instance.log("$err\n$s", level: LogLevel.Error);
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: err,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
return;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "$e",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await secureStore.write(key: "auto_adk_string", value: adkString);
|
||||
await secureStore.write(
|
||||
key: "auto_adk_version_string", value: adkVersion.toString());
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final String fileToSave = createAutoBackupFilename(pathToSave, now);
|
||||
|
||||
final backup = await SWB.createStackWalletJSON(
|
||||
secureStorage: ref.read(secureStoreProvider),
|
||||
);
|
||||
|
||||
bool result = await SWB.encryptStackWalletWithADK(
|
||||
fileToSave,
|
||||
adkString,
|
||||
jsonEncode(backup),
|
||||
adkVersion: adkVersion,
|
||||
);
|
||||
|
||||
// this future should already be complete unless there was an error encrypting
|
||||
await Future.wait([fut]);
|
||||
|
||||
if (mounted) {
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (result) {
|
||||
ref.read(prefsChangeNotifierProvider).autoBackupLocation = pathToSave;
|
||||
ref.read(prefsChangeNotifierProvider).lastAutoBackup = now;
|
||||
|
||||
ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled = true;
|
||||
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title: "Stack Auto Backup saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(title: "Stack Auto Backup saved"),
|
||||
);
|
||||
if (mounted) {
|
||||
passwordController.text = "";
|
||||
passwordRepeatController.text = "";
|
||||
|
||||
Navigator.of(context)
|
||||
.popUntil(ModalRoute.withName(AutoBackupView.routeName));
|
||||
}
|
||||
} else {
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) =>
|
||||
const StackOkDialog(title: "Failed to update Auto Backup"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
secureStore = widget.secureStore;
|
||||
stackFileSystem = StackFileSystem();
|
||||
secureStore = ref.read(secureStoreProvider);
|
||||
stackFileSystem = SWBFileSystem();
|
||||
fileLocationController = TextEditingController();
|
||||
passwordController = TextEditingController();
|
||||
passwordRepeatController = TextEditingController();
|
||||
|
@ -83,6 +243,9 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
fileLocationController.text =
|
||||
ref.read(prefsChangeNotifierProvider).autoBackupLocation ?? "";
|
||||
|
||||
_currentDropDownValue =
|
||||
ref.read(prefsChangeNotifierProvider).backupFrequencyType;
|
||||
|
||||
passwordFocusNode = FocusNode();
|
||||
passwordRepeatFocusNode = FocusNode();
|
||||
|
||||
|
@ -116,8 +279,14 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () {
|
||||
|
@ -138,13 +307,31 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment:
|
||||
isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
Text(
|
||||
"Create your backup",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
"Choose file location",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
|
@ -169,8 +356,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
controller: fileLocationController,
|
||||
|
@ -199,8 +385,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
key: const Key(
|
||||
"createBackupSaveToFileLocationTextFieldKey"),
|
||||
key: const Key("createBackupSaveToFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
|
@ -210,6 +395,18 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
),
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
if (isDesktop)
|
||||
Text(
|
||||
"Create a passphrase",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
if (!Platform.isAndroid)
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
|
@ -231,6 +428,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
passwordFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -246,9 +444,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
hidePassword ? Assets.svg.eye : Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
|
@ -272,8 +468,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
}
|
||||
final result = zxcvbn.evaluate(newValue);
|
||||
String suggestionsAndTips = "";
|
||||
for (var sug
|
||||
in result.feedback.suggestions!.toSet()) {
|
||||
for (var sug in result.feedback.suggestions!.toSet()) {
|
||||
suggestionsAndTips += "$sug\n";
|
||||
}
|
||||
suggestionsAndTips += result.feedback.warning!;
|
||||
|
@ -290,8 +485,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
}
|
||||
|
||||
if (feedback.endsWith("\n")) {
|
||||
feedback =
|
||||
feedback.substring(0, feedback.length - 2);
|
||||
feedback = feedback.substring(0, feedback.length - 2);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
|
@ -327,12 +521,12 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
),
|
||||
child: ProgressBar(
|
||||
key: const Key("createStackBackUpProgressBar"),
|
||||
width: MediaQuery.of(context).size.width - 32 - 24,
|
||||
width: isDesktop
|
||||
? 492
|
||||
: MediaQuery.of(context).size.width - 32 - 24,
|
||||
height: 5,
|
||||
fillColor: passwordStrength < 0.51
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorRed
|
||||
? Theme.of(context).extension<StackColors>()!.accentColorRed
|
||||
: passwordStrength < 1
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -343,12 +537,11 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
percent:
|
||||
passwordStrength < 0.25 ? 0.03 : passwordStrength,
|
||||
percent: passwordStrength < 0.25 ? 0.03 : passwordStrength,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
SizedBox(
|
||||
height: isDesktop ? 16 : 10,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
@ -367,6 +560,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
passwordRepeatFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -382,9 +576,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
});
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
hidePassword ? Assets.svg.eye : Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
|
@ -405,16 +597,99 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
SizedBox(
|
||||
height: isDesktop ? 24 : 32,
|
||||
),
|
||||
Text(
|
||||
"Auto Backup frequency",
|
||||
style: STextStyles.smallMed12(context),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
)
|
||||
: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
if (isDesktop)
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton2(
|
||||
offset: const Offset(0, -10),
|
||||
isExpanded: true,
|
||||
dropdownElevation: 0,
|
||||
value: _currentDropDownValue,
|
||||
items: [
|
||||
..._dropDownItems.map(
|
||||
(e) {
|
||||
String message = "";
|
||||
switch (e) {
|
||||
case BackupFrequencyType.everyTenMinutes:
|
||||
message = "Every 10 minutes";
|
||||
break;
|
||||
case BackupFrequencyType.everyAppStart:
|
||||
message = "Every app startup";
|
||||
break;
|
||||
case BackupFrequencyType.afterClosingAWallet:
|
||||
message = "After closing a cryptocurrency wallet";
|
||||
break;
|
||||
}
|
||||
|
||||
return DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(
|
||||
message,
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value is BackupFrequencyType) {
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.backupFrequencyType !=
|
||||
value) {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.backupFrequencyType = value;
|
||||
}
|
||||
setState(() {
|
||||
_currentDropDownValue = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 10,
|
||||
height: 5,
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark3,
|
||||
),
|
||||
buttonPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
buttonDecoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
dropdownDecoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
Stack(
|
||||
children: [
|
||||
TextField(
|
||||
|
@ -425,9 +700,8 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
),
|
||||
Positioned.fill(
|
||||
child: RawMaterialButton(
|
||||
splashColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.highlight,
|
||||
splashColor:
|
||||
Theme.of(context).extension<StackColors>()!.highlight,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -442,22 +716,18 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
top: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
builder: (_) =>
|
||||
const BackupFrequencyTypeSelectSheet(),
|
||||
builder: (_) => const BackupFrequencyTypeSelectSheet(),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
Format.prettyFrequencyType(ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) =>
|
||||
value.backupFrequencyType))),
|
||||
(value) => value.backupFrequencyType))),
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
Padding(
|
||||
|
@ -478,10 +748,34 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
)
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
if (!isDesktop) const Spacer(),
|
||||
SizedBox(
|
||||
height: isDesktop ? 24 : 10,
|
||||
),
|
||||
if (isDesktop)
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Cancel",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Save",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
enabled: shouldEnableCreate,
|
||||
onPressed: onSavePressed,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
TextButton(
|
||||
style: shouldEnableCreate
|
||||
? Theme.of(context)
|
||||
|
@ -490,160 +784,7 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryDisabledButtonColor(context),
|
||||
onPressed: !shouldEnableCreate
|
||||
? null
|
||||
: () async {
|
||||
final String pathToSave =
|
||||
fileLocationController.text;
|
||||
final String passphrase = passwordController.text;
|
||||
final String repeatPassphrase =
|
||||
passwordRepeatController.text;
|
||||
|
||||
if (pathToSave.isEmpty) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Directory not chosen",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!(await Directory(pathToSave).exists())) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Directory does not exist",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (passphrase.isEmpty) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "A passphrase is required",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (passphrase != repeatPassphrase) {
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Passphrase does not match",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackDialog(
|
||||
title: "Updating Auto Backup",
|
||||
message: "This shouldn't take long",
|
||||
),
|
||||
);
|
||||
// make sure the dialog is able to be displayed for at least 1 second
|
||||
final fut = Future<void>.delayed(
|
||||
const Duration(seconds: 1));
|
||||
|
||||
String adkString;
|
||||
int adkVersion;
|
||||
try {
|
||||
final adk =
|
||||
await compute(generateAdk, passphrase);
|
||||
adkString = Format.uint8listToString(adk.item2);
|
||||
adkVersion = adk.item1;
|
||||
} on Exception catch (e, s) {
|
||||
String err = getErrorMessageFromSWBException(e);
|
||||
Logging.instance
|
||||
.log("$err\n$s", level: LogLevel.Error);
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: err,
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "$e",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await secureStore.write(
|
||||
key: "auto_adk_string", value: adkString);
|
||||
await secureStore.write(
|
||||
key: "auto_adk_version_string",
|
||||
value: adkVersion.toString());
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
final String fileToSave =
|
||||
createAutoBackupFilename(pathToSave, now);
|
||||
|
||||
final backup = await SWB.createStackWalletJSON();
|
||||
|
||||
bool result = await SWB.encryptStackWalletWithADK(
|
||||
fileToSave,
|
||||
adkString,
|
||||
jsonEncode(backup),
|
||||
adkVersion: adkVersion,
|
||||
);
|
||||
|
||||
// this future should already be complete unless there was an error encrypting
|
||||
await Future.wait([fut]);
|
||||
|
||||
if (mounted) {
|
||||
// pop encryption progress dialog
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (result) {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.autoBackupLocation = pathToSave;
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.lastAutoBackup = now;
|
||||
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.isAutoBackupEnabled = true;
|
||||
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title: "Stack Auto Backup saved"),
|
||||
);
|
||||
if (mounted) {
|
||||
passwordController.text = "";
|
||||
passwordRepeatController.text = "";
|
||||
|
||||
Navigator.of(context).popUntil(
|
||||
ModalRoute.withName(
|
||||
AutoBackupView.routeName));
|
||||
}
|
||||
} else {
|
||||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackOkDialog(
|
||||
title: "Failed to update Auto Backup"),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
onPressed: !shouldEnableCreate ? null : onSavePressed,
|
||||
child: Text(
|
||||
"Save",
|
||||
style: STextStyles.button(context),
|
||||
|
@ -651,11 +792,6 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,7 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||
import 'package:stackwallet/hive/db.dart';
|
||||
import 'package:stackwallet/models/contact.dart';
|
||||
import 'package:stackwallet/models/contact_address_entry.dart';
|
||||
|
@ -91,10 +88,13 @@ abstract class SWB {
|
|||
|
||||
static bool _shouldCancelRestore = false;
|
||||
|
||||
static bool _checkShouldCancel(PreRestoreState? revertToState) {
|
||||
static bool _checkShouldCancel(
|
||||
PreRestoreState? revertToState,
|
||||
SecureStorageInterface secureStorageInterface,
|
||||
) {
|
||||
if (_shouldCancelRestore) {
|
||||
if (revertToState != null) {
|
||||
_revert(revertToState);
|
||||
_revert(revertToState, secureStorageInterface);
|
||||
} else {
|
||||
_cancelCompleter!.complete();
|
||||
_shouldCancelRestore = false;
|
||||
|
@ -193,15 +193,15 @@ abstract class SWB {
|
|||
|
||||
/// [secureStorage] parameter exposed for testing purposes
|
||||
static Future<Map<String, dynamic>> createStackWalletJSON({
|
||||
FlutterSecureStorageInterface? secureStorage,
|
||||
required SecureStorageInterface secureStorage,
|
||||
}) async {
|
||||
Logging.instance
|
||||
.log("Starting createStackWalletJSON...", level: LogLevel.Info);
|
||||
final _wallets = Wallets.sharedInstance;
|
||||
Map<String, dynamic> backupJson = {};
|
||||
NodeService nodeService = NodeService();
|
||||
final _secureStore =
|
||||
secureStorage ?? const SecureStorageWrapper(FlutterSecureStorage());
|
||||
NodeService nodeService =
|
||||
NodeService(secureStorageInterface: secureStorage);
|
||||
final _secureStore = secureStorage;
|
||||
|
||||
Logging.instance.log("createStackWalletJSON awaiting DB.instance.mutex...",
|
||||
level: LogLevel.Info);
|
||||
|
@ -448,6 +448,7 @@ abstract class SWB {
|
|||
Map<String, dynamic> validJSON,
|
||||
StackRestoringUIState? uiState,
|
||||
Map<String, String> oldToNewWalletIdMap,
|
||||
SecureStorageInterface secureStorageInterface,
|
||||
) async {
|
||||
Map<String, dynamic> prefs = validJSON["prefs"] as Map<String, dynamic>;
|
||||
List<dynamic>? addressBookEntries =
|
||||
|
@ -486,7 +487,11 @@ abstract class SWB {
|
|||
"SWB restoring nodes",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
await _restoreNodes(nodes, primaryNodes);
|
||||
await _restoreNodes(
|
||||
nodes,
|
||||
primaryNodes,
|
||||
secureStorageInterface,
|
||||
);
|
||||
|
||||
uiState?.nodes = StackRestoringStatus.success;
|
||||
uiState?.trades = StackRestoringStatus.restoring;
|
||||
|
@ -543,6 +548,7 @@ abstract class SWB {
|
|||
static Future<bool?> restoreStackWalletJSON(
|
||||
String jsonBackup,
|
||||
StackRestoringUIState? uiState,
|
||||
SecureStorageInterface secureStorageInterface,
|
||||
) async {
|
||||
if (!Platform.isLinux) await Wakelock.enable();
|
||||
|
||||
|
@ -550,7 +556,8 @@ abstract class SWB {
|
|||
"SWB creating temp backup",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
final preRestoreJSON = await createStackWalletJSON();
|
||||
final preRestoreJSON =
|
||||
await createStackWalletJSON(secureStorage: secureStorageInterface);
|
||||
Logging.instance.log(
|
||||
"SWB temp backup created",
|
||||
level: LogLevel.Warning,
|
||||
|
@ -587,19 +594,34 @@ abstract class SWB {
|
|||
|
||||
// basic cancel check here
|
||||
// no reverting required yet as nothing has been written to store
|
||||
if (_checkShouldCancel(null)) {
|
||||
if (_checkShouldCancel(
|
||||
null,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await _restoreEverythingButWallets(validJSON, uiState, oldToNewWalletIdMap);
|
||||
await _restoreEverythingButWallets(
|
||||
validJSON,
|
||||
uiState,
|
||||
oldToNewWalletIdMap,
|
||||
secureStorageInterface,
|
||||
);
|
||||
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final nodeService = NodeService();
|
||||
final walletsService = WalletsService();
|
||||
final nodeService = NodeService(
|
||||
secureStorageInterface: secureStorageInterface,
|
||||
);
|
||||
final walletsService = WalletsService(
|
||||
secureStorageInterface: secureStorageInterface,
|
||||
);
|
||||
final _prefs = Prefs.instance;
|
||||
await _prefs.init();
|
||||
|
||||
|
@ -609,7 +631,10 @@ abstract class SWB {
|
|||
|
||||
for (var walletbackup in wallets) {
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -647,7 +672,10 @@ abstract class SWB {
|
|||
final failovers = nodeService.failoverNodesFor(coin: coin);
|
||||
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -655,6 +683,7 @@ abstract class SWB {
|
|||
coin,
|
||||
walletId,
|
||||
walletName,
|
||||
secureStorageInterface,
|
||||
node,
|
||||
txTracker,
|
||||
_prefs,
|
||||
|
@ -665,7 +694,10 @@ abstract class SWB {
|
|||
|
||||
managers.add(Tuple2(walletbackup, manager));
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -679,7 +711,10 @@ abstract class SWB {
|
|||
}
|
||||
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -690,7 +725,10 @@ abstract class SWB {
|
|||
// start restoring wallets
|
||||
for (final tuple in managers) {
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
final bools = await asyncRestore(tuple, uiState, walletsService);
|
||||
|
@ -698,13 +736,19 @@ abstract class SWB {
|
|||
}
|
||||
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Future<bool> status in restoreStatuses) {
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
await status;
|
||||
|
@ -712,7 +756,10 @@ abstract class SWB {
|
|||
|
||||
if (!Platform.isLinux) await Wakelock.disable();
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
if (_checkShouldCancel(
|
||||
preRestoreState,
|
||||
secureStorageInterface,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -720,7 +767,10 @@ abstract class SWB {
|
|||
return true;
|
||||
}
|
||||
|
||||
static Future<void> _revert(PreRestoreState revertToState) async {
|
||||
static Future<void> _revert(
|
||||
PreRestoreState revertToState,
|
||||
SecureStorageInterface secureStorageInterface,
|
||||
) async {
|
||||
Map<String, dynamic> prefs =
|
||||
revertToState.validJSON["prefs"] as Map<String, dynamic>;
|
||||
List<dynamic>? addressBookEntries =
|
||||
|
@ -788,7 +838,9 @@ abstract class SWB {
|
|||
}
|
||||
|
||||
// nodes
|
||||
NodeService nodeService = NodeService();
|
||||
NodeService nodeService = NodeService(
|
||||
secureStorageInterface: secureStorageInterface,
|
||||
);
|
||||
final currentNodes = nodeService.nodes;
|
||||
if (nodes == null) {
|
||||
// no pre nodes found so we delete all but defaults
|
||||
|
@ -914,7 +966,8 @@ abstract class SWB {
|
|||
}
|
||||
|
||||
// finally remove any added wallets
|
||||
final walletsService = WalletsService();
|
||||
final walletsService =
|
||||
WalletsService(secureStorageInterface: secureStorageInterface);
|
||||
final namesData = await walletsService.walletNames;
|
||||
for (final entry in namesData.entries) {
|
||||
if (!revertToState.walletIds.contains(entry.value.walletId)) {
|
||||
|
@ -989,8 +1042,11 @@ abstract class SWB {
|
|||
static Future<void> _restoreNodes(
|
||||
List<dynamic>? nodes,
|
||||
List<dynamic>? primaryNodes,
|
||||
SecureStorageInterface secureStorageInterface,
|
||||
) async {
|
||||
NodeService nodeService = NodeService();
|
||||
NodeService nodeService = NodeService(
|
||||
secureStorageInterface: secureStorageInterface,
|
||||
);
|
||||
if (nodes != null) {
|
||||
for (var node in nodes) {
|
||||
await nodeService.add(
|
||||
|
|
|
@ -4,15 +4,16 @@ import 'package:file_picker/file_picker.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class StackFileSystem {
|
||||
class SWBFileSystem {
|
||||
Directory? rootPath;
|
||||
Directory? startPath;
|
||||
|
||||
String? filePath;
|
||||
String? dirPath;
|
||||
|
||||
final bool isDesktop = !(Platform.isAndroid || Platform.isIOS);
|
||||
final bool isDesktop = Util.isDesktop;
|
||||
|
||||
Future<Directory> prepareStorage() async {
|
||||
if (Platform.isAndroid) {
|
||||
|
@ -25,11 +26,20 @@ class StackFileSystem {
|
|||
}
|
||||
debugPrint(rootPath!.absolute.toString());
|
||||
|
||||
Directory sampleFolder =
|
||||
Directory('${rootPath!.path}Documents/Stack_backups');
|
||||
late Directory sampleFolder;
|
||||
|
||||
if (Platform.isIOS) {
|
||||
sampleFolder = Directory(rootPath!.path);
|
||||
} else if (Platform.isAndroid) {
|
||||
sampleFolder = Directory('${rootPath!.path}Documents/Stack_backups');
|
||||
} else if (Platform.isLinux) {
|
||||
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
|
||||
} else if (Platform.isWindows) {
|
||||
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
|
||||
} else if (Platform.isMacOS) {
|
||||
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
|
||||
}
|
||||
|
||||
try {
|
||||
if (!sampleFolder.existsSync()) {
|
||||
sampleFolder.createSync(recursive: true);
|
|
@ -9,9 +9,9 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back
|
|||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.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';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
|
@ -62,8 +62,10 @@ class _RestoreFromEncryptedStringViewState
|
|||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: _onWillPop,
|
||||
child: Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
|
@ -191,11 +193,13 @@ class _RestoreFromEncryptedStringViewState
|
|||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
style: STextStyles.pageTitleH2(
|
||||
style:
|
||||
STextStyles.pageTitleH2(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textWhite,
|
||||
),
|
||||
),
|
||||
|
@ -263,6 +267,7 @@ class _RestoreFromEncryptedStringViewState
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|