Merge pull request 'folders actions' (#5) from login into main
Reviewed-on: #5 tested, and works on win11
This commit is contained in:
commit
283ad8f3f0
118
.gitignore
vendored
118
.gitignore
vendored
@ -1,43 +1,75 @@
|
|||||||
# Miscellaneous
|
# Miscellaneous
|
||||||
*.class
|
*.class
|
||||||
*.log
|
*.log
|
||||||
*.pyc
|
*.pyc
|
||||||
*.swp
|
*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.atom/
|
.atom/
|
||||||
.buildlog/
|
.buildlog/
|
||||||
.history
|
.history
|
||||||
.svn/
|
.svn/
|
||||||
migrate_working_dir/
|
migrate_working_dir/
|
||||||
|
.VSCodeCounter
|
||||||
# IntelliJ related
|
*:Zone.Identifier
|
||||||
*.iml
|
.packages
|
||||||
*.ipr
|
*.lock
|
||||||
*.iws
|
*.tmp
|
||||||
.idea/
|
*.temp
|
||||||
|
*.bak
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
*.old
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
#.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
# IntelliJ related
|
||||||
**/doc/api/
|
*.iml
|
||||||
**/ios/Flutter/.last_build_id
|
*.ipr
|
||||||
.dart_tool/
|
*.iws
|
||||||
.flutter-plugins
|
.idea/
|
||||||
.flutter-plugins-dependencies
|
|
||||||
.pub-cache/
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
.pub/
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
/build/
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
# Symbolication related
|
/linux
|
||||||
app.*.symbols
|
/macos
|
||||||
|
/windows
|
||||||
# Obfuscation related
|
|
||||||
app.*.map.json
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
# Android Studio will place build artifacts here
|
**/ios/Flutter/.last_build_id
|
||||||
/android/app/debug
|
.dart_tool/
|
||||||
/android/app/profile
|
.flutter-plugins
|
||||||
/android/app/release
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
# /android/app/debug
|
||||||
|
# /android/app/profile
|
||||||
|
# /android/app/release
|
||||||
|
# /android
|
||||||
|
|
||||||
|
|
||||||
|
# build
|
||||||
|
# android/.gradle/
|
||||||
|
# android/local.properties
|
||||||
|
ios/Flutter/Generated.xcconfig
|
||||||
|
ios/Flutter/App.framework
|
||||||
|
ios/Flutter/Flutter.framework
|
||||||
|
ios/Flutter/Flutter.podspec
|
||||||
|
ios/.symlinks/
|
||||||
|
ios/Pods/
|
||||||
|
ios/.generated/
|
||||||
|
macos/Flutter/GeneratedPluginRegistrant.swift
|
||||||
|
macos/Pods/
|
||||||
|
macos/.symlinks/
|
||||||
|
linux/flutter/
|
||||||
|
windows/flutter/
|
||||||
|
13
android/.gitignore
vendored
13
android/.gitignore
vendored
@ -1,13 +0,0 @@
|
|||||||
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
|
|
@ -1,58 +0,0 @@
|
|||||||
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.crab_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.crab_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 = "../.."
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
|
||||||
the Flutter tool needs it to communicate with the running application
|
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
|
||||||
-->
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
</manifest>
|
|
@ -1,45 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<application
|
|
||||||
android:label="crab_ui"
|
|
||||||
android:name="${applicationName}"
|
|
||||||
android:icon="@mipmap/ic_launcher">
|
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:taskAffinity=""
|
|
||||||
android:theme="@style/LaunchTheme"
|
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
|
||||||
the Android process has started. This theme is visible to the user
|
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
|
||||||
to determine the Window background behind the Flutter UI. -->
|
|
||||||
<meta-data
|
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
|
||||||
android:resource="@style/NormalTheme"
|
|
||||||
/>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
<!-- Don't delete the meta-data below.
|
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
|
||||||
<meta-data
|
|
||||||
android:name="flutterEmbedding"
|
|
||||||
android:value="2" />
|
|
||||||
</application>
|
|
||||||
<!-- Required to query activities that can process text, see:
|
|
||||||
https://developer.android.com/training/package-visibility and
|
|
||||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
|
||||||
|
|
||||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
|
||||||
<queries>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
|
||||||
<data android:mimeType="text/plain"/>
|
|
||||||
</intent>
|
|
||||||
</queries>
|
|
||||||
</manifest>
|
|
@ -1,5 +0,0 @@
|
|||||||
package com.example.crab_ui
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity()
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:drawable="?android:colorBackground" />
|
|
||||||
|
|
||||||
<!-- You can insert your own image assets here -->
|
|
||||||
<!-- <item>
|
|
||||||
<bitmap
|
|
||||||
android:gravity="center"
|
|
||||||
android:src="@mipmap/launch_image" />
|
|
||||||
</item> -->
|
|
||||||
</layer-list>
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:drawable="@android:color/white" />
|
|
||||||
|
|
||||||
<!-- You can insert your own image assets here -->
|
|
||||||
<!-- <item>
|
|
||||||
<bitmap
|
|
||||||
android:gravity="center"
|
|
||||||
android:src="@mipmap/launch_image" />
|
|
||||||
</item> -->
|
|
||||||
</layer-list>
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
Binary file not shown.
Before Width: | Height: | Size: 544 B |
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
Binary file not shown.
Before Width: | Height: | Size: 442 B |
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
Binary file not shown.
Before Width: | Height: | Size: 721 B |
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
|
||||||
the Flutter engine draws its first frame -->
|
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
|
||||||
</style>
|
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
||||||
This theme determines the color of the Android Window while your
|
|
||||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
|
||||||
running.
|
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
|
||||||
the Flutter engine draws its first frame -->
|
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
|
||||||
</style>
|
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
||||||
This theme determines the color of the Android Window while your
|
|
||||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
|
||||||
running.
|
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,7 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
|
||||||
the Flutter tool needs it to communicate with the running application
|
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
|
||||||
-->
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
</manifest>
|
|
@ -1,18 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
|
||||||
android.useAndroidX=true
|
|
||||||
android.enableJetifier=true
|
|
@ -1,5 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
|
@ -1,25 +0,0 @@
|
|||||||
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"
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
HostUrl=https://onlinepngtools.com/
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
HostUrl=https://onlinepngtools.com/
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
HostUrl=https://onlinepngtools.com/
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
HostUrl=https://onlinepngtools.com/
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -5,6 +5,7 @@ import 'dart:async';
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
import 'collapsableEmails.dart';
|
||||||
|
|
||||||
import 'structs.dart';
|
import 'structs.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -12,15 +13,19 @@ import 'package:http/http.dart' as http;
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:ui_web' as ui;
|
import 'dart:ui_web' as ui;
|
||||||
import 'augment.dart';
|
import 'augment.dart';
|
||||||
import 'dart:html' as html;
|
// import 'dart:html' as html;
|
||||||
import 'dart:js' as js;
|
// import 'dart:js' as js;
|
||||||
|
import 'package:web/web.dart' as web;
|
||||||
|
import 'dart:js_interop' as js;
|
||||||
|
|
||||||
class ApiService {
|
class ApiService {
|
||||||
static String ip = "";
|
static String ip = "";
|
||||||
static String port = "";
|
static String port = "";
|
||||||
static List<AttachmentResponse> threadAttachments = [];
|
static List<AttachmentResponse> threadAttachments =
|
||||||
|
[]; //holds attachments of the thread
|
||||||
static String currFolder = "";
|
static String currFolder = "";
|
||||||
static List<String> currThread = [];
|
static List<String> currThread = []; //holds the email ids of the thread
|
||||||
|
static String currThreadID = ""; //picked an email it prints the threadID
|
||||||
|
|
||||||
Future<List<GetThreadResponse>> fetchEmailsFromFolder(
|
Future<List<GetThreadResponse>> fetchEmailsFromFolder(
|
||||||
String folder, int pagenitaion) async {
|
String folder, int pagenitaion) async {
|
||||||
@ -31,7 +36,6 @@ class ApiService {
|
|||||||
'offset': pagenitaion.toString(),
|
'offset': pagenitaion.toString(),
|
||||||
});
|
});
|
||||||
var response = await http.get(url);
|
var response = await http.get(url);
|
||||||
// print(response);
|
|
||||||
List<GetThreadResponse> allEmails = [];
|
List<GetThreadResponse> allEmails = [];
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
@ -45,6 +49,7 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
currFolder = folder;
|
||||||
return allEmails;
|
return allEmails;
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Failed to load threads');
|
throw Exception('Failed to load threads');
|
||||||
@ -96,7 +101,6 @@ class ApiService {
|
|||||||
List<dynamic> messagesJson = json.decode(response.body);
|
List<dynamic> messagesJson = json.decode(response.body);
|
||||||
List<SerializableMessage> messages =
|
List<SerializableMessage> messages =
|
||||||
messagesJson.map((mj) => SerializableMessage.fromJson(mj)).toList();
|
messagesJson.map((mj) => SerializableMessage.fromJson(mj)).toList();
|
||||||
|
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
print(response.statusCode);
|
print(response.statusCode);
|
||||||
@ -107,11 +111,13 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//returns the html for the email, it gets used in emailView
|
//returns the html for the email, it gets used in emailView
|
||||||
Future<String> fetchEmailContent(
|
Future<List<String>> fetchEmailContent(
|
||||||
List<String> IDsString, String emailFolder) async {
|
List<String> IDsString, String emailFolder) async {
|
||||||
String content = r"""
|
String content = r"""
|
||||||
""";
|
""";
|
||||||
|
List<String> HTMLofThread = [];
|
||||||
threadAttachments = [];
|
threadAttachments = [];
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//attaches email after email from a thread
|
//attaches email after email from a thread
|
||||||
@ -120,10 +126,12 @@ class ApiService {
|
|||||||
var response = await http.get(url);
|
var response = await http.get(url);
|
||||||
currThread.add(id);
|
currThread.add(id);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
counter += 1;
|
||||||
content += response.body;
|
content += response.body;
|
||||||
|
HTMLofThread.add(response.body);
|
||||||
try {
|
try {
|
||||||
List<AttachmentInfo> attachments = await getAttachmentsInfo(
|
List<AttachmentInfo> attachments =
|
||||||
emailFolder, id);
|
await getAttachmentsInfo(emailFolder, id);
|
||||||
for (var attachment in attachments) {
|
for (var attachment in attachments) {
|
||||||
//TODO: for each attachment creaate at the bottom a widget for each individual one
|
//TODO: for each attachment creaate at the bottom a widget for each individual one
|
||||||
threadAttachments
|
threadAttachments
|
||||||
@ -134,19 +142,82 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
content +=
|
content +=
|
||||||
"""<div id="JuanBedarramarker" style="width: 10px; height: 30px;"></div>""";
|
"""<div id="JuanBedarramarker" style="width: 10px; height: 30px;"></div>""";
|
||||||
content += "<hr>";
|
content += "<hr><p>end of email</p>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('_getEmailContent caught error: $e');
|
print('_getEmailContent caught error: $e');
|
||||||
}
|
}
|
||||||
return content;
|
// return content;
|
||||||
|
return HTMLofThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
// void _addMailBox async(BuildContext context){
|
Future<List<SerializableMessage>> threadsInSerializable(
|
||||||
// //add email folder
|
String thread_id) async {
|
||||||
// showDialog(context: context, builder: builder)
|
//actually a xyzwtv@gmail.com
|
||||||
// }
|
// grab all of the emails in thread anyways, for the future it'll come in handy // maybe not
|
||||||
|
var url = Uri.http('$ip:$port', 'get_thread_messages', {'id': thread_id});
|
||||||
|
try {
|
||||||
|
var response = await http.get(url);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
List json = jsonDecode(response.body);
|
||||||
|
List<SerializableMessage> serializableMessages = [];
|
||||||
|
for (var mail in json) {
|
||||||
|
serializableMessages.add(SerializableMessage.fromJson(mail));
|
||||||
|
}
|
||||||
|
return serializableMessages;
|
||||||
|
} else {
|
||||||
|
print(
|
||||||
|
"failed get request with status code ${response.statusCode}, and body ${response.body}");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("caught in threadInSerializable method error: $e");
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> moveEmail(
|
||||||
|
//only moves the first email of the thread //or perhaps should do the last
|
||||||
|
String fromFolder,
|
||||||
|
String thread_id,
|
||||||
|
String toFolder) async {
|
||||||
|
var url = Uri.http('$ip:$port', 'move_email');
|
||||||
|
|
||||||
|
List<SerializableMessage> mailsInSerializable =
|
||||||
|
await this.threadsInSerializable(thread_id);
|
||||||
|
|
||||||
|
if (mailsInSerializable.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializableMessage firstMail = mailsInSerializable[0];
|
||||||
|
|
||||||
|
Map<String, String> requestBody = {
|
||||||
|
'from': fromFolder,
|
||||||
|
'uid': firstMail.uid.toString(),
|
||||||
|
'to': toFolder,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
var response = await http.post(
|
||||||
|
url,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: jsonEncode(requestBody),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('response body ${response.body}');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print('error ${response.statusCode} ${response.body}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("failed trying to post move_email, with error: $e");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<String>> fetchFolders() async {
|
Future<List<String>> fetchFolders() async {
|
||||||
try {
|
try {
|
||||||
@ -182,6 +253,31 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> renameFolder(String oldFolder, String newFolder) async {
|
||||||
|
var url = Uri.http('$ip:$port', 'rename_folder');
|
||||||
|
Map<String, String> requestBody = {
|
||||||
|
'old_name': oldFolder,
|
||||||
|
'new_name': newFolder,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
var response = await http.post(
|
||||||
|
url,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: jsonEncode(requestBody),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('response body: ${response.body}');
|
||||||
|
} else {
|
||||||
|
print('Error: ${response.statusCode}, response body: ${response.body}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('error making post req: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> deleteFolder(String folderName) async {
|
Future<void> deleteFolder(String folderName) async {
|
||||||
var url = Uri.http('$ip:$port', 'delete_folder');
|
var url = Uri.http('$ip:$port', 'delete_folder');
|
||||||
|
|
||||||
@ -206,13 +302,6 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> logIn(String json) async {
|
Future<bool> logIn(String json) async {
|
||||||
// var url = Uri.https('')
|
|
||||||
// try{
|
|
||||||
// String response = await http.post(
|
|
||||||
// url
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +340,6 @@ class ApiService {
|
|||||||
var response = await http.get(url);
|
var response = await http.get(url);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var result = response.body;
|
var result = response.body;
|
||||||
// print(result);
|
|
||||||
Map<String, dynamic> attachmentData = json.decode(result);
|
Map<String, dynamic> attachmentData = json.decode(result);
|
||||||
AttachmentResponse data = AttachmentResponse.fromJson(attachmentData);
|
AttachmentResponse data = AttachmentResponse.fromJson(attachmentData);
|
||||||
print("data $data");
|
print("data $data");
|
||||||
@ -263,87 +351,88 @@ class ApiService {
|
|||||||
return AttachmentResponse(name: "error", data: Uint8List(0));
|
return AttachmentResponse(name: "error", data: Uint8List(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getMarkerPosition() async {
|
//TODO: MOVE THIS INTO WEB
|
||||||
//this is so we can put a widget right below each email, but the way how the email content is generated
|
// Future<List<Map<String, dynamic>>> getMarkerPosition() async {
|
||||||
//leads to problems as for a) the html is added one right after the other in one iframe, b)
|
// //this is so we can put a widget right below each email, but the way how the email content is generated
|
||||||
// if it was multiple iframes then the scrolling to jump would not work as expected
|
// //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
|
||||||
|
|
||||||
|
// print("marker called");
|
||||||
print("marker called");
|
// // JavaScript code embedded as a string
|
||||||
// JavaScript code embedded as a string
|
// String jsCode = '''
|
||||||
String jsCode = '''
|
// (async function waitForIframeAndMarkers() {
|
||||||
(async function waitForIframeAndMarkers() {
|
// try {
|
||||||
try {
|
// return await new Promise((resolve) => {
|
||||||
return await new Promise((resolve) => {
|
// const interval = setInterval(() => {
|
||||||
const interval = setInterval(() => {
|
// console.log("⏳ Checking for iframe...");
|
||||||
console.log("⏳ Checking for iframe...");
|
// var iframe = document.getElementsByTagName('iframe')[0];
|
||||||
var iframe = document.getElementsByTagName('iframe')[0];
|
// if (iframe && iframe.contentDocument) {
|
||||||
if (iframe && iframe.contentDocument) {
|
// console.log("✅ Iframe found!");
|
||||||
console.log("✅ Iframe found!");
|
// var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||||
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
// var markers = iframeDoc.querySelectorAll('[id^="JuanBedarramarker"]');
|
||||||
var markers = iframeDoc.querySelectorAll('[id^="JuanBedarramarker"]');
|
// if (markers.length > 0) {
|
||||||
if (markers.length > 0) {
|
// console.log(`✅ Found markers in the iframe.`);
|
||||||
console.log(`✅ Found markers in the iframe.`);
|
// var positions = [];
|
||||||
var positions = [];
|
// markers.forEach((marker) => {
|
||||||
markers.forEach((marker) => {
|
// var rect = marker.getBoundingClientRect();
|
||||||
var rect = marker.getBoundingClientRect();
|
// positions.push({
|
||||||
positions.push({
|
// id: marker.id,
|
||||||
id: marker.id,
|
// x: rect.left + window.scrollX,
|
||||||
x: rect.left + window.scrollX,
|
// y: rect.top + window.scrollY,
|
||||||
y: rect.top + window.scrollY,
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// console.log("📌 Marker positions:", positions);
|
||||||
console.log("📌 Marker positions:", positions);
|
// clearInterval(interval);
|
||||||
clearInterval(interval);
|
// resolve(JSON.stringify(positions)); // Ensure proper JSON string
|
||||||
resolve(JSON.stringify(positions)); // Ensure proper JSON string
|
// } else {
|
||||||
} else {
|
// console.log("❌ No markers found yet.");
|
||||||
console.log("❌ No markers found yet.");
|
// }
|
||||||
}
|
// } else {
|
||||||
} else {
|
// console.log("❌ Iframe not found or not loaded yet.");
|
||||||
console.log("❌ Iframe not found or not loaded yet.");
|
// }
|
||||||
}
|
// }, 200);
|
||||||
}, 200);
|
// });
|
||||||
});
|
// } catch (error) {
|
||||||
} catch (error) {
|
// console.error("JS Error:", error);
|
||||||
console.error("JS Error:", error);
|
// throw error; // Propagate error to Dart
|
||||||
throw error; // Propagate error to Dart
|
// }
|
||||||
}
|
// })();
|
||||||
})();
|
// ''';
|
||||||
''';
|
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
// Execute the JavaScript code using eval
|
// // Execute the JavaScript code using eval
|
||||||
final result = await js.context.callMethod('eval', [jsCode]);
|
// // final result = await js.context.callMethod('eval', [jsCode]);
|
||||||
|
|
||||||
if (result != null && result is String) {
|
// if (result != null && result is String) {
|
||||||
print("Result received: $result");
|
// print("Result received: $result");
|
||||||
|
|
||||||
// Parse the JSON string returned by JavaScript into a Dart list of maps
|
// // Parse the JSON string returned by JavaScript into a Dart list of maps
|
||||||
final List<dynamic> parsedResult = jsonDecode(result);
|
// final List<dynamic> parsedResult = jsonDecode(result);
|
||||||
var positions = List<Map<String, dynamic>>.from(parsedResult);
|
// var positions = List<Map<String, dynamic>>.from(parsedResult);
|
||||||
print("positions put on");
|
// print("positions put on");
|
||||||
print(positions);
|
// print(positions);
|
||||||
return positions;
|
// return positions;
|
||||||
} else {
|
// } else {
|
||||||
print("result is null or not a string");
|
// print("result is null or not a string");
|
||||||
}
|
// }
|
||||||
} catch (e, stackTrace) {
|
// } catch (e, stackTrace) {
|
||||||
print("Error executing JavaScript: $e");
|
// print("Error executing JavaScript: $e");
|
||||||
print(stackTrace);
|
// print(stackTrace);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return [];
|
// return [];
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
class EmailView extends StatefulWidget {
|
class EmailView extends StatefulWidget {
|
||||||
final String emailContent;
|
final List<String> emailContent;
|
||||||
final String from;
|
final String from;
|
||||||
final String name;
|
final String name;
|
||||||
final String to;
|
final String to;
|
||||||
final String subject;
|
final String subject;
|
||||||
final String date;
|
final String date;
|
||||||
final String id;
|
final String id;
|
||||||
|
final List<String> messages;
|
||||||
|
|
||||||
const EmailView({
|
const EmailView({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -354,6 +443,7 @@ class EmailView extends StatefulWidget {
|
|||||||
required this.subject,
|
required this.subject,
|
||||||
required this.date,
|
required this.date,
|
||||||
required this.id,
|
required this.id,
|
||||||
|
required this.messages,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
_EmailViewState createState() => _EmailViewState();
|
_EmailViewState createState() => _EmailViewState();
|
||||||
@ -363,7 +453,7 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
//html css rendering thing
|
//html css rendering thing
|
||||||
late Key iframeKey;
|
late Key iframeKey;
|
||||||
late String currentContent;
|
late String currentContent;
|
||||||
late String viewTypeId;
|
late String viewTypeId; //make this a list too???
|
||||||
Future<List<Map<String, dynamic>>>? _markerPositionsFuture;
|
Future<List<Map<String, dynamic>>>? _markerPositionsFuture;
|
||||||
// TextEditingController _jumpController = TextEditingController();
|
// TextEditingController _jumpController = TextEditingController();
|
||||||
final hardcodedMarkers = [
|
final hardcodedMarkers = [
|
||||||
@ -371,29 +461,40 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
{'id': 'marker2', 'x': 150, 'y': 200},
|
{'id': 'marker2', 'x': 150, 'y': 200},
|
||||||
{'id': 'marker3', 'x': 250, 'y': 300},
|
{'id': 'marker3', 'x': 250, 'y': 300},
|
||||||
];
|
];
|
||||||
ApiService _apiService = ApiService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
String currentContent = widget.emailContent;
|
print("thread id? ${widget.id}");
|
||||||
viewTypeId = "iframe-${DateTime.now().millisecondsSinceEpoch}";
|
List<String> currentContent = widget
|
||||||
_registerViewFactory(currentContent);
|
.emailContent; //html of the email/ actually entire thread, gives me little space to play in between
|
||||||
_markerPositionsFuture = ApiService().getMarkerPosition();
|
// 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(String currentContent) {
|
// void _registerViewFactory(List<String> currentContent) { // i think this doesnt work anymore
|
||||||
setState(() {
|
// setState(() { //update to do item per item
|
||||||
viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
|
// // each item to have itsviewtype ID
|
||||||
ui.platformViewRegistry.registerViewFactory(
|
// // is this necessarey here??
|
||||||
viewTypeId,
|
|
||||||
(int viewId) => html.IFrameElement()
|
// //could just move to collapsable
|
||||||
..width = '100%'
|
|
||||||
..height = '100%'
|
// viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
|
||||||
..srcdoc = currentContent
|
// final emailHTML = web.document.createElement('div') as web.HTMLDivElement
|
||||||
..style.border = 'none');
|
// ..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) {
|
void _scrollToNumber(String spanId) {
|
||||||
AugmentClasses.handleJump(spanId);
|
AugmentClasses.handleJump(spanId);
|
||||||
@ -403,7 +504,8 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// print(currentContent);
|
// print("thread id ${widget.id}");
|
||||||
|
ApiService.currThreadID = widget.id;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.name),
|
title: Text(widget.name),
|
||||||
@ -422,7 +524,7 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
// <h1>Welcome to My Website</h1>
|
// <h1>Welcome to My Website</h1>
|
||||||
// <p>This is a simple HTML page.</p>
|
// <p>This is a simple HTML page.</p>
|
||||||
// <h2>What is HTML?</h2>
|
// <h2>What is HTML?</h2>
|
||||||
// <p>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).</p>
|
// <p>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).</p>
|
||||||
// <h3>Here's a simple list:</h3>
|
// <h3>Here's a simple list:</h3>
|
||||||
// <ul>
|
// <ul>
|
||||||
// <li>HTML elements are the building blocks of HTML pages</li>
|
// <li>HTML elements are the building blocks of HTML pages</li>
|
||||||
@ -471,59 +573,66 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: HtmlElementView(
|
child: CollapsableEmails(
|
||||||
key: UniqueKey(),
|
//change here
|
||||||
viewType: viewTypeId,
|
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
|
// Overlay widgets dynamically based on marker positions
|
||||||
FutureBuilder<List<Map<String, dynamic>>>(
|
// FutureBuilder<List<Map<String, dynamic>>>(
|
||||||
future: _markerPositionsFuture,
|
// future: _markerPositionsFuture,
|
||||||
builder: (context, snapshot) {
|
// builder: (context, snapshot) {
|
||||||
print("FutureBuilder state: ${snapshot.connectionState}");
|
// print("FutureBuilder state: ${snapshot.connectionState}");
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
// if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return Center(child: CircularProgressIndicator());
|
// return Center(child: CircularProgressIndicator());
|
||||||
}
|
// }
|
||||||
if (snapshot.hasError) {
|
// if (snapshot.hasError) {
|
||||||
print("Error in FutureBuilder: ${snapshot.error}");
|
// print("Error in FutureBuilder: ${snapshot.error}");
|
||||||
return Center(child: Text('error loading markers'));
|
// return Center(child: Text('error loading markers'));
|
||||||
}
|
// }
|
||||||
if (snapshot.hasData && snapshot.data != null) {
|
// if (snapshot.hasData && snapshot.data != null) {
|
||||||
final markers = snapshot.data!;
|
// final markers = snapshot.data!;
|
||||||
return Stack(
|
// return Stack(
|
||||||
children: markers.map((marker) {
|
// children: markers.map((marker) {
|
||||||
return Positioned(
|
// return Positioned(
|
||||||
left: marker['x'].toDouble(),
|
// left: marker['x'].toDouble(),
|
||||||
top: marker['y'].toDouble(),
|
// top: marker['y'].toDouble(),
|
||||||
child: GestureDetector(
|
// child: GestureDetector(
|
||||||
onTap: () {
|
// onTap: () {
|
||||||
print('Tapped on ${marker['id']}');
|
// print('Tapped on ${marker['id']}');
|
||||||
},
|
// },
|
||||||
child: Container(
|
// child: Container(
|
||||||
width: 50,
|
// width: 50,
|
||||||
height: 50,
|
// height: 50,
|
||||||
color: Colors.red,
|
// color: Colors.red,
|
||||||
child: Center(
|
// child: Center(
|
||||||
child: Text(
|
// child: Text(
|
||||||
marker['id'],
|
// marker['id'],
|
||||||
style: TextStyle(color: Colors.white),
|
// style: TextStyle(color: Colors.white),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}).toList(),
|
// }).toList(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
return SizedBox.shrink(); // No markers found
|
// return SizedBox.shrink(); // No markers found
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
// Red widget overlay
|
// Red widget overlay
|
||||||
// Positioned(
|
// Positioned(
|
||||||
// left: 8, // Adjust based on your desired position
|
// left: 8, // Adjust based on your desired position
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:html' as html;
|
import 'dart:html' as html;
|
||||||
|
import 'package:web/web.dart' as web;
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'structs.dart';
|
import 'structs.dart';
|
||||||
import 'package:file_saver/file_saver.dart';
|
import 'package:file_saver/file_saver.dart';
|
||||||
|
234
lib/augment.dart
234
lib/augment.dart
@ -1,10 +1,14 @@
|
|||||||
|
// import 'dart:ffi';
|
||||||
|
|
||||||
import 'package:crab_ui/api_service.dart';
|
import 'package:crab_ui/api_service.dart';
|
||||||
import 'package:crab_ui/attachmentDownload.dart';
|
import 'package:crab_ui/attachmentDownload.dart';
|
||||||
import 'package:crab_ui/structs.dart';
|
import 'package:crab_ui/structs.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'dart:html' as html;
|
// import 'dart:html' as html;
|
||||||
import 'dart:js' as js;
|
// import 'dart:js' as js;
|
||||||
|
import 'package:web/web.dart' as web;
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'attachmentWidget.dart';
|
import 'attachmentWidget.dart';
|
||||||
|
|
||||||
@ -88,6 +92,12 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
|
|||||||
onPressed: AugmentClasses.handleStop,
|
onPressed: AugmentClasses.handleStop,
|
||||||
child: Text('Stop'),
|
child: Text('Stop'),
|
||||||
),
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
AugmentClasses.handleMove(context);
|
||||||
|
},
|
||||||
|
child: Text('Move'),
|
||||||
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
PopupMenuButton<String>(
|
PopupMenuButton<String>(
|
||||||
onSelected: (String value) {
|
onSelected: (String value) {
|
||||||
@ -212,8 +222,9 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AugmentClasses {
|
class AugmentClasses {
|
||||||
ApiService _apiService = ApiService();
|
|
||||||
static OverlayEntry? _overlayEntry;
|
static OverlayEntry? _overlayEntry;
|
||||||
|
static String? selectedFolder; // Manage selected folder at the class level
|
||||||
|
|
||||||
static void handleHome(BuildContext context) {
|
static void handleHome(BuildContext context) {
|
||||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||||
}
|
}
|
||||||
@ -222,11 +233,166 @@ class AugmentClasses {
|
|||||||
print("reload");
|
print("reload");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget listOfFolders(BuildContext context) {
|
||||||
|
//list the emails and make some sort of selection box
|
||||||
|
return FutureBuilder<List<String>>(
|
||||||
|
future: ApiService().fetchFolders(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
} else if (snapshot.hasError) {
|
||||||
|
return Center(child: Text('Error: ${snapshot.error}'));
|
||||||
|
} else if (snapshot.hasData) {
|
||||||
|
String? selectedFolder; // Declare the selected folder state
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
children: snapshot.data!.map((folder) {
|
||||||
|
return RadioListTile<String>(
|
||||||
|
title: Text(folder),
|
||||||
|
value: folder,
|
||||||
|
groupValue: selectedFolder,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
setState(() {
|
||||||
|
selectedFolder = value; // Update the selected folder
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: Text('No folders found.'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleMove(BuildContext context) async {
|
||||||
|
print("current folder: ${ApiService.currFolder}");
|
||||||
|
final overlay = Overlay.of(context);
|
||||||
|
String? selectedFolder; // Variable to store the selected folder
|
||||||
|
|
||||||
|
_overlayEntry = OverlayEntry(
|
||||||
|
builder: (context) => Stack(
|
||||||
|
children: [
|
||||||
|
// Dimmed background
|
||||||
|
Container(
|
||||||
|
color: Colors.black54,
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
),
|
||||||
|
// Focused content window
|
||||||
|
PointerInterceptor(
|
||||||
|
child: Center(
|
||||||
|
child: Material(
|
||||||
|
elevation: 8,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400,
|
||||||
|
maxHeight: 500,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Move email from folder ${ApiService.currFolder} to:',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
Divider(height: 1),
|
||||||
|
Expanded(
|
||||||
|
child: FutureBuilder<List<String>>(
|
||||||
|
future: ApiService().fetchFolders(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState ==
|
||||||
|
ConnectionState.waiting) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator());
|
||||||
|
} else if (snapshot.hasError) {
|
||||||
|
return Center(
|
||||||
|
child: Text('Error: ${snapshot.error}'));
|
||||||
|
} else if (snapshot.hasData) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
children: snapshot.data!.map((folder) {
|
||||||
|
return RadioListTile<String>(
|
||||||
|
title: Text(folder),
|
||||||
|
value: folder,
|
||||||
|
groupValue: selectedFolder,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
setState(() {
|
||||||
|
selectedFolder =
|
||||||
|
value; // Update the selected folder
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(
|
||||||
|
child: Text('No folders found.'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(height: 1),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Handle Accept button
|
||||||
|
if (selectedFolder != null) {
|
||||||
|
print("Selected folder: $selectedFolder");
|
||||||
|
// Store the selected folder or perform any action
|
||||||
|
// ApiService.currFolder = selectedFolder!;
|
||||||
|
ApiService().moveEmail(ApiService.currFolder,
|
||||||
|
ApiService.currThreadID, selectedFolder!);
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
} else {
|
||||||
|
print("No folder selected");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text('Accept'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Handle Cancel button
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
},
|
||||||
|
child: Text('Cancel'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_overlayEntry != null) {
|
||||||
|
overlay.insert(_overlayEntry!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void handleImages(BuildContext context) {
|
static void handleImages(BuildContext context) {
|
||||||
|
//rename to handle attachments
|
||||||
print("Images button pressed");
|
print("Images button pressed");
|
||||||
final overlay = Overlay.of(context);
|
final overlay = Overlay.of(context);
|
||||||
final renderBox = context.findRenderObject() as RenderBox;
|
// final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final offset = renderBox.localToGlobal(Offset.zero);
|
// final offset = renderBox.localToGlobal(Offset.zero);
|
||||||
|
|
||||||
_overlayEntry = OverlayEntry(
|
_overlayEntry = OverlayEntry(
|
||||||
builder: (context) => Stack(
|
builder: (context) => Stack(
|
||||||
@ -295,11 +461,10 @@ class AugmentClasses {
|
|||||||
static List<Widget> _buildMenuItem(BuildContext context) {
|
static List<Widget> _buildMenuItem(BuildContext context) {
|
||||||
List<Widget> listOfFiles = [];
|
List<Widget> listOfFiles = [];
|
||||||
for (AttachmentResponse file in ApiService.threadAttachments) {
|
for (AttachmentResponse file in ApiService.threadAttachments) {
|
||||||
listOfFiles.add(
|
listOfFiles.add(ListTile(
|
||||||
ListTile (
|
|
||||||
leading: Icon(Icons.file_present),
|
leading: Icon(Icons.file_present),
|
||||||
title: Text(file.name.toString()),
|
title: Text(file.name.toString()),
|
||||||
trailing: GestureDetector(
|
trailing: GestureDetector(
|
||||||
child: Icon(Icons.download),
|
child: Icon(Icons.download),
|
||||||
onTap: () => Attachmentdownload().saveFile(file),
|
onTap: () => Attachmentdownload().saveFile(file),
|
||||||
),
|
),
|
||||||
@ -349,7 +514,7 @@ class AugmentClasses {
|
|||||||
console.log('Iframe not found or not loaded.');
|
console.log('Iframe not found or not loaded.');
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
js.context.callMethod('eval', [js_code]);
|
// js.context.callMethod('eval', [js_code]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void invisibility(String htmlClass) {}
|
static void invisibility(String htmlClass) {}
|
||||||
@ -357,7 +522,7 @@ class AugmentClasses {
|
|||||||
static Future<void> JumpButton(BuildContext context) async {
|
static Future<void> JumpButton(BuildContext context) async {
|
||||||
// FocusNode textFieldFocusNode = FocusNode();
|
// FocusNode textFieldFocusNode = FocusNode();
|
||||||
|
|
||||||
AugmentClasses.disableIframePointerEvents();
|
// AugmentClasses.disableIframePointerEvents();
|
||||||
await showDialog(
|
await showDialog(
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
// barrierColor: Colors.yellow,
|
// barrierColor: Colors.yellow,
|
||||||
@ -439,7 +604,7 @@ class AugmentClasses {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
).then((_) {
|
).then((_) {
|
||||||
AugmentClasses.enableIframePointerEvents();
|
// AugmentClasses.enableIframePointerEvents();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,7 +614,7 @@ class AugmentClasses {
|
|||||||
bool blankLines = false;
|
bool blankLines = false;
|
||||||
bool numbering = false;
|
bool numbering = false;
|
||||||
bool statementSignatures = false;
|
bool statementSignatures = false;
|
||||||
AugmentClasses.disableIframePointerEvents();
|
// AugmentClasses.disableIframePointerEvents();
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => Container(
|
builder: (context) => Container(
|
||||||
@ -560,7 +725,7 @@ class AugmentClasses {
|
|||||||
ElevatedButton(onPressed: () {}, child: Text('OK')),
|
ElevatedButton(onPressed: () {}, child: Text('OK')),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
AugmentClasses.disableIframePointerEvents();
|
// AugmentClasses.disableIframePointerEvents();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text('Cancel')),
|
child: Text('Cancel')),
|
||||||
@ -574,7 +739,7 @@ class AugmentClasses {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)).then((_) {
|
)).then((_) {
|
||||||
AugmentClasses.enableIframePointerEvents(); // may be useless?
|
// AugmentClasses.enableIframePointerEvents(); // may be useless?
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,7 +749,7 @@ class AugmentClasses {
|
|||||||
//idea is to search in file, extract the <p> tags that contain these
|
//idea is to search in file, extract the <p> tags that contain these
|
||||||
//words and highlight, then when zoom, you just jump to that paragraph
|
//words and highlight, then when zoom, you just jump to that paragraph
|
||||||
|
|
||||||
AugmentClasses.disableIframePointerEvents();
|
// AugmentClasses.disableIframePointerEvents();
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => Container(
|
builder: (context) => Container(
|
||||||
@ -612,22 +777,27 @@ class AugmentClasses {
|
|||||||
)))));
|
)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disableIframePointerEvents() {
|
// static void disableIframePointerEvents() {
|
||||||
//pretty sure these dont work
|
// //pretty sure these dont work
|
||||||
final iframes = html.document.getElementsByTagName('iframe');
|
// // final iframes = html.document.getElementsByTagName('iframe');
|
||||||
for (var iframe in iframes) {
|
// final iframes = web.document.getElementsByTagName('iframe');
|
||||||
if (iframe is html.Element) {
|
// for (var iframe in iframes) {
|
||||||
iframe.style.pointerEvents = 'none'; // Disable pointer events
|
// // if (iframe is html.Element) {
|
||||||
}
|
// // iframe.style.pointerEvents = 'none'; // Disable pointer events
|
||||||
}
|
// if (iframe is web.Element) {
|
||||||
}
|
// iframe.
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
static void enableIframePointerEvents() {
|
// static void enableIframePointerEvents() {
|
||||||
final iframes = html.document.getElementsByTagName('iframe');
|
// // final iframes = html.document.getElementsByTagName('iframe');
|
||||||
for (var iframe in iframes) {
|
// final iframes = html.document.getElementsByTagName('iframe');
|
||||||
if (iframe is html.Element) {
|
|
||||||
iframe.style.pointerEvents = 'auto'; // Re-enable pointer events
|
// for (var iframe in iframes) {
|
||||||
}
|
// if (iframe is html.Element) {
|
||||||
}
|
// iframe.style.pointerEvents = 'auto'; // Re-enable pointer events
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
132
lib/collapsableEmails.dart
Normal file
132
lib/collapsableEmails.dart
Normal file
@ -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<String> thread; // email id's in the form xyz@gmail.com
|
||||||
|
final List<String> threadHTML;
|
||||||
|
final String threadIDs;
|
||||||
|
|
||||||
|
CollapsableEmails(
|
||||||
|
{required this.thread,
|
||||||
|
required this.threadHTML,
|
||||||
|
required this.threadIDs});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CollapsableEmails> createState() => _CollapsableEmailsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CollapsableEmailsState extends State<CollapsableEmails> {
|
||||||
|
List<String> emailsHTML = []; //html of the emails in the thread
|
||||||
|
// build attachments with the forldar name and id
|
||||||
|
Set<int> _expandedEmails = {}; //open emails
|
||||||
|
List viewtypeIDs = []; //IDs of the viewtypes, order matters
|
||||||
|
List heightOfViewTypes = []; //the height of each viewtype
|
||||||
|
List<SerializableMessage> emailsInThread = [];
|
||||||
|
bool _isLoaded = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// TODO: implement initState
|
||||||
|
super.initState();
|
||||||
|
_registerViewFactory(widget.threadHTML);
|
||||||
|
_serializableData(widget.threadIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _registerViewFactory(List<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 = 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());
|
||||||
|
}
|
||||||
|
}
|
284
lib/email.dart
284
lib/email.dart
@ -1,136 +1,148 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'api_service.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'structs.dart';
|
import 'api_service.dart';
|
||||||
|
import 'structs.dart';
|
||||||
class EmailListScreen extends StatelessWidget {
|
|
||||||
final List<GetThreadResponse> emails;
|
class EmailListScreen extends StatelessWidget {
|
||||||
final Future<String> Function(List<String>, String) getEmailContent;
|
final List<GetThreadResponse> emails;
|
||||||
final String folder;
|
final Future<List<String>> Function(List<String>, String) getEmailContent;
|
||||||
|
final String folder;
|
||||||
|
|
||||||
EmailListScreen({required this.emails, required this.getEmailContent, required this.folder});
|
EmailListScreen(
|
||||||
//fix the email list
|
{required this.emails,
|
||||||
@override
|
required this.getEmailContent,
|
||||||
Widget build(BuildContext context) {
|
required this.folder});
|
||||||
return Scaffold(
|
//fix the email list
|
||||||
body: ListView.separated(
|
@override
|
||||||
itemCount: emails.length,
|
Widget build(BuildContext context) {
|
||||||
itemBuilder: (context, index) {
|
return Scaffold(
|
||||||
final email = emails[index];
|
body: ListView.separated(
|
||||||
return ListTile(
|
itemCount: emails.length,
|
||||||
title: Text(email.from_name,
|
itemBuilder: (context, index) {
|
||||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
final email = emails[index];
|
||||||
subtitle: Column(
|
return ListTile(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
title: Text(email.from_name,
|
||||||
children: [Text(email.subject)],
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
),
|
subtitle: Column(
|
||||||
trailing: Text(email.date.toString()),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
onTap: () async {
|
children: [Text(email.subject)],
|
||||||
String emailContent = await getEmailContent(email.messages, folder);
|
),
|
||||||
Navigator.push(
|
trailing: Text(email.date.toString()),
|
||||||
context,
|
onTap: () async {
|
||||||
MaterialPageRoute(
|
List<String> emailContent = // list of the html
|
||||||
builder: (context) => EmailView(
|
await getEmailContent(email.messages, folder);
|
||||||
emailContent: emailContent,
|
// print("this is what email.messages look like in email.dart ${email.messages}");
|
||||||
from: email.from_address,
|
// List<String> emailIds = email.messages;
|
||||||
name: email.from_name,
|
|
||||||
to: email.to.toString(),
|
print(email.messages); //email ids of the thread
|
||||||
subject: email.subject,
|
Navigator.push(
|
||||||
date: email.date.toString(),
|
context,
|
||||||
id: email.id.toString(),
|
MaterialPageRoute(
|
||||||
),
|
// could call collapsable and inside collable each calls email view?
|
||||||
),
|
builder: (context) => EmailView(
|
||||||
);
|
emailContent: emailContent,
|
||||||
},
|
from: email.from_address,
|
||||||
);
|
name: email.from_name,
|
||||||
},
|
to: email.to.toString(),
|
||||||
separatorBuilder: (context, index) => Divider(),
|
subject: email.subject,
|
||||||
),
|
date: email.date.toString(),
|
||||||
);
|
id: email.id.toString(), //i think this is thread id?
|
||||||
}
|
messages: email.messages,
|
||||||
}
|
),
|
||||||
|
),
|
||||||
// ignore: must_be_immutable
|
);
|
||||||
class EmailPage extends StatefulWidget {
|
},
|
||||||
EmailPage({Key? key}) : super(key: key);
|
);
|
||||||
String selectedFolder = "INBOX"; //starter
|
},
|
||||||
int offset = 0;
|
separatorBuilder: (context, index) => Divider(),
|
||||||
int page = 1;
|
),
|
||||||
|
);
|
||||||
@override
|
}
|
||||||
EmailPageState createState() => EmailPageState();
|
}
|
||||||
}
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
class EmailPageState extends State<EmailPage> {
|
class EmailPage extends StatefulWidget {
|
||||||
final ApiService apiService = ApiService();
|
EmailPage({Key? key}) : super(key: key);
|
||||||
List<GetThreadResponse> emails = [];
|
String selectedFolder = "INBOX"; //starter
|
||||||
int page = 1;
|
int offset = 0;
|
||||||
bool isBackDisabled = false;
|
int page = 1;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
EmailPageState createState() => EmailPageState();
|
||||||
super.initState();
|
}
|
||||||
widget.page = page;
|
|
||||||
isBackDisabled = true;
|
class EmailPageState extends State<EmailPage> {
|
||||||
}
|
final ApiService apiService = ApiService();
|
||||||
|
List<GetThreadResponse> emails = [];
|
||||||
void updateSelectedFolder(String folder) {
|
ValueNotifier<int> currentPageNotifier = ValueNotifier<int>(1);
|
||||||
setState(() {
|
int page = 1;
|
||||||
widget.selectedFolder = folder;
|
bool isBackDisabled = false;
|
||||||
});
|
|
||||||
print(folder);
|
@override
|
||||||
_fetchEmails();
|
void initState() {
|
||||||
}
|
super.initState();
|
||||||
|
widget.page = page;
|
||||||
String getPage() {
|
isBackDisabled = true;
|
||||||
return widget.page.toString();
|
_fetchEmails();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePagenation(String option) {
|
String getPage() => widget.page.toString();
|
||||||
if (option == "next") {
|
bool get backDisabled => isBackDisabled;
|
||||||
setState(() {
|
|
||||||
widget.offset += 50;
|
void updateSelectedFolder(String folder) {
|
||||||
widget.page += 1;
|
setState(() {
|
||||||
isBackDisabled = false;
|
widget.selectedFolder = folder;
|
||||||
});
|
});
|
||||||
} else if (option == "back") {
|
print(folder);
|
||||||
setState(() {
|
_fetchEmails();
|
||||||
widget.offset -= 50;
|
}
|
||||||
widget.page -= 1;
|
|
||||||
if (widget.page == 1) {
|
void updatePagenation(String option) {
|
||||||
isBackDisabled = true;
|
if (option == "next") {
|
||||||
print("back dis");
|
setState(() {
|
||||||
}
|
widget.offset += 50;
|
||||||
});
|
widget.page += 1;
|
||||||
}
|
currentPageNotifier.value = widget.page;
|
||||||
// print(currentPage);
|
isBackDisabled = false;
|
||||||
_fetchEmails();
|
});
|
||||||
}
|
} else if (option == "back") {
|
||||||
|
setState(() {
|
||||||
void _fetchEmails() async {
|
widget.offset -= 50;
|
||||||
// print(selectedFolder)
|
widget.page -= 1;
|
||||||
try {
|
currentPageNotifier.value = widget.page;
|
||||||
List<GetThreadResponse> fetchedEmails = await apiService
|
if (widget.page == 1) {
|
||||||
.fetchEmailsFromFolder(widget.selectedFolder, widget.offset);
|
isBackDisabled = true;
|
||||||
if (!mounted) return;
|
print("back disabled");
|
||||||
|
}
|
||||||
setState(() {
|
});
|
||||||
emails = fetchedEmails; // Update the list of emails
|
}
|
||||||
});
|
// print(currentPage);
|
||||||
} catch (e) {
|
print(widget.page);
|
||||||
print('Error fetching emails: $e');
|
_fetchEmails();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
void _fetchEmails() async {
|
||||||
@override
|
try {
|
||||||
Widget build(BuildContext context) {
|
List<GetThreadResponse> fetchedEmails = await apiService
|
||||||
_fetchEmails();
|
.fetchEmailsFromFolder(widget.selectedFolder, widget.offset);
|
||||||
return Scaffold(
|
if (!mounted) return;
|
||||||
body: EmailListScreen(
|
|
||||||
emails: emails,
|
setState(() {
|
||||||
getEmailContent: apiService.fetchEmailContent,
|
emails = fetchedEmails; // Update the list of emails
|
||||||
folder: widget.selectedFolder,//try to grab from it directly
|
});
|
||||||
),
|
} catch (e) {
|
||||||
);
|
print('Error fetching emails: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: EmailListScreen(
|
||||||
|
emails: emails,
|
||||||
|
getEmailContent: apiService.fetchEmailContent,
|
||||||
|
folder: widget.selectedFolder, //try to grab from it directly
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ class FolderDrawer extends StatefulWidget {
|
|||||||
|
|
||||||
class _FolderDrawerState extends State<FolderDrawer> {
|
class _FolderDrawerState extends State<FolderDrawer> {
|
||||||
List<String> folders = [];
|
List<String> folders = [];
|
||||||
|
final TextEditingController _renameController = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -48,7 +49,7 @@ class _FolderDrawerState extends State<FolderDrawer> {
|
|||||||
icon: Icon(Icons.more_vert),
|
icon: Icon(Icons.more_vert),
|
||||||
onPressed: () => {
|
onPressed: () => {
|
||||||
///show options
|
///show options
|
||||||
_showOptions(context)
|
_showOptions(context, folder)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -65,7 +66,7 @@ class _FolderDrawerState extends State<FolderDrawer> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return NewMailbox(apiService: widget.apiService);
|
return NewMailbox(apiService: widget.apiService);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Navigator.of(context).pop();
|
// Navigator.of(context).pop();
|
||||||
@ -76,46 +77,114 @@ class _FolderDrawerState extends State<FolderDrawer> {
|
|||||||
title: Text('Refresh Folders'),
|
title: Text('Refresh Folders'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_fetchFolders(); // Refresh folders when this tile is tapped
|
_fetchFolders(); // Refresh folders when this tile is tapped
|
||||||
Navigator.pop(context); // Close the drawer after refresh
|
// rebuild
|
||||||
|
|
||||||
|
// Navigator.pop(context); // Close the drawer after refresh
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
void _showOptions(BuildContext context) async {
|
|
||||||
final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
|
|
||||||
|
|
||||||
await showMenu<String>(
|
|
||||||
context: context,
|
|
||||||
position: RelativeRect.fromLTRB(100, 100, overlay.size.width, overlay.size.height),
|
|
||||||
items: <PopupMenuEntry<String>>[
|
|
||||||
PopupMenuItem<String>(
|
|
||||||
value: 'Rename',
|
|
||||||
child: Text('Rename Folder'),
|
|
||||||
),
|
|
||||||
PopupMenuItem<String>(
|
|
||||||
value: 'Delete',
|
|
||||||
child: Text('Delete Folder'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).then((value) {
|
|
||||||
// Handle the action based on the selected menu item
|
|
||||||
if (value == 'Rename') {
|
|
||||||
// Logic for renaming the folder
|
|
||||||
print('Rename folder');
|
|
||||||
} else if (value == 'Delete') {
|
|
||||||
// Logic for deleting the folder
|
|
||||||
print('Delete folder');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Future<bool> _renameDialog(String oldFolder) async {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("Rename Mailbox"),
|
||||||
|
content: TextField(
|
||||||
|
controller: _renameController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "New Name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
String newfolderName = _renameController.text;
|
||||||
|
if (newfolderName.isNotEmpty) {
|
||||||
|
//make an and to make sure there's two folders with the same name
|
||||||
|
ApiService().renameFolder(oldFolder, newfolderName);
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Rename"),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> doubleCheckDelete(String folderTobeDeleted) async {
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("Confirm delete of: $folderTobeDeleted"),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
ApiService().deleteFolder(folderTobeDeleted);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text("Yes")),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text("No")),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showOptions(BuildContext context, String folderName) async {
|
||||||
|
final RenderBox overlay =
|
||||||
|
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||||
|
print(folderName);
|
||||||
|
await showMenu<String>(
|
||||||
|
context: context,
|
||||||
|
position: RelativeRect.fromLTRB(
|
||||||
|
100, 100, overlay.size.width, overlay.size.height),
|
||||||
|
items: <PopupMenuEntry<String>>[
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'Rename',
|
||||||
|
child: Text('Rename Folder'),
|
||||||
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'Delete',
|
||||||
|
child: Text('Delete Folder'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).then((value) {
|
||||||
|
// Handle the action based on the selected menu item
|
||||||
|
if (value == 'Rename') {
|
||||||
|
// Logic for renaming the folder
|
||||||
|
print('Rename folder $folderName');
|
||||||
|
_renameDialog(folderName);
|
||||||
|
} else if (value == 'Delete') {
|
||||||
|
// Logic for deleting the folder
|
||||||
|
print("Deleting $folderName");
|
||||||
|
doubleCheckDelete(folderName);
|
||||||
|
// ApiService().deleteFolder(folderName);
|
||||||
|
print('Deleted folder');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewMailbox extends StatelessWidget {
|
class NewMailbox extends StatelessWidget {
|
||||||
final ApiService apiService;
|
final ApiService apiService;
|
||||||
// final Function(String) onFolderCreated;
|
|
||||||
final TextEditingController _textFieldController = TextEditingController();
|
final TextEditingController _textFieldController = TextEditingController();
|
||||||
|
|
||||||
NewMailbox({required this.apiService});
|
NewMailbox({required this.apiService});
|
||||||
@ -127,7 +196,7 @@ class NewMailbox extends StatelessWidget {
|
|||||||
content: TextField(
|
content: TextField(
|
||||||
controller: _textFieldController,
|
controller: _textFieldController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: "EPIC FOLDER", // Your custom hint text here
|
hintText: "New Folder",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
@ -138,13 +207,17 @@ class NewMailbox extends StatelessWidget {
|
|||||||
|
|
||||||
if (folderName.isNotEmpty) {
|
if (folderName.isNotEmpty) {
|
||||||
apiService.createFolder(folderName);
|
apiService.createFolder(folderName);
|
||||||
// onFolderCreated(folderName);
|
|
||||||
}
|
}
|
||||||
// apiService.createFolder(_textFieldController.text);
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text("Approve"),
|
child: const Text("Approve"),
|
||||||
),
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,440 +1,465 @@
|
|||||||
import 'folder_drawer.dart';
|
import 'package:crab_ui/sonicEmailView.dart';
|
||||||
import 'structs.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'folder_drawer.dart';
|
||||||
import 'api_service.dart';
|
import 'structs.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'email.dart';
|
import 'api_service.dart';
|
||||||
// import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:flutter/material.dart';
|
||||||
// import 'serialize.dart';
|
import 'email.dart';
|
||||||
|
// import 'package:shared_preferences/shared_preferences.dart';
|
||||||
class HomeScreen extends StatefulWidget {
|
// import 'serialize.dart';
|
||||||
@override
|
|
||||||
_HomeScreenState createState() => _HomeScreenState();
|
class HomeScreen extends StatefulWidget {
|
||||||
}
|
@override
|
||||||
|
_HomeScreenState createState() => _HomeScreenState();
|
||||||
class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
}
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
|
||||||
final GlobalKey<EmailPageState> _emailPageKey = GlobalKey<EmailPageState>();
|
class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
||||||
ApiService apiService = ApiService();
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
bool _isSidebarOpen = true;
|
final GlobalKey<EmailPageState> _emailPageKey = GlobalKey<EmailPageState>();
|
||||||
bool querySearches = false;
|
ApiService apiService = ApiService();
|
||||||
String? _selectedOption = "INBOX";
|
bool _isSidebarOpen = true;
|
||||||
|
bool querySearches = false;
|
||||||
List<String> _tabs = ['Emails'];
|
String? _selectedOption = "INBOX";
|
||||||
Map<String, Widget> _tabWidgets = {};
|
|
||||||
TabController? _tabController;
|
List<String> _tabs = ['Emails'];
|
||||||
|
Map<String, Widget> _tabWidgets = {};
|
||||||
@override
|
TabController? _tabController;
|
||||||
void initState() {
|
|
||||||
super.initState();
|
@override
|
||||||
_tabController = TabController(length: _tabs.length, vsync: this);
|
void initState() {
|
||||||
_tabWidgets['Emails'] = EmailPage(
|
super.initState();
|
||||||
key: _emailPageKey,
|
_tabController = TabController(length: _tabs.length, vsync: this);
|
||||||
);
|
_tabWidgets['Emails'] = EmailPage(
|
||||||
}
|
key: _emailPageKey,
|
||||||
|
);
|
||||||
// Add a new tab based on the search
|
}
|
||||||
void _performSearch(String query, String? list) {
|
|
||||||
setState(() {
|
// Add a new tab based on the search
|
||||||
if (!_tabs.contains(query)) {
|
void _performSearch(String query, String? list) {
|
||||||
_tabs.add(query);
|
setState(() {
|
||||||
_tabWidgets[query] = _buildSearchResultsWidget(
|
if (!_tabs.contains(query)) {
|
||||||
query, list); // Store a different widget for this tab
|
_tabs.add(query);
|
||||||
_tabController = TabController(length: _tabs.length, vsync: this);
|
_tabWidgets[query] = _buildSearchResultsWidget(
|
||||||
}
|
query, list); // Store a different widget for this tab
|
||||||
});
|
_tabController = TabController(length: _tabs.length, vsync: this);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
void _showOptionsSearchDialog () async {
|
}
|
||||||
List<String> folders = await apiService.fetchFolders();
|
|
||||||
|
void _showOptionsSearchDialog () async {
|
||||||
if (mounted) {
|
List<String> folders = await apiService.fetchFolders();
|
||||||
showDialog(
|
|
||||||
context: context,
|
if (mounted) {
|
||||||
builder: (BuildContext context) {
|
showDialog(
|
||||||
return AlertDialog(
|
context: context,
|
||||||
title: Text('Choose an Option'),
|
builder: (BuildContext context) {
|
||||||
content: Column(
|
return AlertDialog(
|
||||||
mainAxisSize: MainAxisSize.min,
|
title: Text('Choose an Option'),
|
||||||
children: folders.map((option) {
|
content: Column(
|
||||||
return ListTile(
|
mainAxisSize: MainAxisSize.min,
|
||||||
title: Text(option),
|
children: folders.map((option) {
|
||||||
leading: Radio<String>(
|
return ListTile(
|
||||||
value: option,
|
title: Text(option),
|
||||||
groupValue: _selectedOption, // Bind with _selectedOption
|
leading: Radio<String>(
|
||||||
onChanged: (String? value) {
|
value: option,
|
||||||
setState(() {
|
groupValue: _selectedOption, // Bind with _selectedOption
|
||||||
_selectedOption = value;
|
onChanged: (String? value) {
|
||||||
});
|
setState(() {
|
||||||
Navigator.of(context).pop(); // Close the dialog on selection
|
_selectedOption = value;
|
||||||
},
|
});
|
||||||
),
|
Navigator.of(context).pop(); // Close the dialog on selection
|
||||||
);
|
},
|
||||||
}).toList(),
|
),
|
||||||
),
|
);
|
||||||
actions: <Widget>[
|
}).toList(),
|
||||||
ElevatedButton(
|
),
|
||||||
child: Text('Submit'),
|
actions: <Widget>[
|
||||||
onPressed: () {
|
ElevatedButton(
|
||||||
Navigator.of(context).pop(); // Close the dialog
|
child: Text('Submit'),
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
onPressed: () {
|
||||||
content: Text('You selected: $_selectedOption'),
|
Navigator.of(context).pop(); // Close the dialog
|
||||||
));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
},
|
content: Text('You selected: $_selectedOption'),
|
||||||
),
|
));
|
||||||
],
|
},
|
||||||
);
|
),
|
||||||
},
|
],
|
||||||
);}
|
);
|
||||||
}
|
},
|
||||||
|
);}
|
||||||
|
}
|
||||||
// Remove a tab
|
|
||||||
void _removeTab(int index) {
|
|
||||||
if (_tabs[index] != 'Emails') {
|
// Remove a tab
|
||||||
setState(() {
|
void _removeTab(int index) {
|
||||||
String tabToRemove = _tabs[index];
|
if (_tabs[index] != 'Emails') {
|
||||||
_tabs.removeAt(index);
|
setState(() {
|
||||||
_tabWidgets
|
String tabToRemove = _tabs[index];
|
||||||
.remove(tabToRemove); // Remove widget associated with the tab
|
_tabs.removeAt(index);
|
||||||
_tabController = TabController(length: _tabs.length, vsync: this);
|
_tabWidgets
|
||||||
});
|
.remove(tabToRemove); // Remove widget associated with the tab
|
||||||
}
|
_tabController = TabController(length: _tabs.length, vsync: this);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
// Build a custom widget for each search query
|
}
|
||||||
Widget _buildSearchResultsWidget(String query, String? list) {
|
|
||||||
return FutureBuilder<List<SerializableMessage>>(
|
// Build a custom widget for each search query
|
||||||
future: apiService.sonicSearch(list ?? "INBOX", 50, 0, query),
|
Widget _buildSearchResultsWidget(String query, String? list) {
|
||||||
builder: (BuildContext context,
|
return FutureBuilder<List<SerializableMessage>>(
|
||||||
AsyncSnapshot<List<SerializableMessage>> snapshot) {
|
future: apiService.sonicSearch(list ?? "INBOX", 50, 0, query),
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
builder: (BuildContext context,
|
||||||
return Center(child: CircularProgressIndicator());
|
AsyncSnapshot<List<SerializableMessage>> snapshot) {
|
||||||
} else if (snapshot.hasError) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return Center(child: Text('Error: ${snapshot.error}'));
|
return Center(child: CircularProgressIndicator());
|
||||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
} else if (snapshot.hasError) {
|
||||||
return Center(child: Text('No results found for: $query'));
|
return Center(child: Text('Error: ${snapshot.error}'));
|
||||||
} else {
|
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||||
List<SerializableMessage> result = snapshot.data!;
|
return Center(child: Text('No results found for: $query'));
|
||||||
return Scaffold(
|
} else {
|
||||||
body: ListView.separated(
|
List<SerializableMessage> result = snapshot.data!;
|
||||||
itemCount: result.length,
|
return Scaffold(
|
||||||
itemBuilder: (context, index) {
|
body: ListView.separated(
|
||||||
final SerializableMessage email = result[index];
|
itemCount: result.length,
|
||||||
return ListTile(
|
itemBuilder: (context, index) {
|
||||||
title: Text(email.from,
|
final SerializableMessage email = result[index];
|
||||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
return ListTile(
|
||||||
subtitle: Column(
|
title: Text(email.from,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
children: [Text(email.subject)],
|
subtitle: Column(
|
||||||
),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
trailing: Text(email.date.toString()),
|
children: [Text(email.subject)],
|
||||||
onTap: () async {
|
),
|
||||||
// print('tapped');
|
trailing: Text(email.date.toString()),
|
||||||
String emailContent =
|
onTap: () async {
|
||||||
await apiService.fetchEmailContent([email.id], email.list);
|
// print('tapped');
|
||||||
// print('content below');
|
// List<String> emailContent =
|
||||||
// print(emailContent);
|
// await apiService.fetchEmailContent([email.id], email.list);
|
||||||
Navigator.push(
|
//call the foldable
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
List<String> emailContent = // list of the html
|
||||||
builder: (context) => EmailView(
|
await apiService.fetchEmailContent([email.id], email.list);
|
||||||
emailContent: emailContent,
|
// List<String> emailIds = email.messages;
|
||||||
from: email.from,
|
|
||||||
name: email.name,
|
|
||||||
to: email.to.toString(),
|
Navigator.push(
|
||||||
subject: email.subject,
|
context,
|
||||||
date: email.date.toString(),
|
MaterialPageRoute(
|
||||||
id: email.id.toString(),
|
builder: (context) =>SonicEmailView(
|
||||||
),
|
email: email,
|
||||||
),
|
emailHTML: emailContent[0])
|
||||||
);
|
// builder: (context) => EmailView(
|
||||||
},
|
// emailContent: emailContent,
|
||||||
);
|
// from: email.from,
|
||||||
},
|
// name: email.name,
|
||||||
separatorBuilder: (context, index) => Divider(),
|
// to: email.to.toString(),
|
||||||
),
|
// subject: email.subject,
|
||||||
// child: Column(
|
// date: email.date.toString(),
|
||||||
// mainAxisAlignment: MainAxisAlignment.center,
|
// id: email.id.toString(),
|
||||||
// children: [
|
// messages: [email.id],
|
||||||
// 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()),
|
separatorBuilder: (context, index) => Divider(),
|
||||||
// Text(result[0].list),
|
),
|
||||||
// Text(result[0].id),
|
// child: Column(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
// // Add more fields or customize the display
|
// children: [
|
||||||
// // SerializableEmailListScreen(emails: result, getEmailContent: getEmailContent)
|
// Text("Results for: $query", style: TextStyle(fontSize: 24)),
|
||||||
// // Expanded(
|
// // Display the actual data
|
||||||
|
// Text(result[0].name), // Accessing the first result safely
|
||||||
// // child:
|
// 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(
|
||||||
@override
|
|
||||||
void dispose() {
|
// // child:
|
||||||
_tabController?.dispose();
|
// // ),
|
||||||
super.dispose();
|
// ],
|
||||||
}
|
);
|
||||||
|
// );
|
||||||
@override
|
}
|
||||||
Widget build(BuildContext context) {
|
},
|
||||||
return Scaffold(
|
);
|
||||||
key: _scaffoldKey,
|
}
|
||||||
drawer: FolderDrawer(
|
|
||||||
apiService: apiService,
|
@override
|
||||||
onFolderTap: (folder) {
|
void dispose() {
|
||||||
_emailPageKey.currentState?.updateSelectedFolder(folder);
|
_tabController?.dispose();
|
||||||
},
|
super.dispose();
|
||||||
),
|
}
|
||||||
body: Stack(
|
|
||||||
children: [
|
@override
|
||||||
Row(
|
Widget build(BuildContext context) {
|
||||||
children: [
|
return Scaffold(
|
||||||
// Sidebar
|
key: _scaffoldKey,
|
||||||
if (_isSidebarOpen)
|
drawer: FolderDrawer(
|
||||||
Container(
|
apiService: apiService,
|
||||||
width: 70,
|
onFolderTap: (folder) {
|
||||||
color: Color.fromARGB(17, 96, 122, 135),
|
_emailPageKey.currentState?.updateSelectedFolder(folder);
|
||||||
child: Column(
|
},
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
children: [
|
body: Stack(
|
||||||
ListTile(
|
children: [
|
||||||
leading: Icon(Icons.home),
|
Row(
|
||||||
onTap: () {
|
children: [
|
||||||
// Navigate to Home
|
// Sidebar
|
||||||
},
|
if (_isSidebarOpen)
|
||||||
),
|
Container(
|
||||||
ListTile(
|
width: 70,
|
||||||
leading: Icon(Icons.settings),
|
color: Color.fromARGB(17, 96, 122, 135),
|
||||||
onTap: () {
|
child: Column(
|
||||||
// Navigate to Settings
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
},
|
children: [
|
||||||
),
|
ListTile(
|
||||||
ListTile(
|
leading: Icon(Icons.home),
|
||||||
leading: Icon(Icons.email),
|
onTap: () {
|
||||||
onTap: () {
|
// Navigate to Home
|
||||||
_scaffoldKey.currentState?.openDrawer();
|
},
|
||||||
},
|
),
|
||||||
),
|
ListTile(
|
||||||
Spacer(),
|
leading: Icon(Icons.settings),
|
||||||
Padding(
|
onTap: () {
|
||||||
padding: const EdgeInsets.all(8.0),
|
// Navigate to Settings
|
||||||
child: Align(
|
},
|
||||||
alignment: Alignment.bottomLeft,
|
),
|
||||||
child: IconButton(
|
ListTile(
|
||||||
icon: Icon(Icons.close, color: Colors.white),
|
leading: Icon(Icons.email),
|
||||||
onPressed: () {
|
onTap: () {
|
||||||
setState(() {
|
_scaffoldKey.currentState?.openDrawer();
|
||||||
_isSidebarOpen = false;
|
},
|
||||||
});
|
),
|
||||||
},
|
Spacer(),
|
||||||
),
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.all(8.0),
|
||||||
),
|
child: Align(
|
||||||
],
|
alignment: Alignment.bottomLeft,
|
||||||
),
|
child: IconButton(
|
||||||
),
|
icon: Icon(Icons.close, color: Colors.white),
|
||||||
// Main content
|
onPressed: () {
|
||||||
Expanded(
|
setState(() {
|
||||||
child: Column(
|
_isSidebarOpen = false;
|
||||||
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: [
|
),
|
||||||
Container(
|
),
|
||||||
width: 800,
|
// Main content
|
||||||
height: 40,
|
Expanded(
|
||||||
child: TextField(
|
child: Column(
|
||||||
decoration: InputDecoration(
|
children: [
|
||||||
hintText: 'Search...',
|
Container(
|
||||||
border: OutlineInputBorder(),
|
padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 4.0),
|
||||||
prefixIcon: Icon(Icons.search),
|
color: Color.fromARGB(42, 36, 102, 132),
|
||||||
),
|
child: Row(
|
||||||
onSubmitted: (value) {
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
if (value.isNotEmpty) {
|
children: [
|
||||||
_performSearch(value, _selectedOption);
|
Container(
|
||||||
}
|
width: 800,
|
||||||
//this is the input box i mentioned
|
height: 40,
|
||||||
// if (value == '') {
|
child: TextField(
|
||||||
// setState(() {
|
decoration: InputDecoration(
|
||||||
// querySearches = false;
|
hintText: 'Search...',
|
||||||
// });
|
border: OutlineInputBorder(),
|
||||||
// }
|
prefixIcon: Icon(Icons.search),
|
||||||
// Future<List<String>> results = apiService
|
),
|
||||||
// .sonicSearch('INBOX', 20, 0, value);
|
onSubmitted: (value) {
|
||||||
// // print(value);
|
if (value.isNotEmpty) {
|
||||||
// print(results);
|
_performSearch(value, _selectedOption);
|
||||||
// setState(() {
|
}
|
||||||
// querySearches = true;
|
//this is the input box i mentioned
|
||||||
// });
|
// if (value == '') {
|
||||||
},
|
// setState(() {
|
||||||
),
|
// querySearches = false;
|
||||||
),
|
// });
|
||||||
SizedBox(
|
// }
|
||||||
width: 16,
|
// Future<List<String>> results = apiService
|
||||||
),
|
// .sonicSearch('INBOX', 20, 0, value);
|
||||||
Container(
|
// // print(value);
|
||||||
width: 80,
|
// print(results);
|
||||||
height: 40,
|
// setState(() {
|
||||||
child: ElevatedButton(
|
// querySearches = true;
|
||||||
onPressed: _showOptionsSearchDialog,
|
// });
|
||||||
child: Icon(Icons.manage_search),
|
},
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
SizedBox(
|
||||||
),
|
width: 16,
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.all(0.0),
|
width: 80,
|
||||||
color: Color.fromARGB(42, 36, 102, 132),
|
height: 40,
|
||||||
child: Row(
|
child: ElevatedButton(
|
||||||
children: [
|
onPressed: _showOptionsSearchDialog,
|
||||||
Container(
|
child: Icon(Icons.manage_search),
|
||||||
height: 2,
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: Color.fromARGB(255, 131, 110, 143),
|
padding: EdgeInsets.all(0.0),
|
||||||
child: TabBar(
|
color: Color.fromARGB(42, 36, 102, 132),
|
||||||
controller: _tabController,
|
child: Row(
|
||||||
isScrollable: true,
|
children: [
|
||||||
tabs: _tabs
|
Container(
|
||||||
.asMap()
|
height: 2,
|
||||||
.entries
|
)
|
||||||
.map((entry) => Tab(
|
],
|
||||||
child: Row(
|
),
|
||||||
children: [
|
),
|
||||||
Text(entry.value),
|
Container(
|
||||||
if (entry.value != 'Emails')
|
color: Color.fromARGB(255, 131, 110, 143),
|
||||||
GestureDetector(
|
child: TabBar(
|
||||||
onTap: () => _removeTab(entry.key),
|
controller: _tabController,
|
||||||
child: Icon(Icons.close, size: 16),
|
isScrollable: true,
|
||||||
),
|
tabs: _tabs
|
||||||
],
|
.asMap()
|
||||||
),
|
.entries
|
||||||
))
|
.map((entry) => Tab(
|
||||||
.toList(),
|
child: Row(
|
||||||
labelColor: Colors.white,
|
children: [
|
||||||
indicatorColor: Colors.white,
|
Text(entry.value),
|
||||||
),
|
if (entry.value != 'Emails')
|
||||||
),
|
GestureDetector(
|
||||||
Container(
|
onTap: () => _removeTab(entry.key),
|
||||||
// alignment: Alignment.topLeft,
|
child: Icon(Icons.close, size: 16),
|
||||||
padding: EdgeInsets.all(8.0),
|
),
|
||||||
color: Colors.white,
|
],
|
||||||
child: Row(
|
),
|
||||||
children: [
|
))
|
||||||
ElevatedButton(
|
.toList(),
|
||||||
onPressed: () {
|
labelColor: Colors.white,
|
||||||
_emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState
|
indicatorColor: Colors.white,
|
||||||
?.updatePagenation('back');
|
),
|
||||||
},
|
),
|
||||||
child: Icon(Icons.navigate_before),
|
Container(
|
||||||
),
|
// alignment: Alignment.topLeft,
|
||||||
Text(_emailPageKey.currentState?.getPage() ?? '1'),
|
padding: EdgeInsets.all(8.0),
|
||||||
ElevatedButton(
|
color: Colors.white,
|
||||||
onPressed: () {
|
child: Row(
|
||||||
_emailPageKey.currentState
|
children: [
|
||||||
?.updatePagenation('next');
|
ElevatedButton(
|
||||||
},
|
onPressed: () {
|
||||||
child: Icon(Icons.navigate_next),
|
_emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState
|
||||||
),
|
?.updatePagenation('back');
|
||||||
],
|
},
|
||||||
),
|
child: Icon(Icons.navigate_before),
|
||||||
),
|
),
|
||||||
Expanded(
|
Builder(
|
||||||
child: TabBarView(
|
builder: (context) {
|
||||||
controller: _tabController,
|
final emailState = _emailPageKey.currentState;
|
||||||
children: _tabs.map((tab) {
|
if (emailState == null) {
|
||||||
return _tabWidgets[tab] ??
|
// Schedule a rebuild once the state is available
|
||||||
Center(child: Text("No content found"));
|
Future.microtask(() => setState(() {}));
|
||||||
// return Center(
|
return Text('Loading...');
|
||||||
// child: EmailPage(
|
}
|
||||||
// key: _emailPageKey,
|
|
||||||
// ));
|
return ValueListenableBuilder<int>(
|
||||||
}).toList(),
|
valueListenable: emailState.currentPageNotifier,
|
||||||
),
|
builder: (context, value, _) => Text('$value'),
|
||||||
),
|
);
|
||||||
|
},
|
||||||
// if (_tabs.isEmpty)
|
),
|
||||||
// Expanded(
|
ElevatedButton(
|
||||||
// child: EmailPage(key: _emailPageKey),
|
onPressed: () {
|
||||||
// ),
|
_emailPageKey.currentState
|
||||||
// if (_tabs.isNotEmpty)
|
?.updatePagenation('next');
|
||||||
// Expanded(
|
},
|
||||||
// // child: Text('supposed to be mails'),
|
child: Icon(Icons.navigate_next),
|
||||||
// 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(
|
||||||
if (!_isSidebarOpen)
|
// key: _emailPageKey,
|
||||||
Positioned(
|
// ));
|
||||||
bottom: 16,
|
}).toList(),
|
||||||
left: 16,
|
),
|
||||||
child: FloatingActionButton(
|
),
|
||||||
child: Icon(Icons.menu),
|
|
||||||
onPressed: () {
|
// if (_tabs.isEmpty)
|
||||||
setState(() {
|
// Expanded(
|
||||||
_isSidebarOpen = true;
|
// 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(),
|
||||||
// void _showPopupMenu(BuildContext context, Offset position) async {
|
// ),
|
||||||
// final RenderBox overlay =
|
// ),
|
||||||
// Overlay.of(context).context.findRenderObject() as RenderBox;
|
],
|
||||||
|
),
|
||||||
// await showMenu<String>(
|
),
|
||||||
// context: context,
|
],
|
||||||
// position: RelativeRect.fromLTRB(
|
),
|
||||||
// position.dx,
|
if (!_isSidebarOpen)
|
||||||
// position.dy,
|
Positioned(
|
||||||
// overlay.size.width - position.dx,
|
bottom: 16,
|
||||||
// overlay.size.height - position.dy,
|
left: 16,
|
||||||
// ),
|
child: FloatingActionButton(
|
||||||
// items: <PopupMenuEntry<String>>[
|
child: Icon(Icons.menu),
|
||||||
// PopupMenuItem<String>(
|
onPressed: () {
|
||||||
// value: 'Open',
|
setState(() {
|
||||||
// child: Text('Open'),
|
_isSidebarOpen = true;
|
||||||
// ),
|
});
|
||||||
// PopupMenuItem<String>(
|
},
|
||||||
// value: 'Reply',
|
),
|
||||||
// child: Text('Reply'),
|
),
|
||||||
// ),
|
],
|
||||||
// PopupMenuItem<String>(
|
),
|
||||||
// value: 'Delete',
|
);
|
||||||
// child: Text('Delete'),
|
}
|
||||||
// ),
|
}
|
||||||
// ],
|
// void _showPopupMenu(BuildContext context, Offset position) async {
|
||||||
// );
|
// final RenderBox overlay =
|
||||||
// }
|
// Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||||
// }
|
|
||||||
|
// await showMenu<String>(
|
||||||
|
// context: context,
|
||||||
|
// position: RelativeRect.fromLTRB(
|
||||||
|
// position.dx,
|
||||||
|
// position.dy,
|
||||||
|
// overlay.size.width - position.dx,
|
||||||
|
// overlay.size.height - position.dy,
|
||||||
|
// ),
|
||||||
|
// items: <PopupMenuEntry<String>>[
|
||||||
|
// PopupMenuItem<String>(
|
||||||
|
// value: 'Open',
|
||||||
|
// child: Text('Open'),
|
||||||
|
// ),
|
||||||
|
// PopupMenuItem<String>(
|
||||||
|
// value: 'Reply',
|
||||||
|
// child: Text('Reply'),
|
||||||
|
// ),
|
||||||
|
// PopupMenuItem<String>(
|
||||||
|
// value: 'Delete',
|
||||||
|
// child: Text('Delete'),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'api_service.dart';
|
|
||||||
import 'structs.dart';
|
|
||||||
|
|
||||||
class SerializableMessageListScreen extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
_SerializableMessageListScreenState createState() => _SerializableMessageListScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SerializableMessageListScreenState extends State<SerializableMessageListScreen> {
|
|
||||||
List<SerializableMessage>? messages;
|
|
||||||
bool isLoading = true;
|
|
||||||
bool hasError = false;
|
|
||||||
|
|
||||||
final ApiService apiService = ApiService();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_fetchMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _fetchMessages() async {
|
|
||||||
try {
|
|
||||||
List<SerializableMessage> fetchedMessages = await apiService.sonicSearch("INBOX", 10, 0, "searchQuery");
|
|
||||||
setState(() {
|
|
||||||
messages = fetchedMessages;
|
|
||||||
isLoading = false;
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
setState(() {
|
|
||||||
hasError = true;
|
|
||||||
isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (isLoading) {
|
|
||||||
return Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasError) {
|
|
||||||
return Center(child: Text("Error fetching messages."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages == null || messages!.isEmpty) {
|
|
||||||
return Center(child: Text("No messages found."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ListView.separated(
|
|
||||||
itemCount: messages!.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final message = messages![index];
|
|
||||||
return ListTile(
|
|
||||||
title: Text(message.name, style: TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
subtitle: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [Text(message.subject)],
|
|
||||||
),
|
|
||||||
trailing: Text(message.date),
|
|
||||||
onTap: () async {
|
|
||||||
String emailContent = await apiService.fetchEmailContent([message.id], message.list);
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => EmailView(
|
|
||||||
emailContent: emailContent,
|
|
||||||
from: message.from,
|
|
||||||
name: message.name,
|
|
||||||
to: message.to.toString(),
|
|
||||||
subject: message.subject,
|
|
||||||
date: message.date,
|
|
||||||
id: message.id,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
separatorBuilder: (context, index) => Divider(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
145
lib/sonicEmailView.dart
Normal file
145
lib/sonicEmailView.dart
Normal file
@ -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<SonicEmailView> {
|
||||||
|
String viewTypeIDs = "";
|
||||||
|
int heightOFViewtype = 0;
|
||||||
|
bool _isLoaded = false;
|
||||||
|
|
||||||
|
void _scrollToNumber(String spanId) {
|
||||||
|
AugmentClasses.handleJump(spanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _init() async {
|
||||||
|
await _registerViewFactory(widget.emailHTML);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_isLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
198
pubspec.lock
198
pubspec.lock
@ -9,6 +9,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0"
|
version: "2.5.0"
|
||||||
|
asn1lib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: asn1lib
|
||||||
|
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.8"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -97,14 +105,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.6"
|
||||||
csslib:
|
csslib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -121,6 +137,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
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:
|
||||||
|
name: encrypt
|
||||||
|
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.3"
|
||||||
english_words:
|
english_words:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -141,10 +181,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -153,6 +193,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
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:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -368,6 +416,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.19.0"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -464,6 +520,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.12.0"
|
version: "1.12.0"
|
||||||
|
mime:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.6"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -500,10 +564,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.5"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -544,6 +608,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
pdfrx:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pdfrx
|
||||||
|
sha256: "29c7b03d27d647c80da8cc08bd1256c74df90e5640fdd676646e4bd04f90553a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.94"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -552,6 +624,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
version: "6.0.2"
|
||||||
|
photo_view:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: photo_view
|
||||||
|
sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -568,6 +648,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
|
pointer_interceptor:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pointer_interceptor
|
||||||
|
sha256: "57210410680379aea8b1b7ed6ae0c3ad349bfd56fe845b8ea934a53344b9d523"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.1+2"
|
||||||
|
pointer_interceptor_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pointer_interceptor_ios
|
||||||
|
sha256: a6906772b3205b42c44614fcea28f818b1e5fdad73a4ca742a7bd49818d9c917
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.1"
|
||||||
|
pointer_interceptor_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pointer_interceptor_platform_interface
|
||||||
|
sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.0+1"
|
||||||
|
pointer_interceptor_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pointer_interceptor_web
|
||||||
|
sha256: "7a7087782110f8c1827170660b09f8aa893e0e9a61431dbbe2ac3fc482e8c044"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.2+1"
|
||||||
|
pointycastle:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pointycastle
|
||||||
|
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.9.1"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -592,6 +712,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.7"
|
version: "0.27.7"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.3"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.4"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -697,10 +873,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
|
sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.3.1"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -886,13 +1062,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1"
|
version: "0.2.1"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.1.1"
|
||||||
webview_flutter:
|
webview_flutter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -27,6 +27,7 @@ dependencies:
|
|||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
pdfrx: ^1.0.94
|
pdfrx: ^1.0.94
|
||||||
photo_view: ^0.15.0
|
photo_view: ^0.15.0
|
||||||
|
web: ^1.1.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
@ -1,3 +0,0 @@
|
|||||||
[ZoneTransfer]
|
|
||||||
ZoneId=3
|
|
||||||
ReferrerUrl=C:\Users\juana\Flutter\flutter_windows_3.22.2-stable.zip
|
|
Loading…
Reference in New Issue
Block a user