From 9ecf126869dc17ed7d022220a96c9413b0c971b0 Mon Sep 17 00:00:00 2001 From: juan Date: Thu, 22 May 2025 23:19:44 -0400 Subject: [PATCH 01/28] android build --- .metadata | 30 +- android/.gitignore | 14 + android/app/build.gradle.kts | 44 ++ android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 45 ++ .../com/example/crab_ui/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + android/app/src/main/res/values/styles.xml | 18 + android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle.kts | 21 + android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle.kts | 25 + android_old/.gitignore | 13 + android_old/app/build.gradle | 58 ++ android_old/app/src/debug/AndroidManifest.xml | 7 + android_old/app/src/main/AndroidManifest.xml | 45 ++ .../kotlin/com/example/hym_ui/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + android_old/build.gradle | 32 + android_old/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android_old/settings.gradle | 25 + pubspec.lock | 638 +++--------------- pubspec.yaml | 5 +- 41 files changed, 616 insertions(+), 553 deletions(-) create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle.kts create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/example/crab_ui/MainActivity.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle.kts create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle.kts create mode 100644 android_old/.gitignore create mode 100644 android_old/app/build.gradle create mode 100644 android_old/app/src/debug/AndroidManifest.xml create mode 100644 android_old/app/src/main/AndroidManifest.xml create mode 100644 android_old/app/src/main/kotlin/com/example/hym_ui/MainActivity.kt create mode 100644 android_old/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android_old/app/src/main/res/drawable/launch_background.xml create mode 100644 android_old/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android_old/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android_old/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android_old/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android_old/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android_old/app/src/main/res/values-night/styles.xml create mode 100644 android_old/app/src/main/res/values/styles.xml create mode 100644 android_old/app/src/profile/AndroidManifest.xml create mode 100644 android_old/build.gradle create mode 100644 android_old/gradle.properties create mode 100644 android_old/gradle/wrapper/gradle-wrapper.properties create mode 100644 android_old/settings.gradle diff --git a/.metadata b/.metadata index 6eb54a1..c9704a8 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" + revision: "be698c48a6750c8cb8e61c740ca9991bb947aba2" channel: "stable" project_type: app @@ -13,26 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 + base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 - platform: android - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 + base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 - platform: ios - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 + base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 - platform: linux - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 + base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 - platform: macos - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 + base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 - platform: web - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 + base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 - platform: windows - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 + base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2 # User provided section diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..c908258 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..86eb85d --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.crab_ui" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.crab_ui" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..8ffe024 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6023d1e --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/crab_ui/MainActivity.kt b/android/app/src/main/kotlin/com/example/crab_ui/MainActivity.kt new file mode 100644 index 0000000..7ea6dde --- /dev/null +++ b/android/app/src/main/kotlin/com/example/crab_ui/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.crab_ui + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..1cb7aa2 --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..8403758 --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..360a160 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..5fac679 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..8ffe024 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..2f2f3f0 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..b7cda7b --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..dcc7e10 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..8ddb35d --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.7.3" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") diff --git a/android_old/.gitignore b/android_old/.gitignore new file mode 100644 index 0000000..5d99765 --- /dev/null +++ b/android_old/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android_old/app/build.gradle b/android_old/app/build.gradle new file mode 100644 index 0000000..02e20bc --- /dev/null +++ b/android_old/app/build.gradle @@ -0,0 +1,58 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file("local.properties") +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader("UTF-8") { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") +if (flutterVersionCode == null) { + flutterVersionCode = "1" +} + +def flutterVersionName = localProperties.getProperty("flutter.versionName") +if (flutterVersionName == null) { + flutterVersionName = "1.0" +} + +android { + namespace = "com.example.hym_ui" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.hym_ui" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/android_old/app/src/debug/AndroidManifest.xml b/android_old/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..8ffe024 --- /dev/null +++ b/android_old/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android_old/app/src/main/AndroidManifest.xml b/android_old/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d2914b1 --- /dev/null +++ b/android_old/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android_old/app/src/main/kotlin/com/example/hym_ui/MainActivity.kt b/android_old/app/src/main/kotlin/com/example/hym_ui/MainActivity.kt new file mode 100644 index 0000000..4a7c91b --- /dev/null +++ b/android_old/app/src/main/kotlin/com/example/hym_ui/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.hym_ui + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/android_old/app/src/main/res/drawable-v21/launch_background.xml b/android_old/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..1cb7aa2 --- /dev/null +++ b/android_old/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android_old/app/src/main/res/drawable/launch_background.xml b/android_old/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..8403758 --- /dev/null +++ b/android_old/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android_old/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android_old/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/android_old/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android_old/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/android_old/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android_old/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/android_old/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android_old/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/android_old/app/src/main/res/values-night/styles.xml b/android_old/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..360a160 --- /dev/null +++ b/android_old/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android_old/app/src/main/res/values/styles.xml b/android_old/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..5fac679 --- /dev/null +++ b/android_old/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android_old/app/src/profile/AndroidManifest.xml b/android_old/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..8ffe024 --- /dev/null +++ b/android_old/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android_old/build.gradle b/android_old/build.gradle new file mode 100644 index 0000000..fefa38b --- /dev/null +++ b/android_old/build.gradle @@ -0,0 +1,32 @@ +buildscript { + ext.kotlin_version = '1.9.10' + + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.3.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/android_old/gradle.properties b/android_old/gradle.properties new file mode 100644 index 0000000..0d7fbd5 --- /dev/null +++ b/android_old/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/android_old/gradle/wrapper/gradle-wrapper.properties b/android_old/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..4abf038 --- /dev/null +++ b/android_old/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip diff --git a/android_old/settings.gradle b/android_old/settings.gradle new file mode 100644 index 0000000..0f10e04 --- /dev/null +++ b/android_old/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/pubspec.lock b/pubspec.lock index f4b7708..fe96027 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,106 +5,58 @@ packages: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.7.0" asn1lib: dependency: transitive description: name: asn1lib - sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5" + sha256: "0511d6be23b007e95105ae023db599aea731df604608978dada7f9faf2637623" url: "https://pub.dev" source: hosted - version: "1.5.8" + version: "1.6.4" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" - audio_session: - dependency: transitive - description: - name: audio_session - sha256: "343e83bc7809fbda2591a49e525d6b63213ade10c76f15813be9aed6657b3261" - url: "https://pub.dev" - source: hosted - version: "0.1.21" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" - cached_network_image: - dependency: transitive - description: - name: cached_network_image - sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819" - url: "https://pub.dev" - source: hosted - version: "3.4.0" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" - url: "https://pub.dev" - source: hosted - version: "1.3.0" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" - chewie: - dependency: transitive - description: - name: chewie - sha256: "745e81e84c6d7f3835f89f85bb49771c0a66099e4caf8f8e9e9a372bc66fb2c1" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - chewie_audio: - dependency: transitive - description: - name: chewie_audio - sha256: "73948a8b9841d050433af3498a1f8b11320bd5a2cd70b449bdbe16d4405e97c5" - url: "https://pub.dev" - source: hosted - version: "1.5.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: @@ -121,38 +73,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" - csslib: - dependency: transitive - description: - name: csslib - sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" - url: "https://pub.dev" - source: hosted - version: "0.17.3" - cupertino_icons: - dependency: transitive - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - dio: - dependency: transitive - description: - name: dio - sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" - url: "https://pub.dev" - source: hosted - version: "5.8.0+1" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" - url: "https://pub.dev" - source: hosted - version: "2.1.1" encrypt: dependency: "direct main" description: @@ -173,119 +93,31 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" - file_saver: - dependency: "direct main" - description: - name: file_saver - sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" - url: "https://pub.dev" - source: hosted - version: "0.2.14" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" + version: "7.0.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - sha256: a77f77806a790eb9ba0118a5a3a936e81c4fea2b61533033b2b0c3d50bbde5ea - url: "https://pub.dev" - source: hosted - version: "3.4.0" - flutter_html: - dependency: transitive - description: - name: flutter_html - sha256: "02ad69e813ecfc0728a455e4bf892b9379983e050722b1dce00192ee2e41d1ee" - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_html_all: - dependency: "direct main" - description: - name: flutter_html_all - sha256: "5b4c449df76ecd186bea55414206c21bde83090d4b54d14caed78823718b7f1b" - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_html_audio: - dependency: transitive - description: - name: flutter_html_audio - sha256: "94ae28ab56a8d556b7c5409e3eb59ca5215812bda87c67ddfa768812b76f8511" - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_html_iframe: - dependency: transitive - description: - name: flutter_html_iframe - sha256: "979405fafcbd29c930bf96d9f3f0ade9d87dfd567a03180b13424a0e89a5de46" - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_html_math: - dependency: transitive - description: - name: flutter_html_math - sha256: "7371f2621b77c66399e50b9fd5ff0eb2475c8c581af68e3eb21409c49a811211" - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_html_svg: - dependency: transitive - description: - name: flutter_html_svg - sha256: "793be10cfa6fd0925a7adde9f58d73d2ce6a84c02ae12d01a985244f55ee631a" - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_html_table: - dependency: transitive - description: - name: flutter_html_table - sha256: e20c72d67ea2512e7b4949f6f7dd13d004e773b0f82c586a21f895e6bd90383c - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" - flutter_html_video: - dependency: transitive - description: - name: flutter_html_video - sha256: ecc8bcc614dd8a8286d32ace462481817660f5a7e854c663c1fcb3424fc3be89 - url: "https://pub.dev" - source: hosted - version: "3.0.0-beta.2" flutter_layout_grid: dependency: "direct overridden" description: @@ -314,10 +146,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 url: "https://pub.dev" source: hosted - version: "2.0.10+1" + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -328,78 +160,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_widget_from_html: - dependency: "direct main" - description: - name: flutter_widget_from_html - sha256: "8d2a9a7979a9c1a5d866d1f4134d2ec2cca78716c112c76803d6a552281405cc" - url: "https://pub.dev" - source: hosted - version: "0.10.6" - flutter_widget_from_html_core: - dependency: transitive - description: - name: flutter_widget_from_html_core - sha256: "22140caa191cb4bba0fe4d5e4ad875c7e8a9ba47d61517f56d733019cf76396d" - url: "https://pub.dev" - source: hosted - version: "0.10.6" - fwfh_cached_network_image: - dependency: transitive - description: - name: fwfh_cached_network_image - sha256: "3de22aa3a6943c968e0d9fbcba4463b3dbbf7103171d62c84b6c672fb83eebdf" - url: "https://pub.dev" - source: hosted - version: "0.7.0+7" - fwfh_chewie: - dependency: transitive - description: - name: fwfh_chewie - sha256: "0b51a1c976bb74da5e8e45d545c74cb54a7168ad3938dd77103a7aee485f55fa" - url: "https://pub.dev" - source: hosted - version: "0.7.1+4" - fwfh_just_audio: - dependency: transitive - description: - name: fwfh_just_audio - sha256: "237b93a4cb9f0495a4b51940f361adda2a5abd57231dd44f07459db00144a6cd" - url: "https://pub.dev" - source: hosted - version: "0.9.0+3" - fwfh_svg: - dependency: transitive - description: - name: fwfh_svg - sha256: c6bb6b513f7ce2766aba76d7276caf9a96b6fee729ac3a492c366a42f82ef02e - url: "https://pub.dev" - source: hosted - version: "0.8.2" - fwfh_url_launcher: - dependency: transitive - description: - name: fwfh_url_launcher - sha256: b9f5d55a5ae2c2c07243ba33f7ba49ac9544bdb2f4c16d8139df9ccbebe3449c - url: "https://pub.dev" - source: hosted - version: "0.9.1" - fwfh_webview: - dependency: transitive - description: - name: fwfh_webview - sha256: "90a8dda0695403cf57abd7e8b83f6fb1f1a12933930a0bf9cac7cafb06e06a18" - url: "https://pub.dev" - source: hosted - version: "0.9.0+2" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" http: dependency: "direct main" description: @@ -412,10 +172,10 @@ packages: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.2" intl: dependency: "direct main" description: @@ -428,50 +188,26 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" url: "https://pub.dev" source: hosted - version: "0.6.7" - just_audio: - dependency: transitive - description: - name: just_audio - sha256: b7cb6bbf3750caa924d03f432ba401ec300fd90936b3f73a9b33d58b1e96286b - url: "https://pub.dev" - source: hosted - version: "0.9.37" - just_audio_platform_interface: - dependency: transitive - description: - name: just_audio_platform_interface - sha256: "0243828cce503c8366cc2090cefb2b3c871aa8ed2f520670d76fd47aa1ab2790" - url: "https://pub.dev" - source: hosted - version: "4.3.0" - just_audio_web: - dependency: transitive - description: - name: just_audio_web - sha256: "134356b0fe3d898293102b33b5fd618831ffdc72bb7a1b726140abdf22772b70" - url: "https://pub.dev" - source: hosted - version: "0.4.9" + version: "0.7.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -488,38 +224,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - list_counter: - dependency: transitive - description: - name: list_counter - sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237 - url: "https://pub.dev" - source: hosted - version: "1.0.2" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.16.0" mime: dependency: "direct main" description: @@ -536,30 +264,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - octo_image: - dependency: transitive - description: - name: octo_image - sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_parsing: dependency: transitive description: name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" path_provider: dependency: transitive description: @@ -572,18 +292,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.dev" source: hosted - version: "2.2.9" + version: "2.2.17" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -612,18 +332,18 @@ packages: dependency: "direct main" description: name: pdfrx - sha256: "29c7b03d27d647c80da8cc08bd1256c74df90e5640fdd676646e4bd04f90553a" + sha256: "90747e916a64366b8beb69e9ac175e9134df520110543ab284b839102bf4b7e1" url: "https://pub.dev" source: hosted - version: "1.0.94" + version: "1.1.29" petitparser: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "6.1.0" photo_view: dependency: "direct main" description: @@ -636,10 +356,10 @@ packages: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -692,42 +412,42 @@ packages: dependency: "direct main" description: name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.1.5" quiver: dependency: transitive description: name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" rxdart: dependency: transitive description: name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" url: "https://pub.dev" source: hosted - version: "0.27.7" + version: "0.28.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82" + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.5.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e" + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.10" shared_preferences_foundation: dependency: transitive description: @@ -756,10 +476,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.3" shared_preferences_windows: dependency: transitive description: @@ -772,87 +492,63 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d - url: "https://pub.dev" - source: hosted - version: "2.3.3+1" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" - url: "https://pub.dev" - source: hosted - version: "2.5.4" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" synchronized: dependency: transitive description: name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" url: "https://pub.dev" source: hosted - version: "3.1.0+1" + version: "3.3.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.4" tuple: dependency: transitive description: @@ -865,10 +561,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" url_launcher: dependency: transitive description: @@ -881,34 +577,34 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "94d8ad05f44c6d4e2ffe5567ab4d741b82d62e3c8e288cc1fcea45965edf47c9" + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" url: "https://pub.dev" source: hosted - version: "6.3.8" + version: "6.3.16" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.2.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.2" url_launcher_platform_interface: dependency: transitive description: @@ -921,50 +617,42 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: a36e2d7981122fa185006b216eb6b5b97ede3f9a54b7a511bc966971ab98d049 + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" url: "https://pub.dev" source: hosted - version: "3.1.2" - uuid: - dependency: transitive - description: - name: uuid - sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" - url: "https://pub.dev" - source: hosted - version: "4.4.2" + version: "3.1.4" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.18" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.13" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.16" vector_math: dependency: transitive description: @@ -973,94 +661,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - video_player: - dependency: transitive - description: - name: video_player - sha256: e30df0d226c4ef82e2c150ebf6834b3522cf3f654d8e2f9419d376cdc071425d - url: "https://pub.dev" - source: hosted - version: "2.9.1" - video_player_android: - dependency: transitive - description: - name: video_player_android - sha256: b6f0a6d241e4a3435806cb7cb78cb666db8889c1866e432b6acd204707b3ac01 - url: "https://pub.dev" - source: hosted - version: "2.5.3" - video_player_avfoundation: - dependency: transitive - description: - name: video_player_avfoundation - sha256: d1e9a824f2b324000dc8fb2dcb2a3285b6c1c7c487521c63306cc5b394f68a7c - url: "https://pub.dev" - source: hosted - version: "2.6.1" - video_player_platform_interface: - dependency: transitive - description: - name: video_player_platform_interface - sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" - url: "https://pub.dev" - source: hosted - version: "6.2.2" - video_player_web: - dependency: transitive - description: - name: video_player_web - sha256: "8e9cb7fe94e49490e67bbc15149691792b58a0ade31b32e3f3688d104a0e057b" - url: "https://pub.dev" - source: hosted - version: "2.2.0" vm_service: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.2.1" - wakelock: - dependency: transitive - description: - name: wakelock - sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db" - url: "https://pub.dev" - source: hosted - version: "0.6.2" - wakelock_macos: - dependency: transitive - description: - name: wakelock_macos - sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" - url: "https://pub.dev" - source: hosted - version: "0.4.0" - wakelock_platform_interface: - dependency: transitive - description: - name: wakelock_platform_interface - sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" - url: "https://pub.dev" - source: hosted - version: "0.3.0" - wakelock_web: - dependency: transitive - description: - name: wakelock_web - sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5" - url: "https://pub.dev" - source: hosted - version: "0.4.0" - wakelock_windows: - dependency: transitive - description: - name: wakelock_windows - sha256: "857f77b3fe6ae82dd045455baa626bc4b93cb9bb6c86bf3f27c182167c3a5567" - url: "https://pub.dev" - source: hosted - version: "0.2.1" + version: "15.0.0" web: dependency: "direct main" description: @@ -1069,54 +677,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" - webview_flutter: - dependency: transitive - description: - name: webview_flutter - sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522" - url: "https://pub.dev" - source: hosted - version: "4.8.0" - webview_flutter_android: - dependency: transitive - description: - name: webview_flutter_android - sha256: c66651fba15f9d7ddd31daec42da8d6bce46c85610a7127e3ebcb39a4395c3c9 - url: "https://pub.dev" - source: hosted - version: "3.16.6" - webview_flutter_platform_interface: - dependency: transitive - description: - name: webview_flutter_platform_interface - sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d - url: "https://pub.dev" - source: hosted - version: "2.10.0" - webview_flutter_wkwebview: - dependency: transitive - description: - name: webview_flutter_wkwebview - sha256: "9c62cc46fa4f2d41e10ab81014c1de470a6c6f26051a2de32111b2ee55287feb" - url: "https://pub.dev" - source: hosted - version: "3.14.0" - win32: - dependency: transitive - description: - name: win32 - sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 - url: "https://pub.dev" - source: hosted - version: "3.1.4" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.1.0" xml: dependency: transitive description: @@ -1126,5 +694,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.7.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8de6880..d0633d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,14 +12,12 @@ dependencies: flutter: sdk: flutter http: 1.2.2 - flutter_html_all: 3.0.0-beta.2 - flutter_widget_from_html: ^0.10.0 shared_preferences: ^2.0.6 encrypt: ^5.0.0 pointycastle: ^3.4.0 mime: ^1.0.3 pointer_interceptor: ^0.10.1+2 - file_saver: ^0.2.14 + english_words: ^4.0.0 @@ -39,6 +37,7 @@ dependency_overrides: flutter_layout_grid: 2.0.7 flutter_math_fork: 0.7.2 + flutter: uses-material-design: true assets: -- 2.34.1 From 152d74449805559cf9999fb70ec12799b7b314e1 Mon Sep 17 00:00:00 2001 From: juan Date: Fri, 23 May 2025 16:12:31 -0400 Subject: [PATCH 02/28] working on android, and attachment widget interface with stub, android, and web implementations wip --- lib/attachmentWidget.dart | 106 +-------- lib/attachmentWidgetAndroid.dart | 16 ++ lib/attachmentWidgetStub.dart | 14 ++ lib/attachmentWidgetWeb.dart | 101 +++++++++ pubspec.lock | 360 +++++++++++++++++++++++++++++++ pubspec.yaml | 1 + 6 files changed, 495 insertions(+), 103 deletions(-) create mode 100644 lib/attachmentWidgetAndroid.dart create mode 100644 lib/attachmentWidgetStub.dart create mode 100644 lib/attachmentWidgetWeb.dart diff --git a/lib/attachmentWidget.dart b/lib/attachmentWidget.dart index 5146d9c..c40eae1 100644 --- a/lib/attachmentWidget.dart +++ b/lib/attachmentWidget.dart @@ -1,103 +1,3 @@ -import "dart:typed_data"; - -import "package:crab_ui/attachmentDownload.dart"; -import "package:crab_ui/structs.dart"; -import "package:flutter/material.dart"; -import 'package:pdfrx/pdfrx.dart'; -import 'package:photo_view/photo_view.dart'; - -class AttachmentWidget extends StatelessWidget { - final AttachmentResponse attachment; - AttachmentWidget({required this.attachment}); - - Widget attachmentViewer(AttachmentResponse att) { - String extension = att.name - .toString() - .substring(att.name.toString().indexOf(".") + 1) - .toLowerCase(); - if (extension == "jpg" || extension == "png") { - return Image.memory(att.data); - } else if (extension == "pdf") { - return PdfViewer.data(Uint8List.fromList(att.data), - sourceName: att.name, - params: PdfViewerParams( - enableTextSelection: true, - scrollByMouseWheel: 0.5, - annotationRenderingMode: - PdfAnnotationRenderingMode.annotationAndForms, - )); - } - return Center( - child: Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - color: Color(0xff6C63FF), - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black26, - blurRadius: 10, - ), - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "No preview available", - style: TextStyle( - color: Colors.white, fontSize: 18, decoration: TextDecoration.none), - ), - SizedBox( - height: 5, - ), - GestureDetector( - child: ElevatedButton( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text("Download", style: TextStyle(color: Color(0xff2c3e50)),), - Icon(Icons.download, - color: Color(0xffb0b0b0),), - ]), - onPressed: () => Attachmentdownload().saveFile(att), - )), - ]), - )); - } - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.black38, - child: Stack(children: [ - Container( - color: Colors.white, - child: Padding( - padding: EdgeInsets.fromLTRB(10, 20, 0, 10), - child: Column( - children: [ - Row( - children: [ - CloseButton(onPressed: () => {Navigator.pop(context)}), - Text( - attachment.name - .toString(), //its alr a string but incase ¯\(ツ)/¯ //update: i did that everywhere lol - style: TextStyle( - color: Colors.black, - fontSize: 20, - decoration: TextDecoration - .none), //TODO: personalize your fonts - ), - ], - ), - Expanded( - child: attachmentViewer(attachment), - ) - ], - ), - ), - ), - ])); - } -} +export 'attachmentWidgetStub.dart' + if (dart.library.js_interop) 'attachmentWidgetWeb.dart' + if (dart.library.io) 'attachmentWidgetAndroid.dart'; \ No newline at end of file diff --git a/lib/attachmentWidgetAndroid.dart b/lib/attachmentWidgetAndroid.dart new file mode 100644 index 0000000..72c3cd8 --- /dev/null +++ b/lib/attachmentWidgetAndroid.dart @@ -0,0 +1,16 @@ +import "package:crab_ui/structs.dart"; +import "package:flutter/material.dart"; + + +class AttachmentWidget extends StatelessWidget { + final AttachmentResponse attachment; + AttachmentWidget({required this.attachment}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Text("PDF EVENTUALLY ANDROID") + ); + } +} + diff --git a/lib/attachmentWidgetStub.dart b/lib/attachmentWidgetStub.dart new file mode 100644 index 0000000..5bb6c14 --- /dev/null +++ b/lib/attachmentWidgetStub.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'structs.dart'; + +class AttachmentWidget extends StatelessWidget{ + final AttachmentResponse attachment; + AttachmentWidget({required this.attachment}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Text("PDF EVENTUALLY, STUB") + ); + } +} \ No newline at end of file diff --git a/lib/attachmentWidgetWeb.dart b/lib/attachmentWidgetWeb.dart new file mode 100644 index 0000000..b8b260b --- /dev/null +++ b/lib/attachmentWidgetWeb.dart @@ -0,0 +1,101 @@ +import "dart:typed_data"; +import "package:crab_ui/attachmentDownload.dart"; +import "package:crab_ui/structs.dart"; +import "package:flutter/material.dart"; +import 'package:pdfrx/pdfrx.dart'; + +class AttachmentWidget extends StatelessWidget { + final AttachmentResponse attachment; + AttachmentWidget({required this.attachment}); + + Widget attachmentViewer(AttachmentResponse att) { + String extension = att.name + .toString() + .substring(att.name.toString().indexOf(".") + 1) + .toLowerCase(); + if (extension == "jpg" || extension == "png") { + return Image.memory(att.data); + } else if (extension == "pdf") { + return PdfViewer.data(Uint8List.fromList(att.data), + sourceName: att.name, + params: PdfViewerParams( + enableTextSelection: true, + scrollByMouseWheel: 0.5, + annotationRenderingMode: + PdfAnnotationRenderingMode.annotationAndForms, + )); + } + return Center( + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: Color(0xff6C63FF), + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10, + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "No preview available", + style: TextStyle( + color: Colors.white, fontSize: 18, decoration: TextDecoration.none), + ), + SizedBox( + height: 5, + ), + GestureDetector( + child: ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text("Download", style: TextStyle(color: Color(0xff2c3e50)),), + Icon(Icons.download, + color: Color(0xffb0b0b0),), + ]), + onPressed: () => Attachmentdownload().saveFile(att), + )), + ]), + )); + } + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.black38, + child: Stack(children: [ + Container( + color: Colors.white, + child: Padding( + padding: EdgeInsets.fromLTRB(10, 20, 0, 10), + child: Column( + children: [ + Row( + children: [ + CloseButton(onPressed: () => {Navigator.pop(context)}), + Text( + attachment.name + .toString(), //its alr a string but incase ¯\(ツ)/¯ //update: i did that everywhere lol + style: TextStyle( + color: Colors.black, + fontSize: 20, + decoration: TextDecoration + .none), //TODO: personalize your fonts + ), + ], + ), + Expanded( + child: attachmentViewer(attachment), + ) + ], + ), + ), + ), + ])); + } +} diff --git a/pubspec.lock b/pubspec.lock index fe96027..13a53ff 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.13.0" + audio_session: + dependency: transitive + description: + name: audio_session + sha256: "2b7fff16a552486d078bfc09a8cde19f426dc6d6329262b684182597bec5b1ac" + url: "https://pub.dev" + source: hosted + version: "0.1.25" boolean_selector: dependency: transitive description: @@ -33,6 +41,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + cached_network_image: + dependency: transitive + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" characters: dependency: transitive description: @@ -41,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + chewie: + dependency: transitive + description: + name: chewie + sha256: "4d9554a8f87cc2dc6575dfd5ad20a4375015a29edd567fd6733febe6365e2566" + url: "https://pub.dev" + source: hosted + version: "1.11.3" clock: dependency: transitive description: @@ -73,6 +113,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" + source: hosted + version: "0.7.11" encrypt: dependency: "direct main" description: @@ -113,11 +177,27 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" flutter_layout_grid: dependency: "direct overridden" description: @@ -160,6 +240,78 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_widget_from_html: + dependency: "direct main" + description: + name: flutter_widget_from_html + sha256: "0dfebf7417df2149de93926520c703db9be0c9017e60dc5cf43cebed37f4d11e" + url: "https://pub.dev" + source: hosted + version: "0.16.0" + flutter_widget_from_html_core: + dependency: transitive + description: + name: flutter_widget_from_html_core + sha256: f77ea1aa1ba29a38fcce04483f44f12382f541b9e8c2150df37166c23bbbd30f + url: "https://pub.dev" + source: hosted + version: "0.16.0" + fwfh_cached_network_image: + dependency: transitive + description: + name: fwfh_cached_network_image + sha256: "8f4896109ff3e42424ccacf9058ba3afe5d575b58946c8ac646ac85ae882ce23" + url: "https://pub.dev" + source: hosted + version: "0.16.0" + fwfh_chewie: + dependency: transitive + description: + name: fwfh_chewie + sha256: "1ce7c56894db19881a997813b933835dec142878431370c0eb40f1f878396a25" + url: "https://pub.dev" + source: hosted + version: "0.16.0" + fwfh_just_audio: + dependency: transitive + description: + name: fwfh_just_audio + sha256: "17816168de1fd180fd3d1fd4500e23136630a248a6889b553e2d2067e133c1a6" + url: "https://pub.dev" + source: hosted + version: "0.16.0" + fwfh_svg: + dependency: transitive + description: + name: fwfh_svg + sha256: "82f3eb378186fe39b3e2e01ed48a1830d34b0b9a237d951077e74ff0d99e2ac3" + url: "https://pub.dev" + source: hosted + version: "0.16.0" + fwfh_url_launcher: + dependency: transitive + description: + name: fwfh_url_launcher + sha256: "5cf1b1baa16740abaef8eb41a8e16ba430295d5ec20b880e4cb94e2924774f0a" + url: "https://pub.dev" + source: hosted + version: "0.16.0" + fwfh_webview: + dependency: transitive + description: + name: fwfh_webview + sha256: "894aa7d98ebdc2d86d79ac2309173043dec7f102575de87bf9626ddb26104e49" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" http: dependency: "direct main" description: @@ -192,6 +344,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.2" + just_audio: + dependency: transitive + description: + name: just_audio + sha256: f978d5b4ccea08f267dae0232ec5405c1b05d3f3cd63f82097ea46c015d5c09e + url: "https://pub.dev" + source: hosted + version: "0.9.46" + just_audio_platform_interface: + dependency: transitive + description: + name: just_audio_platform_interface + sha256: "4cd94536af0219fa306205a58e78d67e02b0555283c1c094ee41e402a14a5c4a" + url: "https://pub.dev" + source: hosted + version: "4.5.0" + just_audio_web: + dependency: transitive + description: + name: just_audio_web + sha256: "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663" + url: "https://pub.dev" + source: hosted + version: "0.4.16" leak_tracker: dependency: transitive description: @@ -224,6 +400,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -264,6 +448,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" path: dependency: transitive description: @@ -501,6 +709,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + url: "https://pub.dev" + source: hosted + version: "2.5.5" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: @@ -629,6 +885,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.4" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: @@ -661,6 +925,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + video_player: + dependency: transitive + description: + name: video_player + sha256: "7d78f0cfaddc8c19d4cb2d3bebe1bfef11f2103b0a03e5398b303a1bf65eeb14" + url: "https://pub.dev" + source: hosted + version: "2.9.5" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: "28dcc4122079f40f93a0965b3679aff1a5f4251cf79611bd8011f937eb6b69de" + url: "https://pub.dev" + source: hosted + version: "2.8.4" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: "9ee764e5cd2fc1e10911ae8ad588e1a19db3b6aa9a6eb53c127c42d3a3c3f22f" + url: "https://pub.dev" + source: hosted + version: "2.7.1" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: df534476c341ab2c6a835078066fc681b8265048addd853a1e3c78740316a844 + url: "https://pub.dev" + source: hosted + version: "6.3.0" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: e8bba2e5d1e159d5048c9a491bb2a7b29c535c612bb7d10c1e21107f5bd365ba + url: "https://pub.dev" + source: hosted + version: "2.3.5" vm_service: dependency: transitive description: @@ -669,6 +973,22 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.0" + wakelock_plus: + dependency: transitive + description: + name: wakelock_plus + sha256: a474e314c3e8fb5adef1f9ae2d247e57467ad557fa7483a2b895bc1b421c5678 + url: "https://pub.dev" + source: hosted + version: "1.3.2" + wakelock_plus_platform_interface: + dependency: transitive + description: + name: wakelock_plus_platform_interface + sha256: e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207 + url: "https://pub.dev" + source: hosted + version: "1.2.3" web: dependency: "direct main" description: @@ -677,6 +997,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + webview_flutter: + dependency: transitive + description: + name: webview_flutter + sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba + url: "https://pub.dev" + source: hosted + version: "4.13.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678 + url: "https://pub.dev" + source: hosted + version: "4.7.0" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3 + url: "https://pub.dev" + source: hosted + version: "3.22.0" + win32: + dependency: transitive + description: + name: win32 + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" + url: "https://pub.dev" + source: hosted + version: "5.13.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d0633d2..d3e7a93 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: pdfrx: ^1.0.94 photo_view: ^0.15.0 web: ^1.1.1 + flutter_widget_from_html: ^0.16.0 dev_dependencies: flutter_test: -- 2.34.1 From 04a871a293edf6ec3880149e0b42da8557844818 Mon Sep 17 00:00:00 2001 From: juan Date: Fri, 23 May 2025 16:12:59 -0400 Subject: [PATCH 03/28] collapsable implementation for all platforms, WIP --- lib/collapsableEmails.dart | 135 +----------------------------- lib/collapsableEmailsAndroid.dart | 48 +++++++++++ lib/collapsableEmailsStub.dart | 23 +++++ lib/collapsableEmailsWeb.dart | 132 +++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 132 deletions(-) create mode 100644 lib/collapsableEmailsAndroid.dart create mode 100644 lib/collapsableEmailsStub.dart create mode 100644 lib/collapsableEmailsWeb.dart diff --git a/lib/collapsableEmails.dart b/lib/collapsableEmails.dart index ceed92a..e8b144d 100644 --- a/lib/collapsableEmails.dart +++ b/lib/collapsableEmails.dart @@ -1,132 +1,3 @@ -import 'dart:js_interop'; -import 'package:web/web.dart' as web; -import 'package:flutter/material.dart'; -import 'dart:ui_web' as ui; -import 'api_service.dart'; -import 'structs.dart'; - -class CollapsableEmails extends StatefulWidget { - final List thread; // email id's in the form xyz@gmail.com - final List threadHTML; - final String threadIDs; - - CollapsableEmails( - {required this.thread, - required this.threadHTML, - required this.threadIDs}); - - @override - State createState() => _CollapsableEmailsState(); -} - -class _CollapsableEmailsState extends State { - List emailsHTML = []; //html of the emails in the thread - // build attachments with the forldar name and id - Set _expandedEmails = {}; //open emails - List viewtypeIDs = []; //IDs of the viewtypes, order matters - List heightOfViewTypes = []; //the height of each viewtype - List emailsInThread = []; - bool _isLoaded = false; - - @override - void initState() { - // TODO: implement initState - super.initState(); - _registerViewFactory(widget.threadHTML); - _serializableData(widget.threadIDs); - } - - void _registerViewFactory(List currentContent) async { - // setState(() { //update to do item per item - // each item to have itsviewtype ID - // is this necessarey here?? - - //could just move to collapsable - - for (var emailHTML in widget.threadHTML) { - String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}'; - - final ghost = web.document.createElement('div') as web.HTMLDivElement - ..style.visibility = 'hidden' - ..style.position = 'absolute' - ..style.width = '100%' - ..style.overflow = 'auto' - ..innerHTML = emailHTML - .toJS; // temporarily index because it has to do all of them - web.document.body?.append(ghost); - await Future.delayed(Duration(milliseconds: 10)); - - final heightOfEmail = ghost.scrollHeight; - ghost.remove(); - - final HTMLsnippet = web.document.createElement('div') - as web.HTMLDivElement - ..id = viewTypeId - ..innerHTML = emailHTML - .toJS; // temporarily index because it has to do all of them - HTMLsnippet.style - ..width = '100%' - ..height = '${heightOfEmail}px' - ..overflow = 'auto' - ..scrollBehavior = 'smooth'; - - ui.platformViewRegistry.registerViewFactory( - viewTypeId, - (int viewId) => HTMLsnippet, - ); - viewtypeIDs.add(viewTypeId); - heightOfViewTypes.add(heightOfEmail); - } - } - - void _serializableData(String threadID) async { - emailsInThread = await ApiService().threadsInSerializable(threadID); - print("done thread serializable"); - if (!mounted) return; - setState(() { - _isLoaded = true; - }); - } - - @override - Widget build(BuildContext context) { - return _isLoaded - ?Column(children: [ - Expanded( - child: ListView.builder( - itemCount: widget.thread.length, - itemBuilder: (context, index) { - final isExpanded = - _expandedEmails.contains(index); //check if email is expanded - return Column( - children: [ - ListTile( - title: Text(emailsInThread[index].from), - trailing: Text(emailsInThread[index].date), - onTap: () { - setState(() { - if (isExpanded) { - _expandedEmails.remove(index); - } else { - _expandedEmails.add(index); - } - }); - }, - ), - if (isExpanded) - // if(viewtypeIDs[index] == null || heightOfViewTypes[index] == null) - // const SizedBox(height: 100, child: Center(child: CircularProgressIndicator())), - SizedBox( - height: heightOfViewTypes[index].toDouble(), - child: HtmlElementView( - key: UniqueKey(), viewType: viewtypeIDs[index]), - ), - Divider(), - ], - ); - }, - ), - ) - ]): const Center(child:CircularProgressIndicator()); - } -} +export 'collapsableEmailsStub.dart' + if (dart.library.io) 'collapsableEmailsAndroid.dart' + if (dart.library.js_interop) 'collapsableEmailsWeb.dart'; \ No newline at end of file diff --git a/lib/collapsableEmailsAndroid.dart b/lib/collapsableEmailsAndroid.dart new file mode 100644 index 0000000..f2cdf01 --- /dev/null +++ b/lib/collapsableEmailsAndroid.dart @@ -0,0 +1,48 @@ +import 'structs.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; + +class CollapsableEmails extends StatefulWidget { + final List thread; // email id's in the form xyz@gmail.com + final List threadHTML; + final String threadIDs; + + CollapsableEmails( + {required this.thread, + required this.threadHTML, + required this.threadIDs}); + + @override + State createState() => _CollapsableEmailsState(); +} + +class _CollapsableEmailsState extends State { + List emailsHTML = []; //html of the emails in the thread + // build attachments with the forldar name and id + Set _expandedEmails = {}; //open emails + List viewtypeIDs = []; //IDs of the viewtypes, order matters + List heightOfViewTypes = []; //the height of each viewtype + List emailsInThread = []; + bool _isLoaded = false; + + @override + void initState() { + super.initState(); + //html + } + + + @override + Widget build(BuildContext context) { + return Scaffold( + body: ListView( + children: [ + HtmlWidget( + widget.threadHTML[0], + // renderMode: RenderMode.listView, + ) + ] + ) + ); + } +} diff --git a/lib/collapsableEmailsStub.dart b/lib/collapsableEmailsStub.dart new file mode 100644 index 0000000..7c89a88 --- /dev/null +++ b/lib/collapsableEmailsStub.dart @@ -0,0 +1,23 @@ +import 'structs.dart'; +import 'package:flutter/material.dart'; + +class CollapsableEmails extends StatefulWidget { + final List thread; // email id's in the form xyz@gmail.com + final List threadHTML; + final String threadIDs; + + CollapsableEmails( + {required this.thread, + required this.threadHTML, + required this.threadIDs}); + + @override + State createState() => _CollapsableEmailsState(); +} + +class _CollapsableEmailsState extends State { + @override + Widget build(BuildContext context) { + return Scaffold(body: Text("collapsable stud")); + } +} diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart new file mode 100644 index 0000000..ee8e4ea --- /dev/null +++ b/lib/collapsableEmailsWeb.dart @@ -0,0 +1,132 @@ +import 'dart:js_interop'; +import 'package:web/web.dart' as web; +import 'package:flutter/material.dart'; +import 'dart:ui_web' as ui; +import 'api_service.dart'; +import 'structs.dart'; + +class CollapsableEmails extends StatefulWidget { + final List thread; // email id's in the form xyz@gmail.com + final List threadHTML; + final String threadIDs; + + CollapsableEmails( + {required this.thread, + required this.threadHTML, + required this.threadIDs}); + + @override + State createState() => _CollapsableEmailsState(); +} + +class _CollapsableEmailsState extends State { + List emailsHTML = []; //html of the emails in the thread + // build attachments with the forldar name and id + Set _expandedEmails = {}; //open emails + List viewtypeIDs = []; //IDs of the viewtypes, order matters + List heightOfViewTypes = []; //the height of each viewtype + List emailsInThread = []; + bool _isLoaded = false; + + @override + void initState() { + // TODO: implement initState + super.initState(); + _registerViewFactory(widget.threadHTML); + _serializableData(widget.threadIDs); + } + + void _registerViewFactory(List currentContent) async { + // setState(() { //update to do item per item + // each item to have itsviewtype ID + // is this necessarey here?? + + //could just move to collapsable + + for (var emailHTML in widget.threadHTML) { + String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}'; + + final ghost = web.document.createElement('div') as web.HTMLDivElement + ..style.visibility = 'hidden' + ..style.position = 'absolute' + ..style.width = '100%' + ..style.overflow = 'auto' + ..innerHTML = emailHTML + .toJS; // temporarily index because it has to do all of them + web.document.body?.append(ghost); + await Future.delayed(Duration(milliseconds: 10)); + + final heightOfEmail = ghost.scrollHeight; + ghost.remove(); + + final HTMLsnippet = web.document.createElement('div') + as web.HTMLDivElement + ..id = viewTypeId + ..innerHTML = emailHTML + .toJS; // temporarily index because it has to do all of them + HTMLsnippet.style + ..width = '100%' + ..height = '${heightOfEmail}px' + ..overflow = 'auto' + ..scrollBehavior = 'smooth'; + + ui.platformViewRegistry.registerViewFactory( + viewTypeId, + (int viewId) => HTMLsnippet, + ); + viewtypeIDs.add(viewTypeId); + heightOfViewTypes.add(heightOfEmail); + } + } + + void _serializableData(String threadID) async { + emailsInThread = await ApiService().threadsInSerializable(threadID); + print("done thread serializable"); + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + + @override + Widget build(BuildContext context) { + return _isLoaded + ?Column(children: [ + Expanded( + child: ListView.builder( + itemCount: widget.thread.length, + itemBuilder: (context, index) { + final isExpanded = + _expandedEmails.contains(index); //check if email is expanded + return Column( + children: [ + ListTile( + title: Text(emailsInThread[index].from), + trailing: Text(emailsInThread[index].date), + onTap: () { + setState(() { + if (isExpanded) { + _expandedEmails.remove(index); + } else { + _expandedEmails.add(index); + } + }); + }, + ), + if (isExpanded) + // if(viewtypeIDs[index] == null || heightOfViewTypes[index] == null) + // const SizedBox(height: 100, child: Center(child: CircularProgressIndicator())), + SizedBox( + height: heightOfViewTypes[index].toDouble(), + child: HtmlElementView( + key: UniqueKey(), viewType: viewtypeIDs[index]), + ), + Divider(), + ], + ); + }, + ), + ) + ]): const Center(child:CircularProgressIndicator()); + } +} -- 2.34.1 From 8eeb0b013d4af27977ca1a01c1a4d7eef0a8a22b Mon Sep 17 00:00:00 2001 From: juan Date: Fri, 23 May 2025 16:13:17 -0400 Subject: [PATCH 04/28] attachment download WIP --- lib/attachamentDownloadStub.dart | 7 +++++++ lib/attachmentDownload.dart | 18 +++--------------- lib/attachmentDownloadAndroid.dart | 7 +++++++ lib/attachmentDownloadWeb.dart | 12 ++++++++++++ 4 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 lib/attachamentDownloadStub.dart create mode 100644 lib/attachmentDownloadAndroid.dart create mode 100644 lib/attachmentDownloadWeb.dart diff --git a/lib/attachamentDownloadStub.dart b/lib/attachamentDownloadStub.dart new file mode 100644 index 0000000..5daf854 --- /dev/null +++ b/lib/attachamentDownloadStub.dart @@ -0,0 +1,7 @@ +import 'structs.dart'; + +class Attachmentdownload { + Future saveFile(AttachmentResponse attachment) async { + print("stub attachment download"); + } +} diff --git a/lib/attachmentDownload.dart b/lib/attachmentDownload.dart index 63364ba..f1f8f86 100644 --- a/lib/attachmentDownload.dart +++ b/lib/attachmentDownload.dart @@ -1,15 +1,3 @@ -import 'dart:html' as html; -import 'package:web/web.dart' as web; -import 'dart:io'; -import 'structs.dart'; -import 'package:file_saver/file_saver.dart'; - -class Attachmentdownload { - Future saveFile(AttachmentResponse attachment) async { - await FileSaver.instance.saveFile( - name: attachment.name.toString().substring(0, attachment.name.toString().lastIndexOf('.')), - bytes: attachment.data, - ext: attachment.name.toString().substring(attachment.name.toString().lastIndexOf('.')+1) - ); - } -} +export 'attachamentDownloadStub.dart' + if (dart.library.io) 'attachmentDownloadAndroid.dart'; + // if (dart.library.js_interop) 'attachmentDownloadWeb.dart'; \ No newline at end of file diff --git a/lib/attachmentDownloadAndroid.dart b/lib/attachmentDownloadAndroid.dart new file mode 100644 index 0000000..603253c --- /dev/null +++ b/lib/attachmentDownloadAndroid.dart @@ -0,0 +1,7 @@ +import 'structs.dart'; + +class Attachmentdownload { + Future saveFile(AttachmentResponse attachment) async { + print("android attachment download"); + } +} diff --git a/lib/attachmentDownloadWeb.dart b/lib/attachmentDownloadWeb.dart new file mode 100644 index 0000000..cb4954c --- /dev/null +++ b/lib/attachmentDownloadWeb.dart @@ -0,0 +1,12 @@ +// import 'structs.dart'; +// import 'package:file_saver/file_saver.dart'; + +// class Attachmentdownload { +// Future saveFile(AttachmentResponse attachment) async { +// await FileSaver.instance.saveFile( +// name: attachment.name.toString().substring(0, attachment.name.toString().lastIndexOf('.')), +// bytes: attachment.data, +// ext: attachment.name.toString().substring(attachment.name.toString().lastIndexOf('.')+1) +// ); +// } +// } -- 2.34.1 From 36e6cbb2f115a0f572650bb7162e64d64eb9289c Mon Sep 17 00:00:00 2001 From: juan Date: Fri, 23 May 2025 16:13:45 -0400 Subject: [PATCH 05/28] sonic implementation in all platforms --- lib/SonicEmailViewAndroid.dart | 19 +++++ lib/SonicEmailViewStub.dart | 22 +++++ lib/SonicEmailViewWeb.dart | 145 ++++++++++++++++++++++++++++++++ lib/sonicEmailView.dart | 148 +-------------------------------- 4 files changed, 189 insertions(+), 145 deletions(-) create mode 100644 lib/SonicEmailViewAndroid.dart create mode 100644 lib/SonicEmailViewStub.dart create mode 100644 lib/SonicEmailViewWeb.dart diff --git a/lib/SonicEmailViewAndroid.dart b/lib/SonicEmailViewAndroid.dart new file mode 100644 index 0000000..fc2b67c --- /dev/null +++ b/lib/SonicEmailViewAndroid.dart @@ -0,0 +1,19 @@ +import 'structs.dart'; +import 'package:flutter/material.dart'; + +class SonicEmailView extends StatefulWidget { + SerializableMessage email; + String emailHTML; + + SonicEmailView({required this.email, required this.emailHTML}); + + @override + _SonicEmailViewState createState() => _SonicEmailViewState(); +} + +class _SonicEmailViewState extends State { + @override + Widget build(BuildContext context) { + return Scaffold(body: Text("sonic email android")); + } +} diff --git a/lib/SonicEmailViewStub.dart b/lib/SonicEmailViewStub.dart new file mode 100644 index 0000000..0307a15 --- /dev/null +++ b/lib/SonicEmailViewStub.dart @@ -0,0 +1,22 @@ +import 'structs.dart'; +import 'package:flutter/material.dart'; + +class SonicEmailView extends StatefulWidget { + SerializableMessage email; + String emailHTML; + + SonicEmailView({required this.email, required this.emailHTML}); + + @override + _SonicEmailViewState createState() => _SonicEmailViewState(); +} + +class _SonicEmailViewState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body:Text("sonic email stub") + ); + } + +} diff --git a/lib/SonicEmailViewWeb.dart b/lib/SonicEmailViewWeb.dart new file mode 100644 index 0000000..3ad8f76 --- /dev/null +++ b/lib/SonicEmailViewWeb.dart @@ -0,0 +1,145 @@ +import 'package:crab_ui/augment.dart'; +import 'package:web/web.dart' as web; +import 'dart:ui_web' as ui; +import 'dart:js_interop'; +import 'structs.dart'; +import 'package:flutter/material.dart'; + +class SonicEmailView extends StatefulWidget { + SerializableMessage email; + String emailHTML; + + SonicEmailView({required this.email, required this.emailHTML}); + + @override + _SonicEmailViewState createState() => _SonicEmailViewState(); +} + +class _SonicEmailViewState extends State { + String viewTypeIDs = ""; + int heightOFViewtype = 0; + bool _isLoaded = false; + + void _scrollToNumber(String spanId) { + AugmentClasses.handleJump(spanId); + } + + @override + void initState() { + super.initState(); + _init(); + } + + Future _init() async { + await _registerViewFactory(widget.emailHTML); + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + + Future _registerViewFactory(String currentContent) async { + // setState(() { //update to do item per item + // each item to have itsviewtype ID + // is this necessarey here?? + + //could just move to collapsable + + // for (var emailHTML in widget.threadHTML) { + String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}'; + + final ghost = web.document.createElement('div') as web.HTMLDivElement + ..style.visibility = 'hidden' + ..style.position = 'absolute' + ..style.width = '100%' + ..style.overflow = 'auto' + ..innerHTML = currentContent.toJS; + web.document.body?.append(ghost); + await Future.delayed(Duration(milliseconds: 10)); + + final heightOfEmail = ghost.scrollHeight; + ghost.remove(); + + final HTMLsnippet = web.document.createElement('div') as web.HTMLDivElement + ..id = viewTypeId + ..innerHTML = widget + .emailHTML.toJS; // temporarily index because it has to do all of them + HTMLsnippet.style + ..width = '100%' + ..height = '${heightOfEmail}px' + ..overflow = 'auto' + ..scrollBehavior = 'smooth'; + + ui.platformViewRegistry.registerViewFactory( + viewTypeId, + (int viewId) => HTMLsnippet, + ); + this.viewTypeIDs = viewTypeId; + this.heightOFViewtype = heightOfEmail; + print(viewTypeIDs); + } + + @override + Widget build(BuildContext context) { + return _isLoaded + ? Scaffold( + appBar: AppBar(title: Text(widget.email.subject)), + body: Stack( + children: [ + Column( + children: [ + EmailToolbar( + onButtonPressed: () => {}, + onJumpToSpan: _scrollToNumber), + Row( + // title of email + children: [ + Text( + widget.email.subject, + style: TextStyle(fontSize: 30), + ), + ], + ), + Row( + children: [ + Text( + 'from ${widget.email.name}', + style: TextStyle(fontSize: 18), + ), + Text( + '<${widget.email.from}>', + style: TextStyle(fontSize: 18), + ), + Spacer(), + Text( + '${widget.email.date}', + textAlign: TextAlign.right, + ) + ], + ), + // TODO: make a case where if one of these is the user's email it just says me :))))) + Row( + children: [ + Text( + 'to ${widget.email.to.toString()}', + style: TextStyle(fontSize: 15), + ) + ], + ), + Expanded( + // child: SizedBox( + // height: heightOFViewtype.toDouble(), + child: HtmlElementView( + key: UniqueKey(), viewType: this.viewTypeIDs, + // ), + )) + ], + ), + ], + ), + ) + : const Center( + child: CircularProgressIndicator(), + ); + } +} diff --git a/lib/sonicEmailView.dart b/lib/sonicEmailView.dart index bfe0bbe..aeb7301 100644 --- a/lib/sonicEmailView.dart +++ b/lib/sonicEmailView.dart @@ -1,145 +1,3 @@ -import 'package:crab_ui/augment.dart'; -import 'package:web/web.dart' as web; -import 'dart:ui_web' as ui; -import 'dart:js_interop'; -import 'structs.dart'; -import 'package:flutter/material.dart'; - -class SonicEmailView extends StatefulWidget { - SerializableMessage email; - String emailHTML; - - SonicEmailView({required this.email, required this.emailHTML}); - - @override - _SonicEmailViewState createState() => _SonicEmailViewState(); -} - -class _SonicEmailViewState extends State { - String viewTypeIDs = ""; - int heightOFViewtype = 0; - bool _isLoaded = false; - - void _scrollToNumber(String spanId) { - AugmentClasses.handleJump(spanId); - } - - @override - void initState() { - super.initState(); - _init(); - } - - Future _init() async { - await _registerViewFactory(widget.emailHTML); - if (!mounted) return; - setState(() { - _isLoaded = true; - }); - } - - Future _registerViewFactory(String currentContent) async { - // setState(() { //update to do item per item - // each item to have itsviewtype ID - // is this necessarey here?? - - //could just move to collapsable - - // for (var emailHTML in widget.threadHTML) { - String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}'; - - final ghost = web.document.createElement('div') as web.HTMLDivElement - ..style.visibility = 'hidden' - ..style.position = 'absolute' - ..style.width = '100%' - ..style.overflow = 'auto' - ..innerHTML = currentContent.toJS; - web.document.body?.append(ghost); - await Future.delayed(Duration(milliseconds: 10)); - - final heightOfEmail = ghost.scrollHeight; - ghost.remove(); - - final HTMLsnippet = web.document.createElement('div') as web.HTMLDivElement - ..id = viewTypeId - ..innerHTML = widget - .emailHTML.toJS; // temporarily index because it has to do all of them - HTMLsnippet.style - ..width = '100%' - ..height = '${heightOfEmail}px' - ..overflow = 'auto' - ..scrollBehavior = 'smooth'; - - ui.platformViewRegistry.registerViewFactory( - viewTypeId, - (int viewId) => HTMLsnippet, - ); - this.viewTypeIDs = viewTypeId; - this.heightOFViewtype = heightOfEmail; - print(viewTypeIDs); - } - - @override - Widget build(BuildContext context) { - return _isLoaded - ? Scaffold( - appBar: AppBar(title: Text(widget.email.subject)), - body: Stack( - children: [ - Column( - children: [ - EmailToolbar( - onButtonPressed: () => {}, - onJumpToSpan: _scrollToNumber), - Row( - // title of email - children: [ - Text( - widget.email.subject, - style: TextStyle(fontSize: 30), - ), - ], - ), - Row( - children: [ - Text( - 'from ${widget.email.name}', - style: TextStyle(fontSize: 18), - ), - Text( - '<${widget.email.from}>', - style: TextStyle(fontSize: 18), - ), - Spacer(), - Text( - '${widget.email.date}', - textAlign: TextAlign.right, - ) - ], - ), - // TODO: make a case where if one of these is the user's email it just says me :))))) - Row( - children: [ - Text( - 'to ${widget.email.to.toString()}', - style: TextStyle(fontSize: 15), - ) - ], - ), - Expanded( - // child: SizedBox( - // height: heightOFViewtype.toDouble(), - child: HtmlElementView( - key: UniqueKey(), viewType: this.viewTypeIDs, - // ), - )) - ], - ), - ], - ), - ) - : const Center( - child: CircularProgressIndicator(), - ); - } -} +export 'SonicEmailViewStub.dart' + if (dart.library.js_interop) 'SonicEmailViewWeb.dart' + if (dart.library.io) 'SonicEmailViewAndroid.dart'; \ No newline at end of file -- 2.34.1 From 50528de0f2f5216831f38dde86bd1e0916720906 Mon Sep 17 00:00:00 2001 From: juan Date: Fri, 23 May 2025 16:14:46 -0400 Subject: [PATCH 06/28] email view , WIP --- lib/emailView.dart | 3 + lib/emailViewAndroid.dart | 95 +++++++++++++++ lib/emailViewStub.dart | 39 ++++++ lib/emailViewWeb.dart | 241 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 378 insertions(+) create mode 100644 lib/emailView.dart create mode 100644 lib/emailViewAndroid.dart create mode 100644 lib/emailViewStub.dart create mode 100644 lib/emailViewWeb.dart diff --git a/lib/emailView.dart b/lib/emailView.dart new file mode 100644 index 0000000..61c9bf8 --- /dev/null +++ b/lib/emailView.dart @@ -0,0 +1,3 @@ +export 'emailViewStub.dart' + if (dart.library.io) 'emailViewAndroid.dart' + if (dart.library.js_interop) 'emailViewWeb.dart'; \ No newline at end of file diff --git a/lib/emailViewAndroid.dart b/lib/emailViewAndroid.dart new file mode 100644 index 0000000..d6d6981 --- /dev/null +++ b/lib/emailViewAndroid.dart @@ -0,0 +1,95 @@ +import 'package:crab_ui/collapsableEmailsAndroid.dart'; +import 'package:flutter/material.dart'; +// import 'dart:ui_web' as ui; +// import 'augment.dart'; +// // import 'dart:js_interop' as js; //eventually for manipulating css +// import 'package:pointer_interceptor/pointer_interceptor.dart'; +// import 'collapsableEmails.dart'; +// import 'api_service.dart'; + +class EmailView extends StatefulWidget { + final List emailContent; + final String from; + final String name; + final String to; + final String subject; + final String date; + final String id; + final List messages; + + const EmailView({ + Key? key, + required this.emailContent, + required this.from, + required this.name, // tf is name + required this.to, + required this.subject, + required this.date, + required this.id, + required this.messages, + }) : super(key: key); + @override + _EmailViewState createState() => _EmailViewState(); +} + +class _EmailViewState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.name), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Text( + widget.subject, + style: TextStyle(fontSize: 15), + overflow: TextOverflow.visible, + softWrap: true, + ), + ), + ], + ), + Row( + children: [ + Text( + 'from ${widget.name}', + style: TextStyle(fontSize: 8), + ), + Text( + '<${widget.from}>', + style: TextStyle(fontSize: 8), + ), + Spacer(), + Text( + widget.date, + textAlign: TextAlign.right, + ) + ], + ), + Row( + children: [ + Text( + 'to ${widget.to.toString()}', + style: TextStyle(fontSize: 8), + ) + ], + ), + Expanded( + child: CollapsableEmails( + thread: widget.messages, + threadHTML: widget.emailContent, + threadIDs: widget.id, + ), + ), + ], + ), + ) + ); + } +} \ No newline at end of file diff --git a/lib/emailViewStub.dart b/lib/emailViewStub.dart new file mode 100644 index 0000000..5c5f657 --- /dev/null +++ b/lib/emailViewStub.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + + +class EmailView extends StatefulWidget { + final List emailContent; + final String from; + final String name; + final String to; + final String subject; + final String date; + final String id; + final List messages; + + const EmailView({ + Key? key, + required this.emailContent, + required this.from, + required this.name, + required this.to, + required this.subject, + required this.date, + required this.id, + required this.messages, + }) : super(key: key); + @override + _EmailViewState createState() => _EmailViewState(); +} + +class _EmailViewState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Text(" emailview stub, not supported") + ) + ); + } + +} \ No newline at end of file diff --git a/lib/emailViewWeb.dart b/lib/emailViewWeb.dart new file mode 100644 index 0000000..08551eb --- /dev/null +++ b/lib/emailViewWeb.dart @@ -0,0 +1,241 @@ +import 'package:flutter/material.dart'; +import 'dart:ui_web' as ui; +import 'augment.dart'; +// import 'dart:js_interop' as js; //eventually for manipulating css +import 'package:pointer_interceptor/pointer_interceptor.dart'; +import 'collapsableEmails.dart'; +import 'api_service.dart'; + + +class EmailView extends StatefulWidget { + final List emailContent; + final String from; + final String name; + final String to; + final String subject; + final String date; + final String id; + final List messages; + + const EmailView({ + Key? key, + required this.emailContent, + required this.from, + required this.name, + required this.to, + required this.subject, + required this.date, + required this.id, + required this.messages, + }) : super(key: key); + @override + _EmailViewState createState() => _EmailViewState(); +} + +class _EmailViewState extends State { + //html css rendering thing + late Key iframeKey; + late String currentContent; + late String viewTypeId; //make this a list too??? + Future>>? _markerPositionsFuture; + // TextEditingController _jumpController = TextEditingController(); + final hardcodedMarkers = [ + {'id': 'marker1', 'x': 50, 'y': 100}, + {'id': 'marker2', 'x': 150, 'y': 200}, + {'id': 'marker3', 'x': 250, 'y': 300}, + ]; + + @override + void initState() { + super.initState(); + print("thread id? ${widget.id}"); + List currentContent = widget + .emailContent; //html of the email/ actually entire thread, gives me little space to play in between + // i wonder if the other attributes change? because if so i have to add like some zooms in and out of the emails, as in collapse + // _registerViewFactory(currentContent); + } + + // void _registerViewFactory(List currentContent) { // i think this doesnt work anymore + // setState(() { //update to do item per item + // // each item to have itsviewtype ID + // // is this necessarey here?? + + // //could just move to collapsable + + // viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}'; + // final emailHTML = web.document.createElement('div') as web.HTMLDivElement + // ..id = viewTypeId + // ..innerHTML = currentContent[0].toJS; // temporarily index because it has to do all of them + // emailHTML.style + // ..width = '100%' + // ..height = '100%' + // ..overflow = 'auto' + // ..scrollBehavior = 'smooth'; + + // ui.platformViewRegistry.registerViewFactory( + // viewTypeId, + // (int viewId) => emailHTML, + // ); + // }); + // } + + void _scrollToNumber(String spanId) { + AugmentClasses.handleJump(spanId); + } + + // TODO: void _invisibility(String ) //to make purple numbers not visible + + @override + Widget build(BuildContext context) { + // print("thread id ${widget.id}"); + ApiService.currThreadID = widget.id; + return Scaffold( + appBar: AppBar( + title: Text(widget.name), + ), + body: Stack( + children: [ + Column( + children: [ + EmailToolbar( + onJumpToSpan: _scrollToNumber, + onButtonPressed: () => {}, + // AugmentClasses.handleJump(viewTypeId, '1'); + // print("button got pressed?"); + + // _registerViewFactory(r""" + //

Welcome to My Website

+ //

This is a simple HTML page.

+ //

What is HTML?

+ //

HTML (HyperText Markup Language) is the most basic building~ block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).

+ //

Here's a simple list:

+ //
    + //
  • HTML elements are the building blocks of HTML pages
  • + //
  • HTML uses tags like <tag> to organize and format content
  • + //
  • CSS is used with HTML to style pages
  • + //
+ //

Copyright © 2023

+ // """); + // print("change"); + // widget.emailContent = r" + + // + ), + Row( + // title of email + children: [ + Text( + widget.subject, + style: TextStyle(fontSize: 30), + ), + ], + ), + Row( + children: [ + Text( + 'from ${widget.name}', + style: TextStyle(fontSize: 18), + ), + Text( + '<${widget.from}>', + style: TextStyle(fontSize: 18), + ), + Spacer(), + Text( + '${widget.date}', + textAlign: TextAlign.right, + ) + ], + ), + // TODO: make a case where if one of these is the user's email it just says me :))))) + Row( + children: [ + Text( + 'to ${widget.to.toString()}', + style: TextStyle(fontSize: 15), + ) + ], + ), + Expanded( + child: CollapsableEmails( + //change here + thread: widget.messages, //this wont work in serializable + threadHTML: widget.emailContent, + threadIDs: widget.id, + ), + ), + // Expanded( + // child: HtmlElementView( + // key: UniqueKey(), + // viewType: viewTypeId, + // ), + // ), + ], + ), + + // Overlay widgets dynamically based on marker positions + // FutureBuilder>>( + // future: _markerPositionsFuture, + // builder: (context, snapshot) { + // print("FutureBuilder state: ${snapshot.connectionState}"); + // if (snapshot.connectionState == ConnectionState.waiting) { + // return Center(child: CircularProgressIndicator()); + // } + // if (snapshot.hasError) { + // print("Error in FutureBuilder: ${snapshot.error}"); + // return Center(child: Text('error loading markers')); + // } + // if (snapshot.hasData && snapshot.data != null) { + // final markers = snapshot.data!; + // return Stack( + // children: markers.map((marker) { + // return Positioned( + // left: marker['x'].toDouble(), + // top: marker['y'].toDouble(), + // child: GestureDetector( + // onTap: () { + // print('Tapped on ${marker['id']}'); + // }, + // child: Container( + // width: 50, + // height: 50, + // color: Colors.red, + // child: Center( + // child: Text( + // marker['id'], + // style: TextStyle(color: Colors.white), + // ), + // ), + // ), + // ), + // ); + // }).toList(), + // ); + // } + + // return SizedBox.shrink(); // No markers found + // }, + // ), + // Red widget overlay + // Positioned( + // left: 8, // Adjust based on your desired position + // top: 100 + 44 + 5, // Adjust based on your desired position + // child: IgnorePointer( + // ignoring: true, // Ensures the iframe remains interactive + // child: Container( + // color: Colors.red, + // width: 100, + // height: 50, + // child: Center( + // child: Text( + // 'Overlay', + // style: TextStyle(color: Colors.white), + // ), + // ), + // ), + // ), + // ), + ], + )); + } +} -- 2.34.1 From 7a6eea64e110d14da3fa730a38567e062666a845 Mon Sep 17 00:00:00 2001 From: juan Date: Fri, 23 May 2025 16:15:26 -0400 Subject: [PATCH 07/28] cleaning and adjusting for package --- lib/api_service.dart | 246 +------------------------------------------ lib/augment.dart | 7 -- lib/contact.dart | 4 +- lib/email.dart | 2 +- lib/home_page.dart | 63 ++++++----- 5 files changed, 40 insertions(+), 282 deletions(-) diff --git a/lib/api_service.dart b/lib/api_service.dart index b0b92ea..4769377 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -4,19 +4,12 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; -import 'collapsableEmails.dart'; + import 'structs.dart'; -import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; -import 'dart:ui_web' as ui; -import 'augment.dart'; -// import 'dart:html' as html; -// import 'dart:js' as js; -import 'package:web/web.dart' as web; -import 'dart:js_interop' as js; + class ApiService { static String ip = ""; @@ -422,237 +415,4 @@ class ApiService { // return []; // } -} - -class EmailView extends StatefulWidget { - final List emailContent; - final String from; - final String name; - final String to; - final String subject; - final String date; - final String id; - final List messages; - - const EmailView({ - Key? key, - required this.emailContent, - required this.from, - required this.name, - required this.to, - required this.subject, - required this.date, - required this.id, - required this.messages, - }) : super(key: key); - @override - _EmailViewState createState() => _EmailViewState(); -} - -class _EmailViewState extends State { - //html css rendering thing - late Key iframeKey; - late String currentContent; - late String viewTypeId; //make this a list too??? - Future>>? _markerPositionsFuture; - // TextEditingController _jumpController = TextEditingController(); - final hardcodedMarkers = [ - {'id': 'marker1', 'x': 50, 'y': 100}, - {'id': 'marker2', 'x': 150, 'y': 200}, - {'id': 'marker3', 'x': 250, 'y': 300}, - ]; - - @override - void initState() { - super.initState(); - print("thread id? ${widget.id}"); - List currentContent = widget - .emailContent; //html of the email/ actually entire thread, gives me little space to play in between - // i wonder if the other attributes change? because if so i have to add like some zooms in and out of the emails, as in collapse - // _registerViewFactory(currentContent); - } - - // void _registerViewFactory(List currentContent) { // i think this doesnt work anymore - // setState(() { //update to do item per item - // // each item to have itsviewtype ID - // // is this necessarey here?? - - // //could just move to collapsable - - // viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}'; - // final emailHTML = web.document.createElement('div') as web.HTMLDivElement - // ..id = viewTypeId - // ..innerHTML = currentContent[0].toJS; // temporarily index because it has to do all of them - // emailHTML.style - // ..width = '100%' - // ..height = '100%' - // ..overflow = 'auto' - // ..scrollBehavior = 'smooth'; - - // ui.platformViewRegistry.registerViewFactory( - // viewTypeId, - // (int viewId) => emailHTML, - // ); - // }); - // } - - void _scrollToNumber(String spanId) { - AugmentClasses.handleJump(spanId); - } - - // TODO: void _invisibility(String ) //to make purple numbers not visible - - @override - Widget build(BuildContext context) { - // print("thread id ${widget.id}"); - ApiService.currThreadID = widget.id; - return Scaffold( - appBar: AppBar( - title: Text(widget.name), - ), - body: Stack( - children: [ - Column( - children: [ - EmailToolbar( - onJumpToSpan: _scrollToNumber, - onButtonPressed: () => {}, - // AugmentClasses.handleJump(viewTypeId, '1'); - // print("button got pressed?"); - - // _registerViewFactory(r""" - //

Welcome to My Website

- //

This is a simple HTML page.

- //

What is HTML?

- //

HTML (HyperText Markup Language) is the most basic building~ block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).

- //

Here's a simple list:

- //
    - //
  • HTML elements are the building blocks of HTML pages
  • - //
  • HTML uses tags like <tag> to organize and format content
  • - //
  • CSS is used with HTML to style pages
  • - //
- //

Copyright © 2023

- // """); - // print("change"); - // widget.emailContent = r" - - // - ), - Row( - // title of email - children: [ - Text( - widget.subject, - style: TextStyle(fontSize: 30), - ), - ], - ), - Row( - children: [ - Text( - 'from ${widget.name}', - style: TextStyle(fontSize: 18), - ), - Text( - '<${widget.from}>', - style: TextStyle(fontSize: 18), - ), - Spacer(), - Text( - '${widget.date}', - textAlign: TextAlign.right, - ) - ], - ), - // TODO: make a case where if one of these is the user's email it just says me :))))) - Row( - children: [ - Text( - 'to ${widget.to.toString()}', - style: TextStyle(fontSize: 15), - ) - ], - ), - Expanded( - child: CollapsableEmails( - //change here - thread: widget.messages, //this wont work in serializable - threadHTML: widget.emailContent, - threadIDs: widget.id, - ), - ), - // Expanded( - // child: HtmlElementView( - // key: UniqueKey(), - // viewType: viewTypeId, - // ), - // ), - ], - ), - - // Overlay widgets dynamically based on marker positions - // FutureBuilder>>( - // future: _markerPositionsFuture, - // builder: (context, snapshot) { - // print("FutureBuilder state: ${snapshot.connectionState}"); - // if (snapshot.connectionState == ConnectionState.waiting) { - // return Center(child: CircularProgressIndicator()); - // } - // if (snapshot.hasError) { - // print("Error in FutureBuilder: ${snapshot.error}"); - // return Center(child: Text('error loading markers')); - // } - // if (snapshot.hasData && snapshot.data != null) { - // final markers = snapshot.data!; - // return Stack( - // children: markers.map((marker) { - // return Positioned( - // left: marker['x'].toDouble(), - // top: marker['y'].toDouble(), - // child: GestureDetector( - // onTap: () { - // print('Tapped on ${marker['id']}'); - // }, - // child: Container( - // width: 50, - // height: 50, - // color: Colors.red, - // child: Center( - // child: Text( - // marker['id'], - // style: TextStyle(color: Colors.white), - // ), - // ), - // ), - // ), - // ); - // }).toList(), - // ); - // } - - // return SizedBox.shrink(); // No markers found - // }, - // ), - // Red widget overlay - // Positioned( - // left: 8, // Adjust based on your desired position - // top: 100 + 44 + 5, // Adjust based on your desired position - // child: IgnorePointer( - // ignoring: true, // Ensures the iframe remains interactive - // child: Container( - // color: Colors.red, - // width: 100, - // height: 50, - // child: Center( - // child: Text( - // 'Overlay', - // style: TextStyle(color: Colors.white), - // ), - // ), - // ), - // ), - // ), - ], - )); - } -} +} \ No newline at end of file diff --git a/lib/augment.dart b/lib/augment.dart index b957740..509b3b0 100644 --- a/lib/augment.dart +++ b/lib/augment.dart @@ -1,14 +1,7 @@ -// import 'dart:ffi'; - import 'package:crab_ui/api_service.dart'; import 'package:crab_ui/attachmentDownload.dart'; import 'package:crab_ui/structs.dart'; import 'package:flutter/material.dart'; -import 'package:pdfrx/pdfrx.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; -// import 'dart:html' as html; -// import 'dart:js' as js; -import 'package:web/web.dart' as web; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'attachmentWidget.dart'; diff --git a/lib/contact.dart b/lib/contact.dart index 73d60fd..32b4dae 100644 --- a/lib/contact.dart +++ b/lib/contact.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; -import 'package:flutter_html/flutter_html.dart'; +// import 'package:http/http.dart' as http; +// import 'package:flutter_html/flutter_html.dart'; class ContactsPage extends StatefulWidget { const ContactsPage({super.key}); diff --git a/lib/email.dart b/lib/email.dart index 5dbf1a6..ff4d600 100644 --- a/lib/email.dart +++ b/lib/email.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'api_service.dart'; import 'structs.dart'; +import 'emailView.dart'; class EmailListScreen extends StatelessWidget { final List emails; diff --git a/lib/home_page.dart b/lib/home_page.dart index b4878b0..903a8a4 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -2,7 +2,7 @@ import 'package:crab_ui/sonicEmailView.dart'; import 'folder_drawer.dart'; import 'structs.dart'; -import 'package:flutter/widgets.dart'; +// import 'package:flutter/widgets.dart'; import 'api_service.dart'; import 'package:flutter/material.dart'; import 'email.dart'; @@ -266,40 +266,45 @@ class _HomeScreenState extends State with TickerProviderStateMixin { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Container( - width: 800, - height: 40, - child: TextField( - decoration: InputDecoration( - hintText: 'Search...', - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.search), + Flexible( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: 800, + ), + child: SizedBox( + height: 40, + child: TextField( + decoration: InputDecoration( + hintText: 'Search...', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.search), + ), + onSubmitted: (value) { + if (value.isNotEmpty) { + _performSearch(value, _selectedOption); + } + //this is the input box i mentioned + // if (value == '') { + // setState(() { + // querySearches = false; + // }); + // } + // Future> results = apiService + // .sonicSearch('INBOX', 20, 0, value); + // // print(value); + // print(results); + // setState(() { + // querySearches = true; + // }); + }, + ), ), - onSubmitted: (value) { - if (value.isNotEmpty) { - _performSearch(value, _selectedOption); - } - //this is the input box i mentioned - // if (value == '') { - // setState(() { - // querySearches = false; - // }); - // } - // Future> results = apiService - // .sonicSearch('INBOX', 20, 0, value); - // // print(value); - // print(results); - // setState(() { - // querySearches = true; - // }); - }, ), ), SizedBox( - width: 16, + width: 8, ), Container( - width: 80, height: 40, child: ElevatedButton( onPressed: _showOptionsSearchDialog, -- 2.34.1 From 37c7e6a9353c056a0db6766d13f3ced3760b188d Mon Sep 17 00:00:00 2001 From: juan Date: Sat, 24 May 2025 19:56:15 -0400 Subject: [PATCH 08/28] viewspecs, hiding right purple number --- lib/collapsableEmailsWeb.dart | 104 +++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index ee8e4ea..53b2d41 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -27,13 +27,14 @@ class _CollapsableEmailsState extends State { List heightOfViewTypes = []; //the height of each viewtype List emailsInThread = []; bool _isLoaded = false; + static bool _isListenerRegistered = false; @override void initState() { - // TODO: implement initState super.initState(); _registerViewFactory(widget.threadHTML); - _serializableData(widget.threadIDs); + _serializableData(widget.threadIDs); // this + _keyListener(); } void _registerViewFactory(List currentContent) async { @@ -88,45 +89,68 @@ class _CollapsableEmailsState extends State { }); } + void _keyListener() { + if (_isListenerRegistered) return; + _isListenerRegistered = true; + web.window.document.addEventListener( + 'keydown', + ((web.Event event) { + final keyEvent = event as web.KeyboardEvent; + + if (keyEvent.key.toLowerCase() == 'k') { + print('You pressed the "k" key!'); + final leftPurpleNums = web.document.getElementsByClassName("right"); + + for (int i = 0; i < leftPurpleNums.length; i++) { + final currentElement = leftPurpleNums.item(i) as web.HTMLElement; + final opacity = web.window.getComputedStyle(currentElement).opacity; + + currentElement.style.opacity = + (opacity == '0') ? '1.0' : '0.0'; // works mostly + } + } + }).toJS, + ); + } + @override Widget build(BuildContext context) { - return _isLoaded - ?Column(children: [ - Expanded( - child: ListView.builder( - itemCount: widget.thread.length, - itemBuilder: (context, index) { - final isExpanded = - _expandedEmails.contains(index); //check if email is expanded - return Column( - children: [ - ListTile( - title: Text(emailsInThread[index].from), - trailing: Text(emailsInThread[index].date), - onTap: () { - setState(() { - if (isExpanded) { - _expandedEmails.remove(index); - } else { - _expandedEmails.add(index); - } - }); - }, - ), - if (isExpanded) - // if(viewtypeIDs[index] == null || heightOfViewTypes[index] == null) - // const SizedBox(height: 100, child: Center(child: CircularProgressIndicator())), - SizedBox( - height: heightOfViewTypes[index].toDouble(), - child: HtmlElementView( - key: UniqueKey(), viewType: viewtypeIDs[index]), - ), - Divider(), - ], - ); - }, - ), - ) - ]): const Center(child:CircularProgressIndicator()); + return _isLoaded + ? Column(children: [ + Expanded( + child: ListView.builder( + itemCount: widget.thread.length, + itemBuilder: (context, index) { + final isExpanded = _expandedEmails + .contains(index); //check if email is expanded + return Column( + children: [ + ListTile( + title: Text(emailsInThread[index].from), + trailing: Text(emailsInThread[index].date), + onTap: () { + setState(() { + if (isExpanded) { + _expandedEmails.remove(index); + } else { + _expandedEmails.add(index); + } + }); + }, + ), + if (isExpanded) + SizedBox( + height: heightOfViewTypes[index].toDouble(), + child: HtmlElementView( + key: UniqueKey(), viewType: viewtypeIDs[index]), + ), + Divider(), + ], + ); + }, + ), + ) + ]) + : const Center(child: CircularProgressIndicator()); } } -- 2.34.1 From 13004c6d499974ff97e4862cc299f0680b67b201 Mon Sep 17 00:00:00 2001 From: juan Date: Mon, 26 May 2025 14:29:37 -0400 Subject: [PATCH 09/28] LEFT RIGHT purple numbers on/off --- lib/collapsableEmailsWeb.dart | 57 ++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index 53b2d41..af80fbf 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -28,6 +28,9 @@ class _CollapsableEmailsState extends State { List emailsInThread = []; bool _isLoaded = false; static bool _isListenerRegistered = false; + static bool left = true; + static bool right = true; + web.EventListener? _listener; @override void initState() { @@ -36,6 +39,14 @@ class _CollapsableEmailsState extends State { _serializableData(widget.threadIDs); // this _keyListener(); } + // @override + // void dispose() { + // if (_listener != null) { + // web.window.document.removeEventListener('keydown', _listener!); + // } + // super.dispose(); + // } + void _registerViewFactory(List currentContent) async { // setState(() { //update to do item per item @@ -89,30 +100,40 @@ class _CollapsableEmailsState extends State { }); } + void handleKeyDown(web.Event event) { + final keyEvent = event as web.KeyboardEvent; + + if (keyEvent.key == 'G') { + print('You pressed the "G" key!'); + final rightPurpleNums = web.document.getElementsByClassName("right"); + _CollapsableEmailsState.right = !_CollapsableEmailsState.right; + final newOpacity = _CollapsableEmailsState.right ? '1.0' : '0.0'; + for (int i = 0; i < rightPurpleNums.length; i++) { + final currentElement = rightPurpleNums.item(i) as web.HTMLElement; + currentElement.style.opacity = newOpacity; + } + } else if (keyEvent.key == 'H') { + print('You pressed the "H" key!'); + final leftPurpleNums = web.document.getElementsByClassName("left"); + _CollapsableEmailsState.left = !_CollapsableEmailsState.left; + final newOpacity = _CollapsableEmailsState.left ? '1.0' : '0.0'; + for (int i = 0; i < leftPurpleNums.length; i++) { + final currentElement = leftPurpleNums.item(i) as web.HTMLElement; + currentElement.style.opacity = newOpacity; + } + } + } + void _keyListener() { if (_isListenerRegistered) return; _isListenerRegistered = true; - web.window.document.addEventListener( - 'keydown', - ((web.Event event) { - final keyEvent = event as web.KeyboardEvent; - if (keyEvent.key.toLowerCase() == 'k') { - print('You pressed the "k" key!'); - final leftPurpleNums = web.document.getElementsByClassName("right"); - - for (int i = 0; i < leftPurpleNums.length; i++) { - final currentElement = leftPurpleNums.item(i) as web.HTMLElement; - final opacity = web.window.getComputedStyle(currentElement).opacity; - - currentElement.style.opacity = - (opacity == '0') ? '1.0' : '0.0'; // works mostly - } - } - }).toJS, - ); + // Convert the top-level function to JS-compatible + _listener = handleKeyDown.toJS; + web.window.document.addEventListener('keydown', _listener!); } + @override Widget build(BuildContext context) { return _isLoaded -- 2.34.1 From b4f5abb7e1fe20b562605cd433e51bd038a2613c Mon Sep 17 00:00:00 2001 From: juan Date: Mon, 26 May 2025 14:38:24 -0400 Subject: [PATCH 10/28] added on/off purple numbers 'n', 'm' --- lib/collapsableEmailsWeb.dart | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index af80fbf..bb7e825 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -47,7 +47,6 @@ class _CollapsableEmailsState extends State { // super.dispose(); // } - void _registerViewFactory(List currentContent) async { // setState(() { //update to do item per item // each item to have itsviewtype ID @@ -121,6 +120,26 @@ class _CollapsableEmailsState extends State { final currentElement = leftPurpleNums.item(i) as web.HTMLElement; currentElement.style.opacity = newOpacity; } + } else if (keyEvent.key == 'm') { + print("you pressed 'm'"); + final purpleNums = web.document.getElementsByClassName("purplenumber"); + _CollapsableEmailsState.left = true; + _CollapsableEmailsState.right = true; + + for (int i = 0; i < purpleNums.length; i++) { + final currentElement = purpleNums.item(i) as web.HTMLElement; + currentElement.style.opacity = '1.0'; + } + } else if (keyEvent.key == 'n') { + print("you pressed 'n'"); + final purpleNums = web.document.getElementsByClassName("purplenumber"); + _CollapsableEmailsState.left = false; + _CollapsableEmailsState.right = false; + + for (int i = 0; i < purpleNums.length; i++) { + final currentElement = purpleNums.item(i) as web.HTMLElement; + currentElement.style.opacity = '0.0'; + } } } @@ -133,7 +152,6 @@ class _CollapsableEmailsState extends State { web.window.document.addEventListener('keydown', _listener!); } - @override Widget build(BuildContext context) { return _isLoaded -- 2.34.1 From df6329397741a5d7e229c9ff9b1909ef2e729fe6 Mon Sep 17 00:00:00 2001 From: juan Date: Tue, 27 May 2025 23:38:37 -0400 Subject: [PATCH 11/28] tree data structure for the zooms of augment --- lib/collapsableEmailsWeb.dart | 60 +++++++++++++++++++++++++++++++++++ lib/structs.dart | 12 +++++-- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index bb7e825..966a10e 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -1,4 +1,5 @@ import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'package:web/web.dart' as web; import 'package:flutter/material.dart'; import 'dart:ui_web' as ui; @@ -31,6 +32,8 @@ class _CollapsableEmailsState extends State { static bool left = true; static bool right = true; web.EventListener? _listener; + List hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"]; + List tagsCollected = []; @override void initState() { @@ -140,6 +143,9 @@ class _CollapsableEmailsState extends State { final currentElement = purpleNums.item(i) as web.HTMLElement; currentElement.style.opacity = '0.0'; } + } else if (keyEvent.key == 'w') { + print("you pressed 'w'"); + getTopLevel(); } } @@ -152,6 +158,60 @@ class _CollapsableEmailsState extends State { web.window.document.addEventListener('keydown', _listener!); } + void getTopLevel() { + print("started top"); + int highest = 0; + AugmentTree zoomTreeRoot = AugmentTree(); + // zoomTreeRoot.data = emailsHTML[0]; // whole thing + + while (highest < hirarchy.length - 1) { + var highestElement = web.document.querySelectorAll(hirarchy[highest]); + print("nodelist $highestElement"); + + if (highestElement.isNull || highestElement.length == 0) { + //from h1, h2, h3, ..., p. + highest += 1; + print(hirarchy[highest]); + } else { + AugmentTree newLevel = AugmentTree(); // list of children of each level + for (int i = 0; i < highestElement.length; i++) { + print(highestElement.item(i)?.textContent); + + tagsCollected + .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); + newLevel.children + .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); + } + // traverse to last node and add new level to it + // next + if (zoomTreeRoot.node == null) { + zoomTreeRoot.node = newLevel; + } else { + AugmentTree temp = zoomTreeRoot; + while (true) { + //get to the last and assign node = last node + if (temp.node != null) { + temp = temp.node!; + } else { + temp.node = newLevel; + break; + } + } + } + + highest += 1; + } + } + print("out safely"); + print(tagsCollected); //instead of a list make a tree + print(zoomTreeRoot.children); + print(zoomTreeRoot.node?.children); + print(zoomTreeRoot.node?.node?.children); + print(zoomTreeRoot.node?.node?.node?.children); + + + } + @override Widget build(BuildContext context) { return _isLoaded diff --git a/lib/structs.dart b/lib/structs.dart index c7e9da4..4672792 100644 --- a/lib/structs.dart +++ b/lib/structs.dart @@ -127,7 +127,8 @@ class AttachmentInfoList extends Iterable { AttachmentInfoList(this._attachments); factory AttachmentInfoList.fromJsonList(List> jsonList) { - return AttachmentInfoList(jsonList.map((json) => AttachmentInfo.fromJson(json)).toList()); + return AttachmentInfoList( + jsonList.map((json) => AttachmentInfo.fromJson(json)).toList()); } @override @@ -143,6 +144,13 @@ class AttachmentResponse { AttachmentResponse({required this.name, required this.data}); factory AttachmentResponse.fromJson(Map json) { - return AttachmentResponse(name: json["name"], data: Uint8List.fromList(List.from(json["data"]))); + return AttachmentResponse( + name: json["name"], + data: Uint8List.fromList(List.from(json["data"]))); } } + +class AugmentTree { + List children = []; + AugmentTree? node; +} -- 2.34.1 From a1fde46aec454cc57bed49c940415da5fe7d792d Mon Sep 17 00:00:00 2001 From: juan Date: Mon, 9 Jun 2025 16:37:38 -0400 Subject: [PATCH 12/28] data structure works --- lib/collapsableEmailsWeb.dart | 430 +++++++++++++++++++++++++++------- 1 file changed, 340 insertions(+), 90 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index 966a10e..b748daf 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -1,3 +1,4 @@ +import 'dart:collection'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; import 'package:web/web.dart' as web; @@ -5,6 +6,9 @@ import 'package:flutter/material.dart'; import 'dart:ui_web' as ui; import 'api_service.dart'; import 'structs.dart'; +import 'package:html2md/html2md.dart' as html2md; +import 'package:markdown_widget/markdown_widget.dart'; +import 'package:markdown/markdown.dart' as md; class CollapsableEmails extends StatefulWidget { final List thread; // email id's in the form xyz@gmail.com @@ -33,22 +37,234 @@ class _CollapsableEmailsState extends State { static bool right = true; web.EventListener? _listener; List hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"]; + Map hirarchyDict = { + "h1": 1, + "h2": 2, + "h3": 3, + "h4": 4, + "h5": 6, + "h6": 7, + "p": 8, + "ul": 8, + "li": 8, + }; + List tagsCollected = []; + String markdown = ''; + List> sentinel = []; + int level = 0; + AugmentTree root = AugmentTree(); @override void initState() { super.initState(); - _registerViewFactory(widget.threadHTML); - _serializableData(widget.threadIDs); // this + _markdownConverter(); + // _registerViewFactory(widget.threadHTML); + // _serializableData(widget.threadIDs); // this + _markdown2Tree(markdown); _keyListener(); + _buildForZooms(level); + } + + @override + void dispose() { + if (_listener != null) { + web.window.document.removeEventListener('keydown', _listener!); + _listener = null; + _isListenerRegistered = false; + } + super.dispose(); + } + + void _markdownConverter() async { + markdown = html2md.convert(widget.threadHTML[0]); + } + + void _add2Tree(AugmentTree tree, md.Element node2add) { + // adds node to its corresponding place + AugmentTree newNode = AugmentTree(); + newNode.setData(node2add.textContent); + newNode.ogTag = node2add.tag; + // cases, + //1. a node that comes is lower than the root.children last, if so it goes beneath it + if (tree.children.isEmpty) { + // new level to be created when totally empty + print('is empty'); + tree.children.add(newNode); + newNode.parent = tree; + } else if (tree.children.isNotEmpty && + tree.children.last.ogTag.isNotEmpty) { + if ((hirarchyDict[node2add.tag] ?? + -1) < // e.g. new node is h1 and old is h2, heapify + (hirarchyDict[tree.children.last.ogTag] ?? -1)) { + //have to figure out the borthers + //assuming it all goes right + if ((hirarchyDict[node2add.tag] ?? -1) == -1 || + (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) { + print( + 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} < ${hirarchyDict[tree.children.last.ogTag] ?? -1}'); + return; + } else if (tree.children.last.parent == null) { + // becomes the new top level + for (AugmentTree brother in tree.children) { + brother.parent = newNode; + } + tree.children = [newNode]; + } else { + tree.children.add(newNode); + } + // } else{ // so the node should go high + // _add2Tree(tree.children.last, node2add); + // } + + //maybe? + // } else { + // } + } else if ((hirarchyDict[node2add.tag] ?? + -1) > // go down e.g. new node is h3 and old is h2 or something + (hirarchyDict[tree.children.last.ogTag] ?? -1)) { + if ((hirarchyDict[node2add.tag] ?? -1) == -1 || + (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) { + print( + 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} > ${hirarchyDict[tree.children.last.ogTag] ?? -1}'); + print("-1 ${tree.children.last.ogTag}"); + return; + } + + print("> ${node2add.tag}"); + + _add2Tree(tree.children.last, node2add); + } else if ((hirarchyDict[node2add.tag] ?? -1) == + (hirarchyDict[tree.children.last.ogTag] ?? -1)) { + print("equals??"); + tree.children.add(newNode); + newNode.parent = tree; + } + } + } + + void _markdown2Tree(String text) { + print("started markdown2tree"); + int highest = 0; + AugmentTree zoomTreeRoot = AugmentTree(); + final List nakedList = md.Document().parseLines(text.split('\n')); + List pList = []; + List h1List = []; + List h2List = []; + List h3List = []; + List h4List = []; + List h5List = []; + List h6List = []; + + for (var node in nakedList) { + //maybe do an add function, but isn't this it? + if (node is md.Element) { + // print(node.textContent); + AugmentTree temp = AugmentTree(); + temp.data = node.textContent; + temp.ogTag = node.tag; + if (node.tag == 'h1') { + h1List.add(node.textContent); + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h2') { + // i dont add any since i dont have it, maybe the function makes sense + h2List.add(node.textContent); + } else if (node.tag == 'h3') { + h3List.add(node.textContent); + root.children.add(temp); + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h4') { + h4List.add(node.textContent); //this broke it + _add2Tree(zoomTreeRoot, node); // change to temp + if (root.children.isNotEmpty) { + root.children.last.children.add(temp); + } + } else if (node.tag == 'h5') { + h5List.add(node.textContent); + print(node.textContent); + _add2Tree(zoomTreeRoot, node); + if (root.children.last.children.isNotEmpty) { + print("h5 index ${root.children.last.children.length}"); + root.children.last.children.last.children.add(temp); + print( + "h5 after index length ${root.children.last.children.last.children.length}"); + } + } else if (node.tag == 'h6') { + h6List.add(node.textContent); + if (root.children.last.children.isNotEmpty) { + print( + "h6 index ${root.children.last.children.last.children.length}"); + root.children.last.children.last.children.add(temp); + print(node.textContent); + _add2Tree(zoomTreeRoot, node); + } + // root.children.last.children.last.children.add(temp); + } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') { + pList.add(node.textContent); + _add2Tree(zoomTreeRoot, node); // fix this + + if (root.children.isEmpty) { + root.children.add(temp); + } else if (root.children.last.children.isNotEmpty) { + //perhaps recursive + root.children.last.children.last.children.add(temp); + } + } + } + } + + this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList]; + sentinel.removeWhere((hList) => hList.isEmpty); + + print('algorithm adding: '); + print("first layer: ${zoomTreeRoot.children}"); + print("first node: ${zoomTreeRoot.children[0].data}"); //good + print("second layer: ${zoomTreeRoot.children.last.children}"); //good + print( + "first node: ${zoomTreeRoot.children.last.children.first.data}"); //good + for (int n = 1; n < zoomTreeRoot.children.last.children.length - 1; n++) { + //good + print(zoomTreeRoot.children.last.children[n].data); + } + print("last node: ${zoomTreeRoot.children.last.children.last.data}"); //good + print("third layer"); + for (int thirdLayer = 0; + thirdLayer < zoomTreeRoot.children.last.children.length; + thirdLayer++) { + print(zoomTreeRoot.children.last.children[thirdLayer].children); + } + print("third layer contents first"); //not sure + + print(zoomTreeRoot.children.last.children[5].children[0].data); //good + for (int contentsOf4 = 1; + contentsOf4 < zoomTreeRoot.children.last.children[5].children.length; + contentsOf4++) { + print(zoomTreeRoot.children.last.children[5].children[contentsOf4].data); + } + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + + void handleKeyDownMD(web.Event event) async { + final keyEvent = event as web.KeyboardEvent; + + if (!mounted) return; + if (keyEvent.key == 'a') { + print("key a"); + setState(() { + level = (level - 1).clamp(0, sentinel.length - 1); + }); + // _buildForZooms(level + 1); //probably need a level? + } else if (keyEvent.key == "b") { + print("b"); + setState(() { + level = (level + 1).clamp(0, sentinel.length - 1); + }); + // _buildForZooms(level - 1); //probably need a level? + } } - // @override - // void dispose() { - // if (_listener != null) { - // web.window.document.removeEventListener('keydown', _listener!); - // } - // super.dispose(); - // } void _registerViewFactory(List currentContent) async { // setState(() { //update to do item per item @@ -102,7 +318,7 @@ class _CollapsableEmailsState extends State { }); } - void handleKeyDown(web.Event event) { + void handleKeyDownHTML(web.Event event) { final keyEvent = event as web.KeyboardEvent; if (keyEvent.key == 'G') { @@ -145,7 +361,7 @@ class _CollapsableEmailsState extends State { } } else if (keyEvent.key == 'w') { print("you pressed 'w'"); - getTopLevel(); + // getTopLevel(); } } @@ -154,62 +370,91 @@ class _CollapsableEmailsState extends State { _isListenerRegistered = true; // Convert the top-level function to JS-compatible - _listener = handleKeyDown.toJS; + // _listener = handleKeyDownHTML.toJS; + _listener = handleKeyDownMD.toJS; + web.window.document.addEventListener('keydown', _listener!); } - void getTopLevel() { - print("started top"); - int highest = 0; - AugmentTree zoomTreeRoot = AugmentTree(); - // zoomTreeRoot.data = emailsHTML[0]; // whole thing + // void getTopLevel() { + // print("started top"); + // int highest = 0; + // AugmentTree zoomTreeRoot = AugmentTree(); + // // zoomTreeRoot.data = emailsHTML[0]; // whole thing - while (highest < hirarchy.length - 1) { - var highestElement = web.document.querySelectorAll(hirarchy[highest]); - print("nodelist $highestElement"); + // while (highest < hirarchy.length - 1) { + // var highestElement = web.document.querySelectorAll(hirarchy[highest]); + // print("nodelist $highestElement"); - if (highestElement.isNull || highestElement.length == 0) { - //from h1, h2, h3, ..., p. - highest += 1; - print(hirarchy[highest]); - } else { - AugmentTree newLevel = AugmentTree(); // list of children of each level - for (int i = 0; i < highestElement.length; i++) { - print(highestElement.item(i)?.textContent); + // if (highestElement.isNull || highestElement.length == 0) { + // //from h1, h2, h3, ..., p. + // highest += 1; + // print(hirarchy[highest]); + // } else { + // AugmentTree newLevel = AugmentTree(); // list of children of each level + // for (int i = 0; i < highestElement.length; i++) { + // print(highestElement.item(i)?.textContent); - tagsCollected - .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); - newLevel.children - .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); - } - // traverse to last node and add new level to it - // next - if (zoomTreeRoot.node == null) { - zoomTreeRoot.node = newLevel; - } else { - AugmentTree temp = zoomTreeRoot; - while (true) { - //get to the last and assign node = last node - if (temp.node != null) { - temp = temp.node!; - } else { - temp.node = newLevel; - break; - } - } - } + // tagsCollected + // .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); + // newLevel.children + // .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); + // } + // // traverse to last node and add new level to it + // // next + // if (zoomTreeRoot.node == null) { + // zoomTreeRoot.node = newLevel; + // } else { + // AugmentTree temp = zoomTreeRoot; + // while (true) { + // //get to the last and assign node = last node + // if (temp.node != null) { + // temp = temp.node!; + // } else { + // temp.node = newLevel; + // break; + // } + // } + // } - highest += 1; - } + // highest += 1; + // } + // } + // print("out safely"); + // print(tagsCollected); //instead of a list make a tree + // print(zoomTreeRoot.children); + // print(zoomTreeRoot.node?.children); + // print(zoomTreeRoot.node?.node?.children); + // print(zoomTreeRoot.node?.node?.node?.children); + // } + + Widget _buildForZooms(int lvl) { + this.level = lvl; + if (lvl < 0 || lvl >= sentinel.length) { + return Center(child: Text("No content at level $lvl")); } - print("out safely"); - print(tagsCollected); //instead of a list make a tree - print(zoomTreeRoot.children); - print(zoomTreeRoot.node?.children); - print(zoomTreeRoot.node?.node?.children); - print(zoomTreeRoot.node?.node?.node?.children); - - + return ListView.builder( + itemCount: this.sentinel[level].length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), + child: Material( + elevation: 1, + borderRadius: BorderRadius.circular(12), + color: Theme.of(context).colorScheme.surface, + surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: MarkdownBlock( + data: sentinel[level][index], // one string of markdown + config: MarkdownConfig + .darkConfig, // or lightConfig depending on theme + ), + ), + ), + ); + }, + ); } @override @@ -217,38 +462,43 @@ class _CollapsableEmailsState extends State { return _isLoaded ? Column(children: [ Expanded( - child: ListView.builder( - itemCount: widget.thread.length, - itemBuilder: (context, index) { - final isExpanded = _expandedEmails - .contains(index); //check if email is expanded - return Column( - children: [ - ListTile( - title: Text(emailsInThread[index].from), - trailing: Text(emailsInThread[index].date), - onTap: () { - setState(() { - if (isExpanded) { - _expandedEmails.remove(index); - } else { - _expandedEmails.add(index); - } - }); - }, - ), - if (isExpanded) - SizedBox( - height: heightOfViewTypes[index].toDouble(), - child: HtmlElementView( - key: UniqueKey(), viewType: viewtypeIDs[index]), - ), - Divider(), - ], - ); - }, - ), + // child: MarkdownWidget(data: markdown), //hmmm + child: _buildForZooms(level), ) + + // Expanded( + // child: ListView.builder( + // itemCount: widget.thread.length, + // itemBuilder: (context, index) { + // final isExpanded = _expandedEmails + // .contains(index); //check if email is expanded + // return Column( + // children: [ + // ListTile( + // title: Text(emailsInThread[index].from), + // trailing: Text(emailsInThread[index].date), + // onTap: () { + // setState(() { + // if (isExpanded) { + // _expandedEmails.remove(index); + // } else { + // _expandedEmails.add(index); + // } + // }); + // }, + // ), + // if (isExpanded) + // SizedBox( + // height: heightOfViewTypes[index].toDouble(), + // child: HtmlElementView( + // key: UniqueKey(), viewType: viewtypeIDs[index]), + // ), + // Divider(), + // ], + // ); + // }, + // ), + // ) ]) : const Center(child: CircularProgressIndicator()); } -- 2.34.1 From de3d7f8bfc9a559e5f1565f842789d216b17c045 Mon Sep 17 00:00:00 2001 From: juan Date: Tue, 10 Jun 2025 14:58:34 -0400 Subject: [PATCH 13/28] works, and disabled zoom out button at root, and zoom in, when nodes don't have any children --- lib/collapsableEmailsWeb.dart | 354 +++++++++++++++------------------- 1 file changed, 157 insertions(+), 197 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index b748daf..4dca67b 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -53,17 +53,20 @@ class _CollapsableEmailsState extends State { String markdown = ''; List> sentinel = []; int level = 0; - AugmentTree root = AugmentTree(); + AugmentTree zoomTreeRoot = AugmentTree(); + late AugmentTree currentZoomNode; + bool zoomOut = false; + bool zoomIn = true; @override void initState() { super.initState(); _markdownConverter(); // _registerViewFactory(widget.threadHTML); - // _serializableData(widget.threadIDs); // this + _serializableData(widget.threadIDs); // this _markdown2Tree(markdown); _keyListener(); - _buildForZooms(level); + _buildForZooms(); } @override @@ -111,6 +114,7 @@ class _CollapsableEmailsState extends State { } tree.children = [newNode]; } else { + newNode.parent = tree; tree.children.add(newNode); } // } else{ // so the node should go high @@ -131,12 +135,12 @@ class _CollapsableEmailsState extends State { return; } - print("> ${node2add.tag}"); + // print("> ${node2add.tag}"); _add2Tree(tree.children.last, node2add); } else if ((hirarchyDict[node2add.tag] ?? -1) == (hirarchyDict[tree.children.last.ogTag] ?? -1)) { - print("equals??"); + // print("equals??"); tree.children.add(newNode); newNode.parent = tree; } @@ -145,8 +149,6 @@ class _CollapsableEmailsState extends State { void _markdown2Tree(String text) { print("started markdown2tree"); - int highest = 0; - AugmentTree zoomTreeRoot = AugmentTree(); final List nakedList = md.Document().parseLines(text.split('\n')); List pList = []; List h1List = []; @@ -169,46 +171,23 @@ class _CollapsableEmailsState extends State { } else if (node.tag == 'h2') { // i dont add any since i dont have it, maybe the function makes sense h2List.add(node.textContent); + _add2Tree(zoomTreeRoot, node); // fix this } else if (node.tag == 'h3') { h3List.add(node.textContent); - root.children.add(temp); _add2Tree(zoomTreeRoot, node); } else if (node.tag == 'h4') { h4List.add(node.textContent); //this broke it _add2Tree(zoomTreeRoot, node); // change to temp - if (root.children.isNotEmpty) { - root.children.last.children.add(temp); - } } else if (node.tag == 'h5') { h5List.add(node.textContent); print(node.textContent); _add2Tree(zoomTreeRoot, node); - if (root.children.last.children.isNotEmpty) { - print("h5 index ${root.children.last.children.length}"); - root.children.last.children.last.children.add(temp); - print( - "h5 after index length ${root.children.last.children.last.children.length}"); - } } else if (node.tag == 'h6') { h6List.add(node.textContent); - if (root.children.last.children.isNotEmpty) { - print( - "h6 index ${root.children.last.children.last.children.length}"); - root.children.last.children.last.children.add(temp); - print(node.textContent); - _add2Tree(zoomTreeRoot, node); - } - // root.children.last.children.last.children.add(temp); + _add2Tree(zoomTreeRoot, node); // fix this } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') { pList.add(node.textContent); _add2Tree(zoomTreeRoot, node); // fix this - - if (root.children.isEmpty) { - root.children.add(temp); - } else if (root.children.last.children.isNotEmpty) { - //perhaps recursive - root.children.last.children.last.children.add(temp); - } } } } @@ -216,31 +195,32 @@ class _CollapsableEmailsState extends State { this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList]; sentinel.removeWhere((hList) => hList.isEmpty); - print('algorithm adding: '); - print("first layer: ${zoomTreeRoot.children}"); - print("first node: ${zoomTreeRoot.children[0].data}"); //good - print("second layer: ${zoomTreeRoot.children.last.children}"); //good - print( - "first node: ${zoomTreeRoot.children.last.children.first.data}"); //good - for (int n = 1; n < zoomTreeRoot.children.last.children.length - 1; n++) { - //good - print(zoomTreeRoot.children.last.children[n].data); - } - print("last node: ${zoomTreeRoot.children.last.children.last.data}"); //good - print("third layer"); - for (int thirdLayer = 0; - thirdLayer < zoomTreeRoot.children.last.children.length; - thirdLayer++) { - print(zoomTreeRoot.children.last.children[thirdLayer].children); - } - print("third layer contents first"); //not sure + // print('algorithm adding: '); + // print("first layer: ${zoomTreeRoot.children}"); + // print("first node: ${zoomTreeRoot.children[0].data}"); //good + // print("second layer: ${zoomTreeRoot.children.last.children}"); //good + // print( + // "first node: ${zoomTreeRoot.children.last.children.first.data}"); //good + // for (int n = 1; n < zoomTreeRoot.children.last.children.length - 1; n++) { + // //good + // print(zoomTreeRoot.children.last.children[n].data); + // } + // print("last node: ${zoomTreeRoot.children.last.children.last.data}"); //good + // print("third layer"); + // for (int thirdLayer = 0; + // thirdLayer < zoomTreeRoot.children.last.children.length; + // thirdLayer++) { + // print(zoomTreeRoot.children.last.children[thirdLayer].children); + // } + // print("third layer contents first"); //not sure - print(zoomTreeRoot.children.last.children[5].children[0].data); //good - for (int contentsOf4 = 1; - contentsOf4 < zoomTreeRoot.children.last.children[5].children.length; - contentsOf4++) { - print(zoomTreeRoot.children.last.children[5].children[contentsOf4].data); - } + // print(zoomTreeRoot.children.last.children[5].children[0].data); //good + // for (int contentsOf4 = 1; + // contentsOf4 < zoomTreeRoot.children.last.children[5].children.length; + // contentsOf4++) { + // print(zoomTreeRoot.children.last.children[5].children[contentsOf4].data); + // } + currentZoomNode = zoomTreeRoot; if (!mounted) return; setState(() { _isLoaded = true; @@ -256,57 +236,67 @@ class _CollapsableEmailsState extends State { setState(() { level = (level - 1).clamp(0, sentinel.length - 1); }); - // _buildForZooms(level + 1); //probably need a level? } else if (keyEvent.key == "b") { print("b"); setState(() { level = (level + 1).clamp(0, sentinel.length - 1); }); - // _buildForZooms(level - 1); //probably need a level? } } - void _registerViewFactory(List currentContent) async { - // setState(() { //update to do item per item - // each item to have itsviewtype ID - // is this necessarey here?? - - //could just move to collapsable - - for (var emailHTML in widget.threadHTML) { - String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}'; - - final ghost = web.document.createElement('div') as web.HTMLDivElement - ..style.visibility = 'hidden' - ..style.position = 'absolute' - ..style.width = '100%' - ..style.overflow = 'auto' - ..innerHTML = emailHTML - .toJS; // temporarily index because it has to do all of them - web.document.body?.append(ghost); - await Future.delayed(Duration(milliseconds: 10)); - - final heightOfEmail = ghost.scrollHeight; - ghost.remove(); - - final HTMLsnippet = web.document.createElement('div') - as web.HTMLDivElement - ..id = viewTypeId - ..innerHTML = emailHTML - .toJS; // temporarily index because it has to do all of them - HTMLsnippet.style - ..width = '100%' - ..height = '${heightOfEmail}px' - ..overflow = 'auto' - ..scrollBehavior = 'smooth'; - - ui.platformViewRegistry.registerViewFactory( - viewTypeId, - (int viewId) => HTMLsnippet, - ); - viewtypeIDs.add(viewTypeId); - heightOfViewTypes.add(heightOfEmail); + void _goToChildren(int index) async { + final target = currentZoomNode.children[index]; + if (target.children.isNotEmpty) { + setState(() { + currentZoomNode = target; + }); + } else { + print("This child has no further children."); } + + // if (currentZoomNode.children.isNotEmpty) { + // setState(() { + // zoomIn = true; + // zoomOut = true; + // currentZoomNode = currentZoomNode.children[index]; + // if (currentZoomNode.children[index].children.isEmpty) { + // print('disable in'); + // setState(() { + // zoomIn = false; + // }); + // } + // }); + // } else { + // print("disable zoom down"); + // setState(() { + // zoomIn = false; + // }); + // } + } + + void _goToParent() async { + if (currentZoomNode.parent != null) { + setState(() { + currentZoomNode = currentZoomNode.parent!; + }); + } else { + print("Already at root."); + } + + // print("parent ${currentZoomNode.parent}"); + // print("parent ${currentZoomNode.parent!.parent}"); + // if (currentZoomNode.parent != null) { + // setState(() { + // currentZoomNode = currentZoomNode.parent!; + // if (currentZoomNode.parent == null) { + // setState(() { + // zoomOut = false; + // }); + // } + // }); + // } else if (currentZoomNode.parent == null || + // currentZoomNode.parent!.parent == null) { + // print("disable zoom up"); } void _serializableData(String threadID) async { @@ -361,7 +351,6 @@ class _CollapsableEmailsState extends State { } } else if (keyEvent.key == 'w') { print("you pressed 'w'"); - // getTopLevel(); } } @@ -376,79 +365,56 @@ class _CollapsableEmailsState extends State { web.window.document.addEventListener('keydown', _listener!); } - // void getTopLevel() { - // print("started top"); - // int highest = 0; - // AugmentTree zoomTreeRoot = AugmentTree(); - // // zoomTreeRoot.data = emailsHTML[0]; // whole thing - - // while (highest < hirarchy.length - 1) { - // var highestElement = web.document.querySelectorAll(hirarchy[highest]); - // print("nodelist $highestElement"); - - // if (highestElement.isNull || highestElement.length == 0) { - // //from h1, h2, h3, ..., p. - // highest += 1; - // print(hirarchy[highest]); - // } else { - // AugmentTree newLevel = AugmentTree(); // list of children of each level - // for (int i = 0; i < highestElement.length; i++) { - // print(highestElement.item(i)?.textContent); - - // tagsCollected - // .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); - // newLevel.children - // .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); - // } - // // traverse to last node and add new level to it - // // next - // if (zoomTreeRoot.node == null) { - // zoomTreeRoot.node = newLevel; - // } else { - // AugmentTree temp = zoomTreeRoot; - // while (true) { - // //get to the last and assign node = last node - // if (temp.node != null) { - // temp = temp.node!; - // } else { - // temp.node = newLevel; - // break; - // } - // } - // } - - // highest += 1; - // } - // } - // print("out safely"); - // print(tagsCollected); //instead of a list make a tree - // print(zoomTreeRoot.children); - // print(zoomTreeRoot.node?.children); - // print(zoomTreeRoot.node?.node?.children); - // print(zoomTreeRoot.node?.node?.node?.children); - // } - - Widget _buildForZooms(int lvl) { - this.level = lvl; - if (lvl < 0 || lvl >= sentinel.length) { - return Center(child: Text("No content at level $lvl")); + Widget _buildForZooms({Key? key}) { + if (!_isLoaded) { + return const Center(child: CircularProgressIndicator()); // loading screen } + final canZoomOut = currentZoomNode.parent != null; + return ListView.builder( - itemCount: this.sentinel[level].length, + key: key, + itemCount: currentZoomNode.children.length, itemBuilder: (context, index) { + final childNode = currentZoomNode.children[index]; + final canZoomIn = childNode.children.isNotEmpty; + return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), child: Material( elevation: 1, borderRadius: BorderRadius.circular(12), color: Theme.of(context).colorScheme.surface, - surfaceTintColor: Theme.of(context).colorScheme.surfaceTint, + surfaceTintColor: Theme.of(context).colorScheme.surfaceBright, child: Padding( padding: const EdgeInsets.all(16.0), - child: MarkdownBlock( - data: sentinel[level][index], // one string of markdown - config: MarkdownConfig - .darkConfig, // or lightConfig depending on theme + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: 4.0, + children: [ + OutlinedButton( + onPressed: + canZoomOut ? () => _goToParent() : null, + child: Icon(Icons.north_west_sharp), + ), + OutlinedButton( + onPressed: + canZoomIn ? () => _goToChildren(index) : null, + child: Icon(Icons.south_east_sharp), + ), + ], + ), + SizedBox(width: 12.0), + Expanded( + child: MarkdownBlock( + data: currentZoomNode + .children[index].data, // one string of markdown + config: MarkdownConfig + .darkConfig, // or lightConfig depending on theme + ), + ), + ], ), ), ), @@ -462,43 +428,37 @@ class _CollapsableEmailsState extends State { return _isLoaded ? Column(children: [ Expanded( - // child: MarkdownWidget(data: markdown), //hmmm - child: _buildForZooms(level), + child: ListView.builder( + itemCount: emailsInThread.length, + itemBuilder: (context, index) { + final isExpanded = _expandedEmails + .contains(index); //check if email is expanded + return Column( + children: [ + ListTile( + title: Text(emailsInThread[index].from), + trailing: Text(emailsInThread[index].date), + onTap: () { + setState(() { + if (isExpanded) { + _expandedEmails.remove(index); + } else { + _expandedEmails.add(index); + } + }); + }, + ), + if (isExpanded) + SizedBox( + height: 800, + child: + _buildForZooms(key: ValueKey(currentZoomNode))), + Divider(), + ], + ); + }, + ), ) - - // Expanded( - // child: ListView.builder( - // itemCount: widget.thread.length, - // itemBuilder: (context, index) { - // final isExpanded = _expandedEmails - // .contains(index); //check if email is expanded - // return Column( - // children: [ - // ListTile( - // title: Text(emailsInThread[index].from), - // trailing: Text(emailsInThread[index].date), - // onTap: () { - // setState(() { - // if (isExpanded) { - // _expandedEmails.remove(index); - // } else { - // _expandedEmails.add(index); - // } - // }); - // }, - // ), - // if (isExpanded) - // SizedBox( - // height: heightOfViewTypes[index].toDouble(), - // child: HtmlElementView( - // key: UniqueKey(), viewType: viewtypeIDs[index]), - // ), - // Divider(), - // ], - // ); - // }, - // ), - // ) ]) : const Center(child: CircularProgressIndicator()); } -- 2.34.1 From 69b5408f73259dbca3aac2578046674307eb25c2 Mon Sep 17 00:00:00 2001 From: juan Date: Tue, 10 Jun 2025 17:26:31 -0400 Subject: [PATCH 14/28] dynamicish sizing and cleanup --- lib/collapsableEmailsWeb.dart | 99 ++++++----------------------------- 1 file changed, 15 insertions(+), 84 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index 4dca67b..b070d6a 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -1,9 +1,6 @@ -import 'dart:collection'; import 'dart:js_interop'; -import 'dart:js_interop_unsafe'; import 'package:web/web.dart' as web; import 'package:flutter/material.dart'; -import 'dart:ui_web' as ui; import 'api_service.dart'; import 'structs.dart'; import 'package:html2md/html2md.dart' as html2md; @@ -117,13 +114,6 @@ class _CollapsableEmailsState extends State { newNode.parent = tree; tree.children.add(newNode); } - // } else{ // so the node should go high - // _add2Tree(tree.children.last, node2add); - // } - - //maybe? - // } else { - // } } else if ((hirarchyDict[node2add.tag] ?? -1) > // go down e.g. new node is h3 and old is h2 or something (hirarchyDict[tree.children.last.ogTag] ?? -1)) { @@ -135,8 +125,6 @@ class _CollapsableEmailsState extends State { return; } - // print("> ${node2add.tag}"); - _add2Tree(tree.children.last, node2add); } else if ((hirarchyDict[node2add.tag] ?? -1) == (hirarchyDict[tree.children.last.ogTag] ?? -1)) { @@ -194,32 +182,6 @@ class _CollapsableEmailsState extends State { this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList]; sentinel.removeWhere((hList) => hList.isEmpty); - - // print('algorithm adding: '); - // print("first layer: ${zoomTreeRoot.children}"); - // print("first node: ${zoomTreeRoot.children[0].data}"); //good - // print("second layer: ${zoomTreeRoot.children.last.children}"); //good - // print( - // "first node: ${zoomTreeRoot.children.last.children.first.data}"); //good - // for (int n = 1; n < zoomTreeRoot.children.last.children.length - 1; n++) { - // //good - // print(zoomTreeRoot.children.last.children[n].data); - // } - // print("last node: ${zoomTreeRoot.children.last.children.last.data}"); //good - // print("third layer"); - // for (int thirdLayer = 0; - // thirdLayer < zoomTreeRoot.children.last.children.length; - // thirdLayer++) { - // print(zoomTreeRoot.children.last.children[thirdLayer].children); - // } - // print("third layer contents first"); //not sure - - // print(zoomTreeRoot.children.last.children[5].children[0].data); //good - // for (int contentsOf4 = 1; - // contentsOf4 < zoomTreeRoot.children.last.children[5].children.length; - // contentsOf4++) { - // print(zoomTreeRoot.children.last.children[5].children[contentsOf4].data); - // } currentZoomNode = zoomTreeRoot; if (!mounted) return; setState(() { @@ -253,50 +215,16 @@ class _CollapsableEmailsState extends State { } else { print("This child has no further children."); } - - // if (currentZoomNode.children.isNotEmpty) { - // setState(() { - // zoomIn = true; - // zoomOut = true; - // currentZoomNode = currentZoomNode.children[index]; - // if (currentZoomNode.children[index].children.isEmpty) { - // print('disable in'); - // setState(() { - // zoomIn = false; - // }); - // } - // }); - // } else { - // print("disable zoom down"); - // setState(() { - // zoomIn = false; - // }); - // } } void _goToParent() async { - if (currentZoomNode.parent != null) { - setState(() { - currentZoomNode = currentZoomNode.parent!; - }); - } else { - print("Already at root."); - } - - // print("parent ${currentZoomNode.parent}"); - // print("parent ${currentZoomNode.parent!.parent}"); - // if (currentZoomNode.parent != null) { - // setState(() { - // currentZoomNode = currentZoomNode.parent!; - // if (currentZoomNode.parent == null) { - // setState(() { - // zoomOut = false; - // }); - // } - // }); - // } else if (currentZoomNode.parent == null || - // currentZoomNode.parent!.parent == null) { - // print("disable zoom up"); + if (currentZoomNode.parent != null) { + setState(() { + currentZoomNode = currentZoomNode.parent!; + }); + } else { + print("Already at root."); + } } void _serializableData(String threadID) async { @@ -449,16 +377,19 @@ class _CollapsableEmailsState extends State { }, ), if (isExpanded) - SizedBox( - height: 800, - child: - _buildForZooms(key: ValueKey(currentZoomNode))), + ConstrainedBox( + constraints: BoxConstraints( + minHeight: 100, + maxHeight: MediaQuery.of(context).size.height * 0.6, + ), + child: _buildForZooms(key: ValueKey(currentZoomNode)), + ), Divider(), ], ); }, ), - ) + ), ]) : const Center(child: CircularProgressIndicator()); } -- 2.34.1 From 160fe25be388aee07eb70ef96e5d502687128d80 Mon Sep 17 00:00:00 2001 From: juan Date: Wed, 11 Jun 2025 15:12:58 -0400 Subject: [PATCH 15/28] works, and optimized time for checking the node type --- lib/collapsableEmailsAndroid.dart | 291 ++++++++++++++++++++++++++++-- 1 file changed, 276 insertions(+), 15 deletions(-) diff --git a/lib/collapsableEmailsAndroid.dart b/lib/collapsableEmailsAndroid.dart index f2cdf01..64f2cc8 100644 --- a/lib/collapsableEmailsAndroid.dart +++ b/lib/collapsableEmailsAndroid.dart @@ -1,6 +1,9 @@ -import 'structs.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; +import 'api_service.dart'; +import 'structs.dart'; +import 'package:html2md/html2md.dart' as html2md; +import 'package:markdown_widget/markdown_widget.dart'; +import 'package:markdown/markdown.dart' as md; class CollapsableEmails extends StatefulWidget { final List thread; // email id's in the form xyz@gmail.com @@ -20,29 +23,287 @@ class _CollapsableEmailsState extends State { List emailsHTML = []; //html of the emails in the thread // build attachments with the forldar name and id Set _expandedEmails = {}; //open emails - List viewtypeIDs = []; //IDs of the viewtypes, order matters - List heightOfViewTypes = []; //the height of each viewtype List emailsInThread = []; bool _isLoaded = false; + List hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"]; + Map hirarchyDict = { + "h1": 1, + "h2": 2, + "h3": 3, + "h4": 4, + "h5": 6, + "h6": 7, + "p": 8, + "ul": 8, + "li": 8, + }; + + List tagsCollected = []; + String markdown = ''; + List> sentinel = []; + int level = 0; + AugmentTree zoomTreeRoot = AugmentTree(); + late AugmentTree currentZoomNode; + bool zoomOut = false; + bool zoomIn = true; + @override void initState() { super.initState(); - //html + _markdownConverter(); + _serializableData(widget.threadIDs); // this + _markdown2Tree(markdown); + _buildForZooms(); } + @override + void dispose() { + super.dispose(); + } + + void _markdownConverter() async { + markdown = html2md.convert(widget.threadHTML[0]); + } + + void _add2Tree(AugmentTree tree, md.Element node2add) { + // adds node to its corresponding place + AugmentTree newNode = AugmentTree(); + newNode.setData(node2add.textContent); + newNode.ogTag = node2add.tag; + // cases, + //1. a node that comes is lower than the root.children last, if so it goes beneath it + if (tree.children.isEmpty) { + // new level to be created when totally empty + tree.children.add(newNode); + newNode.parent = tree; + } else if (tree.children.isNotEmpty && + tree.children.last.ogTag.isNotEmpty) { + if ((hirarchyDict[node2add.tag] ?? + -1) < // e.g. new node is h1 and old is h2, heapify + (hirarchyDict[tree.children.last.ogTag] ?? -1)) { + //have to figure out the borthers + //assuming it all goes right + if ((hirarchyDict[node2add.tag] ?? -1) == -1 || + (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) { + print( + 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} < ${hirarchyDict[tree.children.last.ogTag] ?? -1}'); + return; + } else if (tree.children.last.parent == null) { + // becomes the new top level + for (AugmentTree brother in tree.children) { + brother.parent = newNode; + } + tree.children = [newNode]; + } else { + newNode.parent = tree; + tree.children.add(newNode); + } + } else if ((hirarchyDict[node2add.tag] ?? + -1) > // go down e.g. new node is h3 and old is h2 or something + (hirarchyDict[tree.children.last.ogTag] ?? -1)) { + if ((hirarchyDict[node2add.tag] ?? -1) == -1 || + (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) { + print( + 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} > ${hirarchyDict[tree.children.last.ogTag] ?? -1}'); + print("-1 ${tree.children.last.ogTag}"); + return; + } + + _add2Tree(tree.children.last, node2add); + } else if ((hirarchyDict[node2add.tag] ?? -1) == + (hirarchyDict[tree.children.last.ogTag] ?? -1)) { + tree.children.add(newNode); + newNode.parent = tree; + } + } + } + + void _markdown2Tree(String text) { + print("started markdown2tree"); + final List nakedList = md.Document().parseLines(text.split('\n')); + + for (var node in nakedList) { + //maybe do an add function, but isn't this it? + if (node is md.Element) { + // print(node.textContent); + AugmentTree temp = AugmentTree(); + temp.data = node.textContent; + temp.ogTag = node.tag; + if (hirarchyDict.containsKey(node.tag)) { + _add2Tree(zoomTreeRoot, node); + } + } + + currentZoomNode = zoomTreeRoot; + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + } + + void _goToChildren(int index) async { + final target = currentZoomNode.children[index]; + if (target.children.isNotEmpty) { + setState(() { + currentZoomNode = target; + }); + } else { + print("This child has no further children."); + } + + // if (currentZoomNode.children.isNotEmpty) { + // setState(() { + // zoomIn = true; + // zoomOut = true; + // currentZoomNode = currentZoomNode.children[index]; + // if (currentZoomNode.children[index].children.isEmpty) { + // print('disable in'); + // setState(() { + // zoomIn = false; + // }); + // } + // }); + // } else { + // print("disable zoom down"); + // setState(() { + // zoomIn = false; + // }); + // } + } + + void _goToParent() async { + if (currentZoomNode.parent != null) { + setState(() { + currentZoomNode = currentZoomNode.parent!; + }); + } else { + print("Already at root."); + } + + // print("parent ${currentZoomNode.parent}"); + // print("parent ${currentZoomNode.parent!.parent}"); + // if (currentZoomNode.parent != null) { + // setState(() { + // currentZoomNode = currentZoomNode.parent!; + // if (currentZoomNode.parent == null) { + // setState(() { + // zoomOut = false; + // }); + // } + // }); + // } else if (currentZoomNode.parent == null || + // currentZoomNode.parent!.parent == null) { + // print("disable zoom up"); + } + + void _serializableData(String threadID) async { + emailsInThread = await ApiService().threadsInSerializable(threadID); + print("done thread serializable"); + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + + Widget _buildForZooms({Key? key}) { + if (!_isLoaded) { + return const Center(child: CircularProgressIndicator()); // loading screen + } + final canZoomOut = currentZoomNode.parent != null; + + return ListView.builder( + key: key, + itemCount: currentZoomNode.children.length, + itemBuilder: (context, index) { + final childNode = currentZoomNode.children[index]; + final canZoomIn = childNode.children.isNotEmpty; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), + child: Material( + elevation: 1, + borderRadius: BorderRadius.circular(12), + color: Theme.of(context).colorScheme.surface, + surfaceTintColor: Theme.of(context).colorScheme.surfaceBright, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: 4.0, + children: [ + OutlinedButton( + onPressed: canZoomOut ? () => _goToParent() : null, + child: Icon(Icons.north_west_sharp), + ), + OutlinedButton( + onPressed: + canZoomIn ? () => _goToChildren(index) : null, + child: Icon(Icons.south_east_sharp), + ), + ], + ), + SizedBox(width: 12.0), + Expanded( + child: MarkdownBlock( + data: currentZoomNode + .children[index].data, // one string of markdown + config: MarkdownConfig + .darkConfig, // or lightConfig depending on theme + ), + ), + ], + ), + ), + ), + ); + }, + ); + } @override Widget build(BuildContext context) { - return Scaffold( - body: ListView( - children: [ - HtmlWidget( - widget.threadHTML[0], - // renderMode: RenderMode.listView, - ) - ] - ) - ); + return _isLoaded + ? Column(children: [ + Expanded( + child: ListView.builder( + itemCount: emailsInThread.length, + itemBuilder: (context, index) { + final isExpanded = _expandedEmails + .contains(index); //check if email is expanded + return Column( + children: [ + ListTile( + title: Text(emailsInThread[index].from), + trailing: Text(emailsInThread[index].date), + onTap: () { + setState(() { + if (isExpanded) { + _expandedEmails.remove(index); + } else { + _expandedEmails.add(index); + } + }); + }, + ), + if (isExpanded) + ConstrainedBox( + constraints: BoxConstraints( + minHeight: 100, + maxHeight: + MediaQuery.of(context).size.height * 0.6), + child: + _buildForZooms(key: ValueKey(currentZoomNode))), + Divider(), + ], + ); + }, + ), + ), + ]) + : const Center(child: CircularProgressIndicator()); } } -- 2.34.1 From 654520ad3a3fdfe7f0a9129493987170f0322dd2 Mon Sep 17 00:00:00 2001 From: juan Date: Wed, 11 Jun 2025 20:36:15 -0400 Subject: [PATCH 16/28] cleaned and called numbering per tree when its done --- lib/collapsableEmailsWeb.dart | 234 +++++++++++----------------------- 1 file changed, 75 insertions(+), 159 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index b070d6a..0401130 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -1,5 +1,3 @@ -import 'dart:js_interop'; -import 'package:web/web.dart' as web; import 'package:flutter/material.dart'; import 'api_service.dart'; import 'structs.dart'; @@ -25,59 +23,53 @@ class _CollapsableEmailsState extends State { List emailsHTML = []; //html of the emails in the thread // build attachments with the forldar name and id Set _expandedEmails = {}; //open emails - List viewtypeIDs = []; //IDs of the viewtypes, order matters - List heightOfViewTypes = []; //the height of each viewtype + List emailsInThread = []; bool _isLoaded = false; - static bool _isListenerRegistered = false; - static bool left = true; - static bool right = true; - web.EventListener? _listener; List hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"]; Map hirarchyDict = { "h1": 1, "h2": 2, "h3": 3, "h4": 4, - "h5": 6, - "h6": 7, + "h5": 5, + "h6": 6, "p": 8, "ul": 8, "li": 8, }; List tagsCollected = []; - String markdown = ''; + List allMarkdown = []; List> sentinel = []; int level = 0; AugmentTree zoomTreeRoot = AugmentTree(); - late AugmentTree currentZoomNode; + // late AugmentTree currentZoomNode; + late List currentZoomTree = []; bool zoomOut = false; bool zoomIn = true; + late List threadNodes = []; @override void initState() { super.initState(); + threadNodes = []; + currentZoomTree = []; _markdownConverter(); - // _registerViewFactory(widget.threadHTML); _serializableData(widget.threadIDs); // this - _markdown2Tree(markdown); - _keyListener(); - _buildForZooms(); + _markdown2Tree(allMarkdown); } @override void dispose() { - if (_listener != null) { - web.window.document.removeEventListener('keydown', _listener!); - _listener = null; - _isListenerRegistered = false; - } super.dispose(); } void _markdownConverter() async { - markdown = html2md.convert(widget.threadHTML[0]); + for (int email = 0; email < widget.threadHTML.length; email++) { + String markdown = html2md.convert(widget.threadHTML[email]); + allMarkdown.add(markdown); + } } void _add2Tree(AugmentTree tree, md.Element node2add) { @@ -89,7 +81,6 @@ class _CollapsableEmailsState extends State { //1. a node that comes is lower than the root.children last, if so it goes beneath it if (tree.children.isEmpty) { // new level to be created when totally empty - print('is empty'); tree.children.add(newNode); newNode.parent = tree; } else if (tree.children.isNotEmpty && @@ -128,99 +119,69 @@ class _CollapsableEmailsState extends State { _add2Tree(tree.children.last, node2add); } else if ((hirarchyDict[node2add.tag] ?? -1) == (hirarchyDict[tree.children.last.ogTag] ?? -1)) { - // print("equals??"); tree.children.add(newNode); newNode.parent = tree; } } } - void _markdown2Tree(String text) { + void _markdown2Tree(List text) { print("started markdown2tree"); - final List nakedList = md.Document().parseLines(text.split('\n')); - List pList = []; - List h1List = []; - List h2List = []; - List h3List = []; - List h4List = []; - List h5List = []; - List h6List = []; - - for (var node in nakedList) { - //maybe do an add function, but isn't this it? - if (node is md.Element) { - // print(node.textContent); - AugmentTree temp = AugmentTree(); - temp.data = node.textContent; - temp.ogTag = node.tag; - if (node.tag == 'h1') { - h1List.add(node.textContent); - _add2Tree(zoomTreeRoot, node); - } else if (node.tag == 'h2') { - // i dont add any since i dont have it, maybe the function makes sense - h2List.add(node.textContent); - _add2Tree(zoomTreeRoot, node); // fix this - } else if (node.tag == 'h3') { - h3List.add(node.textContent); - _add2Tree(zoomTreeRoot, node); - } else if (node.tag == 'h4') { - h4List.add(node.textContent); //this broke it - _add2Tree(zoomTreeRoot, node); // change to temp - } else if (node.tag == 'h5') { - h5List.add(node.textContent); - print(node.textContent); - _add2Tree(zoomTreeRoot, node); - } else if (node.tag == 'h6') { - h6List.add(node.textContent); - _add2Tree(zoomTreeRoot, node); // fix this - } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') { - pList.add(node.textContent); - _add2Tree(zoomTreeRoot, node); // fix this + for (int emailsMD = 0; emailsMD < text.length; emailsMD++) { + final List nakedList = + md.Document().parseLines(text[emailsMD].split('\n')); + zoomTreeRoot = AugmentTree(); + for (var node in nakedList) { + //maybe do an add function, but isn't this it? + if (node is md.Element) { + AugmentTree temp = AugmentTree(); + temp.data = node.textContent; + temp.ogTag = node.tag; + if (node.tag == 'h1') { + // make this O(1) + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h2') { + // i dont add any since i dont have it, maybe the function makes sense + _add2Tree(zoomTreeRoot, node); // fix this + } else if (node.tag == 'h3') { + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h4') { + _add2Tree(zoomTreeRoot, node); // change to temp + } else if (node.tag == 'h5') { + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h6') { + _add2Tree(zoomTreeRoot, node); // fix this + } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') { + _add2Tree(zoomTreeRoot, node); // fix this + } } } + zoomTreeRoot.addNumbering(); + threadNodes.add(zoomTreeRoot); + currentZoomTree.add(zoomTreeRoot); } - this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList]; - sentinel.removeWhere((hList) => hList.isEmpty); - currentZoomNode = zoomTreeRoot; if (!mounted) return; setState(() { _isLoaded = true; }); } - void handleKeyDownMD(web.Event event) async { - final keyEvent = event as web.KeyboardEvent; - - if (!mounted) return; - if (keyEvent.key == 'a') { - print("key a"); - setState(() { - level = (level - 1).clamp(0, sentinel.length - 1); - }); - } else if (keyEvent.key == "b") { - print("b"); - setState(() { - level = (level + 1).clamp(0, sentinel.length - 1); - }); - } - } - - void _goToChildren(int index) async { - final target = currentZoomNode.children[index]; + void _goToChildren(int indexThread, int index) async { + final target = currentZoomTree[indexThread].children[index]; if (target.children.isNotEmpty) { setState(() { - currentZoomNode = target; + currentZoomTree[indexThread] = target; }); } else { print("This child has no further children."); } } - void _goToParent() async { - if (currentZoomNode.parent != null) { + void _goToParent(int indexThread) async { + if (currentZoomTree[indexThread].parent != null) { setState(() { - currentZoomNode = currentZoomNode.parent!; + currentZoomTree[indexThread] = currentZoomTree[indexThread].parent!; }); } else { print("Already at root."); @@ -230,82 +191,30 @@ class _CollapsableEmailsState extends State { void _serializableData(String threadID) async { emailsInThread = await ApiService().threadsInSerializable(threadID); print("done thread serializable"); + if (!mounted) return; setState(() { _isLoaded = true; }); } - void handleKeyDownHTML(web.Event event) { - final keyEvent = event as web.KeyboardEvent; - - if (keyEvent.key == 'G') { - print('You pressed the "G" key!'); - final rightPurpleNums = web.document.getElementsByClassName("right"); - _CollapsableEmailsState.right = !_CollapsableEmailsState.right; - final newOpacity = _CollapsableEmailsState.right ? '1.0' : '0.0'; - for (int i = 0; i < rightPurpleNums.length; i++) { - final currentElement = rightPurpleNums.item(i) as web.HTMLElement; - currentElement.style.opacity = newOpacity; - } - } else if (keyEvent.key == 'H') { - print('You pressed the "H" key!'); - final leftPurpleNums = web.document.getElementsByClassName("left"); - _CollapsableEmailsState.left = !_CollapsableEmailsState.left; - final newOpacity = _CollapsableEmailsState.left ? '1.0' : '0.0'; - for (int i = 0; i < leftPurpleNums.length; i++) { - final currentElement = leftPurpleNums.item(i) as web.HTMLElement; - currentElement.style.opacity = newOpacity; - } - } else if (keyEvent.key == 'm') { - print("you pressed 'm'"); - final purpleNums = web.document.getElementsByClassName("purplenumber"); - _CollapsableEmailsState.left = true; - _CollapsableEmailsState.right = true; - - for (int i = 0; i < purpleNums.length; i++) { - final currentElement = purpleNums.item(i) as web.HTMLElement; - currentElement.style.opacity = '1.0'; - } - } else if (keyEvent.key == 'n') { - print("you pressed 'n'"); - final purpleNums = web.document.getElementsByClassName("purplenumber"); - _CollapsableEmailsState.left = false; - _CollapsableEmailsState.right = false; - - for (int i = 0; i < purpleNums.length; i++) { - final currentElement = purpleNums.item(i) as web.HTMLElement; - currentElement.style.opacity = '0.0'; - } - } else if (keyEvent.key == 'w') { - print("you pressed 'w'"); - } - } - - void _keyListener() { - if (_isListenerRegistered) return; - _isListenerRegistered = true; - - // Convert the top-level function to JS-compatible - // _listener = handleKeyDownHTML.toJS; - _listener = handleKeyDownMD.toJS; - - web.window.document.addEventListener('keydown', _listener!); - } - - Widget _buildForZooms({Key? key}) { + Widget _buildForZooms(int indexThread) { + // IF I GIVE IT THE INDEX???? if (!_isLoaded) { return const Center(child: CircularProgressIndicator()); // loading screen } - final canZoomOut = currentZoomNode.parent != null; + + final AugmentTree currentZoomNodeForThisEmail = + currentZoomTree[indexThread]; + + final canZoomOut = currentZoomNodeForThisEmail.parent != null; return ListView.builder( - key: key, - itemCount: currentZoomNode.children.length, + itemCount: currentZoomNodeForThisEmail.children.length, itemBuilder: (context, index) { - final childNode = currentZoomNode.children[index]; + final childNode = currentZoomNodeForThisEmail.children[index]; final canZoomIn = childNode.children.isNotEmpty; - + // currentZoomNodeForThisEmail.addNumbering(); return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), child: Material( @@ -323,12 +232,13 @@ class _CollapsableEmailsState extends State { children: [ OutlinedButton( onPressed: - canZoomOut ? () => _goToParent() : null, + canZoomOut ? () => _goToParent(indexThread) : null, child: Icon(Icons.north_west_sharp), ), OutlinedButton( - onPressed: - canZoomIn ? () => _goToChildren(index) : null, + onPressed: canZoomIn + ? () => _goToChildren(indexThread, index) + : null, child: Icon(Icons.south_east_sharp), ), ], @@ -336,12 +246,17 @@ class _CollapsableEmailsState extends State { SizedBox(width: 12.0), Expanded( child: MarkdownBlock( - data: currentZoomNode - .children[index].data, // one string of markdown + data: childNode.data, + // data: currentZoomNode + // .children[index].data, // one string of markdown config: MarkdownConfig .darkConfig, // or lightConfig depending on theme ), ), + Text( + childNode.numbering, + style: TextStyle(color: Color(Colors.purple[400]!.value)), + ) ], ), ), @@ -382,7 +297,8 @@ class _CollapsableEmailsState extends State { minHeight: 100, maxHeight: MediaQuery.of(context).size.height * 0.6, ), - child: _buildForZooms(key: ValueKey(currentZoomNode)), + child: _buildForZooms(index), //show the tree + // child: _buildForZooms(key: ValueKey(currentZoomNode)), ), Divider(), ], -- 2.34.1 From 8568eafba33ddb15de10c6315acd5ecb6efeeaac Mon Sep 17 00:00:00 2001 From: juan Date: Wed, 11 Jun 2025 20:37:09 -0400 Subject: [PATCH 17/28] struct for markdown, with a numbering algorithm for each node that goes in the form of #a#b#c --- lib/structs.dart | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/structs.dart b/lib/structs.dart index 4672792..b9dbad3 100644 --- a/lib/structs.dart +++ b/lib/structs.dart @@ -1,6 +1,7 @@ //data structures import 'dart:typed_data'; +import 'package:markdown/markdown.dart' as md; class GetThreadResponse { final int id; @@ -151,6 +152,48 @@ class AttachmentResponse { } class AugmentTree { - List children = []; - AugmentTree? node; + List children = []; + + String data = ''; + AugmentTree? parent; + String ogTag = ''; + String numbering = ''; + + void setData(String data) { + this.data = data; + } + + static String _intToLetter(int index) { + return String.fromCharCode('a'.runes.first + index); + } + + void addNumbering({String prefix = ''}) { + //if called in root, numbers them all + for (int i = 0; i < children.length; i++) { + final child = children[i]; + String childNumbering; + bool parentIsLettered = prefix.contains(RegExp(r'[a-z]')); + if (prefix.isEmpty) { + parentIsLettered = false; + } else { + parentIsLettered = prefix.runes.last >= 'a'.runes.first && + prefix.runes.last <= 'z'.runes.first; + } + + if (prefix.isEmpty) { + // Top-level children (direct children of the original root being numbered) get 1, 2, 3... + childNumbering = (i + 1).toString(); + } else if (parentIsLettered) { + // Deeper children get '1a', '1b', '2a', '2b', etc. + childNumbering = '$prefix${i + 1}'; + } else { + childNumbering = '$prefix${_intToLetter(i)}'; + } + + child.numbering = childNumbering; + + // Recursively call for children + child.addNumbering(prefix: childNumbering); + } + } } -- 2.34.1 From 69ed5eb6ab5d4108c6e42ace5891d2aa3d5eebb7 Mon Sep 17 00:00:00 2001 From: juan Date: Thu, 12 Jun 2025 01:01:01 -0400 Subject: [PATCH 18/28] handle jumping --- lib/collapsableEmailsWeb.dart | 69 +++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index 0401130..c58d907 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -9,11 +9,14 @@ class CollapsableEmails extends StatefulWidget { final List thread; // email id's in the form xyz@gmail.com final List threadHTML; final String threadIDs; + final String? targetJumpNumbering; - CollapsableEmails( - {required this.thread, - required this.threadHTML, - required this.threadIDs}); + const CollapsableEmails({ + required this.thread, + required this.threadHTML, + required this.threadIDs, + this.targetJumpNumbering, + }); @override State createState() => _CollapsableEmailsState(); @@ -60,6 +63,16 @@ class _CollapsableEmailsState extends State { _markdown2Tree(allMarkdown); } + @override + void didUpdateWidget(covariant CollapsableEmails oldWidget) { + // TODO: implement didUpdateWidget + super.didUpdateWidget(oldWidget); + if (widget.targetJumpNumbering != null && + widget.targetJumpNumbering != oldWidget.targetJumpNumbering) { + _handleJump(widget.targetJumpNumbering!); + } + } + @override void dispose() { super.dispose(); @@ -266,6 +279,54 @@ class _CollapsableEmailsState extends State { ); } + void _handleJump(String queryNumbering) { + print(queryNumbering); + if (queryNumbering.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Please enter a numbering to jump to.')), + ); + return; + } + + final int targetEmailIndex = _expandedEmails.first; + if (targetEmailIndex >= threadNodes.length) { + // Error handling + return; + } + + final AugmentTree rootOfCurrentEmail = threadNodes[targetEmailIndex]; + final AugmentTree? foundNode = + _findNodeByNumbering(rootOfCurrentEmail, queryNumbering); + + if (foundNode != null) { + setState(() { + currentZoomTree[targetEmailIndex] = foundNode; // Update the state + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Numbering "$queryNumbering" not found.')), + ); + } + } + + // Placeholder for your actual node finding logic within AugmentTree + AugmentTree? _findNodeByNumbering(AugmentTree root, String numbering) { + // Implement your tree traversal here (e.g., DFS or BFS) + // to find the AugmentTree node corresponding to the `numbering`. + // This is a simplified example: + if (root.numbering == numbering) { + // Assuming 'id' can be your numbering + return root; + } + for (var child in root.children) { + final found = _findNodeByNumbering(child, numbering); + if (found != null) { + return found; + } + } + return null; + } + @override Widget build(BuildContext context) { return _isLoaded -- 2.34.1 From 34989d821393d2d01e8d292fa34875459a9176bb Mon Sep 17 00:00:00 2001 From: juan Date: Thu, 12 Jun 2025 01:01:31 -0400 Subject: [PATCH 19/28] cleaned, and added the new paremeter for jumping --- lib/emailViewWeb.dart | 128 ++++-------------------------------------- 1 file changed, 11 insertions(+), 117 deletions(-) diff --git a/lib/emailViewWeb.dart b/lib/emailViewWeb.dart index 08551eb..ace3e7f 100644 --- a/lib/emailViewWeb.dart +++ b/lib/emailViewWeb.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'dart:ui_web' as ui; import 'augment.dart'; // import 'dart:js_interop' as js; //eventually for manipulating css -import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'collapsableEmails.dart'; import 'api_service.dart'; @@ -44,6 +43,7 @@ class _EmailViewState extends State { {'id': 'marker2', 'x': 150, 'y': 200}, {'id': 'marker3', 'x': 250, 'y': 300}, ]; + String? _targetJumpNumbering; @override void initState() { @@ -55,39 +55,19 @@ class _EmailViewState extends State { // _registerViewFactory(currentContent); } - // void _registerViewFactory(List currentContent) { // i think this doesnt work anymore - // setState(() { //update to do item per item - // // each item to have itsviewtype ID - // // is this necessarey here?? - - // //could just move to collapsable - - // viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}'; - // final emailHTML = web.document.createElement('div') as web.HTMLDivElement - // ..id = viewTypeId - // ..innerHTML = currentContent[0].toJS; // temporarily index because it has to do all of them - // emailHTML.style - // ..width = '100%' - // ..height = '100%' - // ..overflow = 'auto' - // ..scrollBehavior = 'smooth'; - - // ui.platformViewRegistry.registerViewFactory( - // viewTypeId, - // (int viewId) => emailHTML, - // ); - // }); - // } - void _scrollToNumber(String spanId) { AugmentClasses.handleJump(spanId); } + void _handleJumpRequest(String numbering) { + setState(() { + _targetJumpNumbering = numbering; + }); + } // TODO: void _invisibility(String ) //to make purple numbers not visible @override Widget build(BuildContext context) { - // print("thread id ${widget.id}"); ApiService.currThreadID = widget.id; return Scaffold( appBar: AppBar( @@ -98,28 +78,10 @@ class _EmailViewState extends State { Column( children: [ EmailToolbar( - onJumpToSpan: _scrollToNumber, - onButtonPressed: () => {}, - // AugmentClasses.handleJump(viewTypeId, '1'); - // print("button got pressed?"); - - // _registerViewFactory(r""" - //

Welcome to My Website

- //

This is a simple HTML page.

- //

What is HTML?

- //

HTML (HyperText Markup Language) is the most basic building~ block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).

- //

Here's a simple list:

- //
    - //
  • HTML elements are the building blocks of HTML pages
  • - //
  • HTML uses tags like <tag> to organize and format content
  • - //
  • CSS is used with HTML to style pages
  • - //
- //

Copyright © 2023

- // """); - // print("change"); - // widget.emailContent = r" - - // + onJumpToSpan: _handleJumpRequest, + onButtonPressed: () => { + print("email tool bar pressed") + }, ), Row( // title of email @@ -162,79 +124,11 @@ class _EmailViewState extends State { thread: widget.messages, //this wont work in serializable threadHTML: widget.emailContent, threadIDs: widget.id, + targetJumpNumbering: _targetJumpNumbering, ), ), - // Expanded( - // child: HtmlElementView( - // key: UniqueKey(), - // viewType: viewTypeId, - // ), - // ), ], ), - - // Overlay widgets dynamically based on marker positions - // FutureBuilder>>( - // future: _markerPositionsFuture, - // builder: (context, snapshot) { - // print("FutureBuilder state: ${snapshot.connectionState}"); - // if (snapshot.connectionState == ConnectionState.waiting) { - // return Center(child: CircularProgressIndicator()); - // } - // if (snapshot.hasError) { - // print("Error in FutureBuilder: ${snapshot.error}"); - // return Center(child: Text('error loading markers')); - // } - // if (snapshot.hasData && snapshot.data != null) { - // final markers = snapshot.data!; - // return Stack( - // children: markers.map((marker) { - // return Positioned( - // left: marker['x'].toDouble(), - // top: marker['y'].toDouble(), - // child: GestureDetector( - // onTap: () { - // print('Tapped on ${marker['id']}'); - // }, - // child: Container( - // width: 50, - // height: 50, - // color: Colors.red, - // child: Center( - // child: Text( - // marker['id'], - // style: TextStyle(color: Colors.white), - // ), - // ), - // ), - // ), - // ); - // }).toList(), - // ); - // } - - // return SizedBox.shrink(); // No markers found - // }, - // ), - // Red widget overlay - // Positioned( - // left: 8, // Adjust based on your desired position - // top: 100 + 44 + 5, // Adjust based on your desired position - // child: IgnorePointer( - // ignoring: true, // Ensures the iframe remains interactive - // child: Container( - // color: Colors.red, - // width: 100, - // height: 50, - // child: Center( - // child: Text( - // 'Overlay', - // style: TextStyle(color: Colors.white), - // ), - // ), - // ), - // ), - // ), ], )); } -- 2.34.1 From edec45669dd4665922e19828bff8a9b94352f4c7 Mon Sep 17 00:00:00 2001 From: juan Date: Thu, 12 Jun 2025 16:07:31 -0400 Subject: [PATCH 20/28] viewspecs and jump textfield controller --- lib/augment.dart | 65 +++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/lib/augment.dart b/lib/augment.dart index 509b3b0..2b8c5de 100644 --- a/lib/augment.dart +++ b/lib/augment.dart @@ -1,16 +1,18 @@ import 'package:crab_ui/api_service.dart'; import 'package:crab_ui/attachmentDownload.dart'; +import 'package:crab_ui/collapsableEmails.dart'; import 'package:crab_ui/structs.dart'; import 'package:flutter/material.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'attachmentWidget.dart'; class EmailToolbar extends StatefulWidget { - final Function(String) onJumpToSpan; + final Function(String) onJumpToNumbering; + final Function(String) onViewspecs; final VoidCallback onButtonPressed; EmailToolbar( - {Key? key, required this.onButtonPressed, required this.onJumpToSpan}) + {Key? key, required this.onButtonPressed, required this.onJumpToNumbering, required this.onViewspecs}) : super(key: key); @override @@ -19,7 +21,8 @@ class EmailToolbar extends StatefulWidget { class _DynamicClassesAugment extends State { String selectedClass = 'Class 1'; - // TextEditingController _jumpController = TextEditingController(); + TextEditingController _jumpController = TextEditingController(); + TextEditingController _viewspecsController = TextEditingController(); // late final FocusNode _JumpItemfocusNode; // late final FocusNode _viewSpecsfocusNode; @@ -46,7 +49,7 @@ class _DynamicClassesAugment extends State { void dispose() { // _JumpItemfocusNode.dispose(); // _viewSpecsfocusNode.dispose(); - // _jumpController.dispose(); + _jumpController.dispose(); super.dispose(); } @@ -71,20 +74,20 @@ class _DynamicClassesAugment extends State { child: Text('Attachments'), ), SizedBox(width: 8), - ElevatedButton( - onPressed: AugmentClasses.handleOpen, - child: Text('Open'), - ), + // ElevatedButton( + // onPressed: AugmentClasses.handleOpen, + // child: Text('Open'), + // ), // SizedBox(width: 8), ElevatedButton( onPressed: AugmentClasses.handleFind, child: Text('Find'), ), // SizedBox(width: 8), - ElevatedButton( - onPressed: AugmentClasses.handleStop, - child: Text('Stop'), - ), + // ElevatedButton( + // onPressed: AugmentClasses.handleStop, + // child: Text('Stop'), + // ), ElevatedButton( onPressed: () { AugmentClasses.handleMove(context); @@ -131,10 +134,10 @@ class _DynamicClassesAugment extends State { // width: 8, // ), Container( - width: 50, + width: 100, height: 30, child: TextField( - // controller: _jumpController, + controller: _jumpController, decoration: InputDecoration( border: OutlineInputBorder(), // suffixIcon: Icon(Icons.search) @@ -142,7 +145,7 @@ class _DynamicClassesAugment extends State { onSubmitted: (value) { print("onSubmitted"); if (value.isNotEmpty) { - widget.onJumpToSpan(value); + widget.onJumpToNumbering(value); } }, ), @@ -179,14 +182,18 @@ class _DynamicClassesAugment extends State { onPressed: () => AugmentClasses.ViewSpecsButton(context), child: Text('ViewSpecs:')), Container( - width: 50, + width: 100, height: 30, child: TextField( + controller: _viewspecsController, decoration: InputDecoration( labelText: '', border: OutlineInputBorder(), // suffixIcon: Icon(Icons.style_rounded) ), + onSubmitted: (value) { + widget.onViewspecs(value); + }, ), ), ElevatedButton( @@ -484,30 +491,8 @@ class AugmentClasses { print("Stop button pressed"); } - static void handleJump(String spanId) { - String js_code = ''' - var iframe = document.getElementsByTagName('iframe')[0]; // 0 for the first iframe, 1 for the second, etc. - - // Check if the iframe is loaded and has content - if (iframe && iframe.contentDocument) { - // Access the document inside the iframe - var iframeDoc = iframe.contentDocument || iframe.contentWindow.document; - - // Find the element with the specific id inside the iframe - var targetElement = iframeDoc.getElementById("$spanId"); // Replace '36 ' with the actual id of the target element - - // If the element exists, scroll to it - if (targetElement) { - targetElement.scrollIntoView(); - console.log('Scrolled to element with id "$spanId" inside the iframe.'); - } else { - console.log('Element with id "$spanId" not found inside the iframe.'); - } - } else { - console.log('Iframe not found or not loaded.'); - } - '''; - // js.context.callMethod('eval', [js_code]); + static void handleJump(String value) { + print(value); } static void invisibility(String htmlClass) {} -- 2.34.1 From 361a3add3914a4af7e7310b32a7bda1181cfb192 Mon Sep 17 00:00:00 2001 From: juan Date: Thu, 12 Jun 2025 16:08:19 -0400 Subject: [PATCH 21/28] viewspecs handling callback function added --- lib/emailViewWeb.dart | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/emailViewWeb.dart b/lib/emailViewWeb.dart index ace3e7f..df50e6d 100644 --- a/lib/emailViewWeb.dart +++ b/lib/emailViewWeb.dart @@ -5,7 +5,6 @@ import 'augment.dart'; import 'collapsableEmails.dart'; import 'api_service.dart'; - class EmailView extends StatefulWidget { final List emailContent; final String from; @@ -44,6 +43,7 @@ class _EmailViewState extends State { {'id': 'marker3', 'x': 250, 'y': 300}, ]; String? _targetJumpNumbering; + String? _targetViewspecs; @override void initState() { @@ -58,12 +58,19 @@ class _EmailViewState extends State { void _scrollToNumber(String spanId) { AugmentClasses.handleJump(spanId); } + void _handleJumpRequest(String numbering) { setState(() { _targetJumpNumbering = numbering; }); } + void _handleViewspecsRequest(String viewspecsCommand) { + setState(() { + _targetViewspecs = viewspecsCommand; + }); + } + // TODO: void _invisibility(String ) //to make purple numbers not visible @override @@ -78,10 +85,9 @@ class _EmailViewState extends State { Column( children: [ EmailToolbar( - onJumpToSpan: _handleJumpRequest, - onButtonPressed: () => { - print("email tool bar pressed") - }, + onJumpToNumbering: _handleJumpRequest, + onViewspecs: _handleViewspecsRequest, + onButtonPressed: () => {print("email tool bar pressed")}, ), Row( // title of email @@ -121,10 +127,11 @@ class _EmailViewState extends State { Expanded( child: CollapsableEmails( //change here - thread: widget.messages, //this wont work in serializable + thread: widget.messages, //this wont work in serializable threadHTML: widget.emailContent, threadIDs: widget.id, targetJumpNumbering: _targetJumpNumbering, + targetViewspecs: _targetViewspecs, ), ), ], -- 2.34.1 From eadc39c8cf7263c3a6fa00fbe8263f3a80660365 Mon Sep 17 00:00:00 2001 From: juan Date: Thu, 12 Jun 2025 16:09:04 -0400 Subject: [PATCH 22/28] viewspecs left, and right numbering visibility --- lib/collapsableEmailsWeb.dart | 86 +++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index c58d907..7422d9e 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -10,12 +10,14 @@ class CollapsableEmails extends StatefulWidget { final List threadHTML; final String threadIDs; final String? targetJumpNumbering; + final String? targetViewspecs; const CollapsableEmails({ required this.thread, required this.threadHTML, required this.threadIDs, this.targetJumpNumbering, + this.targetViewspecs, }); @override @@ -52,6 +54,9 @@ class _CollapsableEmailsState extends State { bool zoomOut = false; bool zoomIn = true; late List threadNodes = []; + static bool leftNumbering = true; + static bool rightNumbering = true; + bool showWhole = false; @override void initState() { @@ -71,6 +76,10 @@ class _CollapsableEmailsState extends State { widget.targetJumpNumbering != oldWidget.targetJumpNumbering) { _handleJump(widget.targetJumpNumbering!); } + if (widget.targetViewspecs != null && + widget.targetViewspecs != oldWidget.targetViewspecs) { + _handleViewspecs(widget.targetViewspecs!); + } } @override @@ -257,6 +266,15 @@ class _CollapsableEmailsState extends State { ], ), SizedBox(width: 12.0), + if (leftNumbering) + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 5, 0), + child: Text( + childNode.numbering, + style: + TextStyle(color: Color(Colors.purple[400]!.value)), + ), + ), Expanded( child: MarkdownBlock( data: childNode.data, @@ -266,10 +284,15 @@ class _CollapsableEmailsState extends State { .darkConfig, // or lightConfig depending on theme ), ), - Text( - childNode.numbering, - style: TextStyle(color: Color(Colors.purple[400]!.value)), - ) + if (rightNumbering) + Padding( + padding: const EdgeInsets.fromLTRB(5, 10, 5, 0), + child: Text( + childNode.numbering, + style: + TextStyle(color: Color(Colors.purple[400]!.value)), + ), + ), ], ), ), @@ -309,13 +332,60 @@ class _CollapsableEmailsState extends State { } } - // Placeholder for your actual node finding logic within AugmentTree + void _handleViewspecs(String viewspecsQuery) { + print(viewspecsQuery); + if (viewspecsQuery.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Please enter the viewspecs.')), + ); + return; + } + + final int targetEmailIndex = _expandedEmails.first; + if (targetEmailIndex >= threadNodes.length) { + // Error handling + return; + } + + if (viewspecsQuery.contains('n')) { + setState(() { + leftNumbering = false; // Update the state + rightNumbering = false; + }); + } + if (viewspecsQuery.contains('m')) { + setState(() { + rightNumbering = true; + leftNumbering = true; + }); + } + if (viewspecsQuery.contains('H')) { + setState(() { + leftNumbering = !leftNumbering; + }); + } + if (viewspecsQuery.contains('G')) { + setState(() { + rightNumbering = !rightNumbering; + }); + } + if (viewspecsQuery.contrains('w')) { + setState(() { + showWhole = true; + }); + } + + // else { + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text('Numbering "$viewspecsQuery" not found.')), + // ); + // } + } + AugmentTree? _findNodeByNumbering(AugmentTree root, String numbering) { - // Implement your tree traversal here (e.g., DFS or BFS) + //recursively finds the node you mentioned // to find the AugmentTree node corresponding to the `numbering`. - // This is a simplified example: if (root.numbering == numbering) { - // Assuming 'id' can be your numbering return root; } for (var child in root.children) { -- 2.34.1 From e26146ead2e1da6066c3558fad03c458db8b3001 Mon Sep 17 00:00:00 2001 From: juan Date: Wed, 18 Jun 2025 16:32:49 -0400 Subject: [PATCH 23/28] clean and fetchMarkdown content added --- lib/api_service.dart | 114 +++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 76 deletions(-) diff --git a/lib/api_service.dart b/lib/api_service.dart index 4769377..ecd5a83 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -1,16 +1,13 @@ // this file should handle most of the API calls -// it also builds some widgets, but it will be modulated later +// it also builds some widgets, but it will be modulated later // chat it did import 'dart:async'; import 'dart:typed_data'; - - import 'structs.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; - class ApiService { static String ip = ""; static String port = ""; @@ -141,7 +138,6 @@ class ApiService { } catch (e) { print('_getEmailContent caught error: $e'); } - // return content; return HTMLofThread; } @@ -344,75 +340,41 @@ class ApiService { return AttachmentResponse(name: "error", data: Uint8List(0)); } - //TODO: MOVE THIS INTO WEB -// Future>> getMarkerPosition() async { -// //this is so we can put a widget right below each email, but the way how the email content is generated -// //leads to problems as for a) the html is added one right after the other in one iframe, b) -// // if it was multiple iframes then the scrolling to jump would not work as expected + Future> fetchMarkdownContent( + List IDsString, String emailFolder) async { + List MDofThread = []; + threadAttachments = []; + int counter = 0; -// print("marker called"); -// // JavaScript code embedded as a string -// String jsCode = ''' -// (async function waitForIframeAndMarkers() { -// try { -// return await new Promise((resolve) => { -// const interval = setInterval(() => { -// console.log("⏳ Checking for iframe..."); -// var iframe = document.getElementsByTagName('iframe')[0]; -// if (iframe && iframe.contentDocument) { -// console.log("✅ Iframe found!"); -// var iframeDoc = iframe.contentDocument || iframe.contentWindow.document; -// var markers = iframeDoc.querySelectorAll('[id^="JuanBedarramarker"]'); -// if (markers.length > 0) { -// console.log(`✅ Found markers in the iframe.`); -// var positions = []; -// markers.forEach((marker) => { -// var rect = marker.getBoundingClientRect(); -// positions.push({ -// id: marker.id, -// x: rect.left + window.scrollX, -// y: rect.top + window.scrollY, -// }); -// }); -// console.log("📌 Marker positions:", positions); -// clearInterval(interval); -// resolve(JSON.stringify(positions)); // Ensure proper JSON string -// } else { -// console.log("❌ No markers found yet."); -// } -// } else { -// console.log("❌ Iframe not found or not loaded yet."); -// } -// }, 200); -// }); -// } catch (error) { -// console.error("JS Error:", error); -// throw error; // Propagate error to Dart -// } -// })(); -// '''; - -// try { -// // Execute the JavaScript code using eval -// // final result = await js.context.callMethod('eval', [jsCode]); - -// if (result != null && result is String) { -// print("Result received: $result"); - -// // Parse the JSON string returned by JavaScript into a Dart list of maps -// final List parsedResult = jsonDecode(result); -// var positions = List>.from(parsedResult); -// print("positions put on"); -// print(positions); -// return positions; -// } else { -// print("result is null or not a string"); -// } -// } catch (e, stackTrace) { -// print("Error executing JavaScript: $e"); -// print(stackTrace); -// } - -// return []; -// } -} \ No newline at end of file + try { + //attaches email after email from a thread + for (var id in IDsString) { + var url = Uri.http('$ip:$port', 'email_md', {'id': id}); + print(url); + var response = await http.get(url); + currThread.add(id); + if (response.statusCode == 200) { + counter += 1; + Map json = jsonDecode(response.body); + + MDofThread.add(json['md'] ?? ''); + try { + List attachments = + await getAttachmentsInfo(emailFolder, id); + for (var attachment in attachments) { + //TODO: for each attachment creaate at the bottom a widget for each individual one + threadAttachments + .add(await getAttachment(emailFolder, id, attachment.name)); + } + } catch (innerError) { + print('_getAttachment info caught error $innerError'); + } + } + } + } catch (e) { + print('_getMDContent caught error: $e'); + } + + return MDofThread; + } +} -- 2.34.1 From 1fb4cfd64c75c893f45bc6d439d803cf3befcef1 Mon Sep 17 00:00:00 2001 From: juan Date: Wed, 18 Jun 2025 16:35:16 -0400 Subject: [PATCH 24/28] modified for using the markdown --- lib/collapsableEmailsStub.dart | 6 +++--- lib/collapsableEmailsWeb.dart | 29 ++++++++++++++++++----------- lib/email.dart | 3 ++- lib/emailViewWeb.dart | 3 ++- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/collapsableEmailsStub.dart b/lib/collapsableEmailsStub.dart index 7c89a88..dd9168f 100644 --- a/lib/collapsableEmailsStub.dart +++ b/lib/collapsableEmailsStub.dart @@ -3,13 +3,13 @@ import 'package:flutter/material.dart'; class CollapsableEmails extends StatefulWidget { final List thread; // email id's in the form xyz@gmail.com - final List threadHTML; + final List threadMarkdown; final String threadIDs; CollapsableEmails( {required this.thread, - required this.threadHTML, - required this.threadIDs}); + required this.threadMarkdown, + required this.threadIDs, String? targetJumpNumbering, String? targetViewspecs}); @override State createState() => _CollapsableEmailsState(); diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index 7422d9e..1183259 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -1,3 +1,4 @@ +import 'package:english_words/english_words.dart'; import 'package:flutter/material.dart'; import 'api_service.dart'; import 'structs.dart'; @@ -7,14 +8,16 @@ import 'package:markdown/markdown.dart' as md; class CollapsableEmails extends StatefulWidget { final List thread; // email id's in the form xyz@gmail.com - final List threadHTML; + // final List threadHTML; to be replaced with the MD + final List threadMarkdown; final String threadIDs; final String? targetJumpNumbering; final String? targetViewspecs; const CollapsableEmails({ required this.thread, - required this.threadHTML, + // required this.threadHTML, + required this.threadMarkdown, required this.threadIDs, this.targetJumpNumbering, this.targetViewspecs, @@ -63,9 +66,9 @@ class _CollapsableEmailsState extends State { super.initState(); threadNodes = []; currentZoomTree = []; - _markdownConverter(); + // _markdownConverter(); _serializableData(widget.threadIDs); // this - _markdown2Tree(allMarkdown); + _markdown2Tree(widget.threadMarkdown); } @override @@ -87,12 +90,16 @@ class _CollapsableEmailsState extends State { super.dispose(); } - void _markdownConverter() async { - for (int email = 0; email < widget.threadHTML.length; email++) { - String markdown = html2md.convert(widget.threadHTML[email]); - allMarkdown.add(markdown); - } - } + // void _markdownConverter() async { + // // to list of markdown + // // for (int email = 0; email < widget.threadHTML.length; email++) { + // // String markdown = html2md.convert(widget.threadHTML[email]); + // // allMarkdown.add(markdown); + // // } + // for (int email = 0; email < widget.threadMarkdown.length; email++) { + // allMarkdown.add(email); + // } + // } void _add2Tree(AugmentTree tree, md.Element node2add) { // adds node to its corresponding place @@ -369,7 +376,7 @@ class _CollapsableEmailsState extends State { rightNumbering = !rightNumbering; }); } - if (viewspecsQuery.contrains('w')) { + if (viewspecsQuery.contains('w')) { setState(() { showWhole = true; }); diff --git a/lib/email.dart b/lib/email.dart index ff4d600..0b1ded3 100644 --- a/lib/email.dart +++ b/lib/email.dart @@ -140,7 +140,8 @@ class EmailPageState extends State { return Scaffold( body: EmailListScreen( emails: emails, - getEmailContent: apiService.fetchEmailContent, + // getEmailContent: apiService.fetchEmailContent, + getEmailContent: apiService.fetchMarkdownContent, folder: widget.selectedFolder, //try to grab from it directly ), ); diff --git a/lib/emailViewWeb.dart b/lib/emailViewWeb.dart index df50e6d..ceb4ef1 100644 --- a/lib/emailViewWeb.dart +++ b/lib/emailViewWeb.dart @@ -128,7 +128,8 @@ class _EmailViewState extends State { child: CollapsableEmails( //change here thread: widget.messages, //this wont work in serializable - threadHTML: widget.emailContent, + // threadHTML: widget.emailContent, // old html + threadMarkdown: widget.emailContent, threadIDs: widget.id, targetJumpNumbering: _targetJumpNumbering, targetViewspecs: _targetViewspecs, -- 2.34.1 From c1afc8875eea14d51f474d38dae547eb99d51202 Mon Sep 17 00:00:00 2001 From: juan Date: Wed, 18 Jun 2025 17:12:52 -0400 Subject: [PATCH 25/28] dump --- lib/SonicEmailViewWeb.dart | 7 ++++++- lib/emailViewAndroid.dart | 15 +++++++++++++++ lib/structs.dart | 15 +++++++++++++-- pubspec.yaml | 3 +++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/SonicEmailViewWeb.dart b/lib/SonicEmailViewWeb.dart index 3ad8f76..b5b127a 100644 --- a/lib/SonicEmailViewWeb.dart +++ b/lib/SonicEmailViewWeb.dart @@ -23,6 +23,9 @@ class _SonicEmailViewState extends State { void _scrollToNumber(String spanId) { AugmentClasses.handleJump(spanId); } + void _handleViewspecs(String queryViewspecs) { + return; + } @override void initState() { @@ -90,7 +93,9 @@ class _SonicEmailViewState extends State { children: [ EmailToolbar( onButtonPressed: () => {}, - onJumpToSpan: _scrollToNumber), + onJumpToNumbering: _scrollToNumber, + onViewspecs: _handleViewspecs + ), Row( // title of email children: [ diff --git a/lib/emailViewAndroid.dart b/lib/emailViewAndroid.dart index d6d6981..33eedd3 100644 --- a/lib/emailViewAndroid.dart +++ b/lib/emailViewAndroid.dart @@ -1,3 +1,4 @@ +import 'package:crab_ui/augment.dart'; import 'package:crab_ui/collapsableEmailsAndroid.dart'; import 'package:flutter/material.dart'; // import 'dart:ui_web' as ui; @@ -33,6 +34,16 @@ class EmailView extends StatefulWidget { } class _EmailViewState extends State { + + @override + void initState() { + super.initState(); + } + + void _scrollToNumber(String spanId) { + // AugmentClasses.handleJump(spanId); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -43,6 +54,10 @@ class _EmailViewState extends State { padding: const EdgeInsets.all(8.0), child: Column( children: [ + EmailToolbar( + onButtonPressed: () => {}, + onJumpToSpan: _scrollToNumber + ), Row( children: [ Expanded( diff --git a/lib/structs.dart b/lib/structs.dart index b9dbad3..e99c0d2 100644 --- a/lib/structs.dart +++ b/lib/structs.dart @@ -1,7 +1,6 @@ //data structures import 'dart:typed_data'; -import 'package:markdown/markdown.dart' as md; class GetThreadResponse { final int id; @@ -177,7 +176,7 @@ class AugmentTree { parentIsLettered = false; } else { parentIsLettered = prefix.runes.last >= 'a'.runes.first && - prefix.runes.last <= 'z'.runes.first; + prefix.runes.last <= 'z'.runes.first; } if (prefix.isEmpty) { @@ -197,3 +196,15 @@ class AugmentTree { } } } + + +class MarkdownParsed{ + final String text; + MarkdownParsed({required this.text}); + factory MarkdownParsed.fromJson(Map json){ + return MarkdownParsed( + text: json['md'] ?? '', + ); + } +} + diff --git a/pubspec.yaml b/pubspec.yaml index d3e7a93..c425b10 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,9 @@ dependencies: photo_view: ^0.15.0 web: ^1.1.1 flutter_widget_from_html: ^0.16.0 + html2md: ^1.3.2 + markdown_widget: ^2.3.2+8 + markdown: ^7.3.0 dev_dependencies: flutter_test: -- 2.34.1 From 3410007f55f1b2ea4c75f2d9bb28c3c5bca2b234 Mon Sep 17 00:00:00 2001 From: juan Date: Wed, 18 Jun 2025 21:09:02 -0400 Subject: [PATCH 26/28] clean, and update to match the web --- lib/augment.dart | 1 - lib/collapsableEmailsAndroid.dart | 294 +++++++++++++++++++++--------- lib/emailViewAndroid.dart | 9 +- 3 files changed, 217 insertions(+), 87 deletions(-) diff --git a/lib/augment.dart b/lib/augment.dart index 2b8c5de..8b8fa60 100644 --- a/lib/augment.dart +++ b/lib/augment.dart @@ -1,6 +1,5 @@ import 'package:crab_ui/api_service.dart'; import 'package:crab_ui/attachmentDownload.dart'; -import 'package:crab_ui/collapsableEmails.dart'; import 'package:crab_ui/structs.dart'; import 'package:flutter/material.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; diff --git a/lib/collapsableEmailsAndroid.dart b/lib/collapsableEmailsAndroid.dart index 64f2cc8..d89afb4 100644 --- a/lib/collapsableEmailsAndroid.dart +++ b/lib/collapsableEmailsAndroid.dart @@ -7,13 +7,19 @@ import 'package:markdown/markdown.dart' as md; class CollapsableEmails extends StatefulWidget { final List thread; // email id's in the form xyz@gmail.com - final List threadHTML; + // final List threadHTML; + final List threadMarkdown; final String threadIDs; + final String? targetJumpNumbering; + final String? targetViewspecs; CollapsableEmails( {required this.thread, - required this.threadHTML, - required this.threadIDs}); + required this.threadMarkdown, + required this.threadIDs, + this.targetJumpNumbering, + this.targetViewspecs, + }); @override State createState() => _CollapsableEmailsState(); @@ -40,21 +46,41 @@ class _CollapsableEmailsState extends State { }; List tagsCollected = []; - String markdown = ''; + List allMarkdown = []; List> sentinel = []; int level = 0; AugmentTree zoomTreeRoot = AugmentTree(); - late AugmentTree currentZoomNode; + // late AugmentTree currentZoomNode; + late List currentZoomTree = []; bool zoomOut = false; bool zoomIn = true; + late List threadNodes = []; + static bool leftNumbering = false; + static bool rightNumbering = true; + bool showWhole = false; + @override void initState() { super.initState(); - _markdownConverter(); + threadNodes = []; + currentZoomTree = []; + // _markdownConverter(); _serializableData(widget.threadIDs); // this - _markdown2Tree(markdown); - _buildForZooms(); + _markdown2Tree(widget.threadMarkdown); + } + @override + void didUpdateWidget(covariant CollapsableEmails oldWidget) { + // TODO: implement didUpdateWidget + super.didUpdateWidget(oldWidget); + if (widget.targetJumpNumbering != null && + widget.targetJumpNumbering != oldWidget.targetJumpNumbering) { + _handleJump(widget.targetJumpNumbering!); + } + if (widget.targetViewspecs != null && + widget.targetViewspecs != oldWidget.targetViewspecs) { + _handleViewspecs(widget.targetViewspecs!); + } } @override @@ -62,10 +88,6 @@ class _CollapsableEmailsState extends State { super.dispose(); } - void _markdownConverter() async { - markdown = html2md.convert(widget.threadHTML[0]); - } - void _add2Tree(AugmentTree tree, md.Element node2add) { // adds node to its corresponding place AugmentTree newNode = AugmentTree(); @@ -119,83 +141,67 @@ class _CollapsableEmailsState extends State { } } - void _markdown2Tree(String text) { + void _markdown2Tree(List text) { print("started markdown2tree"); - final List nakedList = md.Document().parseLines(text.split('\n')); - - for (var node in nakedList) { - //maybe do an add function, but isn't this it? - if (node is md.Element) { - // print(node.textContent); - AugmentTree temp = AugmentTree(); - temp.data = node.textContent; - temp.ogTag = node.tag; - if (hirarchyDict.containsKey(node.tag)) { - _add2Tree(zoomTreeRoot, node); + for (int emailsMD = 0; emailsMD < text.length; emailsMD++) { + final List nakedList = + md.Document().parseLines(text[emailsMD].split('\n')); + zoomTreeRoot = AugmentTree(); + for (var node in nakedList) { + //maybe do an add function, but isn't this it? + if (node is md.Element) { + AugmentTree temp = AugmentTree(); + temp.data = node.textContent; + temp.ogTag = node.tag; + if (node.tag == 'h1') { + // make this O(1) + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h2') { + // i dont add any since i dont have it, maybe the function makes sense + _add2Tree(zoomTreeRoot, node); // fix this + } else if (node.tag == 'h3') { + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h4') { + _add2Tree(zoomTreeRoot, node); // change to temp + } else if (node.tag == 'h5') { + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h6') { + _add2Tree(zoomTreeRoot, node); // fix this + } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') { + _add2Tree(zoomTreeRoot, node); // fix this + } } } - - currentZoomNode = zoomTreeRoot; - if (!mounted) return; - setState(() { - _isLoaded = true; - }); + zoomTreeRoot.addNumbering(); + threadNodes.add(zoomTreeRoot); + currentZoomTree.add(zoomTreeRoot); } + + if (!mounted) return; + setState(() { + _isLoaded = true; + }); } - void _goToChildren(int index) async { - final target = currentZoomNode.children[index]; + void _goToChildren(int indexThread, int index) async { + final target = currentZoomTree[indexThread].children[index]; if (target.children.isNotEmpty) { setState(() { - currentZoomNode = target; + currentZoomTree[indexThread] = target; }); } else { print("This child has no further children."); } - - // if (currentZoomNode.children.isNotEmpty) { - // setState(() { - // zoomIn = true; - // zoomOut = true; - // currentZoomNode = currentZoomNode.children[index]; - // if (currentZoomNode.children[index].children.isEmpty) { - // print('disable in'); - // setState(() { - // zoomIn = false; - // }); - // } - // }); - // } else { - // print("disable zoom down"); - // setState(() { - // zoomIn = false; - // }); - // } } - void _goToParent() async { - if (currentZoomNode.parent != null) { + void _goToParent(int indexThread) async { + if (currentZoomTree[indexThread].parent != null) { setState(() { - currentZoomNode = currentZoomNode.parent!; + currentZoomTree[indexThread] = currentZoomTree[indexThread].parent!; }); } else { print("Already at root."); } - - // print("parent ${currentZoomNode.parent}"); - // print("parent ${currentZoomNode.parent!.parent}"); - // if (currentZoomNode.parent != null) { - // setState(() { - // currentZoomNode = currentZoomNode.parent!; - // if (currentZoomNode.parent == null) { - // setState(() { - // zoomOut = false; - // }); - // } - // }); - // } else if (currentZoomNode.parent == null || - // currentZoomNode.parent!.parent == null) { - // print("disable zoom up"); } void _serializableData(String threadID) async { @@ -207,19 +213,23 @@ class _CollapsableEmailsState extends State { }); } - Widget _buildForZooms({Key? key}) { + Widget _buildForZooms(int indexThread) { + // IF I GIVE IT THE INDEX???? if (!_isLoaded) { return const Center(child: CircularProgressIndicator()); // loading screen } - final canZoomOut = currentZoomNode.parent != null; + + final AugmentTree currentZoomNodeForThisEmail = + currentZoomTree[indexThread]; + + final canZoomOut = currentZoomNodeForThisEmail.parent != null; return ListView.builder( - key: key, - itemCount: currentZoomNode.children.length, + itemCount: currentZoomNodeForThisEmail.children.length, itemBuilder: (context, index) { - final childNode = currentZoomNode.children[index]; + final childNode = currentZoomNodeForThisEmail.children[index]; final canZoomIn = childNode.children.isNotEmpty; - + // currentZoomNodeForThisEmail.addNumbering(); return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), child: Material( @@ -236,25 +246,46 @@ class _CollapsableEmailsState extends State { spacing: 4.0, children: [ OutlinedButton( - onPressed: canZoomOut ? () => _goToParent() : null, + onPressed: + canZoomOut ? () => _goToParent(indexThread) : null, child: Icon(Icons.north_west_sharp), ), OutlinedButton( - onPressed: - canZoomIn ? () => _goToChildren(index) : null, + onPressed: canZoomIn + ? () => _goToChildren(indexThread, index) + : null, child: Icon(Icons.south_east_sharp), ), ], ), SizedBox(width: 12.0), + if (leftNumbering) + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 5, 0), + child: Text( + childNode.numbering, + style: + TextStyle(color: Color(Colors.purple[400]!.value)), + ), + ), Expanded( child: MarkdownBlock( - data: currentZoomNode - .children[index].data, // one string of markdown + data: childNode.data, + // data: currentZoomNode + // .children[index].data, // one string of markdown config: MarkdownConfig .darkConfig, // or lightConfig depending on theme ), ), + if (rightNumbering) + Padding( + padding: const EdgeInsets.fromLTRB(5, 10, 5, 0), + child: Text( + childNode.numbering, + style: + TextStyle(color: Color(Colors.purple[400]!.value)), + ), + ), ], ), ), @@ -263,6 +294,100 @@ class _CollapsableEmailsState extends State { }, ); } + void _handleJump(String queryNumbering) { + print(queryNumbering); + if (queryNumbering.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Please enter a numbering to jump to.')), + ); + return; + } + + final int targetEmailIndex = _expandedEmails.first; + if (targetEmailIndex >= threadNodes.length) { + // Error handling + return; + } + + final AugmentTree rootOfCurrentEmail = threadNodes[targetEmailIndex]; + final AugmentTree? foundNode = + _findNodeByNumbering(rootOfCurrentEmail, queryNumbering); + + if (foundNode != null) { + setState(() { + currentZoomTree[targetEmailIndex] = foundNode; // Update the state + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Numbering "$queryNumbering" not found.')), + ); + } + } + + void _handleViewspecs(String viewspecsQuery) { + print(viewspecsQuery); + if (viewspecsQuery.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Please enter the viewspecs.')), + ); + return; + } + + final int targetEmailIndex = _expandedEmails.first; + if (targetEmailIndex >= threadNodes.length) { + // Error handling + return; + } + + if (viewspecsQuery.contains('n')) { + setState(() { + leftNumbering = false; // Update the state + rightNumbering = false; + }); + } + if (viewspecsQuery.contains('m')) { + setState(() { + rightNumbering = true; + leftNumbering = true; + }); + } + if (viewspecsQuery.contains('H')) { + setState(() { + leftNumbering = !leftNumbering; + }); + } + if (viewspecsQuery.contains('G')) { + setState(() { + rightNumbering = !rightNumbering; + }); + } + if (viewspecsQuery.contains('w')) { + setState(() { + showWhole = true; + }); + } + + // else { + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text('Numbering "$viewspecsQuery" not found.')), + // ); + // } + } + + AugmentTree? _findNodeByNumbering(AugmentTree root, String numbering) { + //recursively finds the node you mentioned + // to find the AugmentTree node corresponding to the `numbering`. + if (root.numbering == numbering) { + return root; + } + for (var child in root.children) { + final found = _findNodeByNumbering(child, numbering); + if (found != null) { + return found; + } + } + return null; + } @override Widget build(BuildContext context) { @@ -294,9 +419,10 @@ class _CollapsableEmailsState extends State { constraints: BoxConstraints( minHeight: 100, maxHeight: - MediaQuery.of(context).size.height * 0.6), - child: - _buildForZooms(key: ValueKey(currentZoomNode))), + MediaQuery.of(context).size.height * 0.6, + ), + child: _buildForZooms(index), + ), Divider(), ], ); diff --git a/lib/emailViewAndroid.dart b/lib/emailViewAndroid.dart index 33eedd3..7b257a2 100644 --- a/lib/emailViewAndroid.dart +++ b/lib/emailViewAndroid.dart @@ -43,6 +43,9 @@ class _EmailViewState extends State { void _scrollToNumber(String spanId) { // AugmentClasses.handleJump(spanId); } + void _viewSpecs(String command){ + + } @override Widget build(BuildContext context) { @@ -56,7 +59,9 @@ class _EmailViewState extends State { children: [ EmailToolbar( onButtonPressed: () => {}, - onJumpToSpan: _scrollToNumber + onJumpToNumbering: _scrollToNumber, + onViewspecs: _viewSpecs, + ), Row( children: [ @@ -98,7 +103,7 @@ class _EmailViewState extends State { Expanded( child: CollapsableEmails( thread: widget.messages, - threadHTML: widget.emailContent, + threadMarkdown: widget.emailContent, threadIDs: widget.id, ), ), -- 2.34.1 From fb31051b0391f15f681c4fbc6b4f0547f979b199 Mon Sep 17 00:00:00 2001 From: juan Date: Mon, 23 Jun 2025 11:40:32 -0400 Subject: [PATCH 27/28] homepage layout fix? --- lib/home_page.dart | 505 ++++++++++++++++++++------------------------- 1 file changed, 226 insertions(+), 279 deletions(-) diff --git a/lib/home_page.dart b/lib/home_page.dart index 903a8a4..455c994 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -148,16 +148,6 @@ class _HomeScreenState extends State with TickerProviderStateMixin { builder: (context) =>SonicEmailView( email: email, emailHTML: emailContent[0]) - // builder: (context) => EmailView( - // emailContent: emailContent, - // from: email.from, - // name: email.name, - // to: email.to.toString(), - // subject: email.subject, - // date: email.date.toString(), - // id: email.id.toString(), - // messages: [email.id], - // ), ), ); }, @@ -165,28 +155,8 @@ class _HomeScreenState extends State with TickerProviderStateMixin { }, separatorBuilder: (context, index) => Divider(), ), - // child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Text("Results for: $query", style: TextStyle(fontSize: 24)), - // // Display the actual data - // Text(result[0].name), // Accessing the first result safely - // Text(result[0].from), // Displaying the 'from' field as an example - // Text(result[0].hash), - // Text(result[0].subject), - // Text(result[0].uid.toString()), - // Text(result[0].list), - // Text(result[0].id), - - // // Add more fields or customize the display - // // SerializableEmailListScreen(emails: result, getEmailContent: getEmailContent) - // // Expanded( - - // // child: - // // ), - // ], + ); - // ); } }, ); @@ -200,271 +170,248 @@ class _HomeScreenState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { - return Scaffold( - key: _scaffoldKey, - drawer: FolderDrawer( - apiService: apiService, - onFolderTap: (folder) { - _emailPageKey.currentState?.updateSelectedFolder(folder); - }, - ), - body: Stack( - children: [ - Row( + return Padding( + padding: const EdgeInsets.fromLTRB(0, 20, 0 , 20), + child: Scaffold( + key: _scaffoldKey, + drawer: FolderDrawer( + apiService: apiService, + onFolderTap: (folder) { + _emailPageKey.currentState?.updateSelectedFolder(folder); + }, + ), + body: Padding( + padding: const EdgeInsets.fromLTRB(0, 20, 0, 0), + child: Stack( children: [ - // Sidebar - if (_isSidebarOpen) - Container( - width: 70, - color: Color.fromARGB(17, 96, 122, 135), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListTile( - leading: Icon(Icons.home), - onTap: () { - // Navigate to Home - }, - ), - ListTile( - leading: Icon(Icons.settings), - onTap: () { - // Navigate to Settings - }, - ), - ListTile( - leading: Icon(Icons.email), - onTap: () { - _scaffoldKey.currentState?.openDrawer(); - }, - ), - Spacer(), - Padding( - padding: const EdgeInsets.all(8.0), - child: Align( - alignment: Alignment.bottomLeft, - child: IconButton( - icon: Icon(Icons.close, color: Colors.white), - onPressed: () { - setState(() { - _isSidebarOpen = false; - }); + Row( + children: [ + // Sidebar + if (_isSidebarOpen) + Container( + width: 70, + color: Color.fromARGB(17, 96, 122, 135), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + leading: Icon(Icons.home), + onTap: () { + // Navigate to Home }, ), - ), - ), - ], - ), - ), - // Main content - Expanded( - child: Column( - children: [ - Container( - padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 4.0), - color: Color.fromARGB(42, 36, 102, 132), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: 800, + ListTile( + leading: Icon(Icons.settings), + onTap: () { + // Navigate to Settings + }, + ), + ListTile( + leading: Icon(Icons.email), + onTap: () { + _scaffoldKey.currentState?.openDrawer(); + }, + ), + Spacer(), + Padding( + padding: const EdgeInsets.all(8.0), + child: Align( + alignment: Alignment.bottomLeft, + child: IconButton( + icon: Icon(Icons.close, color: Colors.white), + onPressed: () { + setState(() { + _isSidebarOpen = false; + }); + }, ), - child: SizedBox( - height: 40, - child: TextField( - decoration: InputDecoration( - hintText: 'Search...', - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.search), + ), + ), + ], + ), + ), + // Main content + Expanded( + child: Column( + children: [ + Container( + padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 4.0), + color: Color.fromARGB(42, 36, 102, 132), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: 800, + ), + child: SizedBox( + height: 40, + child: TextField( + decoration: InputDecoration( + hintText: 'Search...', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.search), + ), + onSubmitted: (value) { + if (value.isNotEmpty) { + _performSearch(value, _selectedOption); + } + //this is the input box i mentioned + // if (value == '') { + // setState(() { + // querySearches = false; + // }); + // } + // Future> results = apiService + // .sonicSearch('INBOX', 20, 0, value); + // // print(value); + // print(results); + // setState(() { + // querySearches = true; + // }); + }, + ), ), - onSubmitted: (value) { - if (value.isNotEmpty) { - _performSearch(value, _selectedOption); - } - //this is the input box i mentioned - // if (value == '') { - // setState(() { - // querySearches = false; - // }); - // } - // Future> results = apiService - // .sonicSearch('INBOX', 20, 0, value); - // // print(value); - // print(results); - // setState(() { - // querySearches = true; - // }); - }, ), ), - ), + SizedBox( + width: 8, + ), + Container( + height: 40, + child: ElevatedButton( + onPressed: _showOptionsSearchDialog, + child: Icon(Icons.manage_search), + ), + ) + ], ), - SizedBox( - width: 8, + ), + Container( + padding: EdgeInsets.all(0.0), + color: Color.fromARGB(42, 36, 102, 132), + child: Row( + children: [ + Container( + height: 2, + ) + ], ), - Container( - height: 40, - child: ElevatedButton( - onPressed: _showOptionsSearchDialog, - child: Icon(Icons.manage_search), - ), - ) - ], - ), - ), - Container( - padding: EdgeInsets.all(0.0), - color: Color.fromARGB(42, 36, 102, 132), - child: Row( - children: [ - Container( - height: 2, - ) - ], - ), - ), - Container( - color: Color.fromARGB(255, 131, 110, 143), - child: TabBar( - controller: _tabController, - isScrollable: true, - tabs: _tabs - .asMap() - .entries - .map((entry) => Tab( - child: Row( - children: [ - Text(entry.value), - if (entry.value != 'Emails') - GestureDetector( - onTap: () => _removeTab(entry.key), - child: Icon(Icons.close, size: 16), - ), - ], - ), - )) - .toList(), - labelColor: Colors.white, - indicatorColor: Colors.white, - ), - ), - Container( - // alignment: Alignment.topLeft, - padding: EdgeInsets.all(8.0), - color: Colors.white, - child: Row( - children: [ - ElevatedButton( - onPressed: () { - _emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState - ?.updatePagenation('back'); - }, - child: Icon(Icons.navigate_before), + ), + Container( + color: Color.fromARGB(255, 131, 110, 143), + child: TabBar( + controller: _tabController, + isScrollable: true, + tabs: _tabs + .asMap() + .entries + .map((entry) => Tab( + child: Row( + children: [ + Text(entry.value), + if (entry.value != 'Emails') + GestureDetector( + onTap: () => _removeTab(entry.key), + child: Icon(Icons.close, size: 16), + ), + ], + ), + )) + .toList(), + labelColor: Colors.white, + indicatorColor: Colors.white, ), - Builder( - builder: (context) { - final emailState = _emailPageKey.currentState; - if (emailState == null) { - // Schedule a rebuild once the state is available - Future.microtask(() => setState(() {})); - return Text('Loading...'); - } - - return ValueListenableBuilder( - valueListenable: emailState.currentPageNotifier, - builder: (context, value, _) => Text('$value'), - ); - }, + ), + Container( + // alignment: Alignment.topLeft, + padding: EdgeInsets.all(8.0), + color: Colors.white, + child: Row( + children: [ + ElevatedButton( + onPressed: () { + _emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState + ?.updatePagenation('back'); + }, + child: Icon(Icons.navigate_before), + ), + Builder( + builder: (context) { + final emailState = _emailPageKey.currentState; + if (emailState == null) { + // Schedule a rebuild once the state is available + Future.microtask(() => setState(() {})); + return Text('Loading...'); + } + + return ValueListenableBuilder( + valueListenable: emailState.currentPageNotifier, + builder: (context, value, _) => Text('$value'), + ); + }, + ), + ElevatedButton( + onPressed: () { + _emailPageKey.currentState + ?.updatePagenation('next'); + }, + child: Icon(Icons.navigate_next), + ), + ], ), - ElevatedButton( - onPressed: () { - _emailPageKey.currentState - ?.updatePagenation('next'); - }, - child: Icon(Icons.navigate_next), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: _tabs.map((tab) { + return _tabWidgets[tab] ?? + Center(child: Text("No content found")); + // return Center( + // child: EmailPage( + // key: _emailPageKey, + // )); + }).toList(), ), - ], - ), + ), + + // if (_tabs.isEmpty) + // Expanded( + // child: EmailPage(key: _emailPageKey), + // ), + // if (_tabs.isNotEmpty) + // Expanded( + // // child: Text('supposed to be mails'), + // child: TabBarView( + // controller: _tabController, + // children: _tabs + // .map((tab) => Center(child: Text('Results for $tab'))) + // .toList(), + // ), + // ), + ], ), - Expanded( - child: TabBarView( - controller: _tabController, - children: _tabs.map((tab) { - return _tabWidgets[tab] ?? - Center(child: Text("No content found")); - // return Center( - // child: EmailPage( - // key: _emailPageKey, - // )); - }).toList(), - ), - ), - - // if (_tabs.isEmpty) - // Expanded( - // child: EmailPage(key: _emailPageKey), - // ), - // if (_tabs.isNotEmpty) - // Expanded( - // // child: Text('supposed to be mails'), - // child: TabBarView( - // controller: _tabController, - // children: _tabs - // .map((tab) => Center(child: Text('Results for $tab'))) - // .toList(), - // ), - // ), - ], - ), + ), + ], ), + if (!_isSidebarOpen) + Positioned( + bottom: 16, + left: 16, + child: FloatingActionButton( + child: Icon(Icons.menu), + onPressed: () { + setState(() { + _isSidebarOpen = true; + }); + }, + ), + ), ], ), - if (!_isSidebarOpen) - Positioned( - bottom: 16, - left: 16, - child: FloatingActionButton( - child: Icon(Icons.menu), - onPressed: () { - setState(() { - _isSidebarOpen = true; - }); - }, - ), - ), - ], + ), ), ); } } -// void _showPopupMenu(BuildContext context, Offset position) async { -// final RenderBox overlay = -// Overlay.of(context).context.findRenderObject() as RenderBox; - -// await showMenu( -// context: context, -// position: RelativeRect.fromLTRB( -// position.dx, -// position.dy, -// overlay.size.width - position.dx, -// overlay.size.height - position.dy, -// ), -// items: >[ -// PopupMenuItem( -// value: 'Open', -// child: Text('Open'), -// ), -// PopupMenuItem( -// value: 'Reply', -// child: Text('Reply'), -// ), -// PopupMenuItem( -// value: 'Delete', -// child: Text('Delete'), -// ), -// ], -// ); -// } -// } -- 2.34.1 From 5d4bc01a59337a37c3d70a008cf0b59c16be8c99 Mon Sep 17 00:00:00 2001 From: juan Date: Tue, 24 Jun 2025 12:06:27 -0400 Subject: [PATCH 28/28] color theme and padding for the non-visible parts of the devices (andorid, ios) --- lib/home_page.dart | 452 +++++++++++++++++++++++---------------------- lib/main.dart | 5 +- 2 files changed, 233 insertions(+), 224 deletions(-) diff --git a/lib/home_page.dart b/lib/home_page.dart index 455c994..2849444 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -170,245 +170,251 @@ class _HomeScreenState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.fromLTRB(0, 20, 0 , 20), - child: Scaffold( - key: _scaffoldKey, - drawer: FolderDrawer( - apiService: apiService, - onFolderTap: (folder) { - _emailPageKey.currentState?.updateSelectedFolder(folder); - }, - ), - body: Padding( - padding: const EdgeInsets.fromLTRB(0, 20, 0, 0), - child: Stack( - children: [ - Row( + return Scaffold( + backgroundColor: Theme.of(context).colorScheme.onPrimary, + body: Padding( + padding: const EdgeInsets.fromLTRB(0, 20, 0 , 20), + child: Scaffold( + key: _scaffoldKey, + drawer: FolderDrawer( + apiService: apiService, + onFolderTap: (folder) { + _emailPageKey.currentState?.updateSelectedFolder(folder); + }, + ), + body: Scaffold( + backgroundColor: Theme.of(context).colorScheme.onPrimary, + body: Padding( + padding: const EdgeInsets.fromLTRB(0, 20, 0, 0), + child: Stack( children: [ - // Sidebar - if (_isSidebarOpen) - Container( - width: 70, - color: Color.fromARGB(17, 96, 122, 135), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListTile( - leading: Icon(Icons.home), - onTap: () { - // Navigate to Home - }, - ), - ListTile( - leading: Icon(Icons.settings), - onTap: () { - // Navigate to Settings - }, - ), - ListTile( - leading: Icon(Icons.email), - onTap: () { - _scaffoldKey.currentState?.openDrawer(); - }, - ), - Spacer(), - Padding( - padding: const EdgeInsets.all(8.0), - child: Align( - alignment: Alignment.bottomLeft, - child: IconButton( - icon: Icon(Icons.close, color: Colors.white), - onPressed: () { - setState(() { - _isSidebarOpen = false; - }); + Row( + children: [ + // Sidebar + if (_isSidebarOpen) + Container( + width: 70, + color: Color.fromARGB(17, 96, 122, 135), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + leading: Icon(Icons.home), + onTap: () { + // Navigate to Home }, ), - ), - ), - ], - ), - ), - // Main content - Expanded( - child: Column( - children: [ - Container( - padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 4.0), - color: Color.fromARGB(42, 36, 102, 132), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: 800, + ListTile( + leading: Icon(Icons.settings), + onTap: () { + // Navigate to Settings + }, + ), + ListTile( + leading: Icon(Icons.email), + onTap: () { + _scaffoldKey.currentState?.openDrawer(); + }, + ), + Spacer(), + Padding( + padding: const EdgeInsets.all(8.0), + child: Align( + alignment: Alignment.bottomLeft, + child: IconButton( + icon: Icon(Icons.close, color: Colors.white), + onPressed: () { + setState(() { + _isSidebarOpen = false; + }); + }, ), - child: SizedBox( - height: 40, - child: TextField( - decoration: InputDecoration( - hintText: 'Search...', - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.search), + ), + ), + ], + ), + ), + // Main content + Expanded( + child: Column( + children: [ + Container( + padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 4.0), + color: Color.fromARGB(42, 36, 102, 132), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: 800, + ), + child: SizedBox( + height: 40, + child: TextField( + decoration: InputDecoration( + hintText: 'Search...', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.search), + ), + onSubmitted: (value) { + if (value.isNotEmpty) { + _performSearch(value, _selectedOption); + } + //this is the input box i mentioned + // if (value == '') { + // setState(() { + // querySearches = false; + // }); + // } + // Future> results = apiService + // .sonicSearch('INBOX', 20, 0, value); + // // print(value); + // print(results); + // setState(() { + // querySearches = true; + // }); + }, + ), ), - onSubmitted: (value) { - if (value.isNotEmpty) { - _performSearch(value, _selectedOption); - } - //this is the input box i mentioned - // if (value == '') { - // setState(() { - // querySearches = false; - // }); - // } - // Future> results = apiService - // .sonicSearch('INBOX', 20, 0, value); - // // print(value); - // print(results); - // setState(() { - // querySearches = true; - // }); - }, ), ), - ), + SizedBox( + width: 8, + ), + Container( + height: 40, + child: ElevatedButton( + onPressed: _showOptionsSearchDialog, + child: Icon(Icons.manage_search), + ), + ) + ], ), - SizedBox( - width: 8, + ), + Container( + padding: EdgeInsets.all(0.0), + color: Color.fromARGB(42, 36, 102, 132), + child: Row( + children: [ + Container( + height: 2, + ) + ], ), - Container( - height: 40, - child: ElevatedButton( - onPressed: _showOptionsSearchDialog, - child: Icon(Icons.manage_search), - ), - ) - ], - ), - ), - Container( - padding: EdgeInsets.all(0.0), - color: Color.fromARGB(42, 36, 102, 132), - child: Row( - children: [ - Container( - height: 2, - ) - ], - ), - ), - Container( - color: Color.fromARGB(255, 131, 110, 143), - child: TabBar( - controller: _tabController, - isScrollable: true, - tabs: _tabs - .asMap() - .entries - .map((entry) => Tab( - child: Row( - children: [ - Text(entry.value), - if (entry.value != 'Emails') - GestureDetector( - onTap: () => _removeTab(entry.key), - child: Icon(Icons.close, size: 16), - ), - ], - ), - )) - .toList(), - labelColor: Colors.white, - indicatorColor: Colors.white, - ), - ), - Container( - // alignment: Alignment.topLeft, - padding: EdgeInsets.all(8.0), - color: Colors.white, - child: Row( - children: [ - ElevatedButton( - onPressed: () { - _emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState - ?.updatePagenation('back'); - }, - child: Icon(Icons.navigate_before), + ), + Container( + color: Color.fromARGB(255, 131, 110, 143), + child: TabBar( + controller: _tabController, + isScrollable: true, + tabs: _tabs + .asMap() + .entries + .map((entry) => Tab( + child: Row( + children: [ + Text(entry.value), + if (entry.value != 'Emails') + GestureDetector( + onTap: () => _removeTab(entry.key), + child: Icon(Icons.close, size: 16), + ), + ], + ), + )) + .toList(), + labelColor: Colors.white, + indicatorColor: Colors.white, ), - Builder( - builder: (context) { - final emailState = _emailPageKey.currentState; - if (emailState == null) { - // Schedule a rebuild once the state is available - Future.microtask(() => setState(() {})); - return Text('Loading...'); - } - - return ValueListenableBuilder( - valueListenable: emailState.currentPageNotifier, - builder: (context, value, _) => Text('$value'), - ); - }, + ), + Container( + // alignment: Alignment.topLeft, + padding: EdgeInsets.all(8.0), + color: Colors.white, + child: Row( + children: [ + ElevatedButton( + onPressed: () { + _emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState + ?.updatePagenation('back'); + }, + child: Icon(Icons.navigate_before), + ), + Builder( + builder: (context) { + final emailState = _emailPageKey.currentState; + if (emailState == null) { + // Schedule a rebuild once the state is available + Future.microtask(() => setState(() {})); + return Text('Loading...'); + } + + return ValueListenableBuilder( + valueListenable: emailState.currentPageNotifier, + builder: (context, value, _) => Text('$value'), + ); + }, + ), + ElevatedButton( + onPressed: () { + _emailPageKey.currentState + ?.updatePagenation('next'); + }, + child: Icon(Icons.navigate_next), + ), + ], ), - ElevatedButton( - onPressed: () { - _emailPageKey.currentState - ?.updatePagenation('next'); - }, - child: Icon(Icons.navigate_next), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: _tabs.map((tab) { + return _tabWidgets[tab] ?? + Center(child: Text("No content found")); + // return Center( + // child: EmailPage( + // key: _emailPageKey, + // )); + }).toList(), ), - ], - ), + ), + + // if (_tabs.isEmpty) + // Expanded( + // child: EmailPage(key: _emailPageKey), + // ), + // if (_tabs.isNotEmpty) + // Expanded( + // // child: Text('supposed to be mails'), + // child: TabBarView( + // controller: _tabController, + // children: _tabs + // .map((tab) => Center(child: Text('Results for $tab'))) + // .toList(), + // ), + // ), + ], ), - Expanded( - child: TabBarView( - controller: _tabController, - children: _tabs.map((tab) { - return _tabWidgets[tab] ?? - Center(child: Text("No content found")); - // return Center( - // child: EmailPage( - // key: _emailPageKey, - // )); - }).toList(), - ), - ), - - // if (_tabs.isEmpty) - // Expanded( - // child: EmailPage(key: _emailPageKey), - // ), - // if (_tabs.isNotEmpty) - // Expanded( - // // child: Text('supposed to be mails'), - // child: TabBarView( - // controller: _tabController, - // children: _tabs - // .map((tab) => Center(child: Text('Results for $tab'))) - // .toList(), - // ), - // ), - ], - ), + ), + ], ), + if (!_isSidebarOpen) + Positioned( + bottom: 16, + left: 16, + child: FloatingActionButton( + child: Icon(Icons.menu), + onPressed: () { + setState(() { + _isSidebarOpen = true; + }); + }, + ), + ), ], ), - if (!_isSidebarOpen) - Positioned( - bottom: 16, - left: 16, - child: FloatingActionButton( - child: Icon(Icons.menu), - onPressed: () { - setState(() { - _isSidebarOpen = true; - }); - }, - ), - ), - ], + ), ), ), ), diff --git a/lib/main.dart b/lib/main.dart index da90a50..7790390 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,7 +17,10 @@ class HyM extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, - theme: ThemeData.light(), + theme: ThemeData( + colorScheme: ColorScheme.light(), + useMaterial3: true, + ), title: 'HyM', // home: HomeScreen(), initialRoute: "/", -- 2.34.1