Compare commits

..

No commits in common. "126f499bd55d8800fd8acf3c0fad11015514edfa" and "250510c53d3a8a55940568af9404701f02026405" have entirely different histories.

42 changed files with 539 additions and 2011 deletions

3
.gitignore vendored
View File

@ -1,6 +1,3 @@
android/app/.cxx
android/app/.cxx/Debug
# Miscellaneous # Miscellaneous
*.class *.class
*.log *.log

View File

@ -36,8 +36,6 @@ android {
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
} }
kotlinOptions { kotlinOptions {
@ -60,10 +58,10 @@ android {
signingConfigs { signingConfigs {
release { release {
storeFile file("keystore.jks") keyAlias keystoreProperties['keyAlias']
storePassword "12799721" keyPassword keystoreProperties['keyPassword']
keyAlias "upload" storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
keyPassword "12799721" storePassword keystoreProperties['storePassword']
} }
} }
buildTypes { buildTypes {
@ -71,7 +69,6 @@ android {
signingConfig signingConfigs.release signingConfig signingConfigs.release
minifyEnabled false minifyEnabled false
shrinkResources false shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
@ -112,5 +109,4 @@ dependencies {
implementation "androidx.room:room-runtime:2.2.5" implementation "androidx.room:room-runtime:2.2.5"
implementation "androidx.sqlite:sqlite-framework:2.1.0" implementation "androidx.sqlite:sqlite-framework:2.1.0"
implementation "androidx.sqlite:sqlite:2.1.0" implementation "androidx.sqlite:sqlite:2.1.0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
} }

View File

@ -18,7 +18,7 @@ repositories {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.2.1" apply false id "com.android.application" version "8.1.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.0" apply false id "org.jetbrains.kotlin.android" version "1.9.0" apply false
} }

View File

@ -1,3 +0,0 @@
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.14592 0.145917C8.19236 0.0993539 8.24754 0.0624112 8.30828 0.0372047C8.36903 0.0119983 8.43415 -0.000976563 8.49992 -0.000976562C8.56568 -0.000976562 8.63081 0.0119983 8.69155 0.0372047C8.7523 0.0624112 8.80747 0.0993539 8.85392 0.145917L11.8539 3.14592C11.9005 3.19236 11.9374 3.24754 11.9626 3.30828C11.9878 3.36903 12.0008 3.43415 12.0008 3.49992C12.0008 3.56568 11.9878 3.63081 11.9626 3.69155C11.9374 3.7523 11.9005 3.80747 11.8539 3.85392L8.85392 6.85392C8.80743 6.9004 8.75224 6.93728 8.6915 6.96244C8.63076 6.9876 8.56566 7.00055 8.49992 7.00055C8.43417 7.00055 8.36907 6.9876 8.30833 6.96244C8.24759 6.93728 8.1924 6.9004 8.14592 6.85392C8.09943 6.80743 8.06255 6.75224 8.03739 6.6915C8.01223 6.63076 7.99929 6.56566 7.99929 6.49992C7.99929 6.43417 8.01223 6.36907 8.03739 6.30833C8.06255 6.24759 8.09943 6.1924 8.14592 6.14592L10.2929 3.99992H0.499917C0.367309 3.99992 0.240132 3.94724 0.146364 3.85347C0.0525958 3.7597 -8.27312e-05 3.63253 -8.27312e-05 3.49992C-8.27312e-05 3.36731 0.0525958 3.24013 0.146364 3.14636C0.240132 3.0526 0.367309 2.99992 0.499917 2.99992H10.2929L8.14592 0.853917C8.09935 0.807472 8.06241 0.752296 8.0372 0.691551C8.012 0.630806 7.99902 0.565685 7.99902 0.499917C7.99902 0.43415 8.012 0.369029 8.0372 0.308284C8.06241 0.247538 8.09935 0.192363 8.14592 0.145917ZM3.85392 7.14592C3.90048 7.19236 3.93742 7.24754 3.96263 7.30828C3.98784 7.36903 4.00081 7.43415 4.00081 7.49992C4.00081 7.56568 3.98784 7.63081 3.96263 7.69155C3.93742 7.7523 3.90048 7.80747 3.85392 7.85392L1.70692 9.99992H11.4999C11.6325 9.99992 11.7597 10.0526 11.8535 10.1464C11.9472 10.2401 11.9999 10.3673 11.9999 10.4999C11.9999 10.6325 11.9472 10.7597 11.8535 10.8535C11.7597 10.9472 11.6325 10.9999 11.4999 10.9999H1.70692L3.85392 13.1459C3.9478 13.2398 4.00055 13.3671 4.00055 13.4999C4.00055 13.6327 3.9478 13.76 3.85392 13.8539C3.76003 13.9478 3.63269 14.0005 3.49992 14.0005C3.36714 14.0005 3.2398 13.9478 3.14592 13.8539L0.145917 10.8539C0.0993539 10.8075 0.062411 10.7523 0.0372045 10.6916C0.0119981 10.6308 -0.000976562 10.5657 -0.000976562 10.4999C-0.000976562 10.4341 0.0119981 10.369 0.0372045 10.3083C0.062411 10.2475 0.0993539 10.1924 0.145917 10.1459L3.14592 7.14592C3.19236 7.09935 3.24754 7.06241 3.30828 7.0372C3.36903 7.012 3.43415 6.99902 3.49992 6.99902C3.56568 6.99902 3.63081 7.012 3.69155 7.0372C3.7523 7.06241 3.80747 7.09935 3.85392 7.14592Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,3 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="12" height="12" rx="6" fill="#2BB34A"/>
</svg>

Before

Width:  |  Height:  |  Size: 156 B

View File

@ -1,3 +0,0 @@
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.5" width="12" height="12" rx="6" fill="#F00505"/>
</svg>

Before

Width:  |  Height:  |  Size: 164 B

View File

@ -1,4 +0,0 @@
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 10C0 4.47715 4.47715 0 10 0H32V24H10C4.47715 24 0 19.5228 0 14V10Z" fill="#B30436"/>
<path d="M10.375 9.875C10.1641 9.875 10 9.66406 10 9.5C10 9.3125 10.1641 9.125 10.375 9.125H18.25L18.2266 7.0625C18.2266 6.82812 18.3672 6.61719 18.5781 6.54688C18.7891 6.45312 19.0234 6.5 19.1875 6.64062L21.8125 9.07812C21.9297 9.19531 22 9.3125 22 9.5C22 9.66406 21.9297 9.80469 21.8125 9.92188L19.1875 12.3594C19.0234 12.5234 18.7891 12.5469 18.5781 12.4531C18.3672 12.3828 18.2266 12.1719 18.2266 11.9375L18.25 9.875H10.375ZM19 7.50781V11.5156L21.1562 9.5L19 7.50781ZM21.625 15.125C21.8125 15.125 22 15.3359 22 15.5C22 15.6875 21.8125 15.875 21.625 15.875H13.75V17.9375C13.75 18.1719 13.6094 18.3828 13.3984 18.4531C13.1875 18.5234 12.9531 18.5 12.7891 18.3359L10.1641 15.8984C10.0469 15.7812 10 15.6172 10 15.5C10 15.3594 10.0469 15.2188 10.1641 15.1016L12.7891 12.6641C12.9531 12.5 13.1875 12.4766 13.3984 12.5469C13.6094 12.6406 13.75 12.8516 13.75 13.0625V15.125H21.625ZM13 17.5156V13.5078L10.8203 15.5L13 17.5156Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,3 +0,0 @@
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.75391 1.16406C9.17578 0.707031 9.91406 0.707031 10.3359 1.16406C10.793 1.58594 10.793 2.32422 10.3359 2.74609L9.45703 3.625H14.625C16.4883 3.625 18 5.13672 18 7V8.125C18 8.75781 17.4727 9.25 16.875 9.25C16.2422 9.25 15.75 8.75781 15.75 8.125V7C15.75 6.40234 15.2227 5.875 14.625 5.875H9.45703L10.3359 6.78906C10.793 7.21094 10.793 7.94922 10.3359 8.37109C9.91406 8.82812 9.17578 8.82812 8.75391 8.37109L5.94141 5.55859C5.48438 5.13672 5.48438 4.39844 5.94141 3.97656L8.75391 1.16406ZM12.0234 12.9766C12.4805 13.3984 12.4805 14.1367 12.0234 14.5586L9.21094 17.3711C8.78906 17.8281 8.05078 17.8281 7.62891 17.3711C7.17188 16.9492 7.17188 16.2109 7.62891 15.7891L8.50781 14.875H3.375C2.74219 14.875 2.25 15.4023 2.25 16V17.125C2.25 17.7578 1.72266 18.25 1.125 18.25C0.492188 18.25 0 17.7578 0 17.125V16C0 14.1367 1.47656 12.625 3.375 12.625H8.50781L7.62891 11.7461C7.17188 11.3242 7.17188 10.5859 7.62891 10.1641C8.05078 9.70703 8.78906 9.70703 9.21094 10.1641L12.0234 12.9766ZM18 13.75C18 15.0156 16.9805 16 15.75 16C14.4844 16 13.5 15.0156 13.5 13.75C13.5 12.5195 14.4844 11.5 15.75 11.5C16.9805 11.5 18 12.5195 18 13.75ZM4.5 4.75C4.5 6.01562 3.48047 7 2.25 7C0.984375 7 0 6.01562 0 4.75C0 3.51953 0.984375 2.5 2.25 2.5C3.48047 2.5 4.5 3.51953 4.5 4.75Z" fill="#1B3C59"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.125 8.875C3.49219 8.875 3 8.3125 3 7.75C3 7.15234 3.49219 6.625 4.125 6.625H15.3398V4.09375C15.3398 3.77734 15.5508 3.46094 15.8672 3.32031C16.1836 3.21484 16.5352 3.25 16.7812 3.49609L20.7188 7.15234C21.0703 7.46875 21.0703 8.06641 20.7188 8.38281L16.7812 12.0391C16.5352 12.2852 16.1836 12.3203 15.8672 12.1797C15.5508 12.0742 15.3398 11.7578 15.3398 11.4062V8.875H4.125ZM19.875 15.625C20.4727 15.625 21 16.1523 21 16.75C21 17.3828 20.4727 17.875 19.875 17.875H8.625V20.4062C8.625 20.7578 8.41406 21.0742 8.09766 21.1797C7.78125 21.3203 7.42969 21.2852 7.18359 21.0391L3.24609 17.3828C2.89453 17.0664 2.89453 16.4688 3.24609 16.1523L7.18359 12.4961C7.42969 12.25 7.78125 12.2148 8.09766 12.3203C8.41406 12.4258 8.625 12.7422 8.625 13.0938V15.625H19.875Z" fill="#1B3C59"/>
</svg>

Before

Width:  |  Height:  |  Size: 889 B

View File

@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.875 12.375H3.125V2.625C3.125 2.55625 3.06875 2.5 3 2.5H2.125C2.05625 2.5 2 2.55625 2 2.625V13.375C2 13.4438 2.05625 13.5 2.125 13.5H13.875C13.9438 13.5 14 13.4438 14 13.375V12.5C14 12.4312 13.9438 12.375 13.875 12.375ZM4.5 9.4375C4.5 9.70272 4.60536 9.95707 4.79289 10.1446C4.98043 10.3321 5.23478 10.4375 5.5 10.4375C5.76522 10.4375 6.01957 10.3321 6.20711 10.1446C6.39464 9.95707 6.5 9.70272 6.5 9.4375C6.5 9.17228 6.39464 8.91793 6.20711 8.73039C6.01957 8.54286 5.76522 8.4375 5.5 8.4375C5.23478 8.4375 4.98043 8.54286 4.79289 8.73039C4.60536 8.91793 4.5 9.17228 4.5 9.4375ZM6.34375 5.9375C6.34375 6.13641 6.42277 6.32718 6.56342 6.46783C6.70407 6.60848 6.89484 6.6875 7.09375 6.6875C7.29266 6.6875 7.48343 6.60848 7.62408 6.46783C7.76473 6.32718 7.84375 6.13641 7.84375 5.9375C7.84375 5.73859 7.76473 5.54782 7.62408 5.40717C7.48343 5.26652 7.29266 5.1875 7.09375 5.1875C6.89484 5.1875 6.70407 5.26652 6.56342 5.40717C6.42277 5.54782 6.34375 5.73859 6.34375 5.9375ZM8.8125 9.5C8.8125 9.89782 8.97054 10.2794 9.25184 10.5607C9.53314 10.842 9.91468 11 10.3125 11C10.7103 11 11.0919 10.842 11.3732 10.5607C11.6545 10.2794 11.8125 9.89782 11.8125 9.5C11.8125 9.10218 11.6545 8.72064 11.3732 8.43934C11.0919 8.15804 10.7103 8 10.3125 8C9.91468 8 9.53314 8.15804 9.25184 8.43934C8.97054 8.72064 8.8125 9.10218 8.8125 9.5ZM11.125 4.59375C11.125 4.82581 11.2172 5.04837 11.3813 5.21247C11.5454 5.37656 11.7679 5.46875 12 5.46875C12.2321 5.46875 12.4546 5.37656 12.6187 5.21247C12.7828 5.04837 12.875 4.82581 12.875 4.59375C12.875 4.36169 12.7828 4.13913 12.6187 3.97503C12.4546 3.81094 12.2321 3.71875 12 3.71875C11.7679 3.71875 11.5454 3.81094 11.3813 3.97503C11.2172 4.13913 11.125 4.36169 11.125 4.59375Z" fill="#666666"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.207 2.28998L19.207 6.28598C19.3793 6.45805 19.4829 6.68708 19.4984 6.93011C19.5139 7.17314 19.4401 7.41346 19.291 7.60598L19.208 7.69998L15.208 11.706C15.0285 11.8872 14.7864 11.993 14.5315 12.0017C14.2765 12.0103 14.0278 11.9212 13.8364 11.7525C13.645 11.5838 13.5253 11.3484 13.5018 11.0944C13.4783 10.8403 13.5528 10.5869 13.71 10.386L13.793 10.292L16.083 7.99998H5.5C5.25507 7.99995 5.01866 7.91003 4.83563 7.74727C4.65259 7.58451 4.53566 7.36023 4.507 7.11698L4.5 6.99898C4.50003 6.75405 4.58996 6.51765 4.75272 6.33461C4.91547 6.15158 5.13975 6.03464 5.383 6.00598L5.5 5.99898H16.09L13.794 3.70598C13.6217 3.53392 13.5181 3.30488 13.5026 3.06185C13.4871 2.81883 13.5609 2.5785 13.71 2.38598L13.793 2.29098C13.9651 2.11866 14.1941 2.01504 14.4371 1.99958C14.6802 1.98411 14.9205 2.05786 15.113 2.20698L15.207 2.28998ZM19.491 16.882L19.497 16.999C19.497 17.2439 19.407 17.4803 19.2443 17.6634C19.0815 17.8464 18.8573 17.9633 18.614 17.992L18.497 17.999H7.914L10.208 20.292C10.3802 20.4642 10.4836 20.6933 10.4989 20.9363C10.5142 21.1793 10.4403 21.4196 10.291 21.612L10.208 21.706C10.0359 21.8783 9.8069 21.9819 9.56387 21.9974C9.32084 22.0129 9.08052 21.9391 8.888 21.79L8.794 21.706L4.794 17.71C4.62168 17.5379 4.51806 17.3089 4.50259 17.0659C4.48713 16.8228 4.56088 16.5825 4.71 16.39L4.793 16.296L8.793 12.292C8.97254 12.1107 9.21457 12.005 9.46955 11.9963C9.72453 11.9876 9.97317 12.0768 10.1646 12.2454C10.356 12.4141 10.4757 12.6496 10.4992 12.9036C10.5227 13.1576 10.4482 13.4111 10.291 13.612L10.208 13.706L7.918 15.999H18.498C18.7429 15.999 18.9793 16.0889 19.1624 16.2517C19.3454 16.4145 19.4623 16.6387 19.491 16.882Z" fill="#195D80"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,20 +0,0 @@
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_8341_39661)">
<rect x="8" y="8" width="56" height="56" rx="10" fill="#181B1F"/>
<path d="M26.1502 40.9985C25.5013 39.5318 25.1663 37.9456 25.1668 36.3418C25.1668 30.1668 30.0168 25.1651 36.0002 25.1651C41.9835 25.1651 46.8335 30.1685 46.8335 36.3418C46.834 37.9456 46.4991 39.5318 45.8502 40.9985M36.0002 19.3318V20.9985M52.6668 35.9985H51.0002M21.0002 35.9985H19.3335M47.7835 24.2135L46.6052 25.3918M25.3952 25.3935L24.2168 24.2151M40.1952 48.1768C41.8785 47.6318 42.5552 46.0901 42.7452 44.5401C42.8018 44.0768 42.4202 43.6918 41.9535 43.6918H30.1285C30.0145 43.69 29.9015 43.7124 29.7968 43.7575C29.6922 43.8027 29.5983 43.8695 29.5214 43.9536C29.4445 44.0378 29.3864 44.1373 29.3508 44.2456C29.3153 44.3538 29.3031 44.4685 29.3152 44.5818C29.5018 46.1285 29.9718 47.2585 31.7552 48.1751M40.1952 48.1768L31.7552 48.1751M40.1952 48.1768C39.9935 51.4185 39.0568 52.7018 36.0118 52.6651C32.7552 52.7251 32.0052 51.1368 31.7552 48.1751" stroke="#B8B8B8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M31.2627 33.6231C33.3296 29.9575 34.363 28.125 36 28.125C37.637 28.125 38.6704 29.9575 40.7373 33.6231L40.9952 34.0792C42.7129 37.1251 43.5721 38.648 42.7958 39.7615C42.0194 40.875 40.0984 40.875 36.2578 40.875H35.7422C31.9016 40.875 29.9806 40.875 29.2043 39.7615C28.4279 38.648 29.2871 37.1251 31.0048 34.0792L31.2627 33.6231Z" stroke="#007EA7" stroke-width="0.944444"/>
<path d="M36 31.6667V35.2084" stroke="#007EA7" stroke-width="0.944444" stroke-linecap="round"/>
<path d="M35.9998 38.0417C36.391 38.0417 36.7082 37.7245 36.7082 37.3333C36.7082 36.9421 36.391 36.625 35.9998 36.625C35.6086 36.625 35.2915 36.9421 35.2915 37.3333C35.2915 37.7245 35.6086 38.0417 35.9998 38.0417Z" fill="#007EA7"/>
</g>
<defs>
<filter id="filter0_d_8341_39661" x="0" y="0" width="72" height="72" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="4"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.301961 0 0 0 0 0.301961 0 0 0 0 0.301961 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_8341_39661"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_8341_39661" result="shape"/>
</filter>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,6 +0,0 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.1502 24.9985C9.50126 23.5318 9.16631 21.9456 9.16683 20.3418C9.16683 14.1668 14.0168 9.16512 20.0002 9.16512C25.9835 9.16512 30.8335 14.1685 30.8335 20.3418C30.834 21.9456 30.4991 23.5318 29.8502 24.9985M20.0002 3.33179V4.99845M36.6668 19.9985H35.0002M5.00016 19.9985H3.3335M31.7835 8.21345L30.6052 9.39179M9.39516 9.39345L8.21683 8.21512M24.1952 32.1768C25.8785 31.6318 26.5552 30.0901 26.7452 28.5401C26.8018 28.0768 26.4202 27.6918 25.9535 27.6918H14.1285C14.0145 27.69 13.9015 27.7124 13.7968 27.7575C13.6922 27.8027 13.5983 27.8695 13.5214 27.9536C13.4445 28.0378 13.3864 28.1373 13.3508 28.2456C13.3153 28.3538 13.3031 28.4685 13.3152 28.5818C13.5018 30.1285 13.9718 31.2585 15.7552 32.1751M24.1952 32.1768L15.7552 32.1751M24.1952 32.1768C23.9935 35.4185 23.0568 36.7018 20.0118 36.6651C16.7552 36.7251 16.0052 35.1368 15.7552 32.1751" stroke="#1B3C59" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.2627 17.6231C17.3296 13.9575 18.363 12.125 20 12.125C21.637 12.125 22.6704 13.9575 24.7373 17.6231L24.9952 18.0792C26.7129 21.1251 27.5721 22.648 26.7958 23.7615C26.0194 24.875 24.0984 24.875 20.2578 24.875H19.7422C15.9016 24.875 13.9806 24.875 13.2043 23.7615C12.4279 22.648 13.2871 21.1251 15.0048 18.0792L15.2627 17.6231Z" stroke="#B30436" stroke-width="0.944444"/>
<path d="M20 15.6667V19.2084" stroke="#B30436" stroke-width="0.944444" stroke-linecap="round"/>
<path d="M19.9998 22.0417C20.391 22.0417 20.7082 21.7245 20.7082 21.3333C20.7082 20.9421 20.391 20.625 19.9998 20.625C19.6086 20.625 19.2915 20.9421 19.2915 21.3333C19.2915 21.7245 19.6086 22.0417 19.9998 22.0417Z" fill="#B30436"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -84,7 +84,6 @@ class Assets {
static String get risk => '$_baseFeaturesPath/risk-$_themeSuffix.svg'; static String get risk => '$_baseFeaturesPath/risk-$_themeSuffix.svg';
static String get saha => '$_baseFeaturesPath/saha-$_themeSuffix.svg'; static String get saha => '$_baseFeaturesPath/saha-$_themeSuffix.svg';
static String get ai => '$_baseFeaturesPath/ai-$_themeSuffix.svg'; static String get ai => '$_baseFeaturesPath/ai-$_themeSuffix.svg';
static String get hugeideas => '$_baseFeaturesPath/hugeicons_idea-$_themeSuffix.svg';
static String get startup => '$_baseFeaturesPath/startup-$_themeSuffix.svg'; static String get startup => '$_baseFeaturesPath/startup-$_themeSuffix.svg';
static String get stats => '$_baseFeaturesPath/stats-$_themeSuffix.svg'; static String get stats => '$_baseFeaturesPath/stats-$_themeSuffix.svg';
static String get tech => '$_baseFeaturesPath/tech-$_themeSuffix.svg'; static String get tech => '$_baseFeaturesPath/tech-$_themeSuffix.svg';

View File

@ -1,37 +0,0 @@
class SwotItem {
final int id;
final String title;
final String description;
final String category;
final String type;
final String imageUrl;
final double x1;
final double y1;
final String createdAt;
SwotItem({
required this.id,
required this.title,
required this.description,
required this.category,
required this.type,
required this.imageUrl,
required this.x1,
required this.y1,
required this.createdAt,
});
factory SwotItem.fromJson(Map<String, dynamic> json) {
return SwotItem(
id: json['id'],
title: json['title'],
description: json['description'],
category: json['category'],
type: json['type'],
imageUrl: json["url"],
x1: json["scoreX1"],
y1: json["scoreY1"],
createdAt: json["createdAt"]
);
}
}

View File

@ -1,18 +0,0 @@
class Note {
final int postId;
final String content;
Note({required this.postId, required this.content});
factory Note.fromJson(Map<String, dynamic> json) {
return Note(
postId: json['postId'],
content: json['content'],
);
}
Map<String, dynamic> toJson() => {
'postId': postId,
'content': content,
};
}

View File

@ -299,19 +299,14 @@ class RouteGenerator {
), ),
); );
case Routes.filteredBookmarks: case Routes.filteredBookmarks:
final args = settings.arguments as Map<String, dynamic>;
final type = args['type'] as int;
final onDeleted = args['onDeleted'] as void Function(int id)?;
return _createRoute( return _createRoute(
ChangeNotifierProvider<FilteredBookmarksState>( ChangeNotifierProvider<FilteredBookmarksState>(
create: (context) => FilteredBookmarksState( create: (context) => FilteredBookmarksState(
type, (settings.arguments as Map<String, dynamic>)['type'],
), ),
child: FilteredBookmarks( child: FilteredBookmarks(
onDeleted: onDeleted, onDeleted:
type: type, (settings.arguments as Map<String, dynamic>)['onDeleted']),
),
), ),
); );

View File

@ -6,7 +6,6 @@ import 'package:didvan/models/requests/studio.dart';
class RequestHelper { class RequestHelper {
static const String baseUrl = 'https://api.didvan.app'; static const String baseUrl = 'https://api.didvan.app';
static const String baseUrl2 = 'http://opportunity-threat.didvan.com';
static const String _baseUserUrl = '$baseUrl/user'; static const String _baseUserUrl = '$baseUrl/user';
static const String _baseRadarUrl = '$baseUrl/radar'; static const String _baseRadarUrl = '$baseUrl/radar';
static const String _baseNewsUrl = '$baseUrl/news'; static const String _baseNewsUrl = '$baseUrl/news';

View File

@ -1,128 +0,0 @@
import 'dart:convert';
import 'package:didvan/models/note.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:http/http.dart' as RequestServices;
class NoteService {
static Future<Note?> getNoteByPostId(int postId) async {
try {
final response = await RequestServices.get(
Uri.parse('${RequestHelper.baseUrl2}/api/notes'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
},
);
print(
'Get note - PostId: $postId, Status: ${response.statusCode}, Body: ${response.body}');
if (response.statusCode == 200) {
final List data = jsonDecode(response.body);
print('Notes list: $data');
final note = data.firstWhere(
(e) => e['postId'] == postId,
orElse: () => null,
);
print('Found note for PostId $postId: $note');
return note != null ? Note.fromJson(note) : null;
}
print('Failed to get note: ${response.statusCode}, ${response.body}');
return null;
} catch (e) {
print('Error in getNoteByPostId: $e');
return null;
}
}
static Future<bool> saveNote(int postId, String content) async {
try {
final response = await RequestServices.post(
Uri.parse('${RequestHelper.baseUrl2}/api/notes'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json'
},
body: jsonEncode({
'postId': postId,
'content': content,
}),
);
print(
'Save note - PostId: $postId, Status: ${response.statusCode}, Body: ${response.body}');
if (response.statusCode == 200 || response.statusCode == 201) {
try {
final responseBody = jsonDecode(response.body);
if (responseBody['postId'] == postId &&
responseBody['content'] == content) {
return true;
} else {
print(
'Server returned success status but data mismatch: ${response.body}');
return false;
}
} catch (e) {
print('Error parsing response body: $e');
return response.statusCode == 200 || response.statusCode == 201;
}
} else {
print('Failed to save note: ${response.statusCode}, ${response.body}');
return false;
}
} catch (e) {
print('Error in saveNote: $e');
return false;
}
}
static Future<bool> deleteNote(int postId) async {
final response = await RequestServices.delete(
Uri.parse('${RequestHelper.baseUrl2}/api/notes/post/$postId'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
},
);
return response.statusCode == 200;
}
static Future<bool> updateNote(int postId, String content) async {
try {
final response = await RequestServices.put(
Uri.parse('${RequestHelper.baseUrl2}/api/notes/post/$postId'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json',
},
body: jsonEncode({
'postId': postId,
'content': content,
}),
);
print(
'Update note - PostId: $postId, Status: ${response.statusCode}, Body: ${response.body}');
if (response.statusCode == 200 || response.statusCode == 201) {
try {
final responseBody = jsonDecode(response.body);
if (responseBody['postId'] == postId &&
responseBody['content'] == content) {
return true;
} else {
print(
'Server returned success status but data mismatch: ${response.body}');
return false;
}
} catch (e) {
print('Error parsing response body: $e');
return response.statusCode == 200 || response.statusCode == 201;
}
} else {
print('Failed to update note: ${response.statusCode}, ${response.body}');
return false;
}
} catch (e) {
print('Error in updateNote: $e');
return false;
}
}
}

View File

@ -1,35 +0,0 @@
import 'dart:convert';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:http/http.dart' as http;
class SwotService {
static Future<List<SwotItem>> fetchSwotItems() async {
final response = await http.get(
Uri.parse('${RequestHelper.baseUrl2}/api/swot'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
},
);
print("Status Code: ${response.statusCode}");
// decode manually using utf8.decode on bodyBytes
final decodedBody = utf8.decode(response.bodyBytes);
print("Decoded Response Body: $decodedBody");
if (response.statusCode == 200) {
final jsonData = json.decode(decodedBody);
if (jsonData['content'] == null || jsonData['content'] is! List) {
throw Exception('The data structure is wrong.');
}
final List<dynamic> items = jsonData['content'];
return items.map((e) => SwotItem.fromJson(e)).toList();
} else {
throw Exception('Error receiving data - statusCode: ${response.statusCode}');
}
}
}

View File

@ -1,127 +0,0 @@
import 'dart:convert';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart' as RequestServicess;
class BookmarkService {
static Future<List<int>> fetchBookmarks() async {
final response = await http.get(
Uri.parse('${RequestHelper.baseUrl2}/api/bookmarks'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
},
);
print("fetchBookmarks: ${response.statusCode} - ${response.body}");
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List;
return data.map<int>((e) => e['postId'] as int).toList();
} else {
throw Exception("Failed to fetch bookmarks");
}
}
static Future<List<int>> fetchSwotBookmarks() async {
final response = await http.get(
Uri.parse('${RequestHelper.baseUrl2}/api/swot_bookmarks'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json',
},
);
print("fetchSwotBookmarks: ${response.statusCode} - ${response.body}");
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List;
return data.map<int>((e) => e['swotId'] as int).toList();
} else {
throw Exception("Failed to fetch swot bookmarks");
}
}
static Future<void> addSwotBookmark(int swotId) async {
final response = await http.post(
Uri.parse('${RequestHelper.baseUrl2}/api/swot_bookmarks'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json',
},
body: jsonEncode({'swotId': swotId}),
);
if (response.statusCode != 200 && response.statusCode != 201) {
throw Exception("Failed to add swot bookmark");
}
}
static Future<void> removeSwotBookmark(int swotId) async {
final response = await http.delete(
Uri.parse('${RequestHelper.baseUrl2}/api/swot_bookmarks/swot/$swotId'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json',
},
);
if (response.statusCode != 200 && response.statusCode != 204) {
throw Exception("Failed to remove swot bookmark");
}
}
static Future<List<SwotItem>> fetchBookmarkedSwotItems() async {
final postIds = await fetchBookmarks(); // استفاده از همان لیست بوکمارکها
final List<SwotItem> items = [];
for (final postId in postIds) {
try {
final response = await http.get(
Uri.parse("${RequestHelper.baseUrl2}/api/swot_items/$postId"),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final postData = jsonDecode(response.body);
items.add(SwotItem.fromJson(postData));
} else {
print('Error fetching swot item $postId: ${response.statusCode}');
}
} catch (e) {
print('Error fetching swot item $postId: $e');
}
}
return items;
}
static Future<void> addBookmark(int postId) async {
final response = await http.post(
Uri.parse('${RequestHelper.baseUrl2}/api/bookmarks'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json',
},
body: jsonEncode({'postId': postId}),
);
if (response.statusCode != 200 && response.statusCode != 201) {
throw Exception("Failed to add bookmark");
}
}
static Future<void> removeBookmark(int postId) async {
final response = await http.delete(
Uri.parse('${RequestHelper.baseUrl2}/api/bookmarks/post/$postId'),
headers: {
'Authorization': 'Bearer ${RequestService.token}',
'Content-Type': 'application/json',
},
);
if (response.statusCode != 200 && response.statusCode != 204) {
throw Exception("Failed to remove bookmark");
}
}
}

View File

@ -1,55 +1,29 @@
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/views/home/bookmarks/bookmark_service.dart';
import 'package:didvan/services/swot_service.dart';
class BookmarksState extends CoreProvier { class BookmarksState extends CoreProvier {
final List<OverviewData> bookmarks = []; final List<OverviewData> bookmarks = [];
List<SwotItem> bookmarkedSwotItems = [];
String search = ''; String search = '';
String lastSearch = ''; String lastSearch = '';
int page = 1; int page = 1;
int lastPage = 1; int lastPage = 1;
bool _swotItemsLoading = false; bool get searching => search != '';
bool get searching => search.isNotEmpty;
bool get swotItemsLoading => _swotItemsLoading;
Future<void> loadInitialData() async { Future<void> getBookmarks({required int page}) async {
appState = AppState.busy; if (search != '') {
_swotItemsLoading = true; lastSearch = search;
}
if (page == 1) {
bookmarks.clear(); bookmarks.clear();
bookmarkedSwotItems.clear();
notifyListeners();
await Future.wait([
_fetchGeneralBookmarks(page: 1),
_fetchSwotBookmarks(),
]);
if (appState != AppState.failed && !searching) {
appState = AppState.idle;
} else if (appState != AppState.failed && searching && bookmarks.isEmpty && bookmarkedSwotItems.isEmpty) {
appState = AppState.idle;
} }
_swotItemsLoading = false;
notifyListeners();
}
Future<void> _fetchGeneralBookmarks({required int page}) async {
if (page == 1) bookmarks.clear();
this.page = page; this.page = page;
if (!searching) lastSearch = search; appState = AppState.busy;
final service = RequestService( final service = RequestService(
RequestHelper.searchMarks(page: page, search: search.isNotEmpty ? search : null), RequestHelper.searchMarks(page: page, search: search),
); );
await service.httpGet(); await service.httpGet();
if (service.isSuccess) { if (service.isSuccess) {
@ -58,56 +32,15 @@ class BookmarksState extends CoreProvier {
for (var i = 0; i < marks.length; i++) { for (var i = 0; i < marks.length; i++) {
bookmarks.add(OverviewData.fromJson(marks[i])); bookmarks.add(OverviewData.fromJson(marks[i]));
} }
} else {
appState = AppState.failed;
}
}
Future<void> searchAndLoadData({required int page}) async {
appState = AppState.busy;
_swotItemsLoading = true;
bookmarks.clear();
notifyListeners();
await Future.wait([
_fetchGeneralBookmarks(page: page),
_fetchSwotBookmarks(),
]);
if (appState != AppState.failed) {
appState = AppState.idle; appState = AppState.idle;
return;
} }
_swotItemsLoading = false; appState = AppState.failed;
notifyListeners();
}
Future<void> _fetchSwotBookmarks() async {
try {
final postIds = await BookmarkService.fetchBookmarks();
if (postIds.isNotEmpty) {
final allSwots = await SwotService.fetchSwotItems();
bookmarkedSwotItems = allSwots.where((swot) {
final isBookmarked = postIds.contains(swot.id);
if (search.isEmpty) return isBookmarked;
return isBookmarked && swot.title.toLowerCase().contains(search.toLowerCase());
}).toList();
} else {
bookmarkedSwotItems = [];
}
} catch (e) {
bookmarkedSwotItems = [];
}
} }
void onMarkChanged(int id, bool value) { void onMarkChanged(int id, bool value) {
if (value) return;
bookmarks.removeWhere((element) => element.id == id); bookmarks.removeWhere((element) => element.id == id);
notifyListeners(); notifyListeners();
} }
void onSwotMarkChanged(int swotId) {
bookmarkedSwotItems.removeWhere((element) => element.id == swotId);
notifyListeners();
}
} }

View File

@ -4,15 +4,14 @@ import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/routes/routes.dart'; import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/home/bookmarks/bookmark_state.dart'; import 'package:didvan/views/home/bookmarks/bookmark_state.dart';
import 'package:didvan/views/home/main/widgets/swot_bookmark.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/menu_item.dart'; import 'package:didvan/views/widgets/menu_item.dart';
import 'package:didvan/views/widgets/overview/multitype.dart'; import 'package:didvan/views/widgets/overview/multitype.dart';
// import 'package:didvan/views/widgets/search_field.dart';
import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/divider.dart';
@ -20,7 +19,6 @@ import 'package:didvan/views/widgets/item_title.dart';
import 'package:didvan/views/widgets/state_handlers/empty_result.dart'; import 'package:didvan/views/widgets/state_handlers/empty_result.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class Bookmarks extends StatefulWidget { class Bookmarks extends StatefulWidget {
@ -30,36 +28,16 @@ class Bookmarks extends StatefulWidget {
State<Bookmarks> createState() => _BookmarksState(); State<Bookmarks> createState() => _BookmarksState();
} }
class _BookmarksState extends State<Bookmarks> { class _BookmarksState extends State<Bookmarks> {
final _focuseNode = FocusNode(); final _focuseNode = FocusNode();
Timer? _timer; // Timer? _timer;
@override @override
void initState() { void initState() {
Future.delayed(Duration.zero, () {
context.read<BookmarksState>().getBookmarks(page: 1);
});
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<BookmarksState>().loadInitialData();
});
}
void _onSearchChanged(String value) {
final state = context.read<BookmarksState>();
if (value.length < 3 && value.isNotEmpty) {
if (state.search.isNotEmpty && value.isEmpty) {
state.search = value;
state.loadInitialData();
}
return;
}
if (state.lastSearch == value && value.isNotEmpty) return;
_timer?.cancel();
_timer = Timer(const Duration(milliseconds: 700), () {
state.search = value;
state.searchAndLoadData(page: 1);
});
} }
@override @override
@ -76,6 +54,7 @@ class _BookmarksState extends State<Bookmarks> {
sliver: SliverToBoxAdapter( sliver: SliverToBoxAdapter(
child: Column( child: Column(
children: [ children: [
const SizedBox(height: 16),
AnimatedVisibility( AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
isVisible: !state.searching, isVisible: !state.searching,
@ -130,18 +109,10 @@ class _BookmarksState extends State<Bookmarks> {
icon: DidvanIcons.infography_regular, icon: DidvanIcons.infography_regular,
iconSize: 24, iconSize: 24,
), ),
const DidvanDivider(),
MenuOption(
onTap: () => _onCategorySelected(8),
title: 'فرصت و تهدید',
iconWidget: SvgPicture.asset("lib/assets/images/features/Saha Solid.svg",width: 24,),
iconSize: 24,
),
], ],
), ),
), ),
), ),
if (!state.searching || (state.bookmarks.isNotEmpty || state.bookmarkedSwotItems.isNotEmpty))
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: AnimatedVisibility( child: AnimatedVisibility(
@ -154,13 +125,33 @@ class _BookmarksState extends State<Bookmarks> {
), ),
), ),
), ),
SliverPadding(
SliverStateHandler<BookmarksState>( padding: const EdgeInsets.symmetric(
horizontal: 16,
),
sliver: SliverStateHandler<BookmarksState>(
state: state, state: state,
enableEmptyState: state.bookmarks.isEmpty && state.bookmarkedSwotItems.isEmpty && !state.searching && !state.swotItemsLoading, centerEmptyState: state.searching,
emptyState: Column( builder: (context, state, index) {
crossAxisAlignment: CrossAxisAlignment.start, index++;
mainAxisAlignment: MainAxisAlignment.start, if (index % 15 == 0 && state.lastPage != state.page) {
state.getBookmarks(page: state.page + 1);
}
index--;
return MultitypeOverview(
item: state.bookmarks[index],
onMarkChanged: state.onMarkChanged,
hasUnmarkConfirmation: true,
enableCaption: true,
enableBookmark: true,
);
},
placeholder: MultitypeOverview.placeholder,
itemPadding: const EdgeInsets.only(bottom: 8),
paddingEmptyState: 0,
emptyState: state.searching
? EmptyResult(onNewSearch: _focuseNode.requestFocus)
: Column(
children: [ children: [
DidvanText( DidvanText(
'در قسمت رصدخانه من، تمامی مطالبی که در قسمت‌های مختلف سوپراپلیکیشن دیدوان، بوکمارک (نشان‌دار) کرده‌اید، به تفکیک نمایش داده می‌شوند. هم‌چنین امکان درج یادداشت شخصی بصورت ضمیمه برای هر محتوا وجود دارد.', 'در قسمت رصدخانه من، تمامی مطالبی که در قسمت‌های مختلف سوپراپلیکیشن دیدوان، بوکمارک (نشان‌دار) کرده‌اید، به تفکیک نمایش داده می‌شوند. هم‌چنین امکان درج یادداشت شخصی بصورت ضمیمه برای هر محتوا وجود دارد.',
@ -175,76 +166,28 @@ class _BookmarksState extends State<Bookmarks> {
), ),
], ],
), ),
placeholder: state.searching && state.bookmarks.isEmpty && state.bookmarkedSwotItems.isEmpty && !state.swotItemsLoading enableEmptyState: state.bookmarks.isEmpty,
? EmptyResult(onNewSearch: _focuseNode.requestFocus) childCount:
: MultitypeOverview.placeholder, state.bookmarks.length + (state.page != state.lastPage ? 1 : 0),
builder: (context, state, index) { onRetry: () => state.getBookmarks(page: state.page),
if (index >= state.bookmarks.length) {
return const Center(child: CircularProgressIndicator());
}
return MultitypeOverview(
item: state.bookmarks[index],
onMarkChanged: state.onMarkChanged,
hasUnmarkConfirmation: true,
enableCaption: true,
enableBookmark: true,
);
},
itemPadding: const EdgeInsets.only(bottom: 8, left: 16, right: 16),
childCount: state.bookmarks.length + (state.page != state.lastPage && state.bookmarks.isNotEmpty ? 1 : 0),
onRetry: () => state.loadInitialData(),
), ),
if (state.appState == AppState.idle && state.bookmarkedSwotItems.isNotEmpty)
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final item = state.bookmarkedSwotItems[index];
return Padding(
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16),
child: SwotBookmark(
item: item,
onSwotUnbookmarked: (postId) {
state.onSwotMarkChanged(postId);
},
), ),
);
},
childCount: state.bookmarkedSwotItems.length,
),
)
else if (state.swotItemsLoading)
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(child: MultitypeOverview.placeholder),
),
)
], ],
); );
} }
void _onCategorySelected(int type) { void _onCategorySelected(int type) {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
final state = context.read<BookmarksState>();
Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: { Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: {
'type': type, 'type': type,
'onDeleted': (int id) { 'onDeleted': (int id) {
final state = context.read<BookmarksState>();
state.bookmarks.removeWhere( state.bookmarks.removeWhere(
(element) => element.id == id && element.typeInteger == type); (element) => element.id == id && element.typeInteger == type);
if (type == 8) {
state.bookmarkedSwotItems.removeWhere((element) => element.id == id);
}
state.update(); state.update();
}, },
}).then((_) {
state.loadInitialData();
}); });
} }
}
// void _onChanged(String value) { // void _onChanged(String value) {
// final state = context.read<BookmarksState>(); // final state = context.read<BookmarksState>();
@ -257,4 +200,4 @@ class _BookmarksState extends State<Bookmarks> {
// state.getBookmarks(page: 1); // state.getBookmarks(page: 1);
// }); // });
// } // }
}

View File

@ -1,17 +1,16 @@
import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/views/home/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart'; import 'package:didvan/views/home/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart';
import 'package:didvan/views/widgets/overview/multitype.dart'; import 'package:didvan/views/widgets/overview/multitype.dart';
import 'package:didvan/views/widgets/overview/radar.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/state_handlers/empty_list.dart'; import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:didvan/views/home/main/widgets/swot_bookmark.dart';
class FilteredBookmarks extends StatefulWidget { class FilteredBookmarks extends StatefulWidget {
final void Function(int id)? onDeleted; final void Function(int id)? onDeleted;
final int type; const FilteredBookmarks({Key? key, this.onDeleted}) : super(key: key);
const FilteredBookmarks({Key? key, this.onDeleted, required this.type}) : super(key: key);
@override @override
State<FilteredBookmarks> createState() => _FilteredBookmarksState(); State<FilteredBookmarks> createState() => _FilteredBookmarksState();
@ -20,18 +19,15 @@ class FilteredBookmarks extends StatefulWidget {
class _FilteredBookmarksState extends State<FilteredBookmarks> { class _FilteredBookmarksState extends State<FilteredBookmarks> {
@override @override
void initState() { void initState() {
super.initState();
final state = context.read<FilteredBookmarksState>();
Future.delayed( Future.delayed(
Duration.zero, Duration.zero,
() => state.getBookmarks(page: 1), () => context.read<FilteredBookmarksState>().getBookmarks(page: 1),
); );
super.initState();
} }
String get _appBarTitle { String get _appBarTitle {
switch (context.read<FilteredBookmarksState>().type) {
switch (widget.type) {
case 1: case 1:
return 'پویش افق'; return 'پویش افق';
case 2: case 2:
@ -46,83 +42,85 @@ class _FilteredBookmarksState extends State<FilteredBookmarks> {
return 'سها'; return 'سها';
case 7: case 7:
return 'اینفوگرافی'; return 'اینفوگرافی';
case 8:
return 'فرصت و تهدید';
default: default:
return 'پویش'; return 'پویش';
} }
} }
Future<void> _onBookmarkChanged(int id, bool value, bool shouldUpdate, String itemType) async {
if (value) return;
widget.onDeleted?.call(id);
if (itemType == "swot") {
context.read<FilteredBookmarksState>().onSwotMarkChanged(id);
} else {
context.read<FilteredBookmarksState>().onMarkChanged(id, value);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.watch<FilteredBookmarksState>();
return DidvanScaffold( return DidvanScaffold(
appBarData: AppBarData(title: _appBarTitle, hasBack: true), appBarData: AppBarData(title: _appBarTitle),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
slivers: [ slivers: [
if (widget.type == 8) Consumer<FilteredBookmarksState>(
SliverStateHandler<FilteredBookmarksState>( builder: (context, state, child) =>
state: state,
enableEmptyState: state.bookmarkedSwotItems.isEmpty && !state.swotItemsLoading,
emptyState: const EmptyList(),
placeholder: MultitypeOverview.placeholder,
builder: (context, state, index) {
final item = state.bookmarkedSwotItems[index];
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: SwotBookmark(
item: item,
onSwotUnbookmarked: (postId) {
_onBookmarkChanged(postId, false, true, "swot");
},
),
);
},
itemPadding: EdgeInsets.zero,
childCount: state.bookmarkedSwotItems.length,
onRetry: () => state.getBookmarks(page: 1),
)
else
SliverStateHandler<FilteredBookmarksState>( SliverStateHandler<FilteredBookmarksState>(
state: state, state: state,
enableEmptyState: state.bookmarks.isEmpty, enableEmptyState: state.bookmarks.isEmpty,
itemPadding: const EdgeInsets.only(bottom: 8), itemPadding: const EdgeInsets.only(bottom: 8),
placeholder: MultitypeOverview.placeholder, placeholder: RadarOverview.placeholder,
emptyState: const EmptyList(), emptyState: const EmptyList(),
builder: (context, state, index) { builder: (context, state, index) {
if (index >= state.bookmarks.length) { index++;
if (state.page < state.lastPage) { if (index % 15 == 0 && state.lastPage != state.page) {
state.getBookmarks(page: state.page + 1); state.getBookmarks(page: state.page + 1);
return MultitypeOverview.placeholder;
} }
return const SizedBox.shrink(); index--;
}
final item = state.bookmarks[index];
return MultitypeOverview( return MultitypeOverview(
item: item, item: state.bookmarks[index],
enableCaption: true, enableCaption: true,
onMarkChanged: (id, value) => _onBookmarkChanged(id, value, true, item.type), onMarkChanged: (id, value) => _onBookmarkChanged(
id,
value,
true,
),
enableBookmark: true, enableBookmark: true,
); );
// if (state.type == 'radar') {
// return RadarOverview(
// radar: state.bookmarks[index],
// onMarkChanged: _onBookmarkChanged,
// onCommentsChanged: state.onCommentsChanged,
// hasUnmarkConfirmation: true,
// );
// }
// if (state.type == 'news') {
// return NewsOverview(
// news: state.bookmarks[index],
// onMarkChanged: _onBookmarkChanged,
// hasUnmarkConfirmation: true,
// );
// }
// if (state.type == 'podcast') {
// return PodcastOverview(
// studioRequestArgs:
// const StudioRequestArgs(page: 0, type: 'podcast'),
// podcast: state.bookmarks[index],
// onMarkChanged: _onBookmarkChanged,
// hasUnmarkConfirmation: true,
// );
// }
// return VideoOverview(
// studioRequestArgs:
// const StudioRequestArgs(page: 0, type: 'video'),
// video: state.bookmarks[index],
// onMarkChanged: _onBookmarkChanged,
// hasUnmarkConfirmation: true,
// );
}, },
childCount: state.bookmarks.length + (state.page < state.lastPage ? 1: 0), childCount: state.bookmarks.length,
onRetry: () => state.getBookmarks(page: state.page), onRetry: () => state.getBookmarks(page: state.page),
), ),
),
], ],
); );
} }
Future<void> _onBookmarkChanged(int id, bool value, bool shouldUpdate) async {
if (value) return;
final state = context.read<FilteredBookmarksState>();
state.onMarkChanged(id, false);
widget.onDeleted?.call(id);
}
} }

View File

@ -1,36 +1,19 @@
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/views/home/bookmarks/bookmark_service.dart';
import 'package:didvan/services/swot_service.dart';
class FilteredBookmarksState extends CoreProvier { class FilteredBookmarksState extends CoreProvier {
final List<OverviewData> bookmarks = []; final List<OverviewData> bookmarks = [];
List<SwotItem> bookmarkedSwotItems = [];
final int type; final int type;
int page = 1; int page = 1;
int lastPage = 1; int lastPage = 1;
bool _swotItemsLoading = false;
bool get swotItemsLoading => _swotItemsLoading;
FilteredBookmarksState(this.type); FilteredBookmarksState(this.type);
Future<void> getBookmarks({required int page}) async { Future<void> getBookmarks({required int page}) async {
if (type == 8) {
await _fetchSwotBookmarksFiltered();
} else {
await _fetchGeneralBookmarks(page: page);
}
}
Future<void> _fetchGeneralBookmarks({required int page}) async {
this.page = page; this.page = page;
appState = AppState.busy;
notifyListeners();
final service = RequestService( final service = RequestService(
RequestHelper.searchMarks( RequestHelper.searchMarks(
types: [type], types: [type],
@ -42,63 +25,22 @@ class FilteredBookmarksState extends CoreProvier {
if (service.isSuccess) { if (service.isSuccess) {
lastPage = service.result['lastPage']; lastPage = service.result['lastPage'];
final marks = service.result['contents']; final marks = service.result['contents'];
bookmarks.clear();
for (var i = 0; i < marks.length; i++) { for (var i = 0; i < marks.length; i++) {
bookmarks.add(OverviewData.fromJson(marks[i])); bookmarks.add(OverviewData.fromJson(marks[i]));
} }
appState = AppState.idle; appState = AppState.idle;
} else { return;
}
appState = AppState.failed; appState = AppState.failed;
} }
notifyListeners();
}
Future<void> _fetchSwotBookmarksFiltered() async {
appState = AppState.busy;
_swotItemsLoading = true;
bookmarkedSwotItems.clear();
notifyListeners();
try {
final bookmarkedPostIds = await BookmarkService.fetchBookmarks();
if (bookmarkedPostIds.isNotEmpty) {
final allSwotItems = await SwotService.fetchSwotItems();
bookmarkedSwotItems = allSwotItems.where((swot) => bookmarkedPostIds.contains(swot.id)).toList();
} else {
bookmarkedSwotItems = [];
}
appState = AppState.idle;
} catch (e) {
appState = AppState.failed;
bookmarkedSwotItems = [];
} finally {
_swotItemsLoading = false;
notifyListeners();
}
}
void onMarkChanged(int id, bool value) { void onMarkChanged(int id, bool value) {
if (type != 8) {
bookmarks.removeWhere((element) => element.id == id); bookmarks.removeWhere((element) => element.id == id);
notifyListeners(); notifyListeners();
} }
}
void onSwotMarkChanged(int id) {
bookmarkedSwotItems.removeWhere((element) => element.id == id);
notifyListeners();
}
void onCommentsChanged(int id, int value) { void onCommentsChanged(int id, int value) {
bookmarks.firstWhere((radar) => radar.id == id).comments = value;
if (type != 8) {
final item = bookmarks.firstWhere((radar) => radar.id == id, orElse: () => OverviewData(id: -1, title: '', image: '', description: '', createdAt: '', type: '', marked: false, liked: false, likes: 0, comments: 0, forManagers: false) );
if(item.id != -1) {
item.comments = value;
notifyListeners(); notifyListeners();
} }
} }
}
}

View File

@ -202,11 +202,6 @@ class HomeState extends CoreProvier {
asset: Assets.ai, asset: Assets.ai,
link: 'tab-2', link: 'tab-2',
), ),
MenuItemType(
label: 'فرصت و تهدید',
asset: Assets.hugeideas,
link: 'http://opportunity-threat.didvan.com/?accessToken=${RequestService.token}',
),
]; ];
categories = [ categories = [

View File

@ -1,9 +1,6 @@
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/main.dart';
import 'package:didvan/models/home_page_content/home_page_list.dart'; import 'package:didvan/models/home_page_content/home_page_list.dart';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/swot_service.dart';
import 'package:didvan/routes/routes.dart'; import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/views/home/main/main_page_state.dart'; import 'package:didvan/views/home/main/main_page_state.dart';
@ -15,16 +12,13 @@ import 'package:didvan/views/widgets/didvan/slider.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// import 'package:url_launcher/url_launcher_string.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:didvan/views/home/main/widgets/swot_item_card.dart';
class MainPage extends StatefulWidget { class MainPage extends StatefulWidget {
const MainPage({ const MainPage({super.key});
super.key,
});
@override @override
State<MainPage> createState() => _MainPageState(); State<MainPage> createState() => _MainPageState();
@ -42,14 +36,13 @@ class _MainPageState extends State<MainPage> {
return StateHandler<MainPageState>( return StateHandler<MainPageState>(
onRetry: context.read<MainPageState>().init, onRetry: context.read<MainPageState>().init,
state: context.watch<MainPageState>(), state: context.watch<MainPageState>(),
builder: (context, state) => ListView( builder: (context, state) => ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
children: [ itemBuilder: (context, index) {
// محتوای اصلی صفحه if (index == 0) {
const MainPageMainContent(), return const MainPageMainContent();
}
// آیتمهای لیستها index--;
...List.generate(state.content.lists.length + 1, (index) {
if (index == 4) { if (index == 4) {
return Padding( return Padding(
padding: const EdgeInsets.only(top: 32), padding: const EdgeInsets.only(top: 32),
@ -93,112 +86,16 @@ class _MainPageState extends State<MainPage> {
), ),
); );
} }
if (index > 3) {
int listIndex = index > 4 ? index - 1 : index; index--;
if (listIndex >= state.content.lists.length) {
return const SizedBox.shrink();
} }
final list = state.content.lists[index];
return _MainPageSection( return _MainPageSection(
list: state.content.lists[listIndex], list: list,
isLast: listIndex == state.content.lists.length - 1, isLast: index == state.content.lists.length - 1,
);
}),
// کانتینر زیر کل لیستها
FutureBuilder<List<SwotItem>>(
future: SwotService.fetchSwotItems(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 10,
);
} else if (snapshot.hasError) {
return const SizedBox(
height: 10,
);
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const SizedBox(
height: 10,
);
}
final items = snapshot.data!;
return Padding(
padding: const EdgeInsets.all(0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Title Row
Padding(
padding: const EdgeInsets.only(right: 20, top: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SvgPicture.asset(
"lib/assets/images/features/Saha Solid.svg",
),
const SizedBox(width: 5),
DidvanText(
"ماژول فرصت و تهدید",
style: Theme.of(context).textTheme.titleMedium,
color: Theme.of(context).colorScheme.title,
),
],
),
GestureDetector(
onTap: () {
AppInitializer.openWebLink(
navigatorKey.currentContext!,
'http://opportunity-threat.didvan.com/?accessToken=${RequestService.token}',
mode: LaunchMode.inAppWebView,
); );
}, },
child: Padding( itemCount: state.content.lists.length + 2,
padding: const EdgeInsets.only(left: 20),
child: Row(
children: [
DidvanText(
"همه",
color:
Theme.of(context).colorScheme.primary,
),
Icon(
DidvanIcons.angle_left_light,
color:
Theme.of(context).colorScheme.primary,
),
],
),
),
),
],
),
),
const SizedBox(height: 16),
/// Swot Items Slider
DidvanSlider(
height: 300,
itemCount: items.length,
viewportFraction: 0.65,
itemBuilder: (context, index, realIndex) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 0.0),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SwotItemCard(item: items[index]),
),
),
),
],
),
);
},
)
],
), ),
); );
} }
@ -226,7 +123,6 @@ class InfoTitle extends StatelessWidget {
class _MainPageSection extends StatelessWidget { class _MainPageSection extends StatelessWidget {
final MainPageList list; final MainPageList list;
final bool isLast; final bool isLast;
const _MainPageSection({required this.list, required this.isLast}); const _MainPageSection({required this.list, required this.isLast});
void _moreHandler(BuildContext context) { void _moreHandler(BuildContext context) {
@ -279,11 +175,6 @@ class _MainPageSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final icon = _generateIcon(); final icon = _generateIcon();
if (list.contents.isEmpty) {
return const SizedBox();
}
return Column( return Column(
children: [ children: [
Padding( Padding(

View File

@ -1,62 +0,0 @@
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/home/bookmarks/bookmark_service.dart';
import 'package:flutter/material.dart';
class BookmarkIcon extends StatefulWidget {
final int postId;
const BookmarkIcon({super.key, required this.postId});
@override
State<BookmarkIcon> createState() => _BookmarkIconState();
}
class _BookmarkIconState extends State<BookmarkIcon> {
bool _bookmarked = false;
bool _loading = false;
@override
void initState() {
super.initState();
_loadStatus();
}
Future<void> _loadStatus() async {
final bookmarks = await BookmarkService.fetchBookmarks();
setState(() => _bookmarked = bookmarks.contains(widget.postId));
}
Future<void> _toggleBookmark() async {
setState(() => _loading = true);
try {
if (_bookmarked) {
await BookmarkService.removeBookmark(widget.postId);
print("Bookmark removed for post ${widget.postId}");
} else {
await BookmarkService.addBookmark(widget.postId);
print("Bookmark added for post ${widget.postId}");
}
setState(() {
_bookmarked = !_bookmarked;
_loading = false;
});
} catch (e) {
print("Error toggling bookmark for post ${widget.postId}: $e");
setState(() => _loading = false);
}
}
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: _loading ? null : _toggleBookmark,
icon: Icon(
_bookmarked ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular,
color: _bookmarked
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.caption,
),
);
}
}

View File

@ -1,103 +0,0 @@
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/view/action_sheet_data.dart'; // Ensure this is used or remove if not
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/home/bookmarks/bookmark_service.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
class BookmarkedIcon extends StatefulWidget {
final int postId;
final void Function(bool isBookmarked)? onBookmarkChanged;
const BookmarkedIcon({
super.key,
required this.postId,
this.onBookmarkChanged,
});
@override
State<BookmarkedIcon> createState() => _BookmarkedIconState();
}
class _BookmarkedIconState extends State<BookmarkedIcon> {
bool _bookmarked = false;
bool _loading = false;
@override
void initState() {
super.initState();
_loadStatus();
}
Future<void> _loadStatus() async {
// setState(() => _loading = true);
final bookmarks = await BookmarkService.fetchBookmarks();
if (mounted) {
setState(() {
_bookmarked = bookmarks.contains(widget.postId);
// _loading = false;
});
}
}
Future<void> _toggleBookmark() async {
if (_loading) return;
if (_bookmarked) {
bool confirmAction = false;
await ActionSheetUtils(context).openDialog(
data: ActionSheetData(
content: const DidvanText(
'آیا می‌خواهید این محتوا از نشان‌ شده‌ها حذف شود؟',
),
titleIcon: DidvanIcons.bookmark_regular,
titleColor: Theme.of(context).colorScheme.secondary,
title: 'تایید عملیات',
onConfirmed: () => confirmAction = true,
),
);
if (!confirmAction) {
return;
}
}
setState(() => _loading = true);
try {
if (_bookmarked) {
await BookmarkService.removeBookmark(widget.postId);
} else {
await BookmarkService.addBookmark(widget.postId);
}
if (mounted) {
final newBookmarkStatus = !_bookmarked;
setState(() {
_bookmarked = newBookmarkStatus;
});
widget.onBookmarkChanged?.call(newBookmarkStatus);
}
} catch (e) {
print("Error toggling bookmark for post ${widget.postId}: $e");
} finally {
if (mounted) {
setState(() => _loading = false);
}
}
}
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: _loading ? null : _toggleBookmark,
icon: Icon(
_bookmarked ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular,
color: _bookmarked
? const Color.fromARGB(255, 0, 126, 167)
: Theme.of(context).colorScheme.caption,
),
);
}
}

View File

@ -1,305 +0,0 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/note_service.dart';
import 'package:didvan/views/home/main/widgets/bookmarked_icon.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/didvan/text_field.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:url_launcher/url_launcher_string.dart';
class SwotBookmark extends StatefulWidget {
final SwotItem item;
final void Function(int postId)? onSwotUnbookmarked;
const SwotBookmark({
super.key,
required this.item, this.onSwotUnbookmarked,
});
@override
State<SwotBookmark> createState() => _SwotBookmark();
}
class _SwotBookmark extends State<SwotBookmark> {
ValueNotifier<bool> myBookmarkStatus = ValueNotifier(true);
String noteText = '';
String tempNoteText = '';
bool isLoading = true;
bool hasExistingNote = false; // Track if a note exists
Timer? _debounce;
@override
void initState() {
super.initState();
loadNote();
}
Future<void> loadNote() async {
final note = await NoteService.getNoteByPostId(widget.item.id);
if (mounted) {
setState(() {
noteText = note?.content ?? '';
tempNoteText = noteText;
hasExistingNote = note != null; // Set flag if note exists
isLoading = false;
});
}
}
void saveOrUpdateNote(String content) async {
print('Processing note for PostId: ${widget.item.id}, Content: $content');
setState(() {
isLoading = true;
});
try {
bool success;
if (hasExistingNote) {
// Update existing note
success = await NoteService.updateNote(widget.item.id, content);
} else {
// Save new note
success = await NoteService.saveNote(widget.item.id, content);
}
if (success) {
await loadNote(); // Reload note from server
if (mounted) {
setState(() {
isLoading = false;
});
}
} else {
if (mounted) {
setState(() {
isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('خطا در ذخیره یادداشت: سرور پاسخ ناموفق داد')),
);
}
}
} catch (e) {
if (mounted) {
setState(() {
isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('خطا در ذخیره یادداشت: $e')),
);
}
}
}
void onNoteChanged(String value) {
setState(() {
tempNoteText = value;
});
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
saveOrUpdateNote(value);
});
}
@override
void dispose() {
_debounce?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
AppInitializer.openWebLink(
context,
'http://opportunity-threat.didvan.com/posts/${widget.item.id}/?accessToken=${RequestService.token}',
mode: LaunchMode.inAppWebView,
);
},
child: Container(
margin: const EdgeInsets.only(bottom: 10, right: 2, left: 2),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
// ... (Row with image and text details for SwotItem) ...
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right: 8, top: 8),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: CachedNetworkImage(
imageUrl: widget.item.imageUrl,
width: 80,
height: 80,
fit: BoxFit.cover,
httpHeaders: {
'Authorization': 'Bearer ${RequestService.token}',
},
imageRenderMethodForWeb:
ImageRenderMethodForWeb.HttpGet,
placeholder: (context, _) => const ShimmerPlaceholder(
width: 100,
height: 100,
),
errorWidget: (context, url, error) => Container(
width: 100,
height: 100,
color: Theme.of(context)
.colorScheme
.disabledBackground,
child:
const Icon(Icons.image_not_supported_outlined),
),
),
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 4, horizontal: 8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: const BorderRadius.horizontal(
left: Radius.circular(10),
),
),
child: SvgPicture.asset("lib/assets/images/categories/Vector.svg",height: 14,)
),
],
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.item.title,
style: Theme.of(context).textTheme.bodyLarge,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(
height: 18,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
DidvanIcons.calendar_day_light,
size: 16,
),
const SizedBox(width: 4),
DidvanText(
DateTime.parse(widget.item.createdAt)
.toPersianDateStr(),
style: Theme.of(context).textTheme.labelSmall,
),
],
),
],
),
),
],
),
// Notes Title Column
Column(
children: [
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.edit_outlined,
size: 16,
color: Theme.of(context).colorScheme.caption,
),
const SizedBox(width: 4),
DidvanText(
'یادداشت‌های من',
style: Theme.of(context).textTheme.labelSmall,
color: Theme.of(context).colorScheme.caption,
),
],
),
],
),
// Row for (Divider + TextField) and BookmarkIcon
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Container(
height: 1,
color: Theme.of(context).colorScheme.primary,
),
),
Flexible(
flex: 2,
child: Container(
height: 1,
color: Theme.of(context).colorScheme.border,
),
)
],
),
const SizedBox(height: 4),
isLoading
? const Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CircularProgressIndicator(strokeWidth: 2),
)
: DidvanTextField(
disableBorders: true,
initialValue: tempNoteText,
hintText: 'برای اضافه کردن یادداشت لمس کنید.',
onChanged: onNoteChanged,
isSmall: true,
textInputAction: TextInputAction.done,
),
],
),
),
const SizedBox(width: 8),
BookmarkedIcon(
postId: widget.item.id,
onBookmarkChanged: (isBookmarked) {
if (!isBookmarked) {
widget.onSwotUnbookmarked?.call(widget.item.id);
}
},
),
],
),
],
),
),
);
}
}

View File

@ -1,157 +0,0 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/views/home/main/widgets/bookmark.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:url_launcher/url_launcher_string.dart';
class SwotItemCard extends StatefulWidget {
final SwotItem item;
const SwotItemCard({super.key, required this.item, this.onBookmarkChangedInList});
final void Function(int postId, bool isBookmarked)? onBookmarkChangedInList;
@override
State<SwotItemCard> createState() => _SwotItemCardState();
}
class _SwotItemCardState extends State<SwotItemCard> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
AppInitializer.openWebLink(
context,
'http://opportunity-threat.didvan.com/posts/${widget.item.id}/?accessToken=${RequestService.token}',
mode: LaunchMode.inAppWebView,
);
},
child: Container(
width: 250,
height: 50,
margin: const EdgeInsets.only(right: 0),
padding: const EdgeInsets.all(0),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(9),
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
child: CachedNetworkImage(
errorWidget: (context, url, error) {
if (kDebugMode) {
print('image fetch complete with Error: $error');
}
return Container(
height: 150,
width: 300,
decoration: BoxDecoration(
color:
Theme.of(context).colorScheme.disabledBackground,
),
child: const Icon(Icons.image_not_supported_outlined),
);
},
errorListener: (value) {},
fit: BoxFit.cover,
imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
httpHeaders: {
'Authorization': 'Bearer ${RequestService.token}'
},
width: 300,
height: 150,
imageUrl: widget.item.imageUrl,
placeholder: (context, _) => const ShimmerPlaceholder(
width: 300,
height: 150,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
widget.item.title,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 5),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
widget.item.type == "THREAT"
? SvgPicture.asset(
"lib/assets/images/features/Badge.svg")
: SvgPicture.asset(
"lib/assets/images/features/Badge-Green.svg"),
const SizedBox(
width: 5,
),
Text(
widget.item.type == "THREAT" ? "تهدید" : "فرصت",
)
],
),
const SizedBox(
height: 10,
),
Row(
children: [
SvgPicture.asset(
"lib/assets/images/features/ant-design_dot-chart-outlined.svg"),
const SizedBox(
width: 5,
),
Text(
'عدد ${widget.item.type == "THREAT" ? "تهدید" : "فرصت"}: ${((widget.item.x1 ?? 0.0) * (widget.item.y1 ?? 0.0)).toStringAsFixed(1)}',
style: Theme.of(context).textTheme.bodyMedium,
)
],
),
// GestureDetector(
// onTap: () {}, child: Icon(DidvanIcons.bookmark_solid)
// // Icon(
// // widget.item.marked
// // ? DidvanIcons.bookmark_solid
// // : DidvanIcons.bookmark_regular,
// // color: widget.item.marked
// // ? Theme.of(context).colorScheme.secondary
// // : Theme.of(context).colorScheme.caption,
// // ),
// ),
],
),
),
],
),
Positioned(
bottom: 0,
left: 0,
child: BookmarkIcon(postId: widget.item.id),
)
],
),
),
);
}
}

View File

@ -19,7 +19,6 @@ class MainCategories extends StatelessWidget {
'$link?accessToken=${RequestService.token}', '$link?accessToken=${RequestService.token}',
mode: LaunchMode.inAppWebView, mode: LaunchMode.inAppWebView,
); );
print("your goddamn token is :${RequestService.token}");
} else if (link.startsWith('tab-')) { } else if (link.startsWith('tab-')) {
final state = context.read<HomeState>(); final state = context.read<HomeState>();
int index = int.parse(link.replaceAll('tab-', '')); int index = int.parse(link.replaceAll('tab-', ''));
@ -34,14 +33,13 @@ class MainCategories extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.read<HomeState>(); final state = context.read<HomeState>();
return Wrap( return Wrap(
alignment: WrapAlignment.start, alignment: WrapAlignment.center,
children: state.menuItems children: state.menuItems
.map( .map(
(e) => GestureDetector( (e) => GestureDetector(
onTap: () => _onTap(e.link, context), onTap: () => _onTap(e.link, context),
child: SizedBox( child: SizedBox(
width: (MediaQuery.of(context).size.width) / 4, width: (MediaQuery.of(context).size.width - 40) / 3,
// (MediaQuery.of(context).size.width - 40) / 3,
child: Column( child: Column(
children: [ children: [
Container( Container(

View File

@ -1,30 +0,0 @@
// import 'package:didvan/models/home_page_content/risk.dart';
// import 'package:flutter/material.dart';
// class RiskCard extends StatelessWidget {
// final Swot risk;
// const RiskCard({Key? key, required this.risk}) : super(key: key);
// @override
// Widget build(BuildContext context) {
// return Container(
// height: 100,
// margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
// padding: const EdgeInsets.all(12),
// decoration: BoxDecoration(
// color: Colors.green[100],
// borderRadius: BorderRadius.circular(12),
// border: Border.all(color: Colors.green, width: 1),
// ),
// child: Text(
// risk.title,
// style: const TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// color: Colors.black87,
// ),
// ),
// );
// }
// }

View File

@ -2,7 +2,6 @@
import 'package:chewie/chewie.dart'; import 'package:chewie/chewie.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/media/media.dart';
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart'; import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
@ -26,26 +25,9 @@ class StudioDetails extends StatefulWidget {
} }
class _StudioDetailsState extends State<StudioDetails> { class _StudioDetailsState extends State<StudioDetails> {
// ignore: unused_field
int _currentlyPlayingId = 0; int _currentlyPlayingId = 0;
late VideoPlayerController _videoPlayerController; late VideoPlayerController _videoPlayerController;
ChewieController? _chewieController; late final ChewieController _chewieController = ChewieController(
@override
void initState() {
super.initState();
final state = context.read<StudioDetailsState>();
state.args = widget.pageData['args'];
Future.delayed(
Duration.zero,
() => state.getStudioDetails(widget.pageData['id']).then((_) {
if (!mounted) return;
_videoPlayerController = VideoPlayerController.network(
state.studio.link,
);
_videoPlayerController.initialize().then((_) {
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController, videoPlayerController: _videoPlayerController,
customControls: const PrimaryControls(), customControls: const PrimaryControls(),
autoPlay: true, autoPlay: true,
@ -53,14 +35,15 @@ class _StudioDetailsState extends State<StudioDetails> {
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
materialProgressColors: ChewieProgressColors( materialProgressColors: ChewieProgressColors(
playedColor: Theme.of(context).colorScheme.title, playedColor: Theme.of(context).colorScheme.title,
handleColor: Theme.of(context).colorScheme.title, handleColor: Theme.of(context).colorScheme.title));
), @override
); void initState() {
setState(() { final state = context.read<StudioDetailsState>();
_currentlyPlayingId = state.studio.id; state.args = widget.pageData['args'];
});
}); Future.delayed(
}), Duration.zero,
() => state.getStudioDetails(widget.pageData['id']),
); );
if (widget.pageData['goToComment'] != null) { if (widget.pageData['goToComment'] != null) {
@ -80,6 +63,7 @@ class _StudioDetailsState extends State<StudioDetails> {
); );
} }
} }
super.initState();
} }
@override @override
@ -96,14 +80,8 @@ class _StudioDetailsState extends State<StudioDetails> {
} }
}, },
builder: (context, state) { builder: (context, state) {
if (!state.isStudioLoaded) { if (_currentlyPlayingId != state.studio.id) {
return Center( _handleVideoPlayback(state);
child: Image.asset(
Assets.loadingAnimation,
width: 100,
height: 100,
),
);
} }
return WillPopScope( return WillPopScope(
onWillPop: () async { onWillPop: () async {
@ -142,14 +120,8 @@ class _StudioDetailsState extends State<StudioDetails> {
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: _chewieController != null child: Chewie(
? Chewie(controller: _chewieController!) controller: _chewieController,
: Center(
child: Image.asset(
Assets.loadingAnimation,
width: 100,
height: 100,
),
), ),
), ),
Expanded( Expanded(
@ -170,11 +142,18 @@ class _StudioDetailsState extends State<StudioDetails> {
); );
} }
Future<void> _handleVideoPlayback(state) async {
_videoPlayerController = VideoPlayerController.network(
state.studio.link,
)..initialize().then((value) async {});
_currentlyPlayingId = state.studio.id;
}
@override @override
void dispose() { void dispose() {
_videoPlayerController.pause(); _videoPlayerController.pause();
_videoPlayerController.dispose(); _videoPlayerController.dispose();
_chewieController?.dispose(); _chewieController.dispose();
super.dispose(); super.dispose();
} }
} }

View File

@ -11,7 +11,6 @@ import 'package:didvan/services/network/request_helper.dart';
class StudioDetailsState extends CoreProvier { class StudioDetailsState extends CoreProvier {
late StudioDetailsData studio; late StudioDetailsData studio;
bool isStudioLoaded = false;
StudioDetailsData? nextStudio; StudioDetailsData? nextStudio;
StudioDetailsData? prevStudio; StudioDetailsData? prevStudio;
late int initialIndex; late int initialIndex;
@ -67,7 +66,6 @@ class StudioDetailsState extends CoreProvier {
studio = prevStudio!; studio = prevStudio!;
prevStudio = null; prevStudio = null;
} }
isStudioLoaded = true;
notifyListeners(); notifyListeners();
_handlePodcastPlayback(studio); _handlePodcastPlayback(studio);
} }
@ -100,11 +98,9 @@ class StudioDetailsState extends CoreProvier {
} }
final result = service.result; final result = service.result;
studio = StudioDetailsData.fromJson(result['studio']); studio = StudioDetailsData.fromJson(result['studio']);
isStudioLoaded = true;
if (args?.page == 0) { if (args?.page == 0) {
initialIndex = 0; initialIndex = 0;
appState = AppState.idle; appState = AppState.idle;
notifyListeners();
return; return;
} }
if (result['nextStudio'].isNotEmpty && this.args?.page != 0) { if (result['nextStudio'].isNotEmpty && this.args?.page != 0) {
@ -118,7 +114,6 @@ class StudioDetailsState extends CoreProvier {
} }
alongSideState = AppState.idle; alongSideState = AppState.idle;
appState = AppState.idle; appState = AppState.idle;
notifyListeners();
return; return;
} }
if (isForward == null) { if (isForward == null) {
@ -128,8 +123,10 @@ class StudioDetailsState extends CoreProvier {
notifyListeners(); notifyListeners();
} }
} catch (e) { } catch (e) {
// MediaService.resetAudioPlayer();
update(); update();
appState = AppState.idle; appState = AppState.idle;
// rethrow;
} }
} }
@ -168,6 +165,7 @@ class StudioDetailsState extends CoreProvier {
} }
} else { } else {
MediaService.audioPlayer.pause(); MediaService.audioPlayer.pause();
// MediaService.audioPlayer.dispose();
} }
} }

View File

@ -39,26 +39,24 @@ class _PlayerNavBarState extends State<PlayerNavBar> {
return StreamBuilder<bool>( return StreamBuilder<bool>(
stream: MediaService.audioPlayer.playingStream, stream: MediaService.audioPlayer.playingStream,
builder: (context, isPlaying) => GestureDetector( builder: (context, isPlaying) => GestureDetector(
onTap: () { onTap: () => (MediaService.currentPodcast == null &&
// (MediaService.currentPodcast == null && (MediaService.audioPlayerTag ?? '')
// (MediaService.audioPlayerTag ?? '') .split('-')[1]
// .split('-')[1] .isNotEmpty) ||
// .isNotEmpty) || MediaService.currentPodcast?.description == 'radar'
// MediaService.currentPodcast?.description == 'radar' ? Navigator.of(context).pushNamed(
// ? Navigator.of(context).pushNamed( Routes.radarDetails,
// Routes.radarDetails, arguments: {
// arguments: { 'onMarkChanged': (id, value) {},
// 'onMarkChanged': (id, value) {}, 'onCommentsChanged': (id, value) {},
// 'onCommentsChanged': (id, value) {}, 'id': MediaService.currentPodcast?.id,
// 'id': MediaService.currentPodcast?.id, 'args': const RadarRequestArgs(page: 0),
// 'args': const RadarRequestArgs(page: 0), 'hasUnmarkConfirmation': false,
// 'hasUnmarkConfirmation': false,
// },
// )
// : (MediaService.audioPlayerTag ?? '').split('-')[1].isNotEmpty
// ? _showPlayerBottomSheet(context)
// : null;
}, },
)
: (MediaService.audioPlayerTag ?? '').split('-')[1].isNotEmpty
? _showPlayerBottomSheet(context)
: null,
child: Consumer<StudioDetailsState>( child: Consumer<StudioDetailsState>(
builder: (context, state, child) => AnimatedContainer( builder: (context, state, child) => AnimatedContainer(
height: widget.inHome height: widget.inHome

View File

@ -107,7 +107,7 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
if (widget.slivers != null) if (widget.slivers != null)
for (var i = 0; i < widget.slivers!.length; i++) for (var i = 0; i < widget.slivers!.length; i++)
SliverPadding( SliverPadding(
padding: widget.padding.copyWith(bottom: 0), padding: widget.padding,
sliver: widget.slivers![i], sliver: widget.slivers![i],
), ),
if (widget.children != null && widget.showSliversFirst) if (widget.children != null && widget.showSliversFirst)

View File

@ -20,7 +20,6 @@ class DidvanTextField extends StatefulWidget {
final bool acceptSpace; final bool acceptSpace;
final String? Function(String value)? validator; final String? Function(String value)? validator;
final TextInputType? textInputType; final TextInputType? textInputType;
final TextInputAction? textInputAction; // پارامتر جدید
final bool disableBorders; final bool disableBorders;
final bool isSmall; final bool isSmall;
final int? maxLine; final int? maxLine;
@ -38,7 +37,6 @@ class DidvanTextField extends StatefulWidget {
this.initialValue, this.initialValue,
this.validator, this.validator,
this.textInputType, this.textInputType,
this.textInputAction, // اضافه کردن به سازنده
this.textAlign, this.textAlign,
this.obsecureText = false, this.obsecureText = false,
this.autoFocus = false, this.autoFocus = false,
@ -117,7 +115,6 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
obscureText: _hideContent, obscureText: _hideContent,
textAlign: widget.textAlign ?? TextAlign.start, textAlign: widget.textAlign ?? TextAlign.start,
keyboardType: widget.textInputType, keyboardType: widget.textInputType,
textInputAction: widget.textInputAction, // پاس دادن به TextFormField
focusNode: _focusNode, focusNode: _focusNode,
controller: _controller, controller: _controller,
onFieldSubmitted: widget.onSubmitted, onFieldSubmitted: widget.onSubmitted,
@ -238,6 +235,7 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
setState(() { setState(() {
_error = null; _error = null;
}); });
// value = value.toEnglishDigit();
widget.onChanged?.call(value); widget.onChanged?.call(value);
} }
@ -261,7 +259,6 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controller.dispose();
_focusNode.dispose();
super.dispose(); super.dispose();
} }
} }

View File

@ -7,19 +7,16 @@ class MenuOption extends StatelessWidget {
final String? title; final String? title;
final Widget? titleWidget; final Widget? titleWidget;
final IconData? icon; final IconData? icon;
final Widget? iconWidget;
final double iconSize; final double iconSize;
final String? suffix; final String? suffix;
final VoidCallback onTap; final VoidCallback onTap;
final Widget? trailing; final Widget? trailing;
final Color? color; final Color? color;
MenuOption({ MenuOption({
Key? key, Key? key,
required this.onTap, required this.onTap,
this.title, this.title,
this.icon, this.icon,
this.iconWidget,
this.suffix, this.suffix,
this.color, this.color,
this.trailing, this.trailing,
@ -27,55 +24,46 @@ class MenuOption extends StatelessWidget {
this.iconSize = 18, this.iconSize = 18,
}) : super(key: key) { }) : super(key: key) {
if (title == null && titleWidget == null) { if (title == null && titleWidget == null) {
throw Exception("MenuOption must have a title or titleWidget"); throw Exception;
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final Color effectiveTitleColor = color ?? theme.colorScheme.title;
Widget? displayIcon;
if (iconWidget != null) {
displayIcon = iconWidget;
} else if (icon != null) {
displayIcon = Icon(icon, size: iconSize, color: effectiveTitleColor);
}
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
child: Container( child: Container(
color: Colors.transparent, color: Colors.transparent,
padding: const EdgeInsets.symmetric(vertical: 0.0),
child: Row( child: Row(
children: [ children: [
if (displayIcon != null) displayIcon, if (icon != null)
if (displayIcon != null) const SizedBox(width: 4), Icon(
icon,
if (titleWidget != null) titleWidget! size: iconSize,
else DidvanText( color: color ?? Theme.of(context).colorScheme.title,
),
if (icon != null) const SizedBox(width: 4),
if (titleWidget != null) titleWidget!,
if (titleWidget == null)
DidvanText(
title!, title!,
color: effectiveTitleColor, color: color ?? Theme.of(context).colorScheme.title,
), ),
const Spacer(), const Spacer(),
if (suffix != null) if (suffix != null)
Padding( DidvanText(
padding: const EdgeInsets.only(left: 8.0),
child: DidvanText(
suffix!, suffix!,
style: theme.textTheme.titleSmall! style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(fontWeight: FontWeight.w400), .copyWith(fontWeight: FontWeight.w400),
color: theme.colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
),
trailing ?? trailing ??
Icon( Icon(
DidvanIcons.angle_left_regular, DidvanIcons.angle_left_regular,
size: 18, size: 18,
color: effectiveTitleColor, color: color ?? Theme.of(context).colorScheme.title,
), ),
], ],
), ),

View File

@ -422,70 +422,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
flutter_inappwebview:
dependency: "direct main"
description:
name: flutter_inappwebview
sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
flutter_inappwebview_android:
dependency: transitive
description:
name: flutter_inappwebview_android
sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
flutter_inappwebview_internal_annotations:
dependency: transitive
description:
name: flutter_inappwebview_internal_annotations
sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
flutter_inappwebview_ios:
dependency: transitive
description:
name: flutter_inappwebview_ios
sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_macos:
dependency: transitive
description:
name: flutter_inappwebview_macos
sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_platform_interface:
dependency: transitive
description:
name: flutter_inappwebview_platform_interface
sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500
url: "https://pub.dev"
source: hosted
version: "1.3.0+1"
flutter_inappwebview_web:
dependency: transitive
description:
name: flutter_inappwebview_web
sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_windows:
dependency: transitive
description:
name: flutter_inappwebview_windows
sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:

View File

@ -109,7 +109,6 @@ dependencies:
image_cropper: ^9.0.0 image_cropper: ^9.0.0
package_info_plus: ^8.3.0 package_info_plus: ^8.3.0
flutter_local_notifications: ^19.1.0 flutter_local_notifications: ^19.1.0
flutter_inappwebview: ^6.1.5
# fading_edge_scrollview: ^4.1.1 # fading_edge_scrollview: ^4.1.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: