forked from firka/student-legacy
edit
This commit is contained in:
commit
f2f3366e2c
9
.gitignore
vendored
9
.gitignore
vendored
@ -12,10 +12,10 @@ refilc/build/
|
|||||||
refilc/android/key.properties
|
refilc/android/key.properties
|
||||||
refilc/android/debug.keystore
|
refilc/android/debug.keystore
|
||||||
|
|
||||||
refilc_desktop_ui/.flutter-plugins
|
# refilc_desktop_ui/.flutter-plugins
|
||||||
refilc_desktop_ui/.flutter-plugins-dependencies
|
# refilc_desktop_ui/.flutter-plugins-dependencies
|
||||||
refilc_desktop_ui/pubspec.lock
|
# refilc_desktop_ui/pubspec.lock
|
||||||
refilc_desktop_ui/.dart_tool/
|
# refilc_desktop_ui/.dart_tool/
|
||||||
|
|
||||||
refilc_kreta_api/.flutter-plugins
|
refilc_kreta_api/.flutter-plugins
|
||||||
refilc_kreta_api/.flutter-plugins-dependencies
|
refilc_kreta_api/.flutter-plugins-dependencies
|
||||||
@ -37,6 +37,7 @@ refilc_mobile_ui/.dart_tool/
|
|||||||
.idea
|
.idea
|
||||||
.gitmodules
|
.gitmodules
|
||||||
.gradle
|
.gradle
|
||||||
|
.kotlin
|
||||||
|
|
||||||
refilc/.DS_Store
|
refilc/.DS_Store
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
7
check-outdated.sh
Normal file
7
check-outdated.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
cd refilc && flutter pub outdated && cd ..
|
||||||
|
cd refilc_kreta_api && flutter pub outdated && cd ..
|
||||||
|
cd refilc_mobile_ui && flutter pub outdated && cd ..
|
||||||
|
# cd refilc_desktop_ui && flutter pub upgrade && cd ..
|
||||||
|
cd refilc_plus && flutter pub outdated && cd ..
|
||||||
|
|
||||||
|
echo Outdated version list above.
|
7
major-upgrade-pub.sh
Executable file
7
major-upgrade-pub.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
cd refilc && flutter pub upgrade --major-versions && cd ..
|
||||||
|
cd refilc_kreta_api && flutter pub upgrade --major-versions && cd ..
|
||||||
|
cd refilc_mobile_ui && flutter pub upgrade --major-versions && cd ..
|
||||||
|
# cd refilc_desktop_ui && flutter pub upgrade && cd ..
|
||||||
|
cd refilc_plus && flutter pub upgrade --major-versions && cd ..
|
||||||
|
|
||||||
|
echo "Upgraded pub (major)."
|
@ -39,8 +39,23 @@ def debugKeystoreProperties = new Properties()
|
|||||||
def debugKeystorePropertiesFile = rootProject.file("debugkey.properties")
|
def debugKeystorePropertiesFile = rootProject.file("debugkey.properties")
|
||||||
debugKeystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile))
|
debugKeystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile))
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
afterEvaluate { project ->
|
||||||
|
if (project.hasProperty('android')) {
|
||||||
|
project.android {
|
||||||
|
if (!hasProperty('namespace') || namespace == null || namespace.isEmpty()) {
|
||||||
|
// Assign a default namespace based on the project name or group
|
||||||
|
namespace = project.group.toString() ?: "com.example.${project.name}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
ndkVersion "25.1.8937393"
|
namespace = "hu.refilc.naplo"
|
||||||
|
|
||||||
|
ndkVersion "27.2.12479018"
|
||||||
|
|
||||||
// compileSdkVersion rootProject.ext.compileSdkVersion
|
// compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
compileSdkVersion 34
|
compileSdkVersion 34
|
||||||
@ -67,11 +82,19 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
// Flag to enable support for the new language APIs
|
// Flag to enable support for the new language APIs
|
||||||
coreLibraryDesugaringEnabled true
|
coreLibraryDesugaringEnabled true
|
||||||
// Sets Java compatibility to Java 8
|
// Sets Java compatibility to Java 8
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
// sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
// targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
// sourceCompatibility JavaVersion.VERSION_21
|
||||||
|
// targetCompatibility JavaVersion.VERSION_21
|
||||||
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
@ -117,7 +140,7 @@ dependencies {
|
|||||||
implementation 'joda-time:joda-time:2.9.4'
|
implementation 'joda-time:joda-time:2.9.4'
|
||||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||||
implementation 'androidx.window:window:1.0.0'
|
implementation 'androidx.window:window:1.0.0'
|
||||||
implementation 'androidx.window:window-java:1.0.0'
|
implementation 'androidx.window:window-java:1.0.0'
|
||||||
}
|
}
|
||||||
|
5
refilc/android/app/proguard-rules.pro
vendored
5
refilc/android/app/proguard-rules.pro
vendored
@ -8,4 +8,7 @@
|
|||||||
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Args
|
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Args
|
||||||
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Error
|
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter$Error
|
||||||
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter
|
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningActivityStarter
|
||||||
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningEphemeralKeyProvider
|
-dontwarn com.stripe.android.pushProvisioning.PushProvisioningEphemeralKeyProvider
|
||||||
|
|
||||||
|
-dontwarn org.joda.convert.FromString
|
||||||
|
-dontwarn org.joda.convert.ToString
|
@ -2,9 +2,6 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools" package="hu.refilc.naplo">
|
xmlns:tools="http://schemas.android.com/tools" package="hu.refilc.naplo">
|
||||||
<application android:name="${applicationName}" android:label="reFilc" tools:replace="android:label" android:icon="@mipmap/ic_launcher"
|
<application android:name="${applicationName}" android:label="reFilc" tools:replace="android:label" android:icon="@mipmap/ic_launcher"
|
||||||
android:requestLegacyExternalStorage="true" android:enableOnBackInvokedCallback="false">
|
android:requestLegacyExternalStorage="true" android:enableOnBackInvokedCallback="false">
|
||||||
<service
|
|
||||||
android:name="com.solusibejo.flutter_dynamic_icon_plus.FlutterDynamicIconPlusService"
|
|
||||||
android:stopWithTask="false"/>
|
|
||||||
|
|
||||||
<activity android:exported="true" android:name="hu.refilc.naplo.MainActivity"
|
<activity android:exported="true" android:name="hu.refilc.naplo.MainActivity"
|
||||||
android:launchMode="singleTop" android:theme="@style/LaunchTheme"
|
android:launchMode="singleTop" android:theme="@style/LaunchTheme"
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536M \
|
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
|
||||||
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.lang=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
|
||||||
--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
# kotlin.jvm.target.validation.mode=IGNORE
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||||
|
@ -24,8 +24,8 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
id "com.android.application" version "7.1.1" apply false
|
id "com.android.application" version "8.7.3" apply false
|
||||||
id "org.jetbrains.kotlin.android" version "1.8.21" apply false
|
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
|
0
refilc/build.sh
Normal file → Executable file
0
refilc/build.sh
Normal file → Executable file
@ -517,9 +517,11 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 280;
|
CURRENT_PROJECT_VERSION = 283;
|
||||||
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
FLUTTER_BUILD_NAME = 5.1.1;
|
||||||
|
FLUTTER_BUILD_NUMBER = 283;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||||
@ -527,7 +529,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 5.1.0;
|
MARKETING_VERSION = 5.1.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc;
|
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@ -549,7 +551,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements;
|
CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 276;
|
CURRENT_PROJECT_VERSION = 283;
|
||||||
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -563,7 +565,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 5.0.5;
|
MARKETING_VERSION = 5.1.1;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc.livecard;
|
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc.livecard;
|
||||||
@ -591,7 +593,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements;
|
CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 276;
|
CURRENT_PROJECT_VERSION = 283;
|
||||||
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -605,7 +607,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 5.0.5;
|
MARKETING_VERSION = 5.1.1;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc.livecard;
|
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc.livecard;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -631,7 +633,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements;
|
CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 276;
|
CURRENT_PROJECT_VERSION = 283;
|
||||||
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -645,7 +647,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 5.0.5;
|
MARKETING_VERSION = 5.1.1;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc.livecard;
|
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc.livecard;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -775,9 +777,11 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 280;
|
CURRENT_PROJECT_VERSION = 283;
|
||||||
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
FLUTTER_BUILD_NAME = 5.1.1;
|
||||||
|
FLUTTER_BUILD_NUMBER = 283;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||||
@ -785,7 +789,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 5.1.0;
|
MARKETING_VERSION = 5.1.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc;
|
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@ -803,9 +807,11 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 280;
|
CURRENT_PROJECT_VERSION = 283;
|
||||||
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
DEVELOPMENT_TEAM = 4J97JVC2FG;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
FLUTTER_BUILD_NAME = 5.1.1;
|
||||||
|
FLUTTER_BUILD_NUMBER = 283;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||||
@ -813,7 +819,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 5.1.0;
|
MARKETING_VERSION = 5.1.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc;
|
PRODUCT_BUNDLE_IDENTIFIER = hu.qwit.refilc;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
@ -11,6 +11,17 @@ struct Widgets: WidgetBundle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// text contrast background
|
||||||
|
extension Text {
|
||||||
|
func getContrastText(backgroundColor: Color) -> some View {
|
||||||
|
var r, g, b, a: CGFloat
|
||||||
|
(r, g, b, a) = (0, 0, 0, 0)
|
||||||
|
UIColor(backgroundColor).getRed(&r, green: &g, blue: &b, alpha: &a)
|
||||||
|
let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
||||||
|
return luminance < 0.6 ? self.foregroundColor(.white) : self.foregroundColor(.black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Color Converter
|
// Color Converter
|
||||||
extension Color {
|
extension Color {
|
||||||
init(hex: String, alpha: Double = 1.0) {
|
init(hex: String, alpha: Double = 1.0) {
|
||||||
@ -62,7 +73,6 @@ struct LockScreenLiveActivityView: View {
|
|||||||
.font(.body)
|
.font(.body)
|
||||||
.bold()
|
.bold()
|
||||||
.padding(.trailing, 90)
|
.padding(.trailing, 90)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
MultilineTextView(text: "\(context.state.index) \(context.state.title) - \(context.state.subtitle)", limit: 25)
|
MultilineTextView(text: "\(context.state.index) \(context.state.title) - \(context.state.subtitle)", limit: 25)
|
||||||
.font(.body)
|
.font(.body)
|
||||||
@ -84,6 +94,7 @@ struct LockScreenLiveActivityView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(width: CGFloat(8), height: CGFloat(8))
|
.frame(width: CGFloat(8), height: CGFloat(8))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
Text("\(context.state.nextSubject) - \(context.state.nextRoom)")
|
Text("\(context.state.nextSubject) - \(context.state.nextRoom)")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
}
|
}
|
||||||
@ -107,11 +118,15 @@ struct LockScreenLiveActivityView: View {
|
|||||||
.monospacedDigit()
|
.monospacedDigit()
|
||||||
.padding(.trailing)
|
.padding(.trailing)
|
||||||
}
|
}
|
||||||
|
// .activityBackgroundTint(
|
||||||
|
// context.state.color != "#676767"
|
||||||
|
// ? Color(hex: context.state.color)
|
||||||
|
// : Color.clear
|
||||||
|
// )
|
||||||
.activityBackgroundTint(
|
.activityBackgroundTint(
|
||||||
context.state.color != "#676767"
|
Color.clear
|
||||||
? Color(hex: context.state.color)
|
|
||||||
: Color.clear
|
|
||||||
)
|
)
|
||||||
|
.foregroundStyle(Color(hex: context.state.color))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,18 +22,18 @@ class FilcAPI {
|
|||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
static const schoolList = "$baseUrl/v3/public/school-list";
|
static const schoolList = "$baseUrl/v3/public/school-list";
|
||||||
static const news = "$baseUrl/v4/public/news";
|
static const news = "0.0.0.0/v4/public/news";
|
||||||
static const supporters = "$baseUrl/v3/public/supporters";
|
static const supporters = "$baseUrl/v3/public/supporters";
|
||||||
|
|
||||||
// Private API
|
// Private API
|
||||||
static const ads = "$baseUrl/v3/private/ads";
|
static const ads = "0.0.0.0/v3/private/ads";
|
||||||
static const config = "$baseUrl/v3/private/config";
|
static const config = "$baseUrl/v3/private/config";
|
||||||
static const reportApi = "$baseUrl/v3/private/crash-report";
|
static const reportApi = "$baseUrl/v3/private/crash-report";
|
||||||
static const rfPlus = "$baseUrl/v3/rf-plus";
|
static const rfPlus = "0.0.0.0/v3/rf-plus";
|
||||||
static const plusAuthLogin = "$rfPlus/auth/login";
|
static const plusAuthLogin = "0.0.0.0/auth/login";
|
||||||
static const plusAuthCallback = "$rfPlus/auth/callback";
|
static const plusAuthCallback = "0.0.0.0/auth/callback";
|
||||||
static const plusActivation = "$rfPlus/activate";
|
static const plusActivation = "0.0.0.0/activate";
|
||||||
static const plusScopes = "$rfPlus/scopes";
|
static const plusScopes = "0.0.0.0/scopes";
|
||||||
|
|
||||||
// Updates
|
// Updates
|
||||||
static const repo = "refilc/naplo";
|
static const repo = "refilc/naplo";
|
||||||
@ -51,11 +51,12 @@ class FilcAPI {
|
|||||||
static const gradeColorsByID = "$gradeColorsGet/";
|
static const gradeColorsByID = "$gradeColorsGet/";
|
||||||
|
|
||||||
// Payment API
|
// Payment API
|
||||||
static const payment = "$baseUrl/v4/payment";
|
static const payment = "0.0.0.0/v4/payment";
|
||||||
static const stripeSheet = "$payment/stripe-sheet";
|
static const stripeSheet = "0.0.0.0/stripe-sheet";
|
||||||
|
|
||||||
// Cloud Sync
|
// Cloud Sync
|
||||||
static const cloudSyncApi = "$baseUrl/v4/me/cloud-sync";
|
// cloud sync? for what reason
|
||||||
|
static const cloudSyncApi = "0.0.0.0/v4/me/cloud-sync";
|
||||||
|
|
||||||
static Future<bool> checkConnectivity() async =>
|
static Future<bool> checkConnectivity() async =>
|
||||||
(await Connectivity().checkConnectivity())[0] != ConnectivityResult.none;
|
(await Connectivity().checkConnectivity())[0] != ConnectivityResult.none;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:refilc/database/query.dart';
|
import 'package:refilc/database/query.dart';
|
||||||
import 'package:refilc/database/store.dart';
|
import 'package:refilc/database/store.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
@ -15,11 +13,7 @@ class DatabaseProvider {
|
|||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
Database db;
|
Database db;
|
||||||
|
|
||||||
if (Platform.isLinux || Platform.isWindows) {
|
db = await openDatabase("app.db");
|
||||||
db = await databaseFactoryFfi.openDatabase("app.db");
|
|
||||||
} else {
|
|
||||||
db = await openDatabase("app.db");
|
|
||||||
}
|
|
||||||
|
|
||||||
query = DatabaseQuery(db: db);
|
query = DatabaseQuery(db: db);
|
||||||
store = DatabaseStore(db: db);
|
store = DatabaseStore(db: db);
|
||||||
|
@ -5,6 +5,7 @@ import 'dart:async';
|
|||||||
import 'package:refilc/api/providers/liveactivity/platform_channel.dart';
|
import 'package:refilc/api/providers/liveactivity/platform_channel.dart';
|
||||||
import 'package:refilc/helpers/subject.dart';
|
import 'package:refilc/helpers/subject.dart';
|
||||||
import 'package:refilc/models/settings.dart';
|
import 'package:refilc/models/settings.dart';
|
||||||
|
import 'package:refilc/ui/flutter_colorpicker/utils.dart';
|
||||||
import 'package:refilc_kreta_api/models/lesson.dart';
|
import 'package:refilc_kreta_api/models/lesson.dart';
|
||||||
import 'package:refilc_kreta_api/models/week.dart';
|
import 'package:refilc_kreta_api/models/week.dart';
|
||||||
import 'package:refilc/utils/format.dart';
|
import 'package:refilc/utils/format.dart';
|
||||||
@ -84,11 +85,14 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> toMap() {
|
Map<String, String> toMap() {
|
||||||
|
// print("LIVE ACTIVITY COLOR BELOW:");
|
||||||
|
// print(_settings.liveActivityColor.toHexString().substring(2));
|
||||||
|
String color = '#${_settings.liveActivityColor.toHexString().substring(2)}';
|
||||||
|
|
||||||
switch (currentState) {
|
switch (currentState) {
|
||||||
case LiveCardState.morning:
|
case LiveCardState.morning:
|
||||||
return {
|
return {
|
||||||
"color":
|
"color": color,
|
||||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
|
||||||
"icon": nextLesson != null
|
"icon": nextLesson != null
|
||||||
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
||||||
: "book",
|
: "book",
|
||||||
@ -97,23 +101,22 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
"description": "",
|
"description": "",
|
||||||
"startDate": storeFirstRunDate != null
|
"startDate": storeFirstRunDate != null
|
||||||
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
|
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
|
||||||
(_delay.inMilliseconds))
|
(_delay.inMilliseconds))
|
||||||
.toString()
|
.toString()
|
||||||
: "",
|
: "",
|
||||||
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||||
_delay.inMilliseconds)
|
_delay.inMilliseconds)
|
||||||
.toString(),
|
.toString(),
|
||||||
"nextSubject": nextLesson != null
|
"nextSubject": nextLesson != null
|
||||||
? nextLesson?.subject.renamedTo ??
|
? nextLesson?.subject.renamedTo ??
|
||||||
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||||
: "",
|
: "",
|
||||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||||
};
|
};
|
||||||
|
|
||||||
case LiveCardState.afternoon:
|
case LiveCardState.afternoon:
|
||||||
return {
|
return {
|
||||||
"color":
|
"color": color,
|
||||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
|
||||||
"icon": nextLesson != null
|
"icon": nextLesson != null
|
||||||
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
||||||
: "book",
|
: "book",
|
||||||
@ -122,23 +125,22 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
"description": "",
|
"description": "",
|
||||||
"startDate": storeFirstRunDate != null
|
"startDate": storeFirstRunDate != null
|
||||||
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
|
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
|
||||||
(_delay.inMilliseconds))
|
(_delay.inMilliseconds))
|
||||||
.toString()
|
.toString()
|
||||||
: "",
|
: "",
|
||||||
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||||
_delay.inMilliseconds)
|
_delay.inMilliseconds)
|
||||||
.toString(),
|
.toString(),
|
||||||
"nextSubject": nextLesson != null
|
"nextSubject": nextLesson != null
|
||||||
? nextLesson?.subject.renamedTo ??
|
? nextLesson?.subject.renamedTo ??
|
||||||
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||||
: "",
|
: "",
|
||||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||||
};
|
};
|
||||||
|
|
||||||
case LiveCardState.night:
|
case LiveCardState.night:
|
||||||
return {
|
return {
|
||||||
"color":
|
"color": color,
|
||||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
|
||||||
"icon": nextLesson != null
|
"icon": nextLesson != null
|
||||||
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
||||||
: "book",
|
: "book",
|
||||||
@ -147,44 +149,44 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
"description": "",
|
"description": "",
|
||||||
"startDate": storeFirstRunDate != null
|
"startDate": storeFirstRunDate != null
|
||||||
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
|
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
|
||||||
(_delay.inMilliseconds))
|
(_delay.inMilliseconds))
|
||||||
.toString()
|
.toString()
|
||||||
: "",
|
: "",
|
||||||
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||||
_delay.inMilliseconds)
|
_delay.inMilliseconds)
|
||||||
.toString(),
|
.toString(),
|
||||||
"nextSubject": nextLesson != null
|
"nextSubject": nextLesson != null
|
||||||
? nextLesson?.subject.renamedTo ??
|
? nextLesson?.subject.renamedTo ??
|
||||||
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||||
: "",
|
: "",
|
||||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||||
};
|
};
|
||||||
|
|
||||||
case LiveCardState.duringLesson:
|
case LiveCardState.duringLesson:
|
||||||
return {
|
return {
|
||||||
"color":
|
"color": color,
|
||||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
|
||||||
"icon": currentLesson != null
|
"icon": currentLesson != null
|
||||||
? SubjectIcon.resolveName(subject: currentLesson?.subject)
|
? SubjectIcon.resolveName(subject: currentLesson?.subject)
|
||||||
: "book",
|
: "book",
|
||||||
"index":
|
"index":
|
||||||
currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
|
currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
|
||||||
"title": currentLesson != null
|
"title": currentLesson != null
|
||||||
? currentLesson?.subject.renamedTo ??
|
? currentLesson?.subject.renamedTo ??
|
||||||
ShortSubject.resolve(subject: currentLesson?.subject)
|
ShortSubject.resolve(subject: currentLesson?.subject)
|
||||||
.capital()
|
.capital()
|
||||||
: "",
|
: "",
|
||||||
"subtitle": "Terem: ${currentLesson?.room.replaceAll("_", " ") ?? ""}",
|
"subtitle":
|
||||||
|
"Terem: ${currentLesson?.room.replaceAll("_", " ") ?? ""}",
|
||||||
"description": currentLesson?.description ?? "",
|
"description": currentLesson?.description ?? "",
|
||||||
"startDate": ((currentLesson?.start.millisecondsSinceEpoch ?? 0) -
|
"startDate": ((currentLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||||
_delay.inMilliseconds)
|
_delay.inMilliseconds)
|
||||||
.toString(),
|
.toString(),
|
||||||
"endDate": ((currentLesson?.end.millisecondsSinceEpoch ?? 0) -
|
"endDate": ((currentLesson?.end.millisecondsSinceEpoch ?? 0) -
|
||||||
_delay.inMilliseconds)
|
_delay.inMilliseconds)
|
||||||
.toString(),
|
.toString(),
|
||||||
"nextSubject": nextLesson != null
|
"nextSubject": nextLesson != null
|
||||||
? nextLesson?.subject.renamedTo ??
|
? nextLesson?.subject.renamedTo ??
|
||||||
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||||
: "",
|
: "",
|
||||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||||
};
|
};
|
||||||
@ -199,24 +201,23 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
final diff = getFloorDifference();
|
final diff = getFloorDifference();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"color":
|
"color": color,
|
||||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
|
||||||
"icon": iconFloorMap[diff] ?? "cup.and.saucer",
|
"icon": iconFloorMap[diff] ?? "cup.and.saucer",
|
||||||
"title": "Szünet",
|
"title": "Szünet",
|
||||||
"description": "go $diff".i18n.fill([
|
"description": "go $diff".i18n.fill([
|
||||||
diff != "to room" ? (nextLesson!.getFloor() ?? 0) : nextLesson!.room
|
diff != "to room" ? (nextLesson!.getFloor() ?? 0) : nextLesson!.room
|
||||||
]),
|
]),
|
||||||
"startDate": ((prevLesson?.end.millisecondsSinceEpoch ?? 0) -
|
"startDate": ((prevLesson?.end.millisecondsSinceEpoch ?? 0) -
|
||||||
_delay.inMilliseconds)
|
_delay.inMilliseconds)
|
||||||
.toString(),
|
.toString(),
|
||||||
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||||
_delay.inMilliseconds)
|
_delay.inMilliseconds)
|
||||||
.toString(),
|
.toString(),
|
||||||
"nextSubject": (nextLesson != null
|
"nextSubject": (nextLesson != null
|
||||||
? nextLesson?.subject.renamedTo ??
|
? nextLesson?.subject.renamedTo ??
|
||||||
ShortSubject.resolve(subject: nextLesson?.subject)
|
ShortSubject.resolve(subject: nextLesson?.subject)
|
||||||
.capital()
|
.capital()
|
||||||
: "")
|
: "")
|
||||||
.capital(),
|
.capital(),
|
||||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||||
"index": "",
|
"index": "",
|
||||||
@ -243,8 +244,8 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
DateTime now = _now().add(_delay);
|
DateTime now = _now().add(_delay);
|
||||||
|
|
||||||
if ((currentState == LiveCardState.morning ||
|
if ((currentState == LiveCardState.morning ||
|
||||||
currentState == LiveCardState.afternoon ||
|
currentState == LiveCardState.afternoon ||
|
||||||
currentState == LiveCardState.night) &&
|
currentState == LiveCardState.night) &&
|
||||||
storeFirstRunDate == null) {
|
storeFirstRunDate == null) {
|
||||||
storeFirstRunDate = now;
|
storeFirstRunDate = now;
|
||||||
}
|
}
|
||||||
@ -253,9 +254,9 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
// Filter label lessons #128
|
// Filter label lessons #128
|
||||||
today = today
|
today = today
|
||||||
.where((lesson) =>
|
.where((lesson) =>
|
||||||
lesson.status?.name != "Elmaradt" &&
|
lesson.status?.name != "Elmaradt" &&
|
||||||
lesson.subject.id != '' &&
|
lesson.subject.id != '' &&
|
||||||
!lesson.isEmpty)
|
!lesson.isEmpty)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (today.isNotEmpty) {
|
if (today.isNotEmpty) {
|
||||||
@ -263,7 +264,7 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
today.sort((a, b) => a.start.compareTo(b.start));
|
today.sort((a, b) => a.start.compareTo(b.start));
|
||||||
|
|
||||||
final _lesson = today.firstWhere(
|
final _lesson = today.firstWhere(
|
||||||
(l) => l.start.isBefore(now) && l.end.isAfter(now),
|
(l) => l.start.isBefore(now) && l.end.isAfter(now),
|
||||||
orElse: () => Lesson.fromJson({}));
|
orElse: () => Lesson.fromJson({}));
|
||||||
|
|
||||||
if (_lesson.start.year != 0) {
|
if (_lesson.start.year != 0) {
|
||||||
@ -328,7 +329,7 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
hasActivityStarted = true;
|
hasActivityStarted = true;
|
||||||
} else if (!hasActivityStarted &&
|
} else if (!hasActivityStarted &&
|
||||||
((currentState == LiveCardState.duringLesson &&
|
((currentState == LiveCardState.duringLesson &&
|
||||||
currentLesson != null) ||
|
currentLesson != null) ||
|
||||||
currentState == LiveCardState.duringBreak)) {
|
currentState == LiveCardState.duringBreak)) {
|
||||||
debugPrint("Óra van, vagy szünet, de nincs LiveActivity. létrehozás...");
|
debugPrint("Óra van, vagy szünet, de nincs LiveActivity. létrehozás...");
|
||||||
PlatformChannel.createLiveActivity(toMap());
|
PlatformChannel.createLiveActivity(toMap());
|
||||||
@ -362,7 +363,11 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//END
|
//END
|
||||||
if ((currentState == LiveCardState.afternoon || currentState == LiveCardState.morning || currentState == LiveCardState.night) && hasActivityStarted && nextLesson != null &&
|
if ((currentState == LiveCardState.afternoon ||
|
||||||
|
currentState == LiveCardState.morning ||
|
||||||
|
currentState == LiveCardState.night) &&
|
||||||
|
hasActivityStarted &&
|
||||||
|
nextLesson != null &&
|
||||||
nextLesson!.start.difference(now).inMinutes > 60) {
|
nextLesson!.start.difference(now).inMinutes > 60) {
|
||||||
debugPrint("Több, mint 1 óra van az első óráig. Befejezés...");
|
debugPrint("Több, mint 1 óra van az első óráig. Befejezés...");
|
||||||
PlatformChannel.endLiveActivity();
|
PlatformChannel.endLiveActivity();
|
||||||
@ -389,4 +394,4 @@ class LiveCardProvider extends ChangeNotifier {
|
|||||||
List<Lesson> _today(TimetableProvider p) => (p.getWeek(Week.current()) ?? [])
|
List<Lesson> _today(TimetableProvider p) => (p.getWeek(Week.current()) ?? [])
|
||||||
.where((l) => _sameDate(l.date, _now()))
|
.where((l) => _sameDate(l.date, _now()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,11 @@ import 'package:refilc_mobile_ui/screens/settings/settings_route.dart'
|
|||||||
import 'package:refilc_mobile_ui/screens/settings/settings_screen.dart'
|
import 'package:refilc_mobile_ui/screens/settings/settings_screen.dart'
|
||||||
as mobile;
|
as mobile;
|
||||||
|
|
||||||
// Desktop UI
|
// Desktop UI (no more desktop ui)
|
||||||
import 'package:refilc_desktop_ui/screens/navigation/navigation_screen.dart'
|
// import 'package:refilc_desktop_ui/screens/navigation/navigation_screen.dart'
|
||||||
as desktop;
|
// as desktop;
|
||||||
import 'package:refilc_desktop_ui/screens/login/login_screen.dart' as desktop;
|
// import 'package:refilc_desktop_ui/screens/login/login_screen.dart' as desktop;
|
||||||
import 'package:refilc_desktop_ui/screens/login/login_route.dart' as desktop;
|
// import 'package:refilc_desktop_ui/screens/login/login_route.dart' as desktop;
|
||||||
|
|
||||||
// Providers
|
// Providers
|
||||||
import 'package:refilc/models/settings.dart';
|
import 'package:refilc/models/settings.dart';
|
||||||
@ -248,17 +248,18 @@ class App extends StatelessWidget {
|
|||||||
|
|
||||||
Route? rootNavigator(RouteSettings route) {
|
Route? rootNavigator(RouteSettings route) {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
switch (route.name) {
|
return null;
|
||||||
case "login_back":
|
// switch (route.name) {
|
||||||
return CupertinoPageRoute(
|
// case "login_back":
|
||||||
builder: (context) => const desktop.LoginScreen(back: true));
|
// return CupertinoPageRoute(
|
||||||
case "login":
|
// builder: (context) => const desktop.LoginScreen(back: true));
|
||||||
return _rootRoute(const desktop.LoginScreen());
|
// case "login":
|
||||||
case "navigation":
|
// return _rootRoute(const desktop.LoginScreen());
|
||||||
return _rootRoute(const desktop.NavigationScreen());
|
// case "navigation":
|
||||||
case "login_to_navigation":
|
// return _rootRoute(const desktop.NavigationScreen());
|
||||||
return desktop.loginRoute(const desktop.NavigationScreen());
|
// case "login_to_navigation":
|
||||||
}
|
// return desktop.loginRoute(const desktop.NavigationScreen());
|
||||||
|
// }
|
||||||
} else if (Platform.isAndroid || Platform.isIOS) {
|
} else if (Platform.isAndroid || Platform.isIOS) {
|
||||||
switch (route.name) {
|
switch (route.name) {
|
||||||
case "login_back":
|
case "login_back":
|
||||||
@ -274,17 +275,18 @@ class App extends StatelessWidget {
|
|||||||
return mobile.settingsRoute(const mobile.SettingsScreen());
|
return mobile.settingsRoute(const mobile.SettingsScreen());
|
||||||
}
|
}
|
||||||
} else if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
|
} else if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
|
||||||
switch (route.name) {
|
return null;
|
||||||
case "login_back":
|
// switch (route.name) {
|
||||||
return CupertinoPageRoute(
|
// case "login_back":
|
||||||
builder: (context) => const desktop.LoginScreen(back: true));
|
// return CupertinoPageRoute(
|
||||||
case "login":
|
// builder: (context) => const desktop.LoginScreen(back: true));
|
||||||
return _rootRoute(const desktop.LoginScreen());
|
// case "login":
|
||||||
case "navigation":
|
// return _rootRoute(const desktop.LoginScreen());
|
||||||
return _rootRoute(const desktop.NavigationScreen());
|
// case "navigation":
|
||||||
case "login_to_navigation":
|
// return _rootRoute(const desktop.NavigationScreen());
|
||||||
return desktop.loginRoute(const desktop.NavigationScreen());
|
// case "login_to_navigation":
|
||||||
}
|
// return desktop.loginRoute(const desktop.NavigationScreen());
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import 'package:refilc/models/settings.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
|
// import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
|
||||||
|
|
||||||
const settingsDB = DatabaseStruct("settings", {
|
const settingsDB = DatabaseStruct("settings", {
|
||||||
"language": String, "start_page": int, "rounding": int, "theme": int,
|
"language": String, "start_page": int, "rounding": int, "theme": int,
|
||||||
@ -110,7 +110,8 @@ Future<Database> initDB(DatabaseProvider database) async {
|
|||||||
Database db;
|
Database db;
|
||||||
|
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
db = await databaseFactoryFfiWeb.openDatabase("app.db");
|
// db = await databaseFactoryFfiWeb.openDatabase("app.db");
|
||||||
|
throw "web is not supported";
|
||||||
} else if (Platform.isLinux || Platform.isWindows) {
|
} else if (Platform.isLinux || Platform.isWindows) {
|
||||||
sqfliteFfiInit();
|
sqfliteFfiInit();
|
||||||
db = await databaseFactoryFfi.openDatabase("app.db");
|
db = await databaseFactoryFfi.openDatabase("app.db");
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:refilc/api/providers/user_provider.dart';
|
import 'package:refilc/api/providers/user_provider.dart';
|
||||||
import 'package:refilc/api/providers/database_provider.dart';
|
import 'package:refilc/api/providers/database_provider.dart';
|
||||||
import 'package:refilc/database/init.dart';
|
import 'package:refilc/database/init.dart';
|
||||||
import 'package:refilc/helpers/notification_helper.dart';
|
// import 'package:refilc/helpers/notification_helper.dart';
|
||||||
import 'package:refilc/models/settings.dart';
|
import 'package:refilc/models/settings.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -104,7 +104,7 @@ class Startup {
|
|||||||
user = await database.query.getUsers(settings);
|
user = await database.query.getUsers(settings);
|
||||||
|
|
||||||
// Set all notification categories to seen to avoid having notifications that the user has already seen in the app
|
// Set all notification categories to seen to avoid having notifications that the user has already seen in the app
|
||||||
NotificationsHelper().setAllCategoriesSeen(user);
|
// NotificationsHelper().setAllCategoriesSeen(user);
|
||||||
|
|
||||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
||||||
// Notifications setup
|
// Notifications setup
|
||||||
@ -146,30 +146,30 @@ class Startup {
|
|||||||
|
|
||||||
// Platform specific settings
|
// Platform specific settings
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
const DarwinInitializationSettings initializationSettingsDarwin =
|
// const DarwinInitializationSettings initializationSettingsDarwin =
|
||||||
DarwinInitializationSettings(
|
// DarwinInitializationSettings(
|
||||||
requestSoundPermission: true,
|
// requestSoundPermission: true,
|
||||||
requestBadgePermission: true,
|
// requestBadgePermission: true,
|
||||||
requestAlertPermission: false,
|
// requestAlertPermission: false,
|
||||||
);
|
// );
|
||||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
// const AndroidInitializationSettings initializationSettingsAndroid =
|
||||||
AndroidInitializationSettings('ic_notification');
|
// AndroidInitializationSettings('ic_notification');
|
||||||
const LinuxInitializationSettings initializationSettingsLinux =
|
// const LinuxInitializationSettings initializationSettingsLinux =
|
||||||
LinuxInitializationSettings(defaultActionName: 'Open notification');
|
// LinuxInitializationSettings(defaultActionName: 'Open notification');
|
||||||
const InitializationSettings initializationSettings =
|
// const InitializationSettings initializationSettings =
|
||||||
InitializationSettings(
|
// InitializationSettings(
|
||||||
android: initializationSettingsAndroid,
|
// android: initializationSettingsAndroid,
|
||||||
iOS: initializationSettingsDarwin,
|
// iOS: initializationSettingsDarwin,
|
||||||
macOS: initializationSettingsDarwin,
|
// macOS: initializationSettingsDarwin,
|
||||||
linux: initializationSettingsLinux,
|
// linux: initializationSettingsLinux,
|
||||||
);
|
// );
|
||||||
|
|
||||||
// Initialize notifications
|
// Initialize notifications
|
||||||
await flutterLocalNotificationsPlugin.initialize(
|
// await flutterLocalNotificationsPlugin.initialize(
|
||||||
initializationSettings,
|
// initializationSettings,
|
||||||
onDidReceiveNotificationResponse:
|
// onDidReceiveNotificationResponse:
|
||||||
NotificationsHelper().onDidReceiveNotificationResponse,
|
// NotificationsHelper().onDidReceiveNotificationResponse,
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (Platform.isAndroid || Platform.isIOS) {
|
// if (Platform.isAndroid || Platform.isIOS) {
|
||||||
@ -214,7 +214,7 @@ Widget errorBuilder(FlutterErrorDetails details) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Container();
|
return Container();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ Future<void> initPlatformState() async {
|
|||||||
if (!Platform.isIOS) return;
|
if (!Platform.isIOS) return;
|
||||||
LiveActivityHelper().backgroundJob();
|
LiveActivityHelper().backgroundJob();
|
||||||
} else {
|
} else {
|
||||||
NotificationsHelper().backgroundJob();
|
// NotificationsHelper().backgroundJob();
|
||||||
}
|
}
|
||||||
BackgroundFetch.finish(taskId);
|
BackgroundFetch.finish(taskId);
|
||||||
}, (String taskId) async {
|
}, (String taskId) async {
|
||||||
@ -279,7 +279,7 @@ void backgroundHeadlessTask(HeadlessTask task) {
|
|||||||
if (!Platform.isIOS) return;
|
if (!Platform.isIOS) return;
|
||||||
LiveActivityHelper().backgroundJob();
|
LiveActivityHelper().backgroundJob();
|
||||||
} else {
|
} else {
|
||||||
NotificationsHelper().backgroundJob();
|
// NotificationsHelper().backgroundJob();
|
||||||
}
|
}
|
||||||
BackgroundFetch.finish(task.taskId);
|
BackgroundFetch.finish(task.taskId);
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
renameSubjectsItalics: false,
|
renameSubjectsItalics: false,
|
||||||
renameTeachersEnabled: false,
|
renameTeachersEnabled: false,
|
||||||
renameTeachersItalics: false,
|
renameTeachersItalics: false,
|
||||||
liveActivityColor: const Color(0xFF676767),
|
liveActivityColor: const Color(0x00000000),
|
||||||
welcomeMessage: '',
|
welcomeMessage: '',
|
||||||
appIcon: 'refilc_default',
|
appIcon: 'refilc_default',
|
||||||
currentThemeId: '',
|
currentThemeId: '',
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:refilc/theme/colors/dark_desktop.dart';
|
// import 'package:refilc/theme/colors/dark_desktop.dart';
|
||||||
import 'package:refilc/theme/colors/dark_mobile.dart';
|
import 'package:refilc/theme/colors/dark_mobile.dart';
|
||||||
import 'package:refilc/theme/colors/light_desktop.dart';
|
// import 'package:refilc/theme/colors/light_desktop.dart';
|
||||||
import 'package:refilc/theme/colors/light_mobile.dart';
|
import 'package:refilc/theme/colors/light_mobile.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -21,10 +21,16 @@ class AppColors {
|
|||||||
} else {
|
} else {
|
||||||
switch (brightness) {
|
switch (brightness) {
|
||||||
case Brightness.light:
|
case Brightness.light:
|
||||||
return LightDesktopAppColors();
|
return LightMobileAppColors();
|
||||||
case Brightness.dark:
|
case Brightness.dark:
|
||||||
return DarkDesktopAppColors();
|
return DarkMobileAppColors();
|
||||||
}
|
}
|
||||||
|
// switch (brightness) {
|
||||||
|
// case Brightness.light:
|
||||||
|
// return LightDesktopAppColors();
|
||||||
|
// case Brightness.dark:
|
||||||
|
// return DarkDesktopAppColors();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,42 +115,42 @@ class AppTheme {
|
|||||||
primary: accent,
|
primary: accent,
|
||||||
onPrimary:
|
onPrimary:
|
||||||
(accent.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
(accent.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
secondary: newSecondary,
|
secondary: newSecondary,
|
||||||
onSecondary: (newSecondary.computeLuminance() > 0.5
|
onSecondary: (newSecondary.computeLuminance() > 0.5
|
||||||
? Colors.black
|
? Colors.black
|
||||||
: Colors.white)
|
: Colors.white)
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
tertiary: newTertiary,
|
tertiary: newTertiary,
|
||||||
onTertiary:
|
onTertiary:
|
||||||
(newTertiary.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
(newTertiary.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
error: lightColors.red,
|
error: lightColors.red,
|
||||||
onError: Colors.white.withOpacity(.9),
|
onError: Colors.white.withValues(alpha: .9),
|
||||||
surface: highlightColor,
|
surface: highlightColor,
|
||||||
onSurface: Colors.black.withOpacity(.9),
|
onSurface: Colors.black.withValues(alpha: .9),
|
||||||
),
|
),
|
||||||
shadowColor: lightColors.shadow.withOpacity(.5),
|
shadowColor: lightColors.shadow.withValues(alpha: .5),
|
||||||
appBarTheme: AppBarTheme(backgroundColor: backgroundColor),
|
appBarTheme: AppBarTheme(backgroundColor: backgroundColor),
|
||||||
indicatorColor: accent,
|
indicatorColor: accent,
|
||||||
iconTheme: IconThemeData(color: lightColors.text.withOpacity(.75)),
|
iconTheme: IconThemeData(color: lightColors.text.withValues(alpha: .75)),
|
||||||
navigationBarTheme: NavigationBarThemeData(
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
indicatorColor:
|
indicatorColor: accent.withValues(
|
||||||
accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
|
alpha: accentColor == AccentColor.adaptive ? 0.4 : 0.8),
|
||||||
iconTheme:
|
iconTheme:
|
||||||
WidgetStateProperty.all(IconThemeData(color: lightColors.text)),
|
WidgetStateProperty.all(IconThemeData(color: lightColors.text)),
|
||||||
backgroundColor: highlightColor,
|
backgroundColor: highlightColor,
|
||||||
labelTextStyle: WidgetStateProperty.all(TextStyle(
|
labelTextStyle: WidgetStateProperty.all(TextStyle(
|
||||||
fontSize: 13.0,
|
fontSize: 13.0,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: lightColors.text.withOpacity(0.8),
|
color: lightColors.text.withValues(alpha: 0.8),
|
||||||
)),
|
)),
|
||||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||||
height: 76.0,
|
height: 76.0,
|
||||||
),
|
),
|
||||||
sliderTheme: SliderThemeData(
|
sliderTheme: SliderThemeData(
|
||||||
inactiveTrackColor: accent.withOpacity(.3),
|
inactiveTrackColor: accent.withValues(alpha: .3),
|
||||||
),
|
),
|
||||||
progressIndicatorTheme: ProgressIndicatorThemeData(color: accent),
|
progressIndicatorTheme: ProgressIndicatorThemeData(color: accent),
|
||||||
expansionTileTheme: ExpansionTileThemeData(iconColor: accent),
|
expansionTileTheme: ExpansionTileThemeData(iconColor: accent),
|
||||||
@ -238,48 +238,48 @@ class AppTheme {
|
|||||||
primary: accent,
|
primary: accent,
|
||||||
onPrimary:
|
onPrimary:
|
||||||
(accent.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
(accent.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
secondary: newSecondary,
|
secondary: newSecondary,
|
||||||
onSecondary: (newSecondary.computeLuminance() > 0.5
|
onSecondary: (newSecondary.computeLuminance() > 0.5
|
||||||
? Colors.black
|
? Colors.black
|
||||||
: Colors.white)
|
: Colors.white)
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
tertiary: newTertiary,
|
tertiary: newTertiary,
|
||||||
onTertiary:
|
onTertiary:
|
||||||
(newTertiary.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
(newTertiary.computeLuminance() > 0.5 ? Colors.black : Colors.white)
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
error: darkColors.red,
|
error: darkColors.red,
|
||||||
onError: Colors.black.withOpacity(.9),
|
onError: Colors.black.withValues(alpha: .9),
|
||||||
surface: highlightColor,
|
surface: highlightColor,
|
||||||
onSurface: Colors.white.withOpacity(.9),
|
onSurface: Colors.white.withValues(alpha: .9),
|
||||||
),
|
),
|
||||||
shadowColor: highlightColor.withOpacity(.5), //darkColors.shadow,
|
shadowColor: highlightColor.withValues(alpha: .5), //darkColors.shadow,
|
||||||
appBarTheme: AppBarTheme(backgroundColor: backgroundColor),
|
appBarTheme: AppBarTheme(backgroundColor: backgroundColor),
|
||||||
indicatorColor: accent,
|
indicatorColor: accent,
|
||||||
iconTheme: IconThemeData(color: darkColors.text.withOpacity(.75)),
|
iconTheme: IconThemeData(color: darkColors.text.withValues(alpha: .75)),
|
||||||
navigationBarTheme: NavigationBarThemeData(
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
indicatorColor:
|
indicatorColor: accent.withValues(
|
||||||
accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
|
alpha: accentColor == AccentColor.adaptive ? 0.4 : 0.8),
|
||||||
iconTheme:
|
iconTheme:
|
||||||
WidgetStateProperty.all(IconThemeData(color: darkColors.text)),
|
WidgetStateProperty.all(IconThemeData(color: darkColors.text)),
|
||||||
backgroundColor: highlightColor,
|
backgroundColor: highlightColor,
|
||||||
labelTextStyle: WidgetStateProperty.all(TextStyle(
|
labelTextStyle: WidgetStateProperty.all(TextStyle(
|
||||||
fontSize: 13.0,
|
fontSize: 13.0,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: darkColors.text.withOpacity(0.8),
|
color: darkColors.text.withValues(alpha: 0.8),
|
||||||
)),
|
)),
|
||||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||||
height: 76.0,
|
height: 76.0,
|
||||||
),
|
),
|
||||||
sliderTheme: SliderThemeData(
|
sliderTheme: SliderThemeData(
|
||||||
inactiveTrackColor: accent.withOpacity(.3),
|
inactiveTrackColor: accent.withValues(alpha: .3),
|
||||||
),
|
),
|
||||||
progressIndicatorTheme: ProgressIndicatorThemeData(color: accent),
|
progressIndicatorTheme: ProgressIndicatorThemeData(color: accent),
|
||||||
expansionTileTheme: ExpansionTileThemeData(iconColor: accent),
|
expansionTileTheme: ExpansionTileThemeData(iconColor: accent),
|
||||||
cardColor: highlightColor,
|
cardColor: highlightColor,
|
||||||
chipTheme: ChipThemeData(
|
chipTheme: ChipThemeData(
|
||||||
backgroundColor: accent.withOpacity(.2),
|
backgroundColor: accent.withValues(alpha: .2),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
),
|
),
|
||||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||||
|
@ -191,7 +191,7 @@ List<Widget> sortDateWidgets(
|
|||||||
width: 150.0,
|
width: 150.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
color: AppColors.of(context).text.withOpacity(.25),
|
color: AppColors.of(context).text.withValues(alpha: .25),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -178,8 +178,7 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData,
|
|||||||
// Ads
|
// Ads
|
||||||
case FilterType.ads:
|
case FilterType.ads:
|
||||||
if (adProvider.available) {
|
if (adProvider.available) {
|
||||||
items = ad_filter.getWidgets(
|
items = ad_filter.getWidgets(adProvider.ads, context);
|
||||||
adProvider.ads, context);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -233,8 +232,8 @@ Widget filterItemBuilder(
|
|||||||
BoxShadow(
|
BoxShadow(
|
||||||
offset: const Offset(0, 21),
|
offset: const Offset(0, 21),
|
||||||
blurRadius: 23.0,
|
blurRadius: 23.0,
|
||||||
color: Theme.of(context).shadowColor.withOpacity(
|
color: Theme.of(context).shadowColor.withValues(
|
||||||
Theme.of(context).shadowColor.opacity *
|
alpha: Theme.of(context).shadowColor.opacity *
|
||||||
CurvedAnimation(
|
CurvedAnimation(
|
||||||
parent: CurvedAnimation(
|
parent: CurvedAnimation(
|
||||||
parent: animation,
|
parent: animation,
|
||||||
|
@ -8,7 +8,9 @@ List<DateWidget> getWidgets(List<Grade> providerGrades) {
|
|||||||
List<DateWidget> items = [];
|
List<DateWidget> items = [];
|
||||||
for (var gradeType in GradeType.values) {
|
for (var gradeType in GradeType.values) {
|
||||||
if ([GradeType.midYear, GradeType.unknown, GradeType.levelExam]
|
if ([GradeType.midYear, GradeType.unknown, GradeType.levelExam]
|
||||||
.contains(gradeType)) continue;
|
.contains(gradeType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
List<Grade> grades =
|
List<Grade> grades =
|
||||||
providerGrades.where((grade) => grade.type == gradeType).toList();
|
providerGrades.where((grade) => grade.type == gradeType).toList();
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import 'package:refilc/ui/date_widget.dart';
|
import 'package:refilc/ui/date_widget.dart';
|
||||||
import 'package:refilc/utils/platform.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/grade.dart';
|
import 'package:refilc_kreta_api/models/grade.dart';
|
||||||
import 'package:refilc_mobile_ui/common/widgets/grade/grade_viewable.dart'
|
import 'package:refilc_mobile_ui/common/widgets/grade/grade_viewable.dart'
|
||||||
as mobile;
|
as mobile;
|
||||||
import 'package:refilc_mobile_ui/common/widgets/grade/new_grades.dart'
|
import 'package:refilc_mobile_ui/common/widgets/grade/new_grades.dart'
|
||||||
as mobile;
|
as mobile;
|
||||||
import 'package:refilc_desktop_ui/common/widgets/grade/grade_viewable.dart'
|
|
||||||
as desktop;
|
|
||||||
|
|
||||||
List<DateWidget> getWidgets(
|
List<DateWidget> getWidgets(
|
||||||
List<Grade> providerGrades, DateTime? lastSeenDate) {
|
List<Grade> providerGrades, DateTime? lastSeenDate) {
|
||||||
@ -19,9 +16,7 @@ List<DateWidget> getWidgets(
|
|||||||
items.add(DateWidget(
|
items.add(DateWidget(
|
||||||
key: grade.id,
|
key: grade.id,
|
||||||
date: grade.date,
|
date: grade.date,
|
||||||
widget: PlatformUtils.isMobile
|
widget: mobile.GradeViewable(grade),
|
||||||
? mobile.GradeViewable(grade)
|
|
||||||
: desktop.GradeViewable(grade),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
/// Blocky Color Picker
|
/// Blocky Color Picker
|
||||||
|
|
||||||
library block_colorpicker;
|
library;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:refilc/theme/colors/accent.dart';
|
import 'package:refilc/theme/colors/accent.dart';
|
||||||
@ -73,7 +73,7 @@ Widget _defaultItemBuilder(
|
|||||||
color: color,
|
color: color,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: color.withOpacity(0.8),
|
color: color.withValues(alpha: 0.8),
|
||||||
offset: const Offset(1, 2),
|
offset: const Offset(1, 2),
|
||||||
blurRadius: 5)
|
blurRadius: 5)
|
||||||
],
|
],
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
// ignore_for_file: use_build_context_synchronously
|
// ignore_for_file: use_build_context_synchronously
|
||||||
|
|
||||||
library hsv_picker;
|
library;
|
||||||
|
|
||||||
import 'package:refilc/models/shared_theme.dart';
|
import 'package:refilc/models/shared_theme.dart';
|
||||||
import 'package:refilc_mobile_ui/common/custom_snack_bar.dart';
|
import 'package:refilc_mobile_ui/common/custom_snack_bar.dart';
|
||||||
@ -380,7 +380,7 @@ class FilcColorPickerState extends State<FilcColorPicker> {
|
|||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
color: AppColors.of(context)
|
color: AppColors.of(context)
|
||||||
.text
|
.text
|
||||||
.withOpacity(isAdvancedView ? 1.0 : .5),
|
.withValues(alpha: isAdvancedView ? 1.0 : .5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -210,32 +210,32 @@ class TrackPainter extends CustomPainter {
|
|||||||
break;
|
break;
|
||||||
case TrackType.red:
|
case TrackType.red:
|
||||||
final List<Color> colors = [
|
final List<Color> colors = [
|
||||||
hsvColor.toColor().withRed(0).withOpacity(1.0),
|
hsvColor.toColor().withRed(0).withValues(alpha: 1.0),
|
||||||
hsvColor.toColor().withRed(255).withOpacity(1.0),
|
hsvColor.toColor().withRed(255).withValues(alpha: 1.0),
|
||||||
];
|
];
|
||||||
Gradient gradient = LinearGradient(colors: colors);
|
Gradient gradient = LinearGradient(colors: colors);
|
||||||
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
||||||
break;
|
break;
|
||||||
case TrackType.green:
|
case TrackType.green:
|
||||||
final List<Color> colors = [
|
final List<Color> colors = [
|
||||||
hsvColor.toColor().withGreen(0).withOpacity(1.0),
|
hsvColor.toColor().withGreen(0).withValues(alpha: 1.0),
|
||||||
hsvColor.toColor().withGreen(255).withOpacity(1.0),
|
hsvColor.toColor().withGreen(255).withValues(alpha: 1.0),
|
||||||
];
|
];
|
||||||
Gradient gradient = LinearGradient(colors: colors);
|
Gradient gradient = LinearGradient(colors: colors);
|
||||||
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
||||||
break;
|
break;
|
||||||
case TrackType.blue:
|
case TrackType.blue:
|
||||||
final List<Color> colors = [
|
final List<Color> colors = [
|
||||||
hsvColor.toColor().withBlue(0).withOpacity(1.0),
|
hsvColor.toColor().withBlue(0).withValues(alpha: 1.0),
|
||||||
hsvColor.toColor().withBlue(255).withOpacity(1.0),
|
hsvColor.toColor().withBlue(255).withValues(alpha: 1.0),
|
||||||
];
|
];
|
||||||
Gradient gradient = LinearGradient(colors: colors);
|
Gradient gradient = LinearGradient(colors: colors);
|
||||||
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
||||||
break;
|
break;
|
||||||
case TrackType.alpha:
|
case TrackType.alpha:
|
||||||
final List<Color> colors = [
|
final List<Color> colors = [
|
||||||
hsvColor.toColor().withOpacity(0.0),
|
hsvColor.toColor().withValues(alpha: 0.0),
|
||||||
hsvColor.toColor().withOpacity(1.0),
|
hsvColor.toColor().withValues(alpha: 1.0),
|
||||||
];
|
];
|
||||||
Gradient gradient = LinearGradient(colors: colors);
|
Gradient gradient = LinearGradient(colors: colors);
|
||||||
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
|
||||||
@ -390,7 +390,7 @@ class ColorPickerInputState extends State<ColorPickerInput> {
|
|||||||
),
|
),
|
||||||
contentPadding:
|
contentPadding:
|
||||||
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
|
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
|
||||||
fillColor: AppColors.of(context).text.withOpacity(.1),
|
fillColor: AppColors.of(context).text.withValues(alpha: .1),
|
||||||
),
|
),
|
||||||
onChanged: (String value) {
|
onChanged: (String value) {
|
||||||
String input = value;
|
String input = value;
|
||||||
@ -603,20 +603,20 @@ class ColorPickerSlider extends StatelessWidget {
|
|||||||
break;
|
break;
|
||||||
case TrackType.red:
|
case TrackType.red:
|
||||||
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().red / 0xff;
|
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().red / 0xff;
|
||||||
thumbColor = hsvColor.toColor().withOpacity(1.0);
|
thumbColor = hsvColor.toColor().withValues(alpha: 1.0);
|
||||||
break;
|
break;
|
||||||
case TrackType.green:
|
case TrackType.green:
|
||||||
thumbOffset +=
|
thumbOffset +=
|
||||||
(box.maxWidth - 30.0) * hsvColor.toColor().green / 0xff;
|
(box.maxWidth - 30.0) * hsvColor.toColor().green / 0xff;
|
||||||
thumbColor = hsvColor.toColor().withOpacity(1.0);
|
thumbColor = hsvColor.toColor().withValues(alpha: 1.0);
|
||||||
break;
|
break;
|
||||||
case TrackType.blue:
|
case TrackType.blue:
|
||||||
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().blue / 0xff;
|
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().blue / 0xff;
|
||||||
thumbColor = hsvColor.toColor().withOpacity(1.0);
|
thumbColor = hsvColor.toColor().withValues(alpha: 1.0);
|
||||||
break;
|
break;
|
||||||
case TrackType.alpha:
|
case TrackType.alpha:
|
||||||
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().opacity;
|
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().opacity;
|
||||||
thumbColor = hsvColor.toColor().withOpacity(hsvColor.alpha);
|
thumbColor = hsvColor.toColor().withValues(alpha: hsvColor.alpha);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,8 +709,8 @@ class ColorIndicator extends StatelessWidget {
|
|||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: useWhiteForeground(color)
|
color: useWhiteForeground(color)
|
||||||
? Colors.white.withOpacity(.5)
|
? Colors.white.withValues(alpha: .5)
|
||||||
: Colors.black.withOpacity(.5),
|
: Colors.black.withValues(alpha: .5),
|
||||||
offset: const Offset(0, 0),
|
offset: const Offset(0, 0),
|
||||||
blurRadius: 5)
|
blurRadius: 5)
|
||||||
],
|
],
|
||||||
|
@ -199,7 +199,7 @@ class GradeTile extends StatelessWidget {
|
|||||||
: Icon(
|
: Icon(
|
||||||
SubjectIcon.resolveVariant(
|
SubjectIcon.resolveVariant(
|
||||||
context: context, subject: grade.subject),
|
context: context, subject: grade.subject),
|
||||||
color: AppColors.of(context).text.withOpacity(.5),
|
color: AppColors.of(context).text.withValues(alpha: .5),
|
||||||
),
|
),
|
||||||
minLeadingWidth: isSubjectView ? 32.0 : 0,
|
minLeadingWidth: isSubjectView ? 32.0 : 0,
|
||||||
),
|
),
|
||||||
@ -288,7 +288,8 @@ class GradeValueWidget extends StatelessWidget {
|
|||||||
shadows: [
|
shadows: [
|
||||||
if (value.weight >= 200)
|
if (value.weight >= 200)
|
||||||
Shadow(
|
Shadow(
|
||||||
color: (contrast ? Colors.white : color).withOpacity(.4),
|
color:
|
||||||
|
(contrast ? Colors.white : color).withValues(alpha: .4),
|
||||||
offset: const Offset(-4, -3),
|
offset: const Offset(-4, -3),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -312,7 +313,7 @@ class GradeValueWidget extends StatelessWidget {
|
|||||||
width: size * 1.4,
|
width: size * 1.4,
|
||||||
height: size * 1.4,
|
height: size * 1.4,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color.withOpacity(contrast ? 1.0 : .25),
|
color: color.withValues(alpha: contrast ? 1.0 : .25),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
if (shadow &&
|
if (shadow &&
|
||||||
|
@ -75,7 +75,7 @@ class LessonTile extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lesson.isEmpty) {
|
if (lesson.isEmpty) {
|
||||||
accent = AppColors.of(context).text.withOpacity(0.6);
|
accent = AppColors.of(context).text.withValues(alpha: 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lesson.studentPresence) {
|
if (!lesson.studentPresence) {
|
||||||
@ -182,9 +182,8 @@ class LessonTile extends StatelessWidget {
|
|||||||
fontSize: 16.5,
|
fontSize: 16.5,
|
||||||
color: fill
|
color: fill
|
||||||
? accent
|
? accent
|
||||||
: AppColors.of(context)
|
: AppColors.of(context).text.withValues(
|
||||||
.text
|
alpha: !lesson.isEmpty ? 1.0 : 0.5),
|
||||||
.withOpacity(!lesson.isEmpty ? 1.0 : 0.5),
|
|
||||||
fontStyle: lesson.subject.isRenamed &&
|
fontStyle: lesson.subject.isRenamed &&
|
||||||
settingsProvider.renamedSubjectsItalics
|
settingsProvider.renamedSubjectsItalics
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
@ -199,8 +198,10 @@ class LessonTile extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 14.0,
|
fontSize: 14.0,
|
||||||
color: fill
|
color: fill
|
||||||
? accent.withOpacity(.9)
|
? accent.withValues(alpha: .9)
|
||||||
: AppColors.of(context).text.withOpacity(.9),
|
: AppColors.of(context)
|
||||||
|
.text
|
||||||
|
.withValues(alpha: .9),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -217,7 +218,7 @@ class LessonTile extends StatelessWidget {
|
|||||||
// color: Theme.of(context)
|
// color: Theme.of(context)
|
||||||
// .colorScheme
|
// .colorScheme
|
||||||
// .secondary
|
// .secondary
|
||||||
// .withOpacity(.15),
|
// .withValues(alpha: .15),
|
||||||
// borderRadius: BorderRadius.circular(10.0),
|
// borderRadius: BorderRadius.circular(10.0),
|
||||||
// ),
|
// ),
|
||||||
// child: Text(
|
// child: Text(
|
||||||
@ -229,7 +230,7 @@ class LessonTile extends StatelessWidget {
|
|||||||
// color: Theme.of(context)
|
// color: Theme.of(context)
|
||||||
// .colorScheme
|
// .colorScheme
|
||||||
// .secondary
|
// .secondary
|
||||||
// .withOpacity(.9),
|
// .withValues(alpha: .9),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
@ -246,11 +247,11 @@ class LessonTile extends StatelessWidget {
|
|||||||
horizontal: 5.5, vertical: 3.0),
|
horizontal: 5.5, vertical: 3.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: fill
|
color: fill
|
||||||
? accent.withOpacity(.15)
|
? accent.withValues(alpha: .15)
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.tertiary
|
.tertiary
|
||||||
.withOpacity(.15),
|
.withValues(alpha: .15),
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -261,11 +262,11 @@ class LessonTile extends StatelessWidget {
|
|||||||
fontSize: 12.0,
|
fontSize: 12.0,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: fill
|
color: fill
|
||||||
? accent.withOpacity(0.9)
|
? accent.withValues(alpha: 0.9)
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondary
|
.secondary
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -277,7 +278,9 @@ class LessonTile extends StatelessWidget {
|
|||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14.0,
|
fontSize: 14.0,
|
||||||
color: fill ? accent.withOpacity(0.5) : null,
|
color: fill
|
||||||
|
? accent.withValues(alpha: 0.5)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -344,7 +347,7 @@ class LessonTile extends StatelessWidget {
|
|||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondary
|
.secondary
|
||||||
.withOpacity(.3)
|
.withValues(alpha: .3)
|
||||||
: const Color(0x00000000),
|
: const Color(0x00000000),
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
@ -353,7 +356,7 @@ class LessonTile extends StatelessWidget {
|
|||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondary
|
.secondary
|
||||||
.withOpacity(.25),
|
.withValues(alpha: .25),
|
||||||
blurRadius: 6.0,
|
blurRadius: 6.0,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -386,7 +389,7 @@ class LessonTile extends StatelessWidget {
|
|||||||
// fontWeight: FontWeight.w500,
|
// fontWeight: FontWeight.w500,
|
||||||
// color: AppColors.of(context)
|
// color: AppColors.of(context)
|
||||||
// .text
|
// .text
|
||||||
// .withOpacity(.75),
|
// .withValues(alpha: .75),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
@ -398,11 +401,11 @@ class LessonTile extends StatelessWidget {
|
|||||||
horizontal: 6.0, vertical: 3.5),
|
horizontal: 6.0, vertical: 3.5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: fill
|
color: fill
|
||||||
? accent.withOpacity(.15)
|
? accent.withValues(alpha: .15)
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.tertiary
|
.tertiary
|
||||||
.withOpacity(.15),
|
.withValues(alpha: .15),
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -413,11 +416,11 @@ class LessonTile extends StatelessWidget {
|
|||||||
fontSize: 12.5,
|
fontSize: 12.5,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: fill
|
color: fill
|
||||||
? accent.withOpacity(0.9)
|
? accent.withValues(alpha: 0.9)
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondary
|
.secondary
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -438,10 +441,10 @@ class LessonTile extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: fill
|
color: fill
|
||||||
? accent.withOpacity(.9)
|
? accent.withValues(alpha: .9)
|
||||||
: AppColors.of(context)
|
: AppColors.of(context)
|
||||||
.text
|
.text
|
||||||
.withOpacity(.9),
|
.withValues(alpha: .9),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -502,8 +505,8 @@ class LessonSubtile extends StatelessWidget {
|
|||||||
Center(
|
Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 30.0,
|
width: 30.0,
|
||||||
child:
|
child: Icon(icon,
|
||||||
Icon(icon, color: iconColor.withOpacity(.75), size: 20.0),
|
color: iconColor.withValues(alpha: .75), size: 20.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -515,7 +518,8 @@ class LessonSubtile extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: AppColors.of(context).text.withOpacity(.65)),
|
color:
|
||||||
|
AppColors.of(context).text.withValues(alpha: .65)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -58,7 +58,8 @@ class MessageTile extends StatelessWidget {
|
|||||||
width: 105,
|
width: 105,
|
||||||
height: 15,
|
height: 15,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.of(context).text.withOpacity(.85),
|
color:
|
||||||
|
AppColors.of(context).text.withValues(alpha: .85),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -89,7 +90,8 @@ class MessageTile extends StatelessWidget {
|
|||||||
width: 150,
|
width: 150,
|
||||||
height: 10,
|
height: 10,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.of(context).text.withOpacity(.45),
|
color:
|
||||||
|
AppColors.of(context).text.withValues(alpha: .45),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -109,7 +111,8 @@ class MessageTile extends StatelessWidget {
|
|||||||
width: 35,
|
width: 35,
|
||||||
height: 15,
|
height: 15,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.of(context).text.withOpacity(.45),
|
color:
|
||||||
|
AppColors.of(context).text.withValues(alpha: .45),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -120,7 +123,7 @@ class MessageTile extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 14.0,
|
fontSize: 14.0,
|
||||||
color: AppColors.of(context).text.withOpacity(.75),
|
color: AppColors.of(context).text.withValues(alpha: .75),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -58,7 +58,9 @@ extension DateFormatUtils on DateTime {
|
|||||||
}
|
}
|
||||||
if (now.year == year &&
|
if (now.year == year &&
|
||||||
now.month == month &&
|
now.month == month &&
|
||||||
now.add(const Duration(days: 1)).day == day) return "Tomorrow".i18n;
|
now.add(const Duration(days: 1)).day == day) {
|
||||||
|
return "Tomorrow".i18n;
|
||||||
|
}
|
||||||
|
|
||||||
String formatString;
|
String formatString;
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ description: "Egy nem hivatalos e-KRÉTA kliens, diákoktól diákoknak."
|
|||||||
homepage: https://refilc.hu
|
homepage: https://refilc.hu
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 5.0.6+277
|
version: 5.1.1+283
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.2 <=3.6.0"
|
sdk: ^3.6.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
@ -16,8 +16,8 @@ dependencies:
|
|||||||
# reFilc packages
|
# reFilc packages
|
||||||
refilc_mobile_ui:
|
refilc_mobile_ui:
|
||||||
path: "../refilc_mobile_ui/"
|
path: "../refilc_mobile_ui/"
|
||||||
refilc_desktop_ui:
|
# refilc_desktop_ui:
|
||||||
path: "../refilc_desktop_ui/"
|
# path: "../refilc_desktop_ui/"
|
||||||
refilc_kreta_api:
|
refilc_kreta_api:
|
||||||
path: "../refilc_kreta_api/"
|
path: "../refilc_kreta_api/"
|
||||||
refilc_plus:
|
refilc_plus:
|
||||||
@ -33,10 +33,6 @@ dependencies:
|
|||||||
uuid: ^4.2.1
|
uuid: ^4.2.1
|
||||||
html: ^0.15.0
|
html: ^0.15.0
|
||||||
open_filex: ^4.3.4
|
open_filex: ^4.3.4
|
||||||
# open_file:
|
|
||||||
# git:
|
|
||||||
# url: https://github.com/crazecoder/open_file
|
|
||||||
# ref: master
|
|
||||||
path_provider: ^2.0.2
|
path_provider: ^2.0.2
|
||||||
permission_handler: ^11.0.1
|
permission_handler: ^11.0.1
|
||||||
share_plus: ^10.0.3
|
share_plus: ^10.0.3
|
||||||
@ -55,17 +51,9 @@ dependencies:
|
|||||||
rive: ^0.12.4
|
rive: ^0.12.4
|
||||||
animated_background: ^2.0.0
|
animated_background: ^2.0.0
|
||||||
dropdown_button2: ^2.3.9
|
dropdown_button2: ^2.3.9
|
||||||
home_widget:
|
home_widget: ^0.7.0+1
|
||||||
git:
|
|
||||||
url: https://github.com/refilc/home_widget.git
|
|
||||||
ref: flutter-beta
|
|
||||||
flutter_expandable_fab: ^2.0.0
|
flutter_expandable_fab: ^2.0.0
|
||||||
uni_links: ^0.5.1
|
|
||||||
url_launcher: ^6.1.6
|
url_launcher: ^6.1.6
|
||||||
# workmanager:
|
|
||||||
# git:
|
|
||||||
# url: https://github.com/refilc/flutter_workmanager.git
|
|
||||||
# ref: v0.5.1
|
|
||||||
flutter_svg: ^2.0.10+1
|
flutter_svg: ^2.0.10+1
|
||||||
image_picker: ^1.0.7
|
image_picker: ^1.0.7
|
||||||
animations: ^2.0.1
|
animations: ^2.0.1
|
||||||
@ -74,16 +62,12 @@ dependencies:
|
|||||||
package_info_plus: ^8.0.0
|
package_info_plus: ^8.0.0
|
||||||
screenshot: ^3.0.0
|
screenshot: ^3.0.0
|
||||||
flutter_staggered_grid_view: ^0.7.0
|
flutter_staggered_grid_view: ^0.7.0
|
||||||
sqflite_common_ffi_web: ^0.4.0
|
image_crop_plus: ^1.0.0
|
||||||
image_crop:
|
|
||||||
git:
|
|
||||||
url: https://github.com/kimaah/image_crop.git
|
|
||||||
googleapis: ^13.2.0
|
googleapis: ^13.2.0
|
||||||
google_sign_in: ^6.2.1
|
google_sign_in: ^6.2.1
|
||||||
extension_google_sign_in_as_googleapis_auth: ^2.0.12
|
extension_google_sign_in_as_googleapis_auth: ^2.0.12
|
||||||
maps_launcher: ^2.2.0
|
maps_launcher: ^3.0.0+1
|
||||||
google_fonts: ^6.1.0
|
google_fonts: ^6.1.0
|
||||||
# flutter_stripe: ^11.3.0
|
|
||||||
get_it: ^7.6.7
|
get_it: ^7.6.7
|
||||||
xml: ^6.5.0
|
xml: ^6.5.0
|
||||||
carousel_slider: ^5.0.0
|
carousel_slider: ^5.0.0
|
||||||
@ -96,6 +80,9 @@ dev_dependencies:
|
|||||||
flutter_native_splash: "^2.3.10"
|
flutter_native_splash: "^2.3.10"
|
||||||
sqflite_common_ffi: ^2.0.0+3
|
sqflite_common_ffi: ^2.0.0+3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
webview_flutter_wkwebview: 3.17.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
|
@ -1,674 +0,0 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The GNU General Public License is a free, copyleft license for
|
|
||||||
software and other kinds of works.
|
|
||||||
|
|
||||||
The licenses for most software and other practical works are designed
|
|
||||||
to take away your freedom to share and change the works. By contrast,
|
|
||||||
the GNU General Public License is intended to guarantee your freedom to
|
|
||||||
share and change all versions of a program--to make sure it remains free
|
|
||||||
software for all its users. We, the Free Software Foundation, use the
|
|
||||||
GNU General Public License for most of our software; it applies also to
|
|
||||||
any other work released this way by its authors. You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
them if you wish), that you receive source code or can get it if you
|
|
||||||
want it, that you can change the software or use pieces of it in new
|
|
||||||
free programs, and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to prevent others from denying you
|
|
||||||
these rights or asking you to surrender the rights. Therefore, you have
|
|
||||||
certain responsibilities if you distribute copies of the software, or if
|
|
||||||
you modify it: responsibilities to respect the freedom of others.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must pass on to the recipients the same
|
|
||||||
freedoms that you received. You must make sure that they, too, receive
|
|
||||||
or can get the source code. And you must show them these terms so they
|
|
||||||
know their rights.
|
|
||||||
|
|
||||||
Developers that use the GNU GPL protect your rights with two steps:
|
|
||||||
(1) assert copyright on the software, and (2) offer you this License
|
|
||||||
giving you legal permission to copy, distribute and/or modify it.
|
|
||||||
|
|
||||||
For the developers' and authors' protection, the GPL clearly explains
|
|
||||||
that there is no warranty for this free software. For both users' and
|
|
||||||
authors' sake, the GPL requires that modified versions be marked as
|
|
||||||
changed, so that their problems will not be attributed erroneously to
|
|
||||||
authors of previous versions.
|
|
||||||
|
|
||||||
Some devices are designed to deny users access to install or run
|
|
||||||
modified versions of the software inside them, although the manufacturer
|
|
||||||
can do so. This is fundamentally incompatible with the aim of
|
|
||||||
protecting users' freedom to change the software. The systematic
|
|
||||||
pattern of such abuse occurs in the area of products for individuals to
|
|
||||||
use, which is precisely where it is most unacceptable. Therefore, we
|
|
||||||
have designed this version of the GPL to prohibit the practice for those
|
|
||||||
products. If such problems arise substantially in other domains, we
|
|
||||||
stand ready to extend this provision to those domains in future versions
|
|
||||||
of the GPL, as needed to protect the freedom of users.
|
|
||||||
|
|
||||||
Finally, every program is threatened constantly by software patents.
|
|
||||||
States should not allow patents to restrict development and use of
|
|
||||||
software on general-purpose computers, but in those that do, we wish to
|
|
||||||
avoid the special danger that patents applied to a free program could
|
|
||||||
make it effectively proprietary. To prevent this, the GPL assures that
|
|
||||||
patents cannot be used to render the program non-free.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
0. Definitions.
|
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU General Public License.
|
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
|
||||||
works, such as semiconductor masks.
|
|
||||||
|
|
||||||
"The Program" refers to any copyrightable work licensed under this
|
|
||||||
License. Each licensee is addressed as "you". "Licensees" and
|
|
||||||
"recipients" may be individuals or organizations.
|
|
||||||
|
|
||||||
To "modify" a work means to copy from or adapt all or part of the work
|
|
||||||
in a fashion requiring copyright permission, other than the making of an
|
|
||||||
exact copy. The resulting work is called a "modified version" of the
|
|
||||||
earlier work or a work "based on" the earlier work.
|
|
||||||
|
|
||||||
A "covered work" means either the unmodified Program or a work based
|
|
||||||
on the Program.
|
|
||||||
|
|
||||||
To "propagate" a work means to do anything with it that, without
|
|
||||||
permission, would make you directly or secondarily liable for
|
|
||||||
infringement under applicable copyright law, except executing it on a
|
|
||||||
computer or modifying a private copy. Propagation includes copying,
|
|
||||||
distribution (with or without modification), making available to the
|
|
||||||
public, and in some countries other activities as well.
|
|
||||||
|
|
||||||
To "convey" a work means any kind of propagation that enables other
|
|
||||||
parties to make or receive copies. Mere interaction with a user through
|
|
||||||
a computer network, with no transfer of a copy, is not conveying.
|
|
||||||
|
|
||||||
An interactive user interface displays "Appropriate Legal Notices"
|
|
||||||
to the extent that it includes a convenient and prominently visible
|
|
||||||
feature that (1) displays an appropriate copyright notice, and (2)
|
|
||||||
tells the user that there is no warranty for the work (except to the
|
|
||||||
extent that warranties are provided), that licensees may convey the
|
|
||||||
work under this License, and how to view a copy of this License. If
|
|
||||||
the interface presents a list of user commands or options, such as a
|
|
||||||
menu, a prominent item in the list meets this criterion.
|
|
||||||
|
|
||||||
1. Source Code.
|
|
||||||
|
|
||||||
The "source code" for a work means the preferred form of the work
|
|
||||||
for making modifications to it. "Object code" means any non-source
|
|
||||||
form of a work.
|
|
||||||
|
|
||||||
A "Standard Interface" means an interface that either is an official
|
|
||||||
standard defined by a recognized standards body, or, in the case of
|
|
||||||
interfaces specified for a particular programming language, one that
|
|
||||||
is widely used among developers working in that language.
|
|
||||||
|
|
||||||
The "System Libraries" of an executable work include anything, other
|
|
||||||
than the work as a whole, that (a) is included in the normal form of
|
|
||||||
packaging a Major Component, but which is not part of that Major
|
|
||||||
Component, and (b) serves only to enable use of the work with that
|
|
||||||
Major Component, or to implement a Standard Interface for which an
|
|
||||||
implementation is available to the public in source code form. A
|
|
||||||
"Major Component", in this context, means a major essential component
|
|
||||||
(kernel, window system, and so on) of the specific operating system
|
|
||||||
(if any) on which the executable work runs, or a compiler used to
|
|
||||||
produce the work, or an object code interpreter used to run it.
|
|
||||||
|
|
||||||
The "Corresponding Source" for a work in object code form means all
|
|
||||||
the source code needed to generate, install, and (for an executable
|
|
||||||
work) run the object code and to modify the work, including scripts to
|
|
||||||
control those activities. However, it does not include the work's
|
|
||||||
System Libraries, or general-purpose tools or generally available free
|
|
||||||
programs which are used unmodified in performing those activities but
|
|
||||||
which are not part of the work. For example, Corresponding Source
|
|
||||||
includes interface definition files associated with source files for
|
|
||||||
the work, and the source code for shared libraries and dynamically
|
|
||||||
linked subprograms that the work is specifically designed to require,
|
|
||||||
such as by intimate data communication or control flow between those
|
|
||||||
subprograms and other parts of the work.
|
|
||||||
|
|
||||||
The Corresponding Source need not include anything that users
|
|
||||||
can regenerate automatically from other parts of the Corresponding
|
|
||||||
Source.
|
|
||||||
|
|
||||||
The Corresponding Source for a work in source code form is that
|
|
||||||
same work.
|
|
||||||
|
|
||||||
2. Basic Permissions.
|
|
||||||
|
|
||||||
All rights granted under this License are granted for the term of
|
|
||||||
copyright on the Program, and are irrevocable provided the stated
|
|
||||||
conditions are met. This License explicitly affirms your unlimited
|
|
||||||
permission to run the unmodified Program. The output from running a
|
|
||||||
covered work is covered by this License only if the output, given its
|
|
||||||
content, constitutes a covered work. This License acknowledges your
|
|
||||||
rights of fair use or other equivalent, as provided by copyright law.
|
|
||||||
|
|
||||||
You may make, run and propagate covered works that you do not
|
|
||||||
convey, without conditions so long as your license otherwise remains
|
|
||||||
in force. You may convey covered works to others for the sole purpose
|
|
||||||
of having them make modifications exclusively for you, or provide you
|
|
||||||
with facilities for running those works, provided that you comply with
|
|
||||||
the terms of this License in conveying all material for which you do
|
|
||||||
not control copyright. Those thus making or running the covered works
|
|
||||||
for you must do so exclusively on your behalf, under your direction
|
|
||||||
and control, on terms that prohibit them from making any copies of
|
|
||||||
your copyrighted material outside their relationship with you.
|
|
||||||
|
|
||||||
Conveying under any other circumstances is permitted solely under
|
|
||||||
the conditions stated below. Sublicensing is not allowed; section 10
|
|
||||||
makes it unnecessary.
|
|
||||||
|
|
||||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
|
||||||
|
|
||||||
No covered work shall be deemed part of an effective technological
|
|
||||||
measure under any applicable law fulfilling obligations under article
|
|
||||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
|
||||||
similar laws prohibiting or restricting circumvention of such
|
|
||||||
measures.
|
|
||||||
|
|
||||||
When you convey a covered work, you waive any legal power to forbid
|
|
||||||
circumvention of technological measures to the extent such circumvention
|
|
||||||
is effected by exercising rights under this License with respect to
|
|
||||||
the covered work, and you disclaim any intention to limit operation or
|
|
||||||
modification of the work as a means of enforcing, against the work's
|
|
||||||
users, your or third parties' legal rights to forbid circumvention of
|
|
||||||
technological measures.
|
|
||||||
|
|
||||||
4. Conveying Verbatim Copies.
|
|
||||||
|
|
||||||
You may convey verbatim copies of the Program's source code as you
|
|
||||||
receive it, in any medium, provided that you conspicuously and
|
|
||||||
appropriately publish on each copy an appropriate copyright notice;
|
|
||||||
keep intact all notices stating that this License and any
|
|
||||||
non-permissive terms added in accord with section 7 apply to the code;
|
|
||||||
keep intact all notices of the absence of any warranty; and give all
|
|
||||||
recipients a copy of this License along with the Program.
|
|
||||||
|
|
||||||
You may charge any price or no price for each copy that you convey,
|
|
||||||
and you may offer support or warranty protection for a fee.
|
|
||||||
|
|
||||||
5. Conveying Modified Source Versions.
|
|
||||||
|
|
||||||
You may convey a work based on the Program, or the modifications to
|
|
||||||
produce it from the Program, in the form of source code under the
|
|
||||||
terms of section 4, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The work must carry prominent notices stating that you modified
|
|
||||||
it, and giving a relevant date.
|
|
||||||
|
|
||||||
b) The work must carry prominent notices stating that it is
|
|
||||||
released under this License and any conditions added under section
|
|
||||||
7. This requirement modifies the requirement in section 4 to
|
|
||||||
"keep intact all notices".
|
|
||||||
|
|
||||||
c) You must license the entire work, as a whole, under this
|
|
||||||
License to anyone who comes into possession of a copy. This
|
|
||||||
License will therefore apply, along with any applicable section 7
|
|
||||||
additional terms, to the whole of the work, and all its parts,
|
|
||||||
regardless of how they are packaged. This License gives no
|
|
||||||
permission to license the work in any other way, but it does not
|
|
||||||
invalidate such permission if you have separately received it.
|
|
||||||
|
|
||||||
d) If the work has interactive user interfaces, each must display
|
|
||||||
Appropriate Legal Notices; however, if the Program has interactive
|
|
||||||
interfaces that do not display Appropriate Legal Notices, your
|
|
||||||
work need not make them do so.
|
|
||||||
|
|
||||||
A compilation of a covered work with other separate and independent
|
|
||||||
works, which are not by their nature extensions of the covered work,
|
|
||||||
and which are not combined with it such as to form a larger program,
|
|
||||||
in or on a volume of a storage or distribution medium, is called an
|
|
||||||
"aggregate" if the compilation and its resulting copyright are not
|
|
||||||
used to limit the access or legal rights of the compilation's users
|
|
||||||
beyond what the individual works permit. Inclusion of a covered work
|
|
||||||
in an aggregate does not cause this License to apply to the other
|
|
||||||
parts of the aggregate.
|
|
||||||
|
|
||||||
6. Conveying Non-Source Forms.
|
|
||||||
|
|
||||||
You may convey a covered work in object code form under the terms
|
|
||||||
of sections 4 and 5, provided that you also convey the
|
|
||||||
machine-readable Corresponding Source under the terms of this License,
|
|
||||||
in one of these ways:
|
|
||||||
|
|
||||||
a) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by the
|
|
||||||
Corresponding Source fixed on a durable physical medium
|
|
||||||
customarily used for software interchange.
|
|
||||||
|
|
||||||
b) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by a
|
|
||||||
written offer, valid for at least three years and valid for as
|
|
||||||
long as you offer spare parts or customer support for that product
|
|
||||||
model, to give anyone who possesses the object code either (1) a
|
|
||||||
copy of the Corresponding Source for all the software in the
|
|
||||||
product that is covered by this License, on a durable physical
|
|
||||||
medium customarily used for software interchange, for a price no
|
|
||||||
more than your reasonable cost of physically performing this
|
|
||||||
conveying of source, or (2) access to copy the
|
|
||||||
Corresponding Source from a network server at no charge.
|
|
||||||
|
|
||||||
c) Convey individual copies of the object code with a copy of the
|
|
||||||
written offer to provide the Corresponding Source. This
|
|
||||||
alternative is allowed only occasionally and noncommercially, and
|
|
||||||
only if you received the object code with such an offer, in accord
|
|
||||||
with subsection 6b.
|
|
||||||
|
|
||||||
d) Convey the object code by offering access from a designated
|
|
||||||
place (gratis or for a charge), and offer equivalent access to the
|
|
||||||
Corresponding Source in the same way through the same place at no
|
|
||||||
further charge. You need not require recipients to copy the
|
|
||||||
Corresponding Source along with the object code. If the place to
|
|
||||||
copy the object code is a network server, the Corresponding Source
|
|
||||||
may be on a different server (operated by you or a third party)
|
|
||||||
that supports equivalent copying facilities, provided you maintain
|
|
||||||
clear directions next to the object code saying where to find the
|
|
||||||
Corresponding Source. Regardless of what server hosts the
|
|
||||||
Corresponding Source, you remain obligated to ensure that it is
|
|
||||||
available for as long as needed to satisfy these requirements.
|
|
||||||
|
|
||||||
e) Convey the object code using peer-to-peer transmission, provided
|
|
||||||
you inform other peers where the object code and Corresponding
|
|
||||||
Source of the work are being offered to the general public at no
|
|
||||||
charge under subsection 6d.
|
|
||||||
|
|
||||||
A separable portion of the object code, whose source code is excluded
|
|
||||||
from the Corresponding Source as a System Library, need not be
|
|
||||||
included in conveying the object code work.
|
|
||||||
|
|
||||||
A "User Product" is either (1) a "consumer product", which means any
|
|
||||||
tangible personal property which is normally used for personal, family,
|
|
||||||
or household purposes, or (2) anything designed or sold for incorporation
|
|
||||||
into a dwelling. In determining whether a product is a consumer product,
|
|
||||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
|
||||||
product received by a particular user, "normally used" refers to a
|
|
||||||
typical or common use of that class of product, regardless of the status
|
|
||||||
of the particular user or of the way in which the particular user
|
|
||||||
actually uses, or expects or is expected to use, the product. A product
|
|
||||||
is a consumer product regardless of whether the product has substantial
|
|
||||||
commercial, industrial or non-consumer uses, unless such uses represent
|
|
||||||
the only significant mode of use of the product.
|
|
||||||
|
|
||||||
"Installation Information" for a User Product means any methods,
|
|
||||||
procedures, authorization keys, or other information required to install
|
|
||||||
and execute modified versions of a covered work in that User Product from
|
|
||||||
a modified version of its Corresponding Source. The information must
|
|
||||||
suffice to ensure that the continued functioning of the modified object
|
|
||||||
code is in no case prevented or interfered with solely because
|
|
||||||
modification has been made.
|
|
||||||
|
|
||||||
If you convey an object code work under this section in, or with, or
|
|
||||||
specifically for use in, a User Product, and the conveying occurs as
|
|
||||||
part of a transaction in which the right of possession and use of the
|
|
||||||
User Product is transferred to the recipient in perpetuity or for a
|
|
||||||
fixed term (regardless of how the transaction is characterized), the
|
|
||||||
Corresponding Source conveyed under this section must be accompanied
|
|
||||||
by the Installation Information. But this requirement does not apply
|
|
||||||
if neither you nor any third party retains the ability to install
|
|
||||||
modified object code on the User Product (for example, the work has
|
|
||||||
been installed in ROM).
|
|
||||||
|
|
||||||
The requirement to provide Installation Information does not include a
|
|
||||||
requirement to continue to provide support service, warranty, or updates
|
|
||||||
for a work that has been modified or installed by the recipient, or for
|
|
||||||
the User Product in which it has been modified or installed. Access to a
|
|
||||||
network may be denied when the modification itself materially and
|
|
||||||
adversely affects the operation of the network or violates the rules and
|
|
||||||
protocols for communication across the network.
|
|
||||||
|
|
||||||
Corresponding Source conveyed, and Installation Information provided,
|
|
||||||
in accord with this section must be in a format that is publicly
|
|
||||||
documented (and with an implementation available to the public in
|
|
||||||
source code form), and must require no special password or key for
|
|
||||||
unpacking, reading or copying.
|
|
||||||
|
|
||||||
7. Additional Terms.
|
|
||||||
|
|
||||||
"Additional permissions" are terms that supplement the terms of this
|
|
||||||
License by making exceptions from one or more of its conditions.
|
|
||||||
Additional permissions that are applicable to the entire Program shall
|
|
||||||
be treated as though they were included in this License, to the extent
|
|
||||||
that they are valid under applicable law. If additional permissions
|
|
||||||
apply only to part of the Program, that part may be used separately
|
|
||||||
under those permissions, but the entire Program remains governed by
|
|
||||||
this License without regard to the additional permissions.
|
|
||||||
|
|
||||||
When you convey a copy of a covered work, you may at your option
|
|
||||||
remove any additional permissions from that copy, or from any part of
|
|
||||||
it. (Additional permissions may be written to require their own
|
|
||||||
removal in certain cases when you modify the work.) You may place
|
|
||||||
additional permissions on material, added by you to a covered work,
|
|
||||||
for which you have or can give appropriate copyright permission.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, for material you
|
|
||||||
add to a covered work, you may (if authorized by the copyright holders of
|
|
||||||
that material) supplement the terms of this License with terms:
|
|
||||||
|
|
||||||
a) Disclaiming warranty or limiting liability differently from the
|
|
||||||
terms of sections 15 and 16 of this License; or
|
|
||||||
|
|
||||||
b) Requiring preservation of specified reasonable legal notices or
|
|
||||||
author attributions in that material or in the Appropriate Legal
|
|
||||||
Notices displayed by works containing it; or
|
|
||||||
|
|
||||||
c) Prohibiting misrepresentation of the origin of that material, or
|
|
||||||
requiring that modified versions of such material be marked in
|
|
||||||
reasonable ways as different from the original version; or
|
|
||||||
|
|
||||||
d) Limiting the use for publicity purposes of names of licensors or
|
|
||||||
authors of the material; or
|
|
||||||
|
|
||||||
e) Declining to grant rights under trademark law for use of some
|
|
||||||
trade names, trademarks, or service marks; or
|
|
||||||
|
|
||||||
f) Requiring indemnification of licensors and authors of that
|
|
||||||
material by anyone who conveys the material (or modified versions of
|
|
||||||
it) with contractual assumptions of liability to the recipient, for
|
|
||||||
any liability that these contractual assumptions directly impose on
|
|
||||||
those licensors and authors.
|
|
||||||
|
|
||||||
All other non-permissive additional terms are considered "further
|
|
||||||
restrictions" within the meaning of section 10. If the Program as you
|
|
||||||
received it, or any part of it, contains a notice stating that it is
|
|
||||||
governed by this License along with a term that is a further
|
|
||||||
restriction, you may remove that term. If a license document contains
|
|
||||||
a further restriction but permits relicensing or conveying under this
|
|
||||||
License, you may add to a covered work material governed by the terms
|
|
||||||
of that license document, provided that the further restriction does
|
|
||||||
not survive such relicensing or conveying.
|
|
||||||
|
|
||||||
If you add terms to a covered work in accord with this section, you
|
|
||||||
must place, in the relevant source files, a statement of the
|
|
||||||
additional terms that apply to those files, or a notice indicating
|
|
||||||
where to find the applicable terms.
|
|
||||||
|
|
||||||
Additional terms, permissive or non-permissive, may be stated in the
|
|
||||||
form of a separately written license, or stated as exceptions;
|
|
||||||
the above requirements apply either way.
|
|
||||||
|
|
||||||
8. Termination.
|
|
||||||
|
|
||||||
You may not propagate or modify a covered work except as expressly
|
|
||||||
provided under this License. Any attempt otherwise to propagate or
|
|
||||||
modify it is void, and will automatically terminate your rights under
|
|
||||||
this License (including any patent licenses granted under the third
|
|
||||||
paragraph of section 11).
|
|
||||||
|
|
||||||
However, if you cease all violation of this License, then your
|
|
||||||
license from a particular copyright holder is reinstated (a)
|
|
||||||
provisionally, unless and until the copyright holder explicitly and
|
|
||||||
finally terminates your license, and (b) permanently, if the copyright
|
|
||||||
holder fails to notify you of the violation by some reasonable means
|
|
||||||
prior to 60 days after the cessation.
|
|
||||||
|
|
||||||
Moreover, your license from a particular copyright holder is
|
|
||||||
reinstated permanently if the copyright holder notifies you of the
|
|
||||||
violation by some reasonable means, this is the first time you have
|
|
||||||
received notice of violation of this License (for any work) from that
|
|
||||||
copyright holder, and you cure the violation prior to 30 days after
|
|
||||||
your receipt of the notice.
|
|
||||||
|
|
||||||
Termination of your rights under this section does not terminate the
|
|
||||||
licenses of parties who have received copies or rights from you under
|
|
||||||
this License. If your rights have been terminated and not permanently
|
|
||||||
reinstated, you do not qualify to receive new licenses for the same
|
|
||||||
material under section 10.
|
|
||||||
|
|
||||||
9. Acceptance Not Required for Having Copies.
|
|
||||||
|
|
||||||
You are not required to accept this License in order to receive or
|
|
||||||
run a copy of the Program. Ancillary propagation of a covered work
|
|
||||||
occurring solely as a consequence of using peer-to-peer transmission
|
|
||||||
to receive a copy likewise does not require acceptance. However,
|
|
||||||
nothing other than this License grants you permission to propagate or
|
|
||||||
modify any covered work. These actions infringe copyright if you do
|
|
||||||
not accept this License. Therefore, by modifying or propagating a
|
|
||||||
covered work, you indicate your acceptance of this License to do so.
|
|
||||||
|
|
||||||
10. Automatic Licensing of Downstream Recipients.
|
|
||||||
|
|
||||||
Each time you convey a covered work, the recipient automatically
|
|
||||||
receives a license from the original licensors, to run, modify and
|
|
||||||
propagate that work, subject to this License. You are not responsible
|
|
||||||
for enforcing compliance by third parties with this License.
|
|
||||||
|
|
||||||
An "entity transaction" is a transaction transferring control of an
|
|
||||||
organization, or substantially all assets of one, or subdividing an
|
|
||||||
organization, or merging organizations. If propagation of a covered
|
|
||||||
work results from an entity transaction, each party to that
|
|
||||||
transaction who receives a copy of the work also receives whatever
|
|
||||||
licenses to the work the party's predecessor in interest had or could
|
|
||||||
give under the previous paragraph, plus a right to possession of the
|
|
||||||
Corresponding Source of the work from the predecessor in interest, if
|
|
||||||
the predecessor has it or can get it with reasonable efforts.
|
|
||||||
|
|
||||||
You may not impose any further restrictions on the exercise of the
|
|
||||||
rights granted or affirmed under this License. For example, you may
|
|
||||||
not impose a license fee, royalty, or other charge for exercise of
|
|
||||||
rights granted under this License, and you may not initiate litigation
|
|
||||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
|
||||||
any patent claim is infringed by making, using, selling, offering for
|
|
||||||
sale, or importing the Program or any portion of it.
|
|
||||||
|
|
||||||
11. Patents.
|
|
||||||
|
|
||||||
A "contributor" is a copyright holder who authorizes use under this
|
|
||||||
License of the Program or a work on which the Program is based. The
|
|
||||||
work thus licensed is called the contributor's "contributor version".
|
|
||||||
|
|
||||||
A contributor's "essential patent claims" are all patent claims
|
|
||||||
owned or controlled by the contributor, whether already acquired or
|
|
||||||
hereafter acquired, that would be infringed by some manner, permitted
|
|
||||||
by this License, of making, using, or selling its contributor version,
|
|
||||||
but do not include claims that would be infringed only as a
|
|
||||||
consequence of further modification of the contributor version. For
|
|
||||||
purposes of this definition, "control" includes the right to grant
|
|
||||||
patent sublicenses in a manner consistent with the requirements of
|
|
||||||
this License.
|
|
||||||
|
|
||||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
|
||||||
patent license under the contributor's essential patent claims, to
|
|
||||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
|
||||||
propagate the contents of its contributor version.
|
|
||||||
|
|
||||||
In the following three paragraphs, a "patent license" is any express
|
|
||||||
agreement or commitment, however denominated, not to enforce a patent
|
|
||||||
(such as an express permission to practice a patent or covenant not to
|
|
||||||
sue for patent infringement). To "grant" such a patent license to a
|
|
||||||
party means to make such an agreement or commitment not to enforce a
|
|
||||||
patent against the party.
|
|
||||||
|
|
||||||
If you convey a covered work, knowingly relying on a patent license,
|
|
||||||
and the Corresponding Source of the work is not available for anyone
|
|
||||||
to copy, free of charge and under the terms of this License, through a
|
|
||||||
publicly available network server or other readily accessible means,
|
|
||||||
then you must either (1) cause the Corresponding Source to be so
|
|
||||||
available, or (2) arrange to deprive yourself of the benefit of the
|
|
||||||
patent license for this particular work, or (3) arrange, in a manner
|
|
||||||
consistent with the requirements of this License, to extend the patent
|
|
||||||
license to downstream recipients. "Knowingly relying" means you have
|
|
||||||
actual knowledge that, but for the patent license, your conveying the
|
|
||||||
covered work in a country, or your recipient's use of the covered work
|
|
||||||
in a country, would infringe one or more identifiable patents in that
|
|
||||||
country that you have reason to believe are valid.
|
|
||||||
|
|
||||||
If, pursuant to or in connection with a single transaction or
|
|
||||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
|
||||||
covered work, and grant a patent license to some of the parties
|
|
||||||
receiving the covered work authorizing them to use, propagate, modify
|
|
||||||
or convey a specific copy of the covered work, then the patent license
|
|
||||||
you grant is automatically extended to all recipients of the covered
|
|
||||||
work and works based on it.
|
|
||||||
|
|
||||||
A patent license is "discriminatory" if it does not include within
|
|
||||||
the scope of its coverage, prohibits the exercise of, or is
|
|
||||||
conditioned on the non-exercise of one or more of the rights that are
|
|
||||||
specifically granted under this License. You may not convey a covered
|
|
||||||
work if you are a party to an arrangement with a third party that is
|
|
||||||
in the business of distributing software, under which you make payment
|
|
||||||
to the third party based on the extent of your activity of conveying
|
|
||||||
the work, and under which the third party grants, to any of the
|
|
||||||
parties who would receive the covered work from you, a discriminatory
|
|
||||||
patent license (a) in connection with copies of the covered work
|
|
||||||
conveyed by you (or copies made from those copies), or (b) primarily
|
|
||||||
for and in connection with specific products or compilations that
|
|
||||||
contain the covered work, unless you entered into that arrangement,
|
|
||||||
or that patent license was granted, prior to 28 March 2007.
|
|
||||||
|
|
||||||
Nothing in this License shall be construed as excluding or limiting
|
|
||||||
any implied license or other defenses to infringement that may
|
|
||||||
otherwise be available to you under applicable patent law.
|
|
||||||
|
|
||||||
12. No Surrender of Others' Freedom.
|
|
||||||
|
|
||||||
If conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot convey a
|
|
||||||
covered work so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you may
|
|
||||||
not convey it at all. For example, if you agree to terms that obligate you
|
|
||||||
to collect a royalty for further conveying from those to whom you convey
|
|
||||||
the Program, the only way you could satisfy both those terms and this
|
|
||||||
License would be to refrain entirely from conveying the Program.
|
|
||||||
|
|
||||||
13. Use with the GNU Affero General Public License.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
|
||||||
permission to link or combine any covered work with a work licensed
|
|
||||||
under version 3 of the GNU Affero General Public License into a single
|
|
||||||
combined work, and to convey the resulting work. The terms of this
|
|
||||||
License will continue to apply to the part which is the covered work,
|
|
||||||
but the special requirements of the GNU Affero General Public License,
|
|
||||||
section 13, concerning interaction through a network will apply to the
|
|
||||||
combination as such.
|
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
|
||||||
the GNU General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Program specifies that a certain numbered version of the GNU General
|
|
||||||
Public License "or any later version" applies to it, you have the
|
|
||||||
option of following the terms and conditions either of that numbered
|
|
||||||
version or of any later version published by the Free Software
|
|
||||||
Foundation. If the Program does not specify a version number of the
|
|
||||||
GNU General Public License, you may choose any version ever published
|
|
||||||
by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Program specifies that a proxy can decide which future
|
|
||||||
versions of the GNU General Public License can be used, that proxy's
|
|
||||||
public statement of acceptance of a version permanently authorizes you
|
|
||||||
to choose that version for the Program.
|
|
||||||
|
|
||||||
Later license versions may give you additional or different
|
|
||||||
permissions. However, no additional obligations are imposed on any
|
|
||||||
author or copyright holder as a result of your choosing to follow a
|
|
||||||
later version.
|
|
||||||
|
|
||||||
15. Disclaimer of Warranty.
|
|
||||||
|
|
||||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
|
||||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
|
||||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
|
||||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
|
||||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
|
||||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. Limitation of Liability.
|
|
||||||
|
|
||||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
|
||||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
|
||||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
|
||||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
|
||||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
|
||||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
|
||||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGES.
|
|
||||||
|
|
||||||
17. Interpretation of Sections 15 and 16.
|
|
||||||
|
|
||||||
If the disclaimer of warranty and limitation of liability provided
|
|
||||||
above cannot be given local legal effect according to their terms,
|
|
||||||
reviewing courts shall apply local law that most closely approximates
|
|
||||||
an absolute waiver of all civil liability in connection with the
|
|
||||||
Program, unless a warranty or assumption of liability accompanies a
|
|
||||||
copy of the Program in return for a fee.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
state the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program does terminal interaction, make it output a short
|
|
||||||
notice like this when it starts in an interactive mode:
|
|
||||||
|
|
||||||
<program> Copyright (C) <year> <name of author>
|
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, your program's commands
|
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
|
||||||
<https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License. But first, please read
|
|
||||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
@ -1,28 +0,0 @@
|
|||||||
# This file configures the analyzer, which statically analyzes Dart code to
|
|
||||||
# check for errors, warnings, and lints.
|
|
||||||
#
|
|
||||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
|
||||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
|
||||||
# invoked from the command line by running `flutter analyze`.
|
|
||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
linter:
|
|
||||||
# The lint rules applied to this project can be customized in the
|
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
|
||||||
# included above or to enable additional rules. A list of all available lints
|
|
||||||
# and their documentation is published at
|
|
||||||
# https://dart-lang.github.io/linter/lints/index.html.
|
|
||||||
#
|
|
||||||
# Instead of disabling a lint rule for the entire project in the
|
|
||||||
# section below, it can also be suppressed for a single line of code
|
|
||||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
|
||||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
|
||||||
# producing the lint.
|
|
||||||
rules:
|
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,91 +0,0 @@
|
|||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class FilterBar extends StatelessWidget implements PreferredSizeWidget {
|
|
||||||
const FilterBar({
|
|
||||||
super.key,
|
|
||||||
required this.items,
|
|
||||||
required this.controller,
|
|
||||||
this.onTap,
|
|
||||||
this.padding = const EdgeInsets.symmetric(horizontal: 24.0),
|
|
||||||
this.disableFading = false,
|
|
||||||
this.scrollable = true,
|
|
||||||
}) : assert(items.length == controller.length);
|
|
||||||
|
|
||||||
final List<Widget> items;
|
|
||||||
final TabController controller;
|
|
||||||
final EdgeInsetsGeometry padding;
|
|
||||||
final Function(int)? onTap;
|
|
||||||
@override
|
|
||||||
final Size preferredSize = const Size.fromHeight(42.0);
|
|
||||||
final bool disableFading;
|
|
||||||
final bool scrollable;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final tabbar = TabBar(
|
|
||||||
controller: controller,
|
|
||||||
isScrollable: scrollable,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
// Label
|
|
||||||
labelStyle: Theme.of(context).textTheme.titleMedium!.copyWith(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 15.0,
|
|
||||||
),
|
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
|
|
||||||
labelColor: Theme.of(context).colorScheme.secondary,
|
|
||||||
unselectedLabelColor: AppColors.of(context).text.withOpacity(0.65),
|
|
||||||
// Indicator
|
|
||||||
indicatorPadding: const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
indicator: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.secondary.withOpacity(0.25),
|
|
||||||
borderRadius: BorderRadius.circular(45.0),
|
|
||||||
),
|
|
||||||
overlayColor: WidgetStateProperty.all(const Color(0x00000000)),
|
|
||||||
// Tabs
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
tabs: items,
|
|
||||||
onTap: onTap,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: MediaQuery.of(context).size.width,
|
|
||||||
height: 48.0,
|
|
||||||
padding: padding,
|
|
||||||
child: disableFading
|
|
||||||
? tabbar
|
|
||||||
: AnimatedBuilder(
|
|
||||||
animation: controller.animation!,
|
|
||||||
builder: (ctx, child) {
|
|
||||||
// avoid fading over selected tab
|
|
||||||
return ShaderMask(
|
|
||||||
shaderCallback: (Rect bounds) {
|
|
||||||
final Color bg =
|
|
||||||
Theme.of(context).scaffoldBackgroundColor;
|
|
||||||
final double index = controller.animation!.value;
|
|
||||||
return LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [
|
|
||||||
index < 0.2 ? Colors.transparent : bg,
|
|
||||||
Colors.transparent,
|
|
||||||
Colors.transparent,
|
|
||||||
index > controller.length - 1.2
|
|
||||||
? Colors.transparent
|
|
||||||
: bg
|
|
||||||
],
|
|
||||||
stops: const [
|
|
||||||
0,
|
|
||||||
0.1,
|
|
||||||
0.9,
|
|
||||||
1
|
|
||||||
]).createShader(bounds);
|
|
||||||
},
|
|
||||||
blendMode: BlendMode.dstOut,
|
|
||||||
child: child);
|
|
||||||
},
|
|
||||||
child: tabbar,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class PanelButton extends StatelessWidget {
|
|
||||||
const PanelButton({
|
|
||||||
super.key,
|
|
||||||
this.onPressed,
|
|
||||||
this.padding = const EdgeInsets.symmetric(horizontal: 14.0),
|
|
||||||
this.leading,
|
|
||||||
this.title,
|
|
||||||
this.trailing,
|
|
||||||
this.trailingDivider = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
final void Function()? onPressed;
|
|
||||||
final EdgeInsetsGeometry padding;
|
|
||||||
final Widget? leading;
|
|
||||||
final Widget? title;
|
|
||||||
final Widget? trailing;
|
|
||||||
final bool trailingDivider;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
|
||||||
child: RawMaterialButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
padding: padding,
|
|
||||||
shape:
|
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
|
||||||
child: ListTile(
|
|
||||||
leading: leading != null
|
|
||||||
? Theme(
|
|
||||||
data: Theme.of(context).copyWith(
|
|
||||||
iconTheme: IconThemeData(
|
|
||||||
color: Theme.of(context).colorScheme.secondary)),
|
|
||||||
child: leading!,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
trailing: trailingDivider
|
|
||||||
? Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(right: 6.0),
|
|
||||||
width: 2.0,
|
|
||||||
height: 32.0,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColors.of(context).text.withOpacity(.15),
|
|
||||||
borderRadius: BorderRadius.circular(45.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (trailing != null) trailing!,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: trailing,
|
|
||||||
title: title != null
|
|
||||||
? DefaultTextStyle(
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.titleMedium!
|
|
||||||
.copyWith(fontWeight: FontWeight.w600, fontSize: 14.0),
|
|
||||||
child: title!,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
import 'package:refilc/models/user.dart';
|
|
||||||
import 'package:refilc/utils/color.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
|
|
||||||
class ProfileImage extends StatelessWidget {
|
|
||||||
const ProfileImage({
|
|
||||||
super.key,
|
|
||||||
this.name,
|
|
||||||
this.radius = 20.0,
|
|
||||||
this.role = Role.student,
|
|
||||||
this.backgroundColor,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String? name;
|
|
||||||
final double radius;
|
|
||||||
final Role? role;
|
|
||||||
final Color? backgroundColor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Color color = ColorUtils.foregroundColor(
|
|
||||||
backgroundColor ?? Theme.of(context).scaffoldBackgroundColor);
|
|
||||||
Color roleColor;
|
|
||||||
|
|
||||||
if (Theme.of(context).brightness == Brightness.light) {
|
|
||||||
roleColor = const Color(0xFF444444);
|
|
||||||
} else {
|
|
||||||
roleColor = const Color(0xFF555555);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
Material(
|
|
||||||
clipBehavior: Clip.hardEdge,
|
|
||||||
shape: const CircleBorder(),
|
|
||||||
color: backgroundColor ?? AppColors.of(context).text.withOpacity(.15),
|
|
||||||
child: InkWell(
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
height: radius * 2,
|
|
||||||
width: radius * 2,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: name != null && (name?.trim().length ?? 0) > 0
|
|
||||||
? Center(
|
|
||||||
child: Text(
|
|
||||||
(name?.trim().length ?? 0) > 0
|
|
||||||
? (name ?? "?").trim()[0]
|
|
||||||
: "?",
|
|
||||||
style: TextStyle(
|
|
||||||
color: color,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 18.0 * (radius / 20.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Role indicator
|
|
||||||
if (role == Role.parent)
|
|
||||||
SizedBox(
|
|
||||||
height: radius * 2,
|
|
||||||
width: radius * 2,
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.bottomRight,
|
|
||||||
child: Icon(Icons.shield, color: roleColor, size: radius / 1.3),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/grade.dart';
|
|
||||||
import 'package:refilc/ui/widgets/grade/grade_tile.dart';
|
|
||||||
|
|
||||||
class GradeViewable extends StatelessWidget {
|
|
||||||
const GradeViewable(this.grade, {super.key});
|
|
||||||
|
|
||||||
final Grade grade;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GradeTile(grade);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import 'package:refilc_kreta_api/models/lesson.dart';
|
|
||||||
import 'package:refilc/ui/widgets/lesson/lesson_tile.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class LessonViewable extends StatelessWidget {
|
|
||||||
const LessonViewable(this.lesson, {super.key, this.swapDesc = false});
|
|
||||||
|
|
||||||
final Lesson lesson;
|
|
||||||
final bool swapDesc;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final tile = LessonTile(lesson, swapDesc: swapDesc);
|
|
||||||
|
|
||||||
if (lesson.subject.id == '' || tile.lesson.isEmpty) return tile;
|
|
||||||
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,408 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
|
||||||
import 'package:refilc/api/providers/update_provider.dart';
|
|
||||||
import 'package:refilc/ui/date_widget.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/absence.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/lesson.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/subject.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/week.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/absence_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/note_provider.dart';
|
|
||||||
import 'package:refilc/api/providers/user_provider.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/action_button.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/empty.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/filter_bar.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/absence/absence_subject_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/absence/absence_viewable.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/statistics_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/miss_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/absences/absence_subject_view.dart';
|
|
||||||
import 'package:refilc/ui/filter/sort.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'absences_page.i18n.dart';
|
|
||||||
|
|
||||||
enum AbsenceFilter { absences, delays, misses }
|
|
||||||
|
|
||||||
class SubjectAbsence {
|
|
||||||
GradeSubject subject;
|
|
||||||
List<Absence> absences;
|
|
||||||
double percentage;
|
|
||||||
|
|
||||||
SubjectAbsence(
|
|
||||||
{required this.subject, this.absences = const [], this.percentage = 0.0});
|
|
||||||
}
|
|
||||||
|
|
||||||
class AbsencesPage extends StatefulWidget {
|
|
||||||
const AbsencesPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
AbsencesPageState createState() => AbsencesPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class AbsencesPageState extends State<AbsencesPage>
|
|
||||||
with TickerProviderStateMixin {
|
|
||||||
late UserProvider user;
|
|
||||||
late AbsenceProvider absenceProvider;
|
|
||||||
late TimetableProvider timetableProvider;
|
|
||||||
late NoteProvider noteProvider;
|
|
||||||
late UpdateProvider updateProvider;
|
|
||||||
late String firstName;
|
|
||||||
late TabController _tabController;
|
|
||||||
late List<SubjectAbsence> absences = [];
|
|
||||||
final Map<GradeSubject, Lesson> _lessonCount = {};
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
_tabController = TabController(length: 3, vsync: this);
|
|
||||||
timetableProvider = Provider.of<TimetableProvider>(context, listen: false);
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
|
||||||
for (final lesson in timetableProvider.getWeek(Week.current()) ?? []) {
|
|
||||||
if (!lesson.isEmpty &&
|
|
||||||
lesson.subject.id != '' &&
|
|
||||||
lesson.lessonYearIndex != null) {
|
|
||||||
_lessonCount.update(
|
|
||||||
lesson.subject,
|
|
||||||
(value) {
|
|
||||||
if (lesson.lessonYearIndex! > value.lessonYearIndex!) {
|
|
||||||
return lesson;
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ifAbsent: () => lesson,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void buildSubjectAbsences() {
|
|
||||||
// ignore: no_leading_underscores_for_local_identifiers
|
|
||||||
Map<GradeSubject, SubjectAbsence> _absences = {};
|
|
||||||
|
|
||||||
for (final absence in absenceProvider.absences) {
|
|
||||||
if (absence.delay != 0) continue;
|
|
||||||
|
|
||||||
if (!_absences.containsKey(absence.subject)) {
|
|
||||||
_absences[absence.subject] =
|
|
||||||
SubjectAbsence(subject: absence.subject, absences: [absence]);
|
|
||||||
} else {
|
|
||||||
_absences[absence.subject]?.absences.add(absence);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_absences.forEach((subject, absence) {
|
|
||||||
final absentLessonsOfSubject = absenceProvider.absences
|
|
||||||
.where((e) => e.subject == subject && e.delay == 0)
|
|
||||||
.length;
|
|
||||||
final totalLessonsOfSubject = _lessonCount[subject]?.lessonYearIndex ?? 0;
|
|
||||||
|
|
||||||
double absentLessonsOfSubjectPercentage;
|
|
||||||
|
|
||||||
if (absentLessonsOfSubject <= totalLessonsOfSubject) {
|
|
||||||
absentLessonsOfSubjectPercentage =
|
|
||||||
absentLessonsOfSubject / totalLessonsOfSubject * 100;
|
|
||||||
} else {
|
|
||||||
absentLessonsOfSubjectPercentage = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_absences[subject]?.percentage =
|
|
||||||
absentLessonsOfSubjectPercentage.clamp(-1, 100.0);
|
|
||||||
});
|
|
||||||
|
|
||||||
absences = _absences.values.toList();
|
|
||||||
absences.sort((a, b) => -a.percentage.compareTo(b.percentage));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
user = Provider.of<UserProvider>(context);
|
|
||||||
absenceProvider = Provider.of<AbsenceProvider>(context);
|
|
||||||
noteProvider = Provider.of<NoteProvider>(context);
|
|
||||||
updateProvider = Provider.of<UpdateProvider>(context);
|
|
||||||
timetableProvider = Provider.of<TimetableProvider>(context);
|
|
||||||
|
|
||||||
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
|
|
||||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
|
||||||
|
|
||||||
buildSubjectAbsences();
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 12.0),
|
|
||||||
child: NestedScrollView(
|
|
||||||
physics: const BouncingScrollPhysics(
|
|
||||||
parent: AlwaysScrollableScrollPhysics()),
|
|
||||||
headerSliverBuilder: (context, _) => [
|
|
||||||
SliverAppBar(
|
|
||||||
pinned: true,
|
|
||||||
floating: false,
|
|
||||||
snap: false,
|
|
||||||
centerTitle: false,
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
shadowColor: Theme.of(context).shadowColor,
|
|
||||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
title: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: Text(
|
|
||||||
"Absences".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.of(context).text,
|
|
||||||
fontSize: 32.0,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
bottom: FilterBar(items: [
|
|
||||||
Tab(text: "Absences".i18n),
|
|
||||||
Tab(text: "Delays".i18n),
|
|
||||||
Tab(text: "Misses".i18n),
|
|
||||||
], controller: _tabController, disableFading: true),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
body: TabBarView(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
controller: _tabController,
|
|
||||||
children: List.generate(
|
|
||||||
3, (index) => filterViewBuilder(context, index))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DateWidget> getFilterWidgets(AbsenceFilter activeData) {
|
|
||||||
List<DateWidget> items = [];
|
|
||||||
switch (activeData) {
|
|
||||||
case AbsenceFilter.absences:
|
|
||||||
for (var a in absences) {
|
|
||||||
items.add(DateWidget(
|
|
||||||
date: DateTime.fromMillisecondsSinceEpoch(0),
|
|
||||||
widget: AbsenceSubjectTile(
|
|
||||||
a.subject,
|
|
||||||
percentage: a.percentage,
|
|
||||||
excused: a.absences
|
|
||||||
.where((a) => a.state == Justification.excused)
|
|
||||||
.length,
|
|
||||||
unexcused: a.absences
|
|
||||||
.where((a) => a.state == Justification.unexcused)
|
|
||||||
.length,
|
|
||||||
pending: a.absences
|
|
||||||
.where((a) => a.state == Justification.pending)
|
|
||||||
.length,
|
|
||||||
onTap: () => AbsenceSubjectView.show(a.subject, a.absences,
|
|
||||||
context: context),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AbsenceFilter.delays:
|
|
||||||
for (var absence in absenceProvider.absences) {
|
|
||||||
if (absence.delay != 0) {
|
|
||||||
items.add(DateWidget(
|
|
||||||
date: absence.date,
|
|
||||||
widget: AbsenceViewable(
|
|
||||||
absence,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AbsenceFilter.misses:
|
|
||||||
for (var note in noteProvider.notes) {
|
|
||||||
if (note.type?.name == "HaziFeladatHiany" ||
|
|
||||||
note.type?.name == "Felszereleshiany") {
|
|
||||||
items.add(DateWidget(
|
|
||||||
date: note.date,
|
|
||||||
widget: MissTile(note),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget filterViewBuilder(context, int activeData) {
|
|
||||||
List<Widget> filterWidgets = [];
|
|
||||||
|
|
||||||
if (activeData > 0) {
|
|
||||||
filterWidgets = sortDateWidgets(
|
|
||||||
context,
|
|
||||||
dateWidgets: getFilterWidgets(AbsenceFilter.values[activeData]),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
hasShadow: true,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
filterWidgets = [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text("Subjects".i18n),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 4.0),
|
|
||||||
child: IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12.0)),
|
|
||||||
title: Text("attention".i18n),
|
|
||||||
content: Text("attention_body".i18n),
|
|
||||||
actions: [
|
|
||||||
ActionButton(
|
|
||||||
label: "Ok",
|
|
||||||
onTap: () => Navigator.of(context).pop())
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
splashRadius: 24.0,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
constraints: BoxConstraints.tight(const Size(42.0, 42.0)),
|
|
||||||
icon: const Icon(FeatherIcons.info),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: PageTransitionSwitcher(
|
|
||||||
transitionBuilder: (
|
|
||||||
Widget child,
|
|
||||||
Animation<double> primaryAnimation,
|
|
||||||
Animation<double> secondaryAnimation,
|
|
||||||
) {
|
|
||||||
return FadeThroughTransition(
|
|
||||||
animation: primaryAnimation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
fillColor: Theme.of(context).colorScheme.surface,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Column(
|
|
||||||
children: getFilterWidgets(AbsenceFilter.values[activeData])
|
|
||||||
.map((e) => e.widget)
|
|
||||||
.cast<Widget>()
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 12.0),
|
|
||||||
child: RefreshIndicator(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
onRefresh: () async {
|
|
||||||
await absenceProvider.fetch();
|
|
||||||
await noteProvider.fetch();
|
|
||||||
},
|
|
||||||
child: ListView.builder(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
itemCount: max(filterWidgets.length + (activeData <= 1 ? 1 : 0), 1),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (filterWidgets.isNotEmpty) {
|
|
||||||
if ((index == 0 && activeData == 1) ||
|
|
||||||
(index == 0 && activeData == 0)) {
|
|
||||||
int value1 = 0;
|
|
||||||
int value2 = 0;
|
|
||||||
String title1 = "";
|
|
||||||
String title2 = "";
|
|
||||||
String suffix = "";
|
|
||||||
|
|
||||||
if (activeData == AbsenceFilter.absences.index) {
|
|
||||||
value1 = absenceProvider.absences
|
|
||||||
.where((e) =>
|
|
||||||
e.delay == 0 && e.state == Justification.excused)
|
|
||||||
.length;
|
|
||||||
value2 = absenceProvider.absences
|
|
||||||
.where((e) =>
|
|
||||||
e.delay == 0 && e.state == Justification.unexcused)
|
|
||||||
.length;
|
|
||||||
title1 = "stat_1".i18n;
|
|
||||||
title2 = "stat_2".i18n;
|
|
||||||
suffix = " ${"hr".i18n}";
|
|
||||||
} else if (activeData == AbsenceFilter.delays.index) {
|
|
||||||
value1 = absenceProvider.absences
|
|
||||||
.where((e) =>
|
|
||||||
e.delay != 0 && e.state == Justification.excused)
|
|
||||||
.map((e) => e.delay)
|
|
||||||
.fold(0, (a, b) => a + b);
|
|
||||||
value2 = absenceProvider.absences
|
|
||||||
.where((e) =>
|
|
||||||
e.delay != 0 && e.state == Justification.unexcused)
|
|
||||||
.map((e) => e.delay)
|
|
||||||
.fold(0, (a, b) => a + b);
|
|
||||||
title1 = "stat_3".i18n;
|
|
||||||
title2 = "stat_4".i18n;
|
|
||||||
suffix = " ${"min".i18n}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
bottom: 24.0, left: 24.0, right: 24.0),
|
|
||||||
child: Row(children: [
|
|
||||||
Expanded(
|
|
||||||
child: StatisticsTile(
|
|
||||||
title: AutoSizeText(
|
|
||||||
title1,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
valueSuffix: suffix,
|
|
||||||
value: value1.toDouble(),
|
|
||||||
decimal: false,
|
|
||||||
color: AppColors.of(context).green,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 24.0),
|
|
||||||
Expanded(
|
|
||||||
child: StatisticsTile(
|
|
||||||
title: AutoSizeText(
|
|
||||||
title2,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
valueSuffix: suffix,
|
|
||||||
value: value2.toDouble(),
|
|
||||||
decimal: false,
|
|
||||||
color: AppColors.of(context).red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 24.0, vertical: 6.0),
|
|
||||||
child: filterWidgets[index - (activeData <= 1 ? 1 : 0)],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return activeData == 1
|
|
||||||
? Empty(subtitle: "emptyDelays".i18n)
|
|
||||||
: Empty(subtitle: "emptyMisses".i18n);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension ScreensLocalization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"Absences": "Absences",
|
|
||||||
"Delays": "Delays",
|
|
||||||
"Misses": "Misses",
|
|
||||||
"emptyDelays": "You have no delays.",
|
|
||||||
"emptyMisses": "You have no missing homeworks or equipments.",
|
|
||||||
"stat_1": "Excused Absences",
|
|
||||||
"stat_2": "Unexcused Absences",
|
|
||||||
"stat_3": "Excused Delay",
|
|
||||||
"stat_4": "Unexcused Delay",
|
|
||||||
"min": "min",
|
|
||||||
"hr": "hrs",
|
|
||||||
"Subjects": "Subjects",
|
|
||||||
"attention": "Attention!",
|
|
||||||
"attention_body": "Percentage calculations are only an approximation so they may not be accurate.",
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"Absences": "Hiányzások",
|
|
||||||
"Delays": "Késések",
|
|
||||||
"Misses": "Hiányok",
|
|
||||||
"emptyDelays": "Nincsenek késéseid.",
|
|
||||||
"emptyMisses": "Nincsenek hiányzó házi feladataid, felszereléseid.",
|
|
||||||
"stat_1": "Igazolt hiányzások",
|
|
||||||
"stat_2": "Igazolatlan hiányzások",
|
|
||||||
"stat_3": "Igazolt Késés",
|
|
||||||
"stat_4": "Igazolatlan Késés",
|
|
||||||
"min": "perc",
|
|
||||||
"hr": "óra",
|
|
||||||
"Subjects": "Tantárgyak",
|
|
||||||
"attention": "Figyelem!",
|
|
||||||
"attention_body": "A százalékos számítások csak közelítések, ezért előfordulhat, hogy nem pontosak.",
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"Absences": "Fehlen",
|
|
||||||
"Delays": "Verspätung",
|
|
||||||
"Misses": "Fehlt",
|
|
||||||
"emptyDelays": "Sie haben keine Abwesenheiten.",
|
|
||||||
"emptyMisses": "Sie haben noch keine fehlende Hausaufgaben oder Austattung.",
|
|
||||||
"stat_1": "Entschuldigte Fehlen",
|
|
||||||
"stat_2": "Unentschuldigte Fehlen",
|
|
||||||
"stat_3": "Entschuldigte Verspätung",
|
|
||||||
"stat_4": "Unentschuldigte Verspätung",
|
|
||||||
"min": "min",
|
|
||||||
"hr": "hrs",
|
|
||||||
"Subjects": "Fächer",
|
|
||||||
"attention": "Achtung!",
|
|
||||||
"attention_body": "Prozentberechnungen sind nur eine Annäherung und können daher ungenau sein.",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,296 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:refilc/models/settings.dart';
|
|
||||||
import 'package:refilc/utils/format.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
|
||||||
import 'package:refilc/helpers/average_helper.dart';
|
|
||||||
import 'package:refilc/helpers/subject.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/grade.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/subject.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/average_display.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/trend_display.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/cretification/certification_tile.dart';
|
|
||||||
import 'package:refilc/ui/widgets/grade/grade_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/grade/grade_viewable.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/hero_scrollview.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/grades/calculator/grade_calculator.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/grades/grades_count.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/grades/graph.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/grades/subject_grades_container.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
|
||||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'grades_page.i18n.dart';
|
|
||||||
//import 'package:refilc_plus/ui/mobile/goal_planner/new_goal.dart';
|
|
||||||
|
|
||||||
class GradeSubjectView extends StatefulWidget {
|
|
||||||
const GradeSubjectView(this.subject, {super.key, this.groupAverage = 0.0});
|
|
||||||
|
|
||||||
final GradeSubject subject;
|
|
||||||
final double groupAverage;
|
|
||||||
|
|
||||||
void push(BuildContext context, {bool root = false}) {
|
|
||||||
Navigator.of(context, rootNavigator: root)
|
|
||||||
.push(CupertinoPageRoute(builder: (context) => this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<GradeSubjectView> createState() => _GradeSubjectViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _GradeSubjectViewState extends State<GradeSubjectView> {
|
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
|
||||||
|
|
||||||
// Controllers
|
|
||||||
PersistentBottomSheetController? _sheetController;
|
|
||||||
final ScrollController _scrollController = ScrollController();
|
|
||||||
|
|
||||||
List<Widget> gradeTiles = [];
|
|
||||||
|
|
||||||
// Providers
|
|
||||||
late GradeProvider gradeProvider;
|
|
||||||
late GradeCalculatorProvider calculatorProvider;
|
|
||||||
late SettingsProvider settingsProvider;
|
|
||||||
|
|
||||||
late double average;
|
|
||||||
late Widget gradeGraph;
|
|
||||||
|
|
||||||
bool gradeCalcMode = false;
|
|
||||||
|
|
||||||
List<Grade> getSubjectGrades(GradeSubject subject) => !gradeCalcMode
|
|
||||||
? gradeProvider.grades.where((e) => e.subject == subject).toList()
|
|
||||||
: calculatorProvider.grades.where((e) => e.subject == subject).toList();
|
|
||||||
|
|
||||||
bool showGraph(List<Grade> subjectGrades) {
|
|
||||||
if (gradeCalcMode) return true;
|
|
||||||
|
|
||||||
final gradeDates = subjectGrades.map((e) => e.date.millisecondsSinceEpoch);
|
|
||||||
final maxGradeDate = gradeDates.fold(0, max);
|
|
||||||
final minGradeDate = gradeDates.fold(0, min);
|
|
||||||
if (maxGradeDate - minGradeDate < const Duration(days: 5).inMilliseconds) {
|
|
||||||
return false; // naplo/#78
|
|
||||||
}
|
|
||||||
|
|
||||||
return subjectGrades.where((e) => e.type == GradeType.midYear).length > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void buildTiles(List<Grade> subjectGrades) {
|
|
||||||
List<Widget> tiles = [];
|
|
||||||
|
|
||||||
if (showGraph(subjectGrades)) {
|
|
||||||
tiles.add(gradeGraph);
|
|
||||||
} else {
|
|
||||||
tiles.add(Container(height: 24.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore: no_leading_underscores_for_local_identifiers
|
|
||||||
List<Widget> _gradeTiles = [];
|
|
||||||
|
|
||||||
if (!gradeCalcMode) {
|
|
||||||
subjectGrades.sort((a, b) => -a.date.compareTo(b.date));
|
|
||||||
for (var grade in subjectGrades) {
|
|
||||||
if (grade.type == GradeType.midYear) {
|
|
||||||
_gradeTiles.add(GradeViewable(grade));
|
|
||||||
} else {
|
|
||||||
_gradeTiles.add(CertificationTile(grade, padding: EdgeInsets.zero));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (subjectGrades.isNotEmpty) {
|
|
||||||
subjectGrades.sort((a, b) => -a.date.compareTo(b.date));
|
|
||||||
for (var grade in subjectGrades) {
|
|
||||||
_gradeTiles.add(GradeTile(grade));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tiles.add(
|
|
||||||
PageTransitionSwitcher(
|
|
||||||
transitionBuilder: (
|
|
||||||
Widget child,
|
|
||||||
Animation<double> primaryAnimation,
|
|
||||||
Animation<double> secondaryAnimation,
|
|
||||||
) {
|
|
||||||
return SharedAxisTransition(
|
|
||||||
animation: primaryAnimation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
transitionType: SharedAxisTransitionType.vertical,
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: _gradeTiles.isNotEmpty
|
|
||||||
? Panel(
|
|
||||||
key: ValueKey(gradeCalcMode),
|
|
||||||
title: Text(
|
|
||||||
gradeCalcMode ? "Ghost Grades".i18n : "Grades".i18n,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: _gradeTiles,
|
|
||||||
))
|
|
||||||
: const SizedBox(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
tiles.add(Padding(
|
|
||||||
padding: EdgeInsets.only(bottom: !gradeCalcMode ? 24.0 : 250.0)));
|
|
||||||
gradeTiles = List.castFrom(tiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
gradeProvider = Provider.of<GradeProvider>(context);
|
|
||||||
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
|
|
||||||
settingsProvider = Provider.of<SettingsProvider>(context);
|
|
||||||
|
|
||||||
List<Grade> subjectGrades = getSubjectGrades(widget.subject).toList();
|
|
||||||
average = AverageHelper.averageEvals(subjectGrades);
|
|
||||||
final prevAvg = subjectGrades.isNotEmpty
|
|
||||||
? AverageHelper.averageEvals(subjectGrades
|
|
||||||
.where((e) => e.date.isBefore(subjectGrades
|
|
||||||
.reduce((v, e) => e.date.isAfter(v.date) ? e : v)
|
|
||||||
.date
|
|
||||||
.subtract(const Duration(days: 30))))
|
|
||||||
.toList())
|
|
||||||
: 0.0;
|
|
||||||
|
|
||||||
gradeGraph = Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 16.0, bottom: 8.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text("annual_average".i18n),
|
|
||||||
if (average != prevAvg)
|
|
||||||
TrendDisplay(current: average, previous: prevAvg),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.only(top: 12.0, right: 12.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: GradeGraph(subjectGrades,
|
|
||||||
dayThreshold: 5, classAvg: widget.groupAverage)),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 24.0),
|
|
||||||
child: GradesCount(grades: subjectGrades),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!gradeCalcMode) {
|
|
||||||
buildTiles(subjectGrades);
|
|
||||||
} else {
|
|
||||||
List<Grade> ghostGrades = calculatorProvider.ghosts
|
|
||||||
.where((e) => e.subject == widget.subject)
|
|
||||||
.toList();
|
|
||||||
buildTiles(ghostGrades);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
key: _scaffoldKey,
|
|
||||||
floatingActionButtonLocation: ExpandableFab.location,
|
|
||||||
floatingActionButton: Visibility(
|
|
||||||
visible: !gradeCalcMode &&
|
|
||||||
subjectGrades
|
|
||||||
.where((e) => e.type == GradeType.midYear)
|
|
||||||
.isNotEmpty,
|
|
||||||
child: ExpandableFab(
|
|
||||||
type: ExpandableFabType.up,
|
|
||||||
distance: 50,
|
|
||||||
children: [
|
|
||||||
FloatingActionButton.small(
|
|
||||||
child: const Icon(FeatherIcons.plus),
|
|
||||||
onPressed: () {
|
|
||||||
gradeCalc(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// FloatingActionButton.small(
|
|
||||||
// child: const Icon(FeatherIcons.flag, size: 20.0),
|
|
||||||
// onPressed: () {
|
|
||||||
// Navigator.of(context).push(CupertinoPageRoute(builder: (context) => PremiumGoalplannerNewGoalScreen(subject: widget.subject)));
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: RefreshIndicator(
|
|
||||||
onRefresh: () async {},
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
child: HeroScrollView(
|
|
||||||
onClose: () {
|
|
||||||
if (_sheetController != null && gradeCalcMode) {
|
|
||||||
_sheetController!.close();
|
|
||||||
} else {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
navBarItems: [
|
|
||||||
const SizedBox(width: 6.0),
|
|
||||||
if (widget.groupAverage != 0)
|
|
||||||
Center(
|
|
||||||
child: AverageDisplay(
|
|
||||||
average: widget.groupAverage, border: true)),
|
|
||||||
const SizedBox(width: 6.0),
|
|
||||||
if (average != 0)
|
|
||||||
Center(child: AverageDisplay(average: average)),
|
|
||||||
const SizedBox(width: 12.0),
|
|
||||||
],
|
|
||||||
icon: SubjectIcon.resolveVariant(
|
|
||||||
subject: widget.subject, context: context),
|
|
||||||
scrollController: _scrollController,
|
|
||||||
title: widget.subject.renamedTo ?? widget.subject.name.capital(),
|
|
||||||
italic: widget.subject.isRenamed &&
|
|
||||||
settingsProvider.renamedSubjectsItalics,
|
|
||||||
child: SubjectGradesContainer(
|
|
||||||
child: CupertinoScrollbar(
|
|
||||||
child: ListView.builder(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemBuilder: (context, index) => gradeTiles[index],
|
|
||||||
itemCount: gradeTiles.length,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
void gradeCalc(BuildContext context) {
|
|
||||||
// Scroll to the top of the page
|
|
||||||
_scrollController.animateTo(75,
|
|
||||||
duration: const Duration(milliseconds: 500), curve: Curves.ease);
|
|
||||||
|
|
||||||
calculatorProvider.clear();
|
|
||||||
calculatorProvider.addAllGrades(gradeProvider.grades);
|
|
||||||
|
|
||||||
_sheetController = _scaffoldKey.currentState?.showBottomSheet(
|
|
||||||
(context) => RoundedBottomSheet(
|
|
||||||
borderRadius: 14.0,
|
|
||||||
child: GradeCalculator(widget.subject)),
|
|
||||||
backgroundColor: const Color(0x00000000),
|
|
||||||
elevation: 12.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Hide the fab and grades
|
|
||||||
setState(() {
|
|
||||||
gradeCalcMode = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
_sheetController!.closed.then((value) {
|
|
||||||
// Show fab and grades
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
gradeCalcMode = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/grade.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/grades/grades_count_item.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
|
|
||||||
class GradesCount extends StatelessWidget {
|
|
||||||
const GradesCount({super.key, required this.grades});
|
|
||||||
|
|
||||||
final List<Grade> grades;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
List<int> gradesCount = List.generate(5,
|
|
||||||
(int index) => grades.where((e) => e.value.value == index + 1).length);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: 75,
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(bottom: 6.0, top: 6.0, left: 12.0, right: 0.0),
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: gradesCount
|
|
||||||
.mapIndexed(
|
|
||||||
(index, e) => GradesCountItem(count: e, value: index + 1))
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import 'package:refilc/ui/widgets/grade/grade_tile.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/grade.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class GradesCountItem extends StatelessWidget {
|
|
||||||
const GradesCountItem({super.key, required this.count, required this.value});
|
|
||||||
|
|
||||||
final int count;
|
|
||||||
final int value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(children: [
|
|
||||||
TextSpan(
|
|
||||||
text: count.toString(),
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
const TextSpan(
|
|
||||||
text: "x",
|
|
||||||
style: TextStyle(fontSize: 13.0),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
style: const TextStyle(fontSize: 15.0),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 5.0),
|
|
||||||
GradeValueWidget(GradeValue(value, "Value", "Value", 100),
|
|
||||||
size: 19.0, fill: true, shadow: false),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,314 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
|
||||||
import 'package:refilc/api/providers/update_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
|
||||||
import 'package:refilc/api/providers/user_provider.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/grade.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/subject.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/group_average.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/average_display.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/empty.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/statistics_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/grade/grade_subject_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/trend_display.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/grades/fail_warning.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/grades/grades_count.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/grades/graph.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/grades/grade_subject_view.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:refilc/helpers/average_helper.dart';
|
|
||||||
import 'package:refilc_mobile_ui/pages/grades/average_selector.dart';
|
|
||||||
import 'grades_page.i18n.dart';
|
|
||||||
|
|
||||||
class GradesPage extends StatefulWidget {
|
|
||||||
const GradesPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
GradesPageState createState() => GradesPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class GradesPageState extends State<GradesPage> {
|
|
||||||
late UserProvider user;
|
|
||||||
late GradeProvider gradeProvider;
|
|
||||||
late UpdateProvider updateProvider;
|
|
||||||
late String firstName;
|
|
||||||
late Widget yearlyGraph;
|
|
||||||
List<Widget> subjectTiles = [];
|
|
||||||
|
|
||||||
int avgDropValue = 0;
|
|
||||||
|
|
||||||
List<Grade> getSubjectGrades(GradeSubject subject, {int days = 0}) =>
|
|
||||||
gradeProvider.grades
|
|
||||||
.where((e) =>
|
|
||||||
e.subject == subject &&
|
|
||||||
e.type == GradeType.midYear &&
|
|
||||||
(days == 0 ||
|
|
||||||
e.date
|
|
||||||
.isBefore(DateTime.now().subtract(Duration(days: days)))))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
void generateTiles() {
|
|
||||||
List<GradeSubject> subjects = gradeProvider.grades
|
|
||||||
.map((e) => e.subject)
|
|
||||||
.toSet()
|
|
||||||
.toList()
|
|
||||||
..sort((a, b) => a.name.compareTo(b.name));
|
|
||||||
List<Widget> tiles = [];
|
|
||||||
|
|
||||||
Map<GradeSubject, double> subjectAvgs = {};
|
|
||||||
|
|
||||||
tiles.addAll(subjects.map((subject) {
|
|
||||||
List<Grade> subjectGrades = getSubjectGrades(subject);
|
|
||||||
|
|
||||||
double avg = AverageHelper.averageEvals(subjectGrades);
|
|
||||||
double averageBefore = 0.0;
|
|
||||||
|
|
||||||
if (avgDropValue != 0) {
|
|
||||||
List<Grade> gradesBefore =
|
|
||||||
getSubjectGrades(subject, days: avgDropValue);
|
|
||||||
averageBefore =
|
|
||||||
avgDropValue == 0 ? 0.0 : AverageHelper.averageEvals(gradesBefore);
|
|
||||||
}
|
|
||||||
var nullavg = GroupAverage(average: 0.0, subject: subject, uid: "0");
|
|
||||||
double groupAverage = gradeProvider.groupAverages
|
|
||||||
.firstWhere((e) => e.subject == subject, orElse: () => nullavg)
|
|
||||||
.average;
|
|
||||||
|
|
||||||
if (avg != 0) subjectAvgs[subject] = avg;
|
|
||||||
|
|
||||||
return GradeSubjectTile(
|
|
||||||
subject,
|
|
||||||
averageBefore: averageBefore,
|
|
||||||
average: avg,
|
|
||||||
groupAverage: avgDropValue == 0 ? groupAverage : 0.0,
|
|
||||||
onTap: () {
|
|
||||||
GradeSubjectView(subject, groupAverage: groupAverage)
|
|
||||||
.push(context, root: true);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (tiles.isNotEmpty) {
|
|
||||||
tiles.insert(0, yearlyGraph);
|
|
||||||
tiles.insert(1, FailWarning(subjectAvgs: subjectAvgs));
|
|
||||||
tiles.insert(
|
|
||||||
2,
|
|
||||||
PanelTitle(
|
|
||||||
title: Text(avgDropValue == 0
|
|
||||||
? "Subjects".i18n
|
|
||||||
: "Subjects_changes".i18n)));
|
|
||||||
tiles.insert(3, const PanelHeader(padding: EdgeInsets.only(top: 12.0)));
|
|
||||||
tiles.add(const PanelFooter(padding: EdgeInsets.only(bottom: 12.0)));
|
|
||||||
tiles.add(const Padding(padding: EdgeInsets.only(bottom: 24.0)));
|
|
||||||
} else {
|
|
||||||
tiles.insert(
|
|
||||||
0,
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 24.0),
|
|
||||||
child: Empty(subtitle: "empty".i18n),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
double subjectAvg = subjectAvgs.isNotEmpty
|
|
||||||
? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) /
|
|
||||||
subjectAvgs.length
|
|
||||||
: 0.0;
|
|
||||||
final double classAvg = gradeProvider.groupAverages.isNotEmpty
|
|
||||||
? gradeProvider.groupAverages
|
|
||||||
.map((e) => e.average)
|
|
||||||
.fold(0.0, (double a, double b) => a + b) /
|
|
||||||
gradeProvider.groupAverages.length
|
|
||||||
: 0.0;
|
|
||||||
|
|
||||||
if (subjectAvg > 0) {
|
|
||||||
tiles.add(Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: StatisticsTile(
|
|
||||||
fill: true,
|
|
||||||
title: AutoSizeText(
|
|
||||||
"subjectavg".i18n,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
value: subjectAvg,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 24.0),
|
|
||||||
Expanded(
|
|
||||||
child: StatisticsTile(
|
|
||||||
outline: true,
|
|
||||||
title: AutoSizeText(
|
|
||||||
// https://discord.com/channels/1111649116020285532/1153397476578050130
|
|
||||||
"classavg".i18n,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
maxLines: 2,
|
|
||||||
wrapWords: false,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
value: classAvg,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// padding
|
|
||||||
tiles.add(const SizedBox(height: 32.0));
|
|
||||||
|
|
||||||
subjectTiles = List.castFrom(tiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
user = Provider.of<UserProvider>(context);
|
|
||||||
gradeProvider = Provider.of<GradeProvider>(context);
|
|
||||||
updateProvider = Provider.of<UpdateProvider>(context);
|
|
||||||
|
|
||||||
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
|
|
||||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
|
||||||
|
|
||||||
final double totalClassAvg = gradeProvider.groupAverages.isEmpty
|
|
||||||
? 0.0
|
|
||||||
: gradeProvider.groupAverages
|
|
||||||
.map((e) => e.average)
|
|
||||||
.fold(0.0, (double a, double b) => a + b) /
|
|
||||||
gradeProvider.groupAverages.length;
|
|
||||||
|
|
||||||
final now = gradeProvider.grades.isNotEmpty
|
|
||||||
? gradeProvider.grades
|
|
||||||
.reduce((v, e) => e.date.isAfter(v.date) ? e : v)
|
|
||||||
.date
|
|
||||||
: DateTime.now();
|
|
||||||
|
|
||||||
final currentStudentAvg = AverageHelper.averageEvals(gradeProvider.grades
|
|
||||||
.where((e) => e.type == GradeType.midYear)
|
|
||||||
.toList());
|
|
||||||
final prevStudentAvg = AverageHelper.averageEvals(gradeProvider.grades
|
|
||||||
.where((e) => e.type == GradeType.midYear)
|
|
||||||
.where((e) => e.date.isBefore(now.subtract(const Duration(days: 30))))
|
|
||||||
.toList());
|
|
||||||
|
|
||||||
List<Grade> graphGrades = gradeProvider.grades
|
|
||||||
.where((e) =>
|
|
||||||
e.type == GradeType.midYear &&
|
|
||||||
(avgDropValue == 0 ||
|
|
||||||
e.date.isAfter(
|
|
||||||
DateTime.now().subtract(Duration(days: avgDropValue)))))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
yearlyGraph = Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 12.0, bottom: 8.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
AverageSelector(
|
|
||||||
value: avgDropValue,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
avgDropValue = value!;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
// if (totalClassAvg >= 1.0) AverageDisplay(average: totalClassAvg, border: true),
|
|
||||||
// const SizedBox(width: 4.0),
|
|
||||||
TrendDisplay(
|
|
||||||
previous: prevStudentAvg, current: currentStudentAvg),
|
|
||||||
if (gradeProvider.grades
|
|
||||||
.where((e) => e.type == GradeType.midYear)
|
|
||||||
.isNotEmpty)
|
|
||||||
AverageDisplay(average: currentStudentAvg),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.only(top: 12.0, right: 12.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: GradeGraph(graphGrades,
|
|
||||||
dayThreshold: 2, classAvg: totalClassAvg)),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 24.0),
|
|
||||||
child: GradesCount(grades: graphGrades),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
generateTiles();
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 9.0),
|
|
||||||
child: NestedScrollView(
|
|
||||||
physics: const BouncingScrollPhysics(
|
|
||||||
parent: AlwaysScrollableScrollPhysics()),
|
|
||||||
headerSliverBuilder: (context, _) => [
|
|
||||||
SliverAppBar(
|
|
||||||
centerTitle: false,
|
|
||||||
pinned: true,
|
|
||||||
floating: false,
|
|
||||||
snap: false,
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
title: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: Text(
|
|
||||||
"page_title_grades".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.of(context).text,
|
|
||||||
fontSize: 32.0,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
shadowColor: Theme.of(context).shadowColor,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
body: RefreshIndicator(
|
|
||||||
onRefresh: () => gradeProvider.fetch(),
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
child: ListView.builder(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
itemCount: max(subjectTiles.length, 1),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (subjectTiles.isNotEmpty) {
|
|
||||||
EdgeInsetsGeometry panelPadding =
|
|
||||||
const EdgeInsets.symmetric(horizontal: 24.0);
|
|
||||||
|
|
||||||
if (subjectTiles[index].runtimeType == GradeSubjectTile) {
|
|
||||||
return Padding(
|
|
||||||
padding: panelPadding,
|
|
||||||
child: PanelBody(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
||||||
child: subjectTiles[index],
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
return Padding(
|
|
||||||
padding: panelPadding, child: subjectTiles[index]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension Localization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"Grades": "Grades",
|
|
||||||
"Ghost Grades": "Grades",
|
|
||||||
"Subjects": "Subjects",
|
|
||||||
"Subjects_changes": "Subject Differences",
|
|
||||||
"empty": "You don't have any subjects.",
|
|
||||||
"annual_average": "Annual average",
|
|
||||||
"3_months_average": "3 Monthly Average",
|
|
||||||
"30_days_average": "Monthly Average",
|
|
||||||
"14_days_average": "2 Weekly Average",
|
|
||||||
"7_days_average": "Weekly Average",
|
|
||||||
"subjectavg": "Subject Average",
|
|
||||||
"classavg": "Class Average",
|
|
||||||
"fail_warning": "Failure warning",
|
|
||||||
"fail_warning_description": "You are failing %d subjects!"
|
|
||||||
.one("You are failing a subject!"),
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"Grades": "Jegyek",
|
|
||||||
"Ghost Grades": "Szellem jegyek",
|
|
||||||
"Subjects": "Tantárgyak",
|
|
||||||
"Subjects_changes": "Tantárgyi változások",
|
|
||||||
"empty": "Még nincs egy tárgyad sem.",
|
|
||||||
"annual_average": "Éves átlag",
|
|
||||||
"3_months_average": "Háromhavi átlag",
|
|
||||||
"30_days_average": "Havi átlag",
|
|
||||||
"14_days_average": "Kétheti átlag",
|
|
||||||
"7_days_average": "Heti átlag",
|
|
||||||
"subjectavg": "Tantárgyi átlag",
|
|
||||||
"classavg": "Osztályátlag",
|
|
||||||
"fail_warning": "Bukás figyelmeztető",
|
|
||||||
"fail_warning_description": "Bukásra állsz %d tantárgyból",
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"Grades": "Noten",
|
|
||||||
"Ghost Grades": "Geist Noten",
|
|
||||||
"Subjects": "Fächer",
|
|
||||||
"Subjects_changes": "Betreff Änderungen",
|
|
||||||
"empty": "Sie haben keine Fächer.",
|
|
||||||
"annual_average": "Jahresdurchschnitt",
|
|
||||||
"3_months_average": "Drei-Monats-Durchschnitt",
|
|
||||||
"30_days_average": "Monatsdurchschnitt",
|
|
||||||
"14_days_average": "Vierzehntägiger Durchschnitt",
|
|
||||||
"7_days_average": "Wöchentlicher Durchschnitt",
|
|
||||||
"subjectavg": "Fächer Durchschnitt",
|
|
||||||
"classavg": "Klassendurchschnitt",
|
|
||||||
"fail_warning": "Ausfallwarnung",
|
|
||||||
"fail_warning_description": "Sie werden in %d des Fachs durchfallen",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,180 +0,0 @@
|
|||||||
import 'package:refilc/api/providers/user_provider.dart';
|
|
||||||
import 'package:refilc/models/settings.dart';
|
|
||||||
import 'package:refilc/ui/date_widget.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/filter_bar.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:animated_list_plus/animated_list_plus.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:refilc/ui/filter/widgets.dart';
|
|
||||||
import 'package:refilc/ui/filter/sort.dart';
|
|
||||||
import 'home_page.i18n.dart';
|
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
|
||||||
const HomePage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<HomePage> createState() => _HomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HomePageState extends State<HomePage>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
late UserProvider user;
|
|
||||||
late SettingsProvider settings;
|
|
||||||
|
|
||||||
late TabController _tabController;
|
|
||||||
late PageController _pageController;
|
|
||||||
|
|
||||||
late String greeting;
|
|
||||||
late String firstName;
|
|
||||||
|
|
||||||
late List<String> listOrder;
|
|
||||||
static const pageCount = 4;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
_tabController = TabController(length: pageCount, vsync: this);
|
|
||||||
_pageController = PageController();
|
|
||||||
|
|
||||||
listOrder = List.generate(pageCount, (index) => "$index");
|
|
||||||
|
|
||||||
user = Provider.of<UserProvider>(context, listen: false);
|
|
||||||
|
|
||||||
DateTime now = DateTime.now();
|
|
||||||
if (now.isBefore(DateTime(now.year, DateTime.august, 31)) &&
|
|
||||||
now.isAfter(DateTime(now.year, DateTime.june, 14))) {
|
|
||||||
greeting = "goodrest";
|
|
||||||
} else if (now.month == user.student?.birth.month &&
|
|
||||||
now.day == user.student?.birth.day) {
|
|
||||||
greeting = "happybirthday";
|
|
||||||
} else if (now.month == DateTime.december &&
|
|
||||||
now.day >= 24 &&
|
|
||||||
now.day <= 26) {
|
|
||||||
greeting = "merryxmas";
|
|
||||||
} else if (now.month == DateTime.january && now.day == 1) {
|
|
||||||
greeting = "happynewyear";
|
|
||||||
} else if (now.hour >= 18) {
|
|
||||||
greeting = "goodevening";
|
|
||||||
} else if (now.hour >= 12) {
|
|
||||||
greeting = "goodafternoon";
|
|
||||||
} else if (now.hour >= 4) {
|
|
||||||
greeting = "goodmorning";
|
|
||||||
} else {
|
|
||||||
greeting = "goodevening";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
user = Provider.of<UserProvider>(context);
|
|
||||||
settings = Provider.of<SettingsProvider>(context);
|
|
||||||
|
|
||||||
List<String> nameParts = user.name?.split(" ") ?? ["?"];
|
|
||||||
if (!settings.presentationMode) {
|
|
||||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
|
||||||
} else {
|
|
||||||
firstName = "Béla";
|
|
||||||
}
|
|
||||||
|
|
||||||
return SafeArea(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Greeting
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 32.0, top: 24.0, bottom: 12.0),
|
|
||||||
child: Text(
|
|
||||||
greeting.i18n.fill([firstName]),
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 24.0,
|
|
||||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 12.0),
|
|
||||||
child: FilterBar(
|
|
||||||
items: [
|
|
||||||
Tab(text: "All".i18n),
|
|
||||||
Tab(text: "Grades".i18n),
|
|
||||||
Tab(text: "Messages".i18n),
|
|
||||||
Tab(text: "Absences".i18n),
|
|
||||||
],
|
|
||||||
controller: _tabController,
|
|
||||||
onTap: (i) async {
|
|
||||||
int selectedPage = _pageController.page!.round();
|
|
||||||
|
|
||||||
if (i == selectedPage) return;
|
|
||||||
if (_pageController.page?.roundToDouble() !=
|
|
||||||
_pageController.page) {
|
|
||||||
_pageController.animateToPage(i,
|
|
||||||
curve: Curves.easeIn, duration: kTabScrollDuration);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// swap current page with target page
|
|
||||||
setState(() {
|
|
||||||
_pageController.jumpToPage(i);
|
|
||||||
String currentList = listOrder[selectedPage];
|
|
||||||
listOrder[selectedPage] = listOrder[i];
|
|
||||||
listOrder[i] = currentList;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
disableFading: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Data filters
|
|
||||||
Expanded(
|
|
||||||
child: PageView.custom(
|
|
||||||
controller: _pageController,
|
|
||||||
childrenDelegate: SliverChildBuilderDelegate(
|
|
||||||
(BuildContext context, int index) {
|
|
||||||
return FutureBuilder<List<DateWidget>>(
|
|
||||||
key: ValueKey<String>(listOrder[index]),
|
|
||||||
future: getFilterWidgets(homeFilters[index],
|
|
||||||
context: context),
|
|
||||||
builder: (context, dateWidgets) => dateWidgets.data !=
|
|
||||||
null
|
|
||||||
? ImplicitlyAnimatedList<Widget>(
|
|
||||||
items: sortDateWidgets(context,
|
|
||||||
dateWidgets: dateWidgets.data!),
|
|
||||||
itemBuilder: filterItemBuilder,
|
|
||||||
spawnIsolate: false,
|
|
||||||
areItemsTheSame: (a, b) => a.key == b.key,
|
|
||||||
physics: const BouncingScrollPhysics(
|
|
||||||
parent: AlwaysScrollableScrollPhysics()),
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 24.0),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
childCount: 4,
|
|
||||||
findChildIndexCallback: (Key key) {
|
|
||||||
final ValueKey<String> valueKey =
|
|
||||||
key as ValueKey<String>;
|
|
||||||
final String data = valueKey.value;
|
|
||||||
return listOrder.indexOf(data);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
physics: const PageScrollPhysics()
|
|
||||||
.applyTo(const BouncingScrollPhysics()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension Localization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"goodmorning": "Good morning, %s!",
|
|
||||||
"goodafternoon": "Good afternoon, %s!",
|
|
||||||
"goodevening": "Good evening, %s!",
|
|
||||||
"goodrest": "⛱️ Have a nice holiday, %s!",
|
|
||||||
"happybirthday": "🎂 Happy birthday, %s!",
|
|
||||||
"merryxmas": "🎄 Merry Christmas, %s!",
|
|
||||||
"happynewyear": "🎉 Happy New Year, %s!",
|
|
||||||
"empty": "Nothing to see here.",
|
|
||||||
"All": "All",
|
|
||||||
"Grades": "Grades",
|
|
||||||
"Messages": "Messages",
|
|
||||||
"Absences": "Absences",
|
|
||||||
"update_available": "Update Available",
|
|
||||||
"missed_exams": "You missed %s exams this week.".one("You missed an exam this week."),
|
|
||||||
"missed_exam_contact": "Contact %s, to resolve it!",
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"goodmorning": "Jó reggelt, %s!",
|
|
||||||
"goodafternoon": "Szép napot, %s!",
|
|
||||||
"goodevening": "Szép estét, %s!",
|
|
||||||
"goodrest": "⛱️ Jó szünetet, %s!",
|
|
||||||
"happybirthday": "🎂 Boldog születésnapot, %s!",
|
|
||||||
"merryxmas": "🎄 Boldog Karácsonyt, %s!",
|
|
||||||
"happynewyear": "🎉 Boldog új évet, %s!",
|
|
||||||
"empty": "Nincs itt semmi látnivaló.",
|
|
||||||
"All": "Összes",
|
|
||||||
"Grades": "Jegyek",
|
|
||||||
"Messages": "Üzenetek",
|
|
||||||
"Absences": "Hiányok",
|
|
||||||
"update_available": "Frissítés elérhető",
|
|
||||||
"missed_exams": "Ezen a héten hiányoztál %s dolgozatról.".one("Ezen a héten hiányoztál egy dolgozatról."),
|
|
||||||
"missed_exam_contact": "Keresd %s-t, ha pótolni szeretnéd!",
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"goodmorning": "Guten morgen, %s!",
|
|
||||||
"goodafternoon": "Guten Tag, %s!",
|
|
||||||
"goodevening": "Guten Abend, %s!",
|
|
||||||
"goodrest": "⛱️ Schöne Ferien, %s!",
|
|
||||||
"happybirthday": "🎂 Alles Gute zum Geburtstag, %s!",
|
|
||||||
"merryxmas": "🎄 Frohe Weihnachten, %s!",
|
|
||||||
"happynewyear": "🎉 Frohes neues Jahr, %s!",
|
|
||||||
"empty": "Hier gibt es nichts zu sehen.",
|
|
||||||
"All": "Alles",
|
|
||||||
"Grades": "Noten",
|
|
||||||
"Messages": "Nachrichten",
|
|
||||||
"Absences": "Fehlen",
|
|
||||||
"update_available": "Update verfügbar",
|
|
||||||
"missed_exams": "Diese Woche haben Sie %s Prüfungen verpasst.".one("Diese Woche haben Sie eine Prüfung verpasst."),
|
|
||||||
"missed_exam_contact": "Wenden Sie sich an %s, um sie zu erneuern!",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:refilc/api/providers/update_provider.dart';
|
|
||||||
import 'package:refilc/ui/date_widget.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/message_provider.dart';
|
|
||||||
import 'package:refilc/api/providers/user_provider.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/message.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/empty.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/filter_bar.dart';
|
|
||||||
import 'package:refilc/ui/filter/sort.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/message/message_viewable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'messages_page.i18n.dart';
|
|
||||||
|
|
||||||
class MessagesPage extends StatefulWidget {
|
|
||||||
const MessagesPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
MessagesPageState createState() => MessagesPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class MessagesPageState extends State<MessagesPage>
|
|
||||||
with TickerProviderStateMixin {
|
|
||||||
late UserProvider user;
|
|
||||||
late MessageProvider messageProvider;
|
|
||||||
late UpdateProvider updateProvider;
|
|
||||||
late String firstName;
|
|
||||||
late TabController tabController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
tabController = TabController(length: 4, vsync: this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
user = Provider.of<UserProvider>(context);
|
|
||||||
messageProvider = Provider.of<MessageProvider>(context);
|
|
||||||
updateProvider = Provider.of<UpdateProvider>(context);
|
|
||||||
|
|
||||||
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
|
|
||||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 12.0),
|
|
||||||
child: NestedScrollView(
|
|
||||||
physics: const BouncingScrollPhysics(
|
|
||||||
parent: AlwaysScrollableScrollPhysics()),
|
|
||||||
headerSliverBuilder: (context, _) => [
|
|
||||||
SliverAppBar(
|
|
||||||
pinned: true,
|
|
||||||
floating: false,
|
|
||||||
snap: false,
|
|
||||||
centerTitle: false,
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
shadowColor: Theme.of(context).shadowColor,
|
|
||||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
title: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: Text(
|
|
||||||
"Messages".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.of(context).text,
|
|
||||||
fontSize: 32.0,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
bottom: FilterBar(
|
|
||||||
items: [
|
|
||||||
Tab(text: "Inbox".i18n),
|
|
||||||
Tab(text: "Sent".i18n),
|
|
||||||
Tab(text: "Trash".i18n),
|
|
||||||
Tab(text: "Draft".i18n),
|
|
||||||
],
|
|
||||||
controller: tabController,
|
|
||||||
disableFading: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
body: TabBarView(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
controller: tabController,
|
|
||||||
children: List.generate(
|
|
||||||
4, (index) => filterViewBuilder(context, index))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DateWidget> getFilterWidgets(MessageType activeData) {
|
|
||||||
List<DateWidget> items = [];
|
|
||||||
switch (activeData) {
|
|
||||||
case MessageType.inbox:
|
|
||||||
for (var message in messageProvider.messages) {
|
|
||||||
if (message.type == MessageType.inbox) {
|
|
||||||
items.add(DateWidget(
|
|
||||||
date: message.date,
|
|
||||||
widget: MessageViewable(message),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MessageType.sent:
|
|
||||||
for (var message in messageProvider.messages) {
|
|
||||||
if (message.type == MessageType.sent) {
|
|
||||||
items.add(DateWidget(
|
|
||||||
date: message.date,
|
|
||||||
widget: MessageViewable(message),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MessageType.trash:
|
|
||||||
for (var message in messageProvider.messages) {
|
|
||||||
if (message.type == MessageType.trash) {
|
|
||||||
items.add(DateWidget(
|
|
||||||
date: message.date,
|
|
||||||
widget: MessageViewable(message),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MessageType.draft:
|
|
||||||
for (var message in messageProvider.messages) {
|
|
||||||
if (message.type == MessageType.draft) {
|
|
||||||
items.add(DateWidget(
|
|
||||||
date: message.date,
|
|
||||||
widget: MessageViewable(message),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget filterViewBuilder(context, int activeData) {
|
|
||||||
List<Widget> filterWidgets = sortDateWidgets(context,
|
|
||||||
dateWidgets: getFilterWidgets(MessageType.values[activeData]),
|
|
||||||
hasShadow: true);
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 12.0),
|
|
||||||
child: RefreshIndicator(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
onRefresh: () {
|
|
||||||
return Future.wait([
|
|
||||||
messageProvider.fetch(type: MessageType.inbox),
|
|
||||||
messageProvider.fetch(type: MessageType.sent),
|
|
||||||
messageProvider.fetch(type: MessageType.trash),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
child: ListView.builder(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
itemBuilder: (context, index) => filterWidgets.isNotEmpty
|
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 24.0, vertical: 6.0),
|
|
||||||
child: filterWidgets[index],
|
|
||||||
)
|
|
||||||
: Empty(subtitle: "empty".i18n),
|
|
||||||
itemCount: max(filterWidgets.length, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension Localization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"Messages": "Messages",
|
|
||||||
"Inbox": "Inbox",
|
|
||||||
"Sent": "Sent",
|
|
||||||
"Trash": "Trash",
|
|
||||||
"Draft": "Draft",
|
|
||||||
"empty": "You have no messages.",
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"Messages": "Üzenetek",
|
|
||||||
"Inbox": "Beérkezett",
|
|
||||||
"Sent": "Elküldött",
|
|
||||||
"Trash": "Kuka",
|
|
||||||
"Draft": "Piszkozat",
|
|
||||||
"empty": "Nincsenek üzeneteid.",
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"Messages": "Nachrichten",
|
|
||||||
"Inbox": "Posteingang",
|
|
||||||
"Sent": "Gesendet",
|
|
||||||
"Trash": "Müll",
|
|
||||||
"Draft": "Entwurf",
|
|
||||||
"empty": "Sie haben keine Nachrichten.",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,378 +0,0 @@
|
|||||||
import 'dart:math';
|
|
||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:refilc/api/providers/update_provider.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:refilc_kreta_api/client/client.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/week.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
|
||||||
import 'package:refilc/api/providers/user_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/lesson.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/empty.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
|
||||||
import 'package:refilc_kreta_api/controllers/timetable_controller.dart';
|
|
||||||
import 'package:refilc_desktop_ui/common/widgets/lesson/lesson_viewable.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:refilc/utils/format.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
import 'timetable_page.i18n.dart';
|
|
||||||
|
|
||||||
// todo: "fix" overflow (priority: -1)
|
|
||||||
|
|
||||||
class TimetablePage extends StatefulWidget {
|
|
||||||
const TimetablePage({super.key, this.initialDay, this.initialWeek});
|
|
||||||
|
|
||||||
final DateTime? initialDay;
|
|
||||||
final Week? initialWeek;
|
|
||||||
|
|
||||||
static void jump(BuildContext context,
|
|
||||||
{Week? week, DateTime? day, Lesson? lesson}) {
|
|
||||||
// Go to timetable page with arguments
|
|
||||||
// NavigationScreen.of(context)?.customRoute(navigationPageRoute((context) => TimetablePage(
|
|
||||||
// initialDay: lesson?.date ?? day,
|
|
||||||
// initialWeek: lesson?.date != null
|
|
||||||
// ? Week.fromDate(lesson!.date)
|
|
||||||
// : day != null
|
|
||||||
// ? Week.fromDate(day)
|
|
||||||
// : week,
|
|
||||||
// )));
|
|
||||||
|
|
||||||
// NavigationScreen.of(context)?.setPage("timetable");
|
|
||||||
|
|
||||||
// Show initial Lesson
|
|
||||||
// if (lesson != null) LessonView.show(lesson, context: context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TimetablePageState createState() => TimetablePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class TimetablePageState extends State<TimetablePage>
|
|
||||||
with TickerProviderStateMixin {
|
|
||||||
late UserProvider user;
|
|
||||||
late TimetableProvider timetableProvider;
|
|
||||||
late UpdateProvider updateProvider;
|
|
||||||
late String firstName;
|
|
||||||
late TimetableController _controller;
|
|
||||||
late TabController _tabController;
|
|
||||||
late Widget empty;
|
|
||||||
|
|
||||||
int _getDayIndex(DateTime date) {
|
|
||||||
int index = 0;
|
|
||||||
if (_controller.days == null || (_controller.days?.isEmpty ?? true)) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the first day with upcoming lessons
|
|
||||||
index = _controller.days!.indexWhere((day) => day.last.end.isAfter(date));
|
|
||||||
if (index == -1) index = 0; // fallback
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update timetable on user change
|
|
||||||
Future<void> _userListener() async {
|
|
||||||
await Provider.of<KretaClient>(context, listen: false).refreshLogin();
|
|
||||||
if (mounted) _controller.jump(_controller.currentWeek, context: context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
// Initalize controllers
|
|
||||||
_controller = TimetableController();
|
|
||||||
_tabController = TabController(length: 0, vsync: this, initialIndex: 0);
|
|
||||||
|
|
||||||
empty = Empty(subtitle: "empty".i18n);
|
|
||||||
|
|
||||||
bool initial = true;
|
|
||||||
|
|
||||||
// Only update the TabController on week changes
|
|
||||||
_controller.addListener(() {
|
|
||||||
if (_controller.days == null) return;
|
|
||||||
setState(() {
|
|
||||||
_tabController = TabController(
|
|
||||||
length: _controller.days!.length,
|
|
||||||
vsync: this,
|
|
||||||
initialIndex:
|
|
||||||
min(_tabController.index, max(_controller.days!.length - 1, 0)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (initial ||
|
|
||||||
_controller.previousWeekId != _controller.currentWeekId) {
|
|
||||||
_tabController
|
|
||||||
.animateTo(_getDayIndex(widget.initialDay ?? DateTime.now()));
|
|
||||||
}
|
|
||||||
initial = false;
|
|
||||||
|
|
||||||
// Empty is updated once every week change
|
|
||||||
empty = Empty(subtitle: "empty".i18n);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
if (widget.initialWeek != null) {
|
|
||||||
_controller.jump(widget.initialWeek!, context: context, initial: true);
|
|
||||||
} else {
|
|
||||||
_controller.jump(_controller.currentWeek,
|
|
||||||
context: context, initial: true, skip: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Listen for user changes
|
|
||||||
user = Provider.of<UserProvider>(context, listen: false);
|
|
||||||
user.addListener(_userListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_tabController.dispose();
|
|
||||||
_controller.dispose();
|
|
||||||
user.removeListener(_userListener);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
String dayTitle(int index) {
|
|
||||||
// Sometimes when changing weeks really fast,
|
|
||||||
// controller.days might be null or won't include index
|
|
||||||
try {
|
|
||||||
return DateFormat("EEEE", I18n.of(context).locale.languageCode)
|
|
||||||
.format(_controller.days![index].first.date);
|
|
||||||
} catch (e) {
|
|
||||||
return "timetable".i18n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
user = Provider.of<UserProvider>(context);
|
|
||||||
timetableProvider = Provider.of<TimetableProvider>(context);
|
|
||||||
updateProvider = Provider.of<UpdateProvider>(context);
|
|
||||||
|
|
||||||
// First name
|
|
||||||
List<String> nameParts = user.name?.split(" ") ?? ["?"];
|
|
||||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 18.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: PageTransitionSwitcher(
|
|
||||||
transitionBuilder: (
|
|
||||||
Widget child,
|
|
||||||
Animation<double> primaryAnimation,
|
|
||||||
Animation<double> secondaryAnimation,
|
|
||||||
) {
|
|
||||||
return FadeThroughTransition(
|
|
||||||
animation: primaryAnimation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: _controller.days != null
|
|
||||||
?
|
|
||||||
// Week view
|
|
||||||
_tabController.length > 0
|
|
||||||
? CupertinoScrollbar(
|
|
||||||
child: ListView.builder(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: _controller.days!.length,
|
|
||||||
itemBuilder: (context, tab) => SizedBox(
|
|
||||||
width: 400,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Day Title
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 24.0,
|
|
||||||
right: 28.0,
|
|
||||||
top: 18.0,
|
|
||||||
bottom: 8.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
dayTitle(tab).capital(),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 32.0,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"${"${_controller.days![tab].first.date.day}"
|
|
||||||
.padLeft(2, '0')}.",
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(.5),
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Lessons
|
|
||||||
Expanded(
|
|
||||||
child: ListView.builder(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
itemCount:
|
|
||||||
_controller.days![tab].length + 2,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (_controller.days == null) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header
|
|
||||||
if (index == 0) {
|
|
||||||
return const Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 8.0,
|
|
||||||
left: 24.0,
|
|
||||||
right: 24.0),
|
|
||||||
child: PanelHeader(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 12.0)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Footer
|
|
||||||
if (index ==
|
|
||||||
_controller.days![tab].length +
|
|
||||||
1) {
|
|
||||||
return const Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: 8.0,
|
|
||||||
left: 24.0,
|
|
||||||
right: 24.0),
|
|
||||||
child: PanelFooter(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 12.0)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Body
|
|
||||||
final Lesson lesson =
|
|
||||||
_controller.days![tab][index - 1];
|
|
||||||
final bool swapDescDay = _controller
|
|
||||||
.days![tab]
|
|
||||||
.map(
|
|
||||||
(l) => l.swapDesc ? 1 : 0)
|
|
||||||
.reduce((a, b) => a + b) >=
|
|
||||||
_controller.days![tab].length *
|
|
||||||
.5;
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 24.0),
|
|
||||||
child: PanelBody(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10.0),
|
|
||||||
child: LessonViewable(
|
|
||||||
lesson,
|
|
||||||
swapDesc: swapDescDay,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Empty week
|
|
||||||
: Expanded(
|
|
||||||
child: Center(child: empty),
|
|
||||||
)
|
|
||||||
: Center(
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
// Previous week
|
|
||||||
IconButton(
|
|
||||||
onPressed: _controller.currentWeekId == 0
|
|
||||||
? null
|
|
||||||
: () => setState(() {
|
|
||||||
_controller.previous(context);
|
|
||||||
}),
|
|
||||||
splashRadius: 24.0,
|
|
||||||
icon: const Icon(FeatherIcons.chevronLeft),
|
|
||||||
color: Theme.of(context).colorScheme.secondary),
|
|
||||||
|
|
||||||
// Week selector
|
|
||||||
InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(6.0),
|
|
||||||
onTap: () => setState(() {
|
|
||||||
_controller.current();
|
|
||||||
if (mounted) {
|
|
||||||
_controller.jump(_controller.currentWeek,
|
|
||||||
context: context,
|
|
||||||
loader: _controller.currentWeekId !=
|
|
||||||
_controller.previousWeekId);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"${_controller.currentWeekId + 1}. ${"week".i18n} (${DateFormat(
|
|
||||||
"${_controller.currentWeek.start.year !=
|
|
||||||
DateTime.now().year
|
|
||||||
? "yy. "
|
|
||||||
: ""}MMM d.",
|
|
||||||
I18n.of(context).locale.languageCode)
|
|
||||||
.format(_controller.currentWeek.start)} - ${DateFormat(
|
|
||||||
"${_controller.currentWeek.start.year !=
|
|
||||||
DateTime.now().year
|
|
||||||
? "yy. "
|
|
||||||
: ""}MMM d.",
|
|
||||||
I18n.of(context).locale.languageCode)
|
|
||||||
.format(_controller.currentWeek.end)})",
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 14.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Next week
|
|
||||||
IconButton(
|
|
||||||
onPressed: _controller.currentWeekId == 51
|
|
||||||
? null
|
|
||||||
: () => setState(() {
|
|
||||||
_controller.next(context);
|
|
||||||
}),
|
|
||||||
splashRadius: 24.0,
|
|
||||||
icon: const Icon(FeatherIcons.chevronRight),
|
|
||||||
color: Theme.of(context).colorScheme.secondary),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension Localization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"timetable": "Timetable",
|
|
||||||
"empty": "No school this week!",
|
|
||||||
"week": "Week",
|
|
||||||
"error": "Failed to fetch timetable!",
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"timetable": "Órarend",
|
|
||||||
"empty": "Ezen a héten nincs iskola.",
|
|
||||||
"week": "Hét",
|
|
||||||
"error": "Nem sikerült lekérni az órarendet!",
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"timetable": "Zeitplan",
|
|
||||||
"empty": "Keine Schule diese Woche.",
|
|
||||||
"week": "Woche",
|
|
||||||
"error": "Der Fahrplan konnte nicht abgerufen werden!",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
Route loginRoute(Widget widget) {
|
|
||||||
return PageRouteBuilder(
|
|
||||||
pageBuilder: (context, animation, secondaryAnimation) => widget,
|
|
||||||
transitionDuration: const Duration(milliseconds: 650),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeThroughTransition(
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
animation: animation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,358 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:elegant_notification/elegant_notification.dart';
|
|
||||||
import 'package:elegant_notification/resources/arrays.dart';
|
|
||||||
import 'package:refilc/api/client.dart';
|
|
||||||
import 'package:refilc/api/login.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/login/login_button.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/login/login_input.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/login/school_input/school_input.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
|
||||||
import 'login_screen.i18n.dart';
|
|
||||||
|
|
||||||
const LinearGradient _backgroundGradient = LinearGradient(
|
|
||||||
colors: [
|
|
||||||
Color.fromARGB(255, 61, 122, 244),
|
|
||||||
Color.fromARGB(255, 23, 77, 185),
|
|
||||||
Color.fromARGB(255, 7, 42, 112),
|
|
||||||
],
|
|
||||||
begin: Alignment(-0.8, -2.0),
|
|
||||||
end: Alignment(0.8, 1.0),
|
|
||||||
stops: [-1.0, 0.0, 1.0],
|
|
||||||
);
|
|
||||||
|
|
||||||
class LoginScreen extends StatefulWidget {
|
|
||||||
const LoginScreen({super.key, this.back = false});
|
|
||||||
|
|
||||||
final bool back;
|
|
||||||
|
|
||||||
@override
|
|
||||||
LoginScreenState createState() => LoginScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginScreenState extends State<LoginScreen> {
|
|
||||||
final usernameController = TextEditingController();
|
|
||||||
final passwordController = TextEditingController();
|
|
||||||
final schoolController = SchoolInputController();
|
|
||||||
final _scrollController = ScrollController();
|
|
||||||
|
|
||||||
LoginState _loginState = LoginState.normal;
|
|
||||||
bool showBack = false;
|
|
||||||
double topInset = 12.0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
showBack = widget.back;
|
|
||||||
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
|
||||||
statusBarColor: Colors.transparent,
|
|
||||||
statusBarIconBrightness: Brightness.light,
|
|
||||||
systemNavigationBarColor: Colors.white,
|
|
||||||
systemNavigationBarIconBrightness: Brightness.dark,
|
|
||||||
));
|
|
||||||
|
|
||||||
FilcAPI.getSchools().then((schools) {
|
|
||||||
if (schools != null) {
|
|
||||||
schoolController.update(() {
|
|
||||||
schoolController.schools = schools;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ElegantNotification.error(
|
|
||||||
background: Colors.white,
|
|
||||||
description: Text(
|
|
||||||
"schools_error".i18n,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: const TextStyle(color: Colors.red),
|
|
||||||
),
|
|
||||||
// onActionPressed: () {},
|
|
||||||
onCloseButtonPressed: () {},
|
|
||||||
onDismiss: () {},
|
|
||||||
onProgressFinished: () {},
|
|
||||||
displayCloseButton: false,
|
|
||||||
showProgressIndicator: false,
|
|
||||||
autoDismiss: true,
|
|
||||||
animation: AnimationType.fromTop,
|
|
||||||
).show(context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Platform.isMacOS) {
|
|
||||||
Window.getTitlebarHeight()
|
|
||||||
.then((value) => setState(() => topInset = value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
body: Container(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: _backgroundGradient,
|
|
||||||
),
|
|
||||||
child: SafeArea(
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
// App logo
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 24.0),
|
|
||||||
child: ClipRect(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 12.0, right: 12.0, bottom: 32.0),
|
|
||||||
child: SizedBox(
|
|
||||||
width: 100.0,
|
|
||||||
height: 100.0,
|
|
||||||
// Png shadow *hack*
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Opacity(
|
|
||||||
opacity: 0.3,
|
|
||||||
child: Image.asset(
|
|
||||||
"assets/icons/ic_splash.png",
|
|
||||||
color: Colors.black)),
|
|
||||||
),
|
|
||||||
BackdropFilter(
|
|
||||||
filter: ImageFilter.blur(
|
|
||||||
sigmaX: 6.0, sigmaY: 6.0),
|
|
||||||
child: Image.asset(
|
|
||||||
"assets/icons/ic_splash.png"),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Inputs
|
|
||||||
SizedBox(
|
|
||||||
width: 400.0,
|
|
||||||
child: AutofillGroup(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Username
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 6.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"username".i18n,
|
|
||||||
maxLines: 1,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 14.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"usernameHint".i18n,
|
|
||||||
maxLines: 1,
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white54,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 12.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 12.0),
|
|
||||||
child: LoginInput(
|
|
||||||
style: LoginInputStyle.username,
|
|
||||||
controller: usernameController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Password
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 6.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"password".i18n,
|
|
||||||
maxLines: 1,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 14.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"passwordHint".i18n,
|
|
||||||
maxLines: 1,
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white54,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 12.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 12.0),
|
|
||||||
child: LoginInput(
|
|
||||||
style: LoginInputStyle.password,
|
|
||||||
controller: passwordController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// School
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 6.0),
|
|
||||||
child: Text(
|
|
||||||
"school".i18n,
|
|
||||||
maxLines: 1,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 14.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SchoolInput(
|
|
||||||
scroll: _scrollController,
|
|
||||||
controller: schoolController,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Log in button
|
|
||||||
SizedBox(
|
|
||||||
width: 400.0,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 42.0),
|
|
||||||
child: Visibility(
|
|
||||||
visible: _loginState != LoginState.inProgress,
|
|
||||||
replacement: const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 6.0),
|
|
||||||
child: Center(
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
|
||||||
Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: LoginButton(
|
|
||||||
child: Text("login".i18n,
|
|
||||||
maxLines: 1,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 15.0,
|
|
||||||
)),
|
|
||||||
onPressed: () => _loginAPI(context: context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_loginState == LoginState.missingFields ||
|
|
||||||
_loginState == LoginState.invalidGrant ||
|
|
||||||
_loginState == LoginState.failed)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Text(
|
|
||||||
[
|
|
||||||
"missing_fields",
|
|
||||||
"invalid_grant",
|
|
||||||
"error"
|
|
||||||
][_loginState.index]
|
|
||||||
.i18n,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.red,
|
|
||||||
fontWeight: FontWeight.w500),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (showBack)
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.topLeft,
|
|
||||||
padding: EdgeInsets.only(left: 16.0, top: topInset),
|
|
||||||
child: const ClipOval(
|
|
||||||
child: Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: BackButton(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _loginAPI({required BuildContext context}) {
|
|
||||||
String username = usernameController.text;
|
|
||||||
String password = passwordController.text;
|
|
||||||
|
|
||||||
if (username == "" ||
|
|
||||||
password == "" ||
|
|
||||||
schoolController.selectedSchool == null) {
|
|
||||||
return setState(() => _loginState = LoginState.missingFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() => _loginState = LoginState.inProgress);
|
|
||||||
|
|
||||||
loginAPI(
|
|
||||||
username: username,
|
|
||||||
password: password,
|
|
||||||
instituteCode: schoolController.selectedSchool!.instituteCode,
|
|
||||||
context: context,
|
|
||||||
onLogin: (user) {
|
|
||||||
ElegantNotification.success(
|
|
||||||
background: Colors.white,
|
|
||||||
description: Text(
|
|
||||||
"welcome".i18n.fill([user.name]),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: const TextStyle(color: Colors.black),
|
|
||||||
),
|
|
||||||
// onActionPressed: () {},
|
|
||||||
onCloseButtonPressed: () {},
|
|
||||||
onDismiss: () {},
|
|
||||||
onProgressFinished: () {},
|
|
||||||
displayCloseButton: false,
|
|
||||||
showProgressIndicator: false,
|
|
||||||
autoDismiss: true,
|
|
||||||
animation: AnimationType.fromTop,
|
|
||||||
).show(context);
|
|
||||||
},
|
|
||||||
onSuccess: () {
|
|
||||||
Navigator.of(context)
|
|
||||||
.pushNamedAndRemoveUntil("login_to_navigation", (_) => false);
|
|
||||||
}).then((res) => setState(() => _loginState = res));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension Localization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"username": "Username",
|
|
||||||
"usernameHint": "Student ID number",
|
|
||||||
"password": "Password",
|
|
||||||
"passwordHint": "Date of birth",
|
|
||||||
"school": "School",
|
|
||||||
"login": "Log in",
|
|
||||||
"welcome": "Welcome, %s!",
|
|
||||||
"missing_fields": "Missing Fields!",
|
|
||||||
"invalid_grant": "Invalid Username/Password!",
|
|
||||||
"error": "Failed to log in.",
|
|
||||||
"schools_error": "Failed to get schools."
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"username": "Felhasználónév",
|
|
||||||
"usernameHint": "Oktatási azonosító",
|
|
||||||
"password": "Jelszó",
|
|
||||||
"passwordHint": "Születési dátum",
|
|
||||||
"school": "Iskola",
|
|
||||||
"login": "Belépés",
|
|
||||||
"welcome": "Üdv, %s!",
|
|
||||||
"missing_fields": "Hiányzó adatok!",
|
|
||||||
"invalid_grant": "Helytelen Felhasználónév/Jelszó!",
|
|
||||||
"error": "Sikertelen bejelentkezés.",
|
|
||||||
"schools_error": "Nem sikerült lekérni az iskolákat."
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"username": "Benutzername",
|
|
||||||
"usernameHint": "Ausbildung ID",
|
|
||||||
"password": "Passwort",
|
|
||||||
"passwordHint": "Geburtsdatum",
|
|
||||||
"school": "Schule",
|
|
||||||
"login": "Einloggen",
|
|
||||||
"welcome": "Wilkommen, %s!",
|
|
||||||
"missing_fields": "Fehlende Felder!",
|
|
||||||
"invalid_grant": "Ungültiger Benutzername/Passwort!",
|
|
||||||
"error": "Anmeldung fehlgeschlagen.",
|
|
||||||
"schools_error": "Keine Schulen gefunden."
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
import 'package:refilc_mobile_ui/screens/login/login_input.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/login/school_input/school_input_overlay.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/login/school_input/school_input_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/login/school_input/school_search.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:refilc_kreta_api/models/school.dart';
|
|
||||||
|
|
||||||
class SchoolInput extends StatefulWidget {
|
|
||||||
const SchoolInput({super.key, required this.controller, required this.scroll});
|
|
||||||
|
|
||||||
final SchoolInputController controller;
|
|
||||||
final ScrollController scroll;
|
|
||||||
|
|
||||||
@override
|
|
||||||
SchoolInputState createState() => SchoolInputState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class SchoolInputState extends State<SchoolInput> {
|
|
||||||
final _focusNode = FocusNode();
|
|
||||||
final _layerLink = LayerLink();
|
|
||||||
late SchoolInputOverlay overlay;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
widget.controller.update = (fn) {
|
|
||||||
if (mounted) setState(fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
overlay = SchoolInputOverlay(layerLink: _layerLink);
|
|
||||||
|
|
||||||
// Show school list when focused
|
|
||||||
_focusNode.addListener(() {
|
|
||||||
if (_focusNode.hasFocus) {
|
|
||||||
WidgetsBinding.instance
|
|
||||||
.addPostFrameCallback((_) => overlay.createOverlayEntry(context));
|
|
||||||
Future.delayed(const Duration(milliseconds: 100)).then((value) {
|
|
||||||
if (mounted && widget.scroll.hasClients) {
|
|
||||||
widget.scroll.animateTo(widget.scroll.offset + 500,
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
curve: Curves.ease);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
overlay.entry?.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// LoginInput TextField listener
|
|
||||||
widget.controller.textController.addListener(() {
|
|
||||||
String text = widget.controller.textController.text;
|
|
||||||
if (text.isEmpty) {
|
|
||||||
overlay.children = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<School> results =
|
|
||||||
searchSchools(widget.controller.schools ?? [], text);
|
|
||||||
setState(() {
|
|
||||||
overlay.children = results
|
|
||||||
.map((School e) => SchoolInputTile(
|
|
||||||
school: e,
|
|
||||||
onTap: () => _selectSchool(e),
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
});
|
|
||||||
Overlay.of(context).setState(() {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _selectSchool(School school) {
|
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
widget.controller.selectedSchool = school;
|
|
||||||
widget.controller.textController.text = school.name;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return CompositedTransformTarget(
|
|
||||||
link: _layerLink,
|
|
||||||
child: widget.controller.schools == null
|
|
||||||
? Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.black.withOpacity(0.15),
|
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
|
||||||
),
|
|
||||||
child: const Center(
|
|
||||||
child: SizedBox(
|
|
||||||
height: 28.0,
|
|
||||||
width: 28.0,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: LoginInput(
|
|
||||||
style: LoginInputStyle.school,
|
|
||||||
focusNode: _focusNode,
|
|
||||||
onClear: () {
|
|
||||||
widget.controller.selectedSchool = null;
|
|
||||||
FocusScope.of(context).requestFocus(_focusNode);
|
|
||||||
},
|
|
||||||
controller: widget.controller.textController,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SchoolInputController {
|
|
||||||
final textController = TextEditingController();
|
|
||||||
School? selectedSchool;
|
|
||||||
List<School>? schools;
|
|
||||||
late void Function(void Function()) update;
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
import 'package:refilc_kreta_api/models/school.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class SchoolInputTile extends StatelessWidget {
|
|
||||||
const SchoolInputTile({super.key, required this.school, this.onTap});
|
|
||||||
|
|
||||||
final School school;
|
|
||||||
final Function()? onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(4.0),
|
|
||||||
child: GestureDetector(
|
|
||||||
onPanDown: (e) {
|
|
||||||
onTap!();
|
|
||||||
},
|
|
||||||
child: InkWell(
|
|
||||||
onTapDown: (e) {},
|
|
||||||
borderRadius: BorderRadius.circular(6.0),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(6.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// School name
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 4.0),
|
|
||||||
child: Text(
|
|
||||||
school.name,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
// School id
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
school.instituteCode,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// School city
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
school.city,
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
class NavigationRoute {
|
|
||||||
late String _name;
|
|
||||||
late int _index;
|
|
||||||
|
|
||||||
final List<String> _internalPageMap = [
|
|
||||||
"home",
|
|
||||||
"grades",
|
|
||||||
"timetable",
|
|
||||||
"messages",
|
|
||||||
"absences",
|
|
||||||
];
|
|
||||||
|
|
||||||
String get name => _name;
|
|
||||||
int get index => _index;
|
|
||||||
|
|
||||||
set name(String n) {
|
|
||||||
_name = n;
|
|
||||||
_index = _internalPageMap.indexOf(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
set index(int i) {
|
|
||||||
_index = i;
|
|
||||||
_name = _internalPageMap.elementAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/absences/absences_page.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/grades/grades_page.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/home/home_page.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/messages/messages_page.dart';
|
|
||||||
import 'package:refilc_desktop_ui/pages/timetable/timetable_page.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
Route navigationRouteHandler(RouteSettings settings) {
|
|
||||||
switch (settings.name) {
|
|
||||||
case "grades":
|
|
||||||
return navigationPageRoute((context) => const GradesPage());
|
|
||||||
case "timetable":
|
|
||||||
return navigationPageRoute((context) => const TimetablePage());
|
|
||||||
case "messages":
|
|
||||||
return navigationPageRoute((context) => const MessagesPage());
|
|
||||||
case "absences":
|
|
||||||
return navigationPageRoute((context) => const AbsencesPage());
|
|
||||||
case "home":
|
|
||||||
default:
|
|
||||||
return navigationPageRoute((context) => const HomePage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Route navigationPageRoute(Widget Function(BuildContext) builder) {
|
|
||||||
return PageRouteBuilder(
|
|
||||||
pageBuilder: (context, _, __) => builder(context),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeThroughTransition(
|
|
||||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
animation: animation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:refilc/api/providers/news_provider.dart';
|
|
||||||
import 'package:refilc/api/providers/sync.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:refilc/theme/observer.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/navigation/navigation_route.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/navigation/navigation_route_handler.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/navigation/sidebar.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:refilc/models/settings.dart';
|
|
||||||
import 'package:refilc_kreta_api/client/client.dart';
|
|
||||||
import 'package:refilc_plus/providers/goal_provider.dart';
|
|
||||||
|
|
||||||
class NavigationScreen extends StatefulWidget {
|
|
||||||
const NavigationScreen({super.key});
|
|
||||||
|
|
||||||
static NavigationScreenState? of(BuildContext context) =>
|
|
||||||
context.findAncestorStateOfType<NavigationScreenState>();
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<NavigationScreen> createState() => NavigationScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class NavigationScreenState extends State<NavigationScreen>
|
|
||||||
with WidgetsBindingObserver {
|
|
||||||
final _navigatorState = GlobalKey<NavigatorState>();
|
|
||||||
late NavigationRoute selected;
|
|
||||||
late SettingsProvider settings;
|
|
||||||
late NewsProvider newsProvider;
|
|
||||||
late GoalProvider goalProvider;
|
|
||||||
double topInset = 0.0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
settings = Provider.of<SettingsProvider>(context, listen: false);
|
|
||||||
selected = NavigationRoute();
|
|
||||||
selected.index = 0;
|
|
||||||
|
|
||||||
// add brightness observer
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
|
|
||||||
// set client User-Agent
|
|
||||||
Provider.of<KretaClient>(context, listen: false).userAgent =
|
|
||||||
settings.config.userAgent;
|
|
||||||
|
|
||||||
// get news
|
|
||||||
newsProvider = Provider.of<NewsProvider>(context, listen: false);
|
|
||||||
newsProvider.restore().then((value) => newsProvider.fetch());
|
|
||||||
|
|
||||||
// get goals
|
|
||||||
// goalProvider = Provider.of<GoalProvider>(context, listen: false);
|
|
||||||
// goalProvider.fetchDone();
|
|
||||||
|
|
||||||
// Initial sync
|
|
||||||
syncAll(context);
|
|
||||||
|
|
||||||
// platform specific shit
|
|
||||||
() async {
|
|
||||||
try {
|
|
||||||
await Window.initialize();
|
|
||||||
} catch (_) {}
|
|
||||||
// Transparent sidebar
|
|
||||||
if (Platform.isLinux) return;
|
|
||||||
|
|
||||||
await Window.setEffect(
|
|
||||||
effect: Platform.isLinux
|
|
||||||
? WindowEffect.transparent
|
|
||||||
: WindowEffect.acrylic,
|
|
||||||
color: Platform.isMacOS
|
|
||||||
? Colors.transparent
|
|
||||||
: const Color.fromARGB(27, 27, 27, 27));
|
|
||||||
|
|
||||||
// todo: do for windows
|
|
||||||
if (Platform.isMacOS) {
|
|
||||||
topInset = await Window.getTitlebarHeight();
|
|
||||||
await Window.enableFullSizeContentView();
|
|
||||||
await Window.hideTitle();
|
|
||||||
await Window.makeTitlebarTransparent();
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangePlatformBrightness() {
|
|
||||||
if (settings.theme == ThemeMode.system) {
|
|
||||||
// ignore: deprecated_member_use
|
|
||||||
Brightness? brightness =
|
|
||||||
// ignore: deprecated_member_use
|
|
||||||
WidgetsBinding.instance.window.platformBrightness;
|
|
||||||
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(
|
|
||||||
brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark);
|
|
||||||
}
|
|
||||||
super.didChangePlatformBrightness();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPage(String page) => setState(() => selected.name = page);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
settings = Provider.of<SettingsProvider>(context);
|
|
||||||
newsProvider = Provider.of<NewsProvider>(context);
|
|
||||||
goalProvider = Provider.of<GoalProvider>(context);
|
|
||||||
|
|
||||||
// show news / complete goals
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (newsProvider.show) {
|
|
||||||
newsProvider.lock();
|
|
||||||
// NewsView.show(newsProvider.news[newsProvider.state], context: context).then((value) => newsProvider.release());
|
|
||||||
}
|
|
||||||
if (goalProvider.hasDoneGoals) {
|
|
||||||
// to-do
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
body: Row(
|
|
||||||
children: [
|
|
||||||
if (_navigatorState.currentState != null)
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
Theme.of(context).scaffoldBackgroundColor.withOpacity(.5),
|
|
||||||
border: Border(
|
|
||||||
right: BorderSide(
|
|
||||||
color: AppColors.of(context).shadow.withOpacity(.7),
|
|
||||||
width: 1.0)),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.only(top: topInset),
|
|
||||||
child: Sidebar(
|
|
||||||
navigator: _navigatorState.currentState!,
|
|
||||||
selected: selected.name,
|
|
||||||
onRouteChange: (name) => setPage(name),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
child: MediaQuery(
|
|
||||||
data: MediaQuery.of(context).copyWith(
|
|
||||||
padding: EdgeInsets.only(top: topInset),
|
|
||||||
),
|
|
||||||
child: Navigator(
|
|
||||||
key: _navigatorState,
|
|
||||||
initialRoute: selected.name,
|
|
||||||
onGenerateRoute: (settings) =>
|
|
||||||
navigationRouteHandler(settings),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,356 +0,0 @@
|
|||||||
// ignore_for_file: no_leading_underscores_for_local_identifiers
|
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:refilc/api/providers/database_provider.dart';
|
|
||||||
import 'package:refilc/api/providers/user_provider.dart';
|
|
||||||
import 'package:refilc/icons/filc_icons.dart';
|
|
||||||
import 'package:refilc/models/settings.dart';
|
|
||||||
import 'package:refilc/utils/color.dart';
|
|
||||||
import 'package:refilc_desktop_ui/common/panel_button.dart';
|
|
||||||
import 'package:refilc_desktop_ui/common/profile_image.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/navigation/sidebar.i18n.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/navigation/sidebar_action.dart';
|
|
||||||
import 'package:refilc_desktop_ui/screens/settings/settings_screen.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/settings/accounts/account_tile.dart';
|
|
||||||
import 'package:refilc_kreta_api/client/client.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/absence_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/event_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/exam_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/homework_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/message_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/note_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
|
|
||||||
class Sidebar extends StatefulWidget {
|
|
||||||
const Sidebar(
|
|
||||||
{super.key,
|
|
||||||
required this.navigator,
|
|
||||||
required this.onRouteChange,
|
|
||||||
this.selected = "home"});
|
|
||||||
|
|
||||||
final NavigatorState navigator;
|
|
||||||
final String selected;
|
|
||||||
final Function(String) onRouteChange;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<Sidebar> createState() => _SidebarState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SidebarState extends State<Sidebar> {
|
|
||||||
late UserProvider user;
|
|
||||||
late SettingsProvider settings;
|
|
||||||
late String firstName;
|
|
||||||
|
|
||||||
String topNav = "";
|
|
||||||
bool expandAccount = false;
|
|
||||||
List<Widget> accountTiles = [];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
user = Provider.of<UserProvider>(context, listen: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> restore() => Future.wait([
|
|
||||||
Provider.of<GradeProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<TimetableProvider>(context, listen: false).restoreUser(),
|
|
||||||
Provider.of<ExamProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<HomeworkProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<MessageProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<MessageProvider>(context, listen: false)
|
|
||||||
.restoreRecipients(),
|
|
||||||
Provider.of<NoteProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<EventProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<AbsenceProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<KretaClient>(context, listen: false).refreshLogin(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
user = Provider.of<UserProvider>(context);
|
|
||||||
settings = Provider.of<SettingsProvider>(context);
|
|
||||||
|
|
||||||
List<String> nameParts = user.name?.split(" ") ?? ["?"];
|
|
||||||
if (!settings.presentationMode) {
|
|
||||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
|
||||||
} else {
|
|
||||||
firstName = "János";
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> pageWidgets = [
|
|
||||||
SidebarAction(
|
|
||||||
title: Text("Home".i18n),
|
|
||||||
icon: const Icon(FilcIcons.home),
|
|
||||||
selected: widget.selected == "home",
|
|
||||||
onTap: () {
|
|
||||||
if (widget.selected != "home") {
|
|
||||||
widget.navigator.pushReplacementNamed("home");
|
|
||||||
widget.onRouteChange("home");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SidebarAction(
|
|
||||||
title: Text("Grades".i18n),
|
|
||||||
icon: const Icon(FeatherIcons.bookmark),
|
|
||||||
selected: widget.selected == "grades",
|
|
||||||
onTap: () {
|
|
||||||
if (widget.selected != "grades") {
|
|
||||||
widget.navigator.pushReplacementNamed("grades");
|
|
||||||
widget.onRouteChange("grades");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SidebarAction(
|
|
||||||
title: Text("Timetable".i18n),
|
|
||||||
icon: const Icon(FeatherIcons.calendar),
|
|
||||||
selected: widget.selected == "timetable",
|
|
||||||
onTap: () {
|
|
||||||
if (widget.selected != "timetable") {
|
|
||||||
widget.navigator.pushReplacementNamed("timetable");
|
|
||||||
widget.onRouteChange("timetable");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SidebarAction(
|
|
||||||
title: Text("Messages".i18n),
|
|
||||||
icon: const Icon(FeatherIcons.messageSquare),
|
|
||||||
selected: widget.selected == "messages",
|
|
||||||
onTap: () {
|
|
||||||
if (widget.selected != "messages") {
|
|
||||||
widget.navigator.pushReplacementNamed("messages");
|
|
||||||
widget.onRouteChange("messages");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SidebarAction(
|
|
||||||
title: Text("Absences".i18n),
|
|
||||||
icon: const Icon(FeatherIcons.clock),
|
|
||||||
selected: widget.selected == "absences",
|
|
||||||
onTap: () {
|
|
||||||
if (widget.selected != "absences") {
|
|
||||||
widget.navigator.pushReplacementNamed("absences");
|
|
||||||
widget.onRouteChange("absences");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
List<Widget> bottomActions = [
|
|
||||||
SidebarAction(
|
|
||||||
title: Text("Settings".i18n),
|
|
||||||
selected: true,
|
|
||||||
icon: const Icon(FeatherIcons.settings),
|
|
||||||
onTap: () {
|
|
||||||
if (topNav != "settings") {
|
|
||||||
widget.navigator
|
|
||||||
.push(CupertinoPageRoute(
|
|
||||||
builder: (context) => const SettingsScreen()))
|
|
||||||
.then((value) => topNav = "");
|
|
||||||
topNav = "settings";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
buildAccountTiles();
|
|
||||||
|
|
||||||
List<Widget> accountWidgets = [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
|
||||||
child: Column(children: accountTiles),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Account settings
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pushNamed("login_back");
|
|
||||||
},
|
|
||||||
title: Text("adduser".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.userPlus),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () async {
|
|
||||||
String? userId = user.id;
|
|
||||||
if (userId == null) return;
|
|
||||||
|
|
||||||
// revoke refresh token
|
|
||||||
await Provider.of<KretaClient>(context, listen: false).logout();
|
|
||||||
|
|
||||||
// delete user from app
|
|
||||||
user.removeUser(userId);
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
await Provider.of<DatabaseProvider>(context, listen: false)
|
|
||||||
.store
|
|
||||||
.removeUser(userId);
|
|
||||||
|
|
||||||
// if no other users left, go back to login screen
|
|
||||||
if (user.getUsers().isNotEmpty) {
|
|
||||||
user.setUser(user.getUsers().first.id);
|
|
||||||
restore().then((_) => user.setUser(user.getUsers().first.id));
|
|
||||||
} else {
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
Navigator.of(context)
|
|
||||||
.pushNamedAndRemoveUntil("login", (_) => false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: Text("logout".i18n),
|
|
||||||
leading: Icon(FeatherIcons.logOut, color: AppColors.of(context).red),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
return SizedBox(
|
|
||||||
height: double.infinity,
|
|
||||||
width: 250.0,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 12.0,
|
|
||||||
top: 18.0,
|
|
||||||
bottom: 24.0,
|
|
||||||
right: 12.0,
|
|
||||||
),
|
|
||||||
child: InkWell(
|
|
||||||
customBorder: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10)),
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
expandAccount = !expandAccount;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
right: 12.0,
|
|
||||||
left: 5.0,
|
|
||||||
top: 5.0,
|
|
||||||
bottom: 5.0,
|
|
||||||
),
|
|
||||||
child: ProfileImage(
|
|
||||||
name: firstName,
|
|
||||||
radius: 18.0,
|
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary, //!settings.presentationMode
|
|
||||||
// ? ColorUtils.stringToColor(user.name ?? "?")
|
|
||||||
// : Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
firstName,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16.0,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PageTransitionSwitcher(
|
|
||||||
transitionBuilder:
|
|
||||||
(child, primaryAnimation, secondaryAnimation) {
|
|
||||||
return FadeThroughTransition(
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
animation: primaryAnimation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: IconButton(
|
|
||||||
key: Key(expandAccount ? "accounts" : "pages"),
|
|
||||||
icon: Icon(expandAccount
|
|
||||||
? FeatherIcons.chevronDown
|
|
||||||
: FeatherIcons.chevronRight),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
expandAccount = !expandAccount;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
splashColor: const Color(0x00000000),
|
|
||||||
focusColor: const Color(0x00000000),
|
|
||||||
hoverColor: const Color(0x00000000),
|
|
||||||
highlightColor: const Color(0x00000000),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Pages
|
|
||||||
Expanded(
|
|
||||||
child: PageTransitionSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
|
|
||||||
return SharedAxisTransition(
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
animation: primaryAnimation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
transitionType: SharedAxisTransitionType.scaled,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: !expandAccount
|
|
||||||
? Column(
|
|
||||||
key: const Key("pages"),
|
|
||||||
children: pageWidgets,
|
|
||||||
)
|
|
||||||
: Column(
|
|
||||||
key: const Key("accounts"),
|
|
||||||
children: accountWidgets,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
...bottomActions,
|
|
||||||
|
|
||||||
// Bottom padding
|
|
||||||
const SizedBox(height: 12.0),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void buildAccountTiles() {
|
|
||||||
accountTiles = [];
|
|
||||||
user.getUsers().forEach((account) {
|
|
||||||
if (account.id == user.id) return;
|
|
||||||
|
|
||||||
String _firstName;
|
|
||||||
|
|
||||||
List<String> _nameParts = user.name?.split(" ") ?? ["?"];
|
|
||||||
if (!settings.presentationMode) {
|
|
||||||
_firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0];
|
|
||||||
} else {
|
|
||||||
_firstName = "János";
|
|
||||||
}
|
|
||||||
|
|
||||||
accountTiles.add(AccountTile(
|
|
||||||
name: Text(!settings.presentationMode ? account.name : "János",
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
|
||||||
username:
|
|
||||||
Text(!settings.presentationMode ? account.username : "72469696969"),
|
|
||||||
profileImage: ProfileImage(
|
|
||||||
name: _firstName,
|
|
||||||
backgroundColor: !settings.presentationMode
|
|
||||||
? ColorUtils.stringToColor(account.name)
|
|
||||||
: Theme.of(context).colorScheme.secondary,
|
|
||||||
role: account.role,
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
user.setUser(account.id);
|
|
||||||
restore().then((_) => user.setUser(account.id));
|
|
||||||
},
|
|
||||||
// onTapMenu: () => _showBottomSheet(account),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension SettingsLocalization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"Home": "Home",
|
|
||||||
"Grades": "Grades",
|
|
||||||
"Timetable": "Timetable",
|
|
||||||
"Messages": "Messages",
|
|
||||||
"Absences": "Absences",
|
|
||||||
"Settings": "Settings",
|
|
||||||
"adduser": "Add User",
|
|
||||||
"logout": "Log Out",
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"Home": "Kezdőlap",
|
|
||||||
"Grades": "Jegyek",
|
|
||||||
"Timetable": "Órarend",
|
|
||||||
"Messages": "Üzenetek",
|
|
||||||
"Absences": "Hiányzások",
|
|
||||||
"Settings": "Beállítások",
|
|
||||||
"adduser": "Fiók hozzáadása",
|
|
||||||
"logout": "Kilépés",
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"Home": "Zuhause",
|
|
||||||
"Grades": "Noten",
|
|
||||||
"Timetable": "Zeitplan",
|
|
||||||
"Messages": "Mitteilungen",
|
|
||||||
"Absences": "Fehlen",
|
|
||||||
"Settings": "Einstellungen",
|
|
||||||
"adduser": "Benutzer hinzufügen",
|
|
||||||
"logout": "Abmelden",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
|
|
||||||
class SidebarAction extends StatelessWidget {
|
|
||||||
const SidebarAction(
|
|
||||||
{super.key, this.title, this.icon, this.onTap, this.selected = false});
|
|
||||||
|
|
||||||
final bool selected;
|
|
||||||
final Widget? icon;
|
|
||||||
final Widget? title;
|
|
||||||
final Function()? onTap;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 12.0),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: onTap,
|
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
if (icon != null)
|
|
||||||
IconTheme(
|
|
||||||
data: IconThemeData(
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(selected ? 1.0 : .3),
|
|
||||||
),
|
|
||||||
child: icon!,
|
|
||||||
),
|
|
||||||
if (title != null)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 24.0),
|
|
||||||
child: AnimatedDefaultTextStyle(
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(selected ? 1.0 : .8),
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontFamily: "Montserrat",
|
|
||||||
),
|
|
||||||
child: title!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class NewsView extends StatelessWidget {
|
|
||||||
const NewsView({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,965 +0,0 @@
|
|||||||
// ignore_for_file: no_leading_underscores_for_local_identifiers
|
|
||||||
|
|
||||||
import 'package:refilc/api/providers/update_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/absence_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/event_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/exam_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/homework_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/message_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/note_provider.dart';
|
|
||||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
|
||||||
import 'package:refilc/api/providers/user_provider.dart';
|
|
||||||
import 'package:refilc/utils/format.dart';
|
|
||||||
import 'package:refilc/models/settings.dart';
|
|
||||||
import 'package:refilc/models/user.dart';
|
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
|
||||||
import 'package:refilc_kreta_api/client/client.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/action_button.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/profile_image/profile_image.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/widgets/update/updates_view.dart';
|
|
||||||
import 'package:refilc_mobile_ui/plus/premium_button.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/news/news_screen.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/settings/accounts/account_tile.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/settings/accounts/account_view.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/settings/debug/subject_icon_gallery.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/settings/privacy_view.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/settings/settings_helper.dart';
|
|
||||||
import 'package:refilc_plus/providers/plus_provider.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart' as tabs;
|
|
||||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
|
||||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'settings_screen.i18n.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:refilc_mobile_ui/screens/settings/user/nickname.dart';
|
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
|
||||||
const SettingsScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
SettingsScreenState createState() => SettingsScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class SettingsScreenState extends State<SettingsScreen>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
int devmodeCountdown = 3;
|
|
||||||
final bool __ss = false; // secret settings
|
|
||||||
|
|
||||||
late UserProvider user;
|
|
||||||
late UpdateProvider updateProvider;
|
|
||||||
late SettingsProvider settings;
|
|
||||||
late KretaClient kretaClient;
|
|
||||||
late String firstName;
|
|
||||||
List<Widget> accountTiles = [];
|
|
||||||
|
|
||||||
late AnimationController _hideContainersController;
|
|
||||||
|
|
||||||
Future<void> restore() => Future.wait([
|
|
||||||
Provider.of<GradeProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<TimetableProvider>(context, listen: false).restoreUser(),
|
|
||||||
Provider.of<ExamProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<HomeworkProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<MessageProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<MessageProvider>(context, listen: false)
|
|
||||||
.restoreRecipients(),
|
|
||||||
Provider.of<NoteProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<EventProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<AbsenceProvider>(context, listen: false).restore(),
|
|
||||||
Provider.of<KretaClient>(context, listen: false).refreshLogin(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
void buildAccountTiles() {
|
|
||||||
accountTiles = [];
|
|
||||||
user.getUsers().forEach((account) {
|
|
||||||
if (account.id == user.id) return;
|
|
||||||
|
|
||||||
String _firstName;
|
|
||||||
|
|
||||||
List<String> _nameParts = user.displayName?.split(" ") ?? ["?"];
|
|
||||||
if (!settings.presentationMode) {
|
|
||||||
_firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0];
|
|
||||||
} else {
|
|
||||||
_firstName = "János";
|
|
||||||
}
|
|
||||||
|
|
||||||
accountTiles.add(AccountTile(
|
|
||||||
name: Text(!settings.presentationMode ? account.name : "János",
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
|
||||||
username:
|
|
||||||
Text(!settings.presentationMode ? account.username : "72469696969"),
|
|
||||||
profileImage: ProfileImage(
|
|
||||||
name: _firstName,
|
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary, //!settings.presentationMode
|
|
||||||
//? ColorUtils.stringToColor(account.name)
|
|
||||||
//: Theme.of(context).colorScheme.secondary,
|
|
||||||
role: account.role,
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
user.setUser(account.id);
|
|
||||||
restore().then((_) => user.setUser(account.id));
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
onTapMenu: () => _showBottomSheet(account),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showBottomSheet(User u) {
|
|
||||||
showBottomSheetMenu(context, items: [
|
|
||||||
BottomSheetMenuItem(
|
|
||||||
onPressed: () => AccountView.show(u, context: context),
|
|
||||||
icon: const Icon(FeatherIcons.user),
|
|
||||||
title: Text("personal_details".i18n),
|
|
||||||
),
|
|
||||||
BottomSheetMenuItem(
|
|
||||||
onPressed: () => _openDKT(u),
|
|
||||||
icon: Icon(FeatherIcons.grid, color: AppColors.of(context).teal),
|
|
||||||
title: Text("open_dkt".i18n),
|
|
||||||
),
|
|
||||||
UserMenuNickname(u),
|
|
||||||
// BottomSheetMenuItem(
|
|
||||||
// onPressed: () {},
|
|
||||||
// icon: Icon(FeatherIcons.camera),
|
|
||||||
// title: Text("edit_profile_picture".i18n),
|
|
||||||
// ),
|
|
||||||
// BottomSheetMenuItem(
|
|
||||||
// onPressed: () {},
|
|
||||||
// icon: Icon(FeatherIcons.trash2, color: AppColors.of(context).red),
|
|
||||||
// title: Text("remove_profile_picture".i18n),
|
|
||||||
// ),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openDKT(User u) => tabs.launchUrl(
|
|
||||||
Uri.parse(
|
|
||||||
"https://dkttanulo.e-kreta.hu/sso?id_token=${kretaClient.idToken}"),
|
|
||||||
customTabsOptions: tabs.CustomTabsOptions(
|
|
||||||
showTitle: true,
|
|
||||||
colorSchemes: tabs.CustomTabsColorSchemes(
|
|
||||||
defaultPrams: tabs.CustomTabsColorSchemeParams(
|
|
||||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_hideContainersController = AnimationController(
|
|
||||||
vsync: this, duration: const Duration(milliseconds: 200));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
user = Provider.of<UserProvider>(context);
|
|
||||||
settings = Provider.of<SettingsProvider>(context);
|
|
||||||
updateProvider = Provider.of<UpdateProvider>(context);
|
|
||||||
kretaClient = Provider.of<KretaClient>(context);
|
|
||||||
|
|
||||||
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
|
|
||||||
if (!settings.presentationMode) {
|
|
||||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
|
||||||
} else {
|
|
||||||
firstName = "János";
|
|
||||||
}
|
|
||||||
|
|
||||||
String startPageTitle =
|
|
||||||
SettingsHelper.localizedPageTitles()[settings.startPage] ?? "?";
|
|
||||||
String themeModeText = {
|
|
||||||
ThemeMode.light: "light".i18n,
|
|
||||||
ThemeMode.dark: "dark".i18n,
|
|
||||||
ThemeMode.system: "system".i18n
|
|
||||||
}[settings.theme] ??
|
|
||||||
"?";
|
|
||||||
String languageText = SettingsHelper.langMap[settings.language] ?? "?";
|
|
||||||
String vibrateTitle = {
|
|
||||||
VibrationStrength.off: "voff".i18n,
|
|
||||||
VibrationStrength.light: "vlight".i18n,
|
|
||||||
VibrationStrength.medium: "vmedium".i18n,
|
|
||||||
VibrationStrength.strong: "vstrong".i18n,
|
|
||||||
}[settings.vibrate] ??
|
|
||||||
"?";
|
|
||||||
|
|
||||||
buildAccountTiles();
|
|
||||||
|
|
||||||
if (settings.developerMode) devmodeCountdown = -1;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 11.5),
|
|
||||||
child: CustomScrollView(
|
|
||||||
slivers: [
|
|
||||||
SliverAppBar(
|
|
||||||
pinned: true,
|
|
||||||
floating: true,
|
|
||||||
snap: false,
|
|
||||||
centerTitle: false,
|
|
||||||
title: const Text("Settings"),
|
|
||||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: AnimatedBuilder(
|
|
||||||
animation: _hideContainersController,
|
|
||||||
builder: (context, child) => Opacity(
|
|
||||||
opacity: 1 - _hideContainersController.value,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
StaggeredGrid.extent(
|
|
||||||
// direction: Axis.horizontal,
|
|
||||||
// crossAxisCount: 3,
|
|
||||||
maxCrossAxisExtent: 600,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 32.0),
|
|
||||||
|
|
||||||
// Updates
|
|
||||||
if (updateProvider.available)
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
child: PanelButton(
|
|
||||||
onPressed: () => _openUpdates(context),
|
|
||||||
title: Text("update_available".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.download),
|
|
||||||
trailing: Text(
|
|
||||||
updateProvider.releases.first.tag,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// const Padding(
|
|
||||||
// padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
|
|
||||||
// child: PremiumBannerButton(),
|
|
||||||
// ),
|
|
||||||
if (!Provider.of<PlusProvider>(context).hasPremium)
|
|
||||||
const ClipRect(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 12.0),
|
|
||||||
child: PremiumButton(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// General Settings
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Text("general".i18n),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () {
|
|
||||||
SettingsHelper.language(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
title: Text("language".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.globe),
|
|
||||||
trailing: Text(languageText),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () {
|
|
||||||
SettingsHelper.startPage(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
title: Text("startpage".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.play),
|
|
||||||
trailing: Text(startPageTitle.capital()),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () {
|
|
||||||
SettingsHelper.rounding(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
title: Text("rounding".i18n),
|
|
||||||
leading:
|
|
||||||
const Icon(FeatherIcons.gitCommit),
|
|
||||||
trailing: Text((settings.rounding / 10)
|
|
||||||
.toStringAsFixed(1)),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () {
|
|
||||||
SettingsHelper.vibrate(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
title: Text("vibrate".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.radio),
|
|
||||||
trailing: Text(vibrateTitle),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 14.0),
|
|
||||||
onPressed: () {
|
|
||||||
SettingsHelper.bellDelay(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
title: Text(
|
|
||||||
"bell_delay".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(
|
|
||||||
settings.bellDelayEnabled
|
|
||||||
? 1.0
|
|
||||||
: .5)),
|
|
||||||
),
|
|
||||||
leading: settings.bellDelayEnabled
|
|
||||||
? const Icon(FeatherIcons.bell)
|
|
||||||
: Icon(FeatherIcons.bellOff,
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(.25)),
|
|
||||||
trailingDivider: true,
|
|
||||||
trailing: Switch(
|
|
||||||
onChanged: (v) => settings.update(
|
|
||||||
bellDelayEnabled: v),
|
|
||||||
value: settings.bellDelayEnabled,
|
|
||||||
activeColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
if (kDebugMode)
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: const Text("Debug"),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
PanelButton(
|
|
||||||
title:
|
|
||||||
const Text("Subject Icon Gallery"),
|
|
||||||
leading: const Icon(CupertinoIcons
|
|
||||||
.rectangle_3_offgrid_fill),
|
|
||||||
trailing:
|
|
||||||
const Icon(Icons.arrow_forward),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context,
|
|
||||||
rootNavigator: true)
|
|
||||||
.push(
|
|
||||||
CupertinoPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
const SubjectIconGallery()),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Secret Settings
|
|
||||||
if (__ss)
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Text("secret".i18n),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Good student mode
|
|
||||||
Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: SwitchListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 12.0),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(12.0)),
|
|
||||||
title: Text("goodstudent".i18n,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w500)),
|
|
||||||
onChanged: (v) {
|
|
||||||
if (v) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => PopScope(
|
|
||||||
onPopInvoked: (didPop) =>
|
|
||||||
false,
|
|
||||||
child: AlertDialog(
|
|
||||||
shape:
|
|
||||||
RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius
|
|
||||||
.circular(
|
|
||||||
12.0)),
|
|
||||||
title:
|
|
||||||
Text("attention".i18n),
|
|
||||||
content: Text(
|
|
||||||
"goodstudent_disclaimer"
|
|
||||||
.i18n),
|
|
||||||
actions: [
|
|
||||||
ActionButton(
|
|
||||||
label:
|
|
||||||
"understand".i18n,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(
|
|
||||||
context)
|
|
||||||
.pop();
|
|
||||||
settings.update(
|
|
||||||
goodStudent: v);
|
|
||||||
Provider.of<GradeProvider>(
|
|
||||||
context,
|
|
||||||
listen:
|
|
||||||
false)
|
|
||||||
.fetch();
|
|
||||||
})
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
settings.update(goodStudent: v);
|
|
||||||
Provider.of<GradeProvider>(
|
|
||||||
context,
|
|
||||||
listen: false)
|
|
||||||
.fetch();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
value: settings.goodStudent,
|
|
||||||
activeColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Presentation mode
|
|
||||||
Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: SwitchListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 12.0),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(12.0)),
|
|
||||||
title: const Text("Presentation Mode",
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w500)),
|
|
||||||
onChanged: (v) => settings.update(
|
|
||||||
presentationMode: v),
|
|
||||||
value: settings.presentationMode,
|
|
||||||
activeColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Theme Settings
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Text("appearance".i18n),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () {
|
|
||||||
SettingsHelper.theme(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
title: Text("theme".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.sun),
|
|
||||||
trailing: Text(themeModeText),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await _hideContainersController
|
|
||||||
.forward();
|
|
||||||
// ignore: use_build_context_synchronously
|
|
||||||
SettingsHelper.accentColor(context);
|
|
||||||
setState(() {});
|
|
||||||
_hideContainersController.reset();
|
|
||||||
},
|
|
||||||
title: Text("color".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.droplet),
|
|
||||||
trailing: Container(
|
|
||||||
width: 12.0,
|
|
||||||
height: 12.0,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
onPressed: () {
|
|
||||||
SettingsHelper.gradeColors(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
title: Text("grade_colors".i18n),
|
|
||||||
leading: const Icon(FeatherIcons.star),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: List.generate(
|
|
||||||
5,
|
|
||||||
(i) => Container(
|
|
||||||
margin: const EdgeInsets.only(
|
|
||||||
left: 2.0),
|
|
||||||
width: 12.0,
|
|
||||||
height: 12.0,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: settings.gradeColors[i],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: SwitchListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 12.0),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(12.0)),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
FeatherIcons.barChart,
|
|
||||||
color: settings.graphClassAvg
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary
|
|
||||||
: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(.25),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 24.0),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"graph_class_avg".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(
|
|
||||||
settings.graphClassAvg
|
|
||||||
? 1.0
|
|
||||||
: .5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onChanged: (v) =>
|
|
||||||
settings.update(graphClassAvg: v),
|
|
||||||
value: settings.graphClassAvg,
|
|
||||||
activeColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// we need icon pack selector here
|
|
||||||
// const PremiumIconPackSelector(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Notifications
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Text("notifications".i18n),
|
|
||||||
child: Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: SwitchListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 12.0),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(12.0)),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.newspaper_outlined,
|
|
||||||
color: settings.newsEnabled
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary
|
|
||||||
: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(.25),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 24.0),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"news".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(
|
|
||||||
settings.newsEnabled
|
|
||||||
? 1.0
|
|
||||||
: .5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onChanged: (v) =>
|
|
||||||
settings.update(newsEnabled: v),
|
|
||||||
value: settings.newsEnabled,
|
|
||||||
activeColor:
|
|
||||||
Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Extras
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Text("extras".i18n),
|
|
||||||
child: Column(children: [
|
|
||||||
Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: SwitchListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 12.0),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(12.0)),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
FeatherIcons.gift,
|
|
||||||
color: settings.gradeOpeningFun
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary
|
|
||||||
: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(.25),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 24.0),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"surprise_grades".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(
|
|
||||||
settings.gradeOpeningFun
|
|
||||||
? 1.0
|
|
||||||
: .5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onChanged: (v) =>
|
|
||||||
settings.update(gradeOpeningFun: v),
|
|
||||||
value: settings.gradeOpeningFun,
|
|
||||||
activeColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// About
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: Text("about".i18n),
|
|
||||||
child: Column(children: [
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.atSign),
|
|
||||||
title: const Text("Discord"),
|
|
||||||
onPressed: () => launchUrl(
|
|
||||||
Uri.parse("https://refilc.hu/discord"),
|
|
||||||
mode: LaunchMode.externalApplication),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.globe),
|
|
||||||
title: const Text("www.refilc.hu"),
|
|
||||||
onPressed: () => launchUrl(
|
|
||||||
Uri.parse("https://refilc.hu"),
|
|
||||||
mode: LaunchMode.externalApplication),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.github),
|
|
||||||
title: const Text("Github"),
|
|
||||||
onPressed: () => launchUrl(
|
|
||||||
Uri.parse("https://github.com/filc"),
|
|
||||||
mode: LaunchMode.externalApplication),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.mail),
|
|
||||||
title: Text("news".i18n),
|
|
||||||
onPressed: () => _openNews(context),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.lock),
|
|
||||||
title: Text("privacy".i18n),
|
|
||||||
onPressed: () => _openPrivacy(context),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.award),
|
|
||||||
title: Text("licenses".i18n),
|
|
||||||
onPressed: () =>
|
|
||||||
showLicensePage(context: context),
|
|
||||||
),
|
|
||||||
Tooltip(
|
|
||||||
message: "data_collected".i18n,
|
|
||||||
padding: const EdgeInsets.all(4.0),
|
|
||||||
textStyle: TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.of(context).text),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.surface),
|
|
||||||
child: Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: SwitchListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 12.0),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(12.0)),
|
|
||||||
secondary: Icon(
|
|
||||||
FeatherIcons.barChart2,
|
|
||||||
color: settings.xFilcId != "none"
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary
|
|
||||||
: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(.25),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
"Analytics".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(
|
|
||||||
settings.xFilcId != "none"
|
|
||||||
? 1.0
|
|
||||||
: .5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
"Anonymous Usage Analytics".i18n,
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(
|
|
||||||
settings.xFilcId != "none"
|
|
||||||
? .5
|
|
||||||
: .2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onChanged: (v) {
|
|
||||||
String newId;
|
|
||||||
if (v == false) {
|
|
||||||
newId = "none";
|
|
||||||
} else if (settings.xFilcId ==
|
|
||||||
"none") {
|
|
||||||
newId = SettingsProvider
|
|
||||||
.defaultSettings()
|
|
||||||
.xFilcId;
|
|
||||||
} else {
|
|
||||||
newId = settings.xFilcId;
|
|
||||||
}
|
|
||||||
settings.update(xFilcId: newId);
|
|
||||||
},
|
|
||||||
value: settings.xFilcId != "none",
|
|
||||||
activeColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (settings.developerMode)
|
|
||||||
Container(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 500),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0, horizontal: 24.0),
|
|
||||||
child: Panel(
|
|
||||||
title: const Text("Developer Settings"),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: SwitchListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 12.0),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(12.0)),
|
|
||||||
title: const Text("Developer Mode",
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w500)),
|
|
||||||
onChanged: (v) => settings.update(
|
|
||||||
developerMode: false),
|
|
||||||
value: settings.developerMode,
|
|
||||||
activeColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.copy),
|
|
||||||
title: const Text("Copy JWT"),
|
|
||||||
onPressed: () => Clipboard.setData(
|
|
||||||
ClipboardData(
|
|
||||||
text: Provider.of<KretaClient>(
|
|
||||||
context,
|
|
||||||
listen: false)
|
|
||||||
.accessToken!)),
|
|
||||||
),
|
|
||||||
// if (Provider.of<PlusProvider>(context,
|
|
||||||
// listen: false)
|
|
||||||
// .hasPremium)
|
|
||||||
// PanelButton(
|
|
||||||
// leading: const Icon(FeatherIcons.key),
|
|
||||||
// title: const Text("Remove Premium"),
|
|
||||||
// onPressed: () {
|
|
||||||
// Provider.of<PlusProvider>(
|
|
||||||
// context,
|
|
||||||
// listen: false)
|
|
||||||
// .activate(removePremium: true);
|
|
||||||
// settings.update(
|
|
||||||
// accentColor: AccentColor.filc,
|
|
||||||
// store: true);
|
|
||||||
// Provider.of<ThemeModeObserver>(
|
|
||||||
// context,
|
|
||||||
// listen: false)
|
|
||||||
// .changeTheme(settings.theme);
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 40,
|
|
||||||
),
|
|
||||||
SafeArea(
|
|
||||||
top: false,
|
|
||||||
child: Center(
|
|
||||||
child: GestureDetector(
|
|
||||||
child: const Panel(
|
|
||||||
title: Text(
|
|
||||||
"v${const String.fromEnvironment("APPVER", defaultValue: "?")}")),
|
|
||||||
onTap: () {
|
|
||||||
if (devmodeCountdown > 0) {
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
.showSnackBar(SnackBar(
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
content: Text(
|
|
||||||
"You are $devmodeCountdown taps away from Developer Mode."),
|
|
||||||
));
|
|
||||||
|
|
||||||
setState(() => devmodeCountdown--);
|
|
||||||
} else if (devmodeCountdown == 0) {
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
.showSnackBar(const SnackBar(
|
|
||||||
content: Text(
|
|
||||||
"Developer Mode successfully activated."),
|
|
||||||
));
|
|
||||||
|
|
||||||
settings.update(developerMode: true);
|
|
||||||
|
|
||||||
setState(() => devmodeCountdown--);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openNews(BuildContext context) =>
|
|
||||||
Navigator.of(context, rootNavigator: true)
|
|
||||||
.push(CupertinoPageRoute(builder: (context) => const NewsScreen()));
|
|
||||||
void _openUpdates(BuildContext context) =>
|
|
||||||
UpdateView.show(updateProvider.releases.first, context: context);
|
|
||||||
void _openPrivacy(BuildContext context) => PrivacyView.show(context);
|
|
||||||
}
|
|
@ -1,191 +0,0 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
|
||||||
|
|
||||||
extension SettingsLocalization on String {
|
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
|
||||||
{
|
|
||||||
"en_en": {
|
|
||||||
"personal_details": "Personal Details",
|
|
||||||
"open_dkt": "Open DCS",
|
|
||||||
"edit_nickname": "Edit Nickname",
|
|
||||||
"edit_profile_picture": "Edit Profile Picture",
|
|
||||||
"remove_profile_picture": "Remove Profile Picture",
|
|
||||||
"light": "Light",
|
|
||||||
"dark": "Dark",
|
|
||||||
"system": "System",
|
|
||||||
"add_user": "Add User",
|
|
||||||
"log_out": "Log Out",
|
|
||||||
"update_available": "Update Available",
|
|
||||||
"general": "General",
|
|
||||||
"language": "Language",
|
|
||||||
"startpage": "Startpage",
|
|
||||||
"rounding": "Rounding",
|
|
||||||
"appearance": "Appearance",
|
|
||||||
"theme": "Theme",
|
|
||||||
"color": "Color",
|
|
||||||
"grade_colors": "Grade Colors",
|
|
||||||
"notifications": "Notifications",
|
|
||||||
"news": "News",
|
|
||||||
"extras": "Extras",
|
|
||||||
"about": "About",
|
|
||||||
"supporters": "Supporters",
|
|
||||||
"privacy": "Privacy Policy",
|
|
||||||
"licenses": "Licenses",
|
|
||||||
"vibrate": "Vibration",
|
|
||||||
"voff": "Off",
|
|
||||||
"vlight": "Light",
|
|
||||||
"vmedium": "Medium",
|
|
||||||
"vstrong": "Strong",
|
|
||||||
"cancel": "Cancel",
|
|
||||||
"done": "Done",
|
|
||||||
"reset": "Reset",
|
|
||||||
"open": "Open",
|
|
||||||
"data_collected":
|
|
||||||
"Data collected: Platform (eg. Android), App version (eg. 3.0.0), Unique Install Identifier",
|
|
||||||
"Analytics": "Analytics",
|
|
||||||
"Anonymous Usage Analytics": "Anonymous Usage Analytics",
|
|
||||||
"graph_class_avg": "Class average on graph",
|
|
||||||
"goodstudent": "Good student mode",
|
|
||||||
"attention": "Attention!",
|
|
||||||
"goodstudent_disclaimer":
|
|
||||||
"reFilc can not be held liable for the usage of this feature.\n\n(if your mother beats you up because you showed her fake grades, you can only blame yourself for it)",
|
|
||||||
"understand": "I understand",
|
|
||||||
"secret": "Secret Settings",
|
|
||||||
"bell_delay": "Bell Delay",
|
|
||||||
"delay": "Delay",
|
|
||||||
"hurry": "Hurry",
|
|
||||||
"sync": "Synchronize",
|
|
||||||
"sync_help": "Press the Synchronize button when the bell rings.",
|
|
||||||
"surprise_grades": "Surprise Grades",
|
|
||||||
"icon_pack": "Icon Pack",
|
|
||||||
"change_username": "Set a nickname",
|
|
||||||
"Accent Color": "Accent Color",
|
|
||||||
"Background Color": "Background Color",
|
|
||||||
"Highlight Color": "Highlight Color",
|
|
||||||
"Adaptive Theme": "Adaptive Theme",
|
|
||||||
},
|
|
||||||
"hu_hu": {
|
|
||||||
"personal_details": "Személyes információk",
|
|
||||||
"open_dkt": "DKT megnyitása",
|
|
||||||
"edit_nickname": "Becenév szerkesztése",
|
|
||||||
"edit_profile_picture": "Profil-kép szerkesztése",
|
|
||||||
"remove_profile_picture": "Profil-kép törlése",
|
|
||||||
"light": "Világos",
|
|
||||||
"dark": "Sötét",
|
|
||||||
"system": "Rendszer",
|
|
||||||
"add_user": "Felhasználó hozzáadása",
|
|
||||||
"log_out": "Kijelentkezés",
|
|
||||||
"update_available": "Frissítés elérhető",
|
|
||||||
"general": "Általános",
|
|
||||||
"language": "Nyelv",
|
|
||||||
"startpage": "Kezdőlap",
|
|
||||||
"rounding": "Kerekítés",
|
|
||||||
"appearance": "Kinézet",
|
|
||||||
"theme": "Téma",
|
|
||||||
"color": "Színek",
|
|
||||||
"grade_colors": "Jegyek színei",
|
|
||||||
"notifications": "Értesítések",
|
|
||||||
"news": "Hírek",
|
|
||||||
"extras": "Extrák",
|
|
||||||
"about": "Névjegy",
|
|
||||||
"supporters": "Támogatók",
|
|
||||||
"privacy": "Adatvédelmi irányelvek",
|
|
||||||
"licenses": "Licenszek",
|
|
||||||
"vibrate": "Rezgés",
|
|
||||||
"voff": "Kikapcsolás",
|
|
||||||
"vlight": "Alacsony",
|
|
||||||
"vmedium": "Közepes",
|
|
||||||
"vstrong": "Erős",
|
|
||||||
"cancel": "Mégsem",
|
|
||||||
"done": "Kész",
|
|
||||||
"reset": "Visszaállítás",
|
|
||||||
"open": "Megnyitás",
|
|
||||||
"data_collected":
|
|
||||||
"Gyűjtött adat: Platform (pl. Android), App verzió (pl. 3.0.0), Egyedi telepítési azonosító",
|
|
||||||
"Analytics": "Analitika",
|
|
||||||
"Anonymous Usage Analytics": "Névtelen használati analitika",
|
|
||||||
"graph_class_avg": "Osztályátlag a grafikonon",
|
|
||||||
"goodstudent": "Jó tanuló mód",
|
|
||||||
"attention": "Figyelem!",
|
|
||||||
"goodstudent_disclaimer":
|
|
||||||
"A reFilc minden felelősséget elhárít a funkció használatával kapcsolatban.\n\n(Értsd: ha az anyád megver, mert megtévesztő ábrákat mutattál neki, azért csakis magadadat hibáztathatod.)",
|
|
||||||
"understand": "Értem",
|
|
||||||
"secret": "Titkos Beállítások",
|
|
||||||
"bell_delay": "Csengő eltolódása",
|
|
||||||
"delay": "Késleltetés",
|
|
||||||
"hurry": "Siettetés",
|
|
||||||
"sync": "Szinkronizálás",
|
|
||||||
"sync_help": "Csengetéskor nyomd meg a Szinkronizálás gombot.",
|
|
||||||
"surprise_grades": "Meglepetés jegyek",
|
|
||||||
"icon_pack": "Ikon séma",
|
|
||||||
"change_username": "Becenév beállítása",
|
|
||||||
"Accent Color": "Egyedi szín",
|
|
||||||
"Background Color": "Háttér színe",
|
|
||||||
"Highlight Color": "Panelek színe",
|
|
||||||
"Adaptive Theme": "Adaptív téma",
|
|
||||||
},
|
|
||||||
"de_de": {
|
|
||||||
"personal_details": "Persönliche Angaben",
|
|
||||||
"open_dkt": "Öffnen RDZ",
|
|
||||||
"edit_nickname": "Spitznamen bearbeiten",
|
|
||||||
"edit_profile_picture": "Profilbild bearbeiten",
|
|
||||||
"remove_profile_picture": "Profilbild entfernen",
|
|
||||||
"light": "Licht",
|
|
||||||
"dark": "Dunkel",
|
|
||||||
"system": "System",
|
|
||||||
"add_user": "Benutzer hinzufügen",
|
|
||||||
"log_out": "Abmelden",
|
|
||||||
"update_available": "Update verfügbar",
|
|
||||||
"general": "Allgemein",
|
|
||||||
"language": "Sprache",
|
|
||||||
"startpage": "Startseite",
|
|
||||||
"rounding": "Rundung",
|
|
||||||
"appearance": "Erscheinungsbild",
|
|
||||||
"theme": "Thema",
|
|
||||||
"color": "Farbe",
|
|
||||||
"grade_colors": "Grad Farben",
|
|
||||||
"notifications": "Benachrichtigungen",
|
|
||||||
"news": "Nachrichten",
|
|
||||||
"extras": "Extras",
|
|
||||||
"about": "Informationen",
|
|
||||||
"supporters": "Unterstützer",
|
|
||||||
"privacy": "Datenschutzbestimmungen",
|
|
||||||
"licenses": "Lizenzen",
|
|
||||||
"vibrate": "Vibration",
|
|
||||||
"voff": "Aus",
|
|
||||||
"vlight": "Leicht",
|
|
||||||
"vmedium": "Mittel",
|
|
||||||
"vstrong": "Stark",
|
|
||||||
"cancel": "Abbrechen",
|
|
||||||
"done": "Fertig",
|
|
||||||
"reset": "Zurücksetzen",
|
|
||||||
"open": "Öffnen",
|
|
||||||
"data_collected":
|
|
||||||
"Erhobene Daten: Plattform (z.B. Android), App version (z.B. 3.0.0), Eindeutige Installationskennung",
|
|
||||||
"Analytics": "Analytik",
|
|
||||||
"Anonymous Usage Analytics": "Anonyme Nutzungsanalyse",
|
|
||||||
"graph_class_avg": "Klassendurchschnitt in der Grafik",
|
|
||||||
"goodstudent": "Guter Student Modus",
|
|
||||||
"attention": "Achtung!",
|
|
||||||
"goodstudent_disclaimer": "Same in English.",
|
|
||||||
"understand": "Ich verstehe",
|
|
||||||
"secret": "Geheime Einstellungen",
|
|
||||||
"bell_delay": "Klingelverzögerung",
|
|
||||||
"delay": "Verzögern",
|
|
||||||
"hurry": "Eile",
|
|
||||||
"sync": "Synchronisieren",
|
|
||||||
"sync_help": "Drücken Sie die Sync-Taste, wenn die Glocke läutet.",
|
|
||||||
"surprise_grades": "Überraschungsnoten",
|
|
||||||
"icon_pack": "Icon-Pack",
|
|
||||||
"change_username": "Einen Spitznamen festlegen",
|
|
||||||
"Accent Color": "Accent Color",
|
|
||||||
"Background Color": "Background Color",
|
|
||||||
"Highlight Color": "Highlight Color",
|
|
||||||
"Adaptive Theme": "Adaptive Theme",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
String get i18n => localize(this, _t);
|
|
||||||
String fill(List<Object> params) => localizeFill(this, params);
|
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
name: refilc_desktop_ui
|
|
||||||
publish_to: "none"
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: ">=3.3.2 <=3.6.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
|
|
||||||
# reFilc Main
|
|
||||||
refilc:
|
|
||||||
path: ../refilc/
|
|
||||||
# e-KRETA API (kreten) client
|
|
||||||
refilc_kreta_api:
|
|
||||||
path: ../refilc_kreta_api/
|
|
||||||
# reFilc Plus
|
|
||||||
refilc_plus:
|
|
||||||
path: ../refilc_plus/
|
|
||||||
# reFilc Mobile
|
|
||||||
refilc_mobile_ui:
|
|
||||||
path: ../refilc_mobile_ui/
|
|
||||||
|
|
||||||
cupertino_icons: ^1.0.2
|
|
||||||
flutter_feather_icons: ^2.0.0+1
|
|
||||||
provider: ^6.1.1
|
|
||||||
url_launcher: ^6.2.5
|
|
||||||
flutter_linkify: ^6.0.0
|
|
||||||
flutter_markdown: ^0.7.2+1
|
|
||||||
animations: ^2.0.11
|
|
||||||
confetti: ^0.7.0
|
|
||||||
auto_size_text: ^3.0.0
|
|
||||||
flutter_acrylic: ^1.1.3
|
|
||||||
elegant_notification: ^2.2.0
|
|
||||||
flutter_staggered_grid_view: ^0.7.0
|
|
||||||
i18n_extension: ^12.0.1
|
|
||||||
flutter_expandable_fab: ^2.0.0
|
|
||||||
collection: ^1.18.0
|
|
||||||
animated_list_plus: ^0.5.2
|
|
||||||
intl: ^0.19.0
|
|
||||||
flutter_custom_tabs: ^2.0.0+1
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_lints: ^4.0.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
3
refilc_kreta_api/devtools_options.yaml
Normal file
3
refilc_kreta_api/devtools_options.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
description: This file stores settings for Dart & Flutter DevTools.
|
||||||
|
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||||
|
extensions:
|
@ -108,7 +108,9 @@ class TimetableController extends ChangeNotifier {
|
|||||||
// Jump to next week on weekends
|
// Jump to next week on weekends
|
||||||
if (skip &&
|
if (skip &&
|
||||||
(days?.length ?? 0) > 0 &&
|
(days?.length ?? 0) > 0 &&
|
||||||
days!.last.last.end.isBefore(DateTime.now())) return next(context);
|
days!.last.last.end.isBefore(DateTime.now())) {
|
||||||
|
return next(context);
|
||||||
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -149,7 +151,9 @@ class TimetableController extends ChangeNotifier {
|
|||||||
lessons.sort((a, b) => a.date.compareTo(b.date));
|
lessons.sort((a, b) => a.date.compareTo(b.date));
|
||||||
for (var lesson in lessons) {
|
for (var lesson in lessons) {
|
||||||
if (days.last.isNotEmpty &&
|
if (days.last.isNotEmpty &&
|
||||||
_differentDate(lesson.date, days.last.last.date)) days.add([]);
|
_differentDate(lesson.date, days.last.last.date)) {
|
||||||
|
days.add([]);
|
||||||
|
}
|
||||||
days.last.add(lesson);
|
days.last.add(lesson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,12 +148,12 @@ class GradeProvider with ChangeNotifier {
|
|||||||
// streak magic
|
// streak magic
|
||||||
int gradeStreak = 0;
|
int gradeStreak = 0;
|
||||||
List<Grade> grs = _grades;
|
List<Grade> grs = _grades;
|
||||||
grs.sort((a, b) => -a.date.compareTo(b.date));
|
grs.sort((a, b) => -a.writeDate.compareTo(b.writeDate));
|
||||||
|
|
||||||
for (Grade grade in grs) {
|
for (Grade grade in grs) {
|
||||||
if (grade.value.value == 5) {
|
if (grade.value.value == 5) {
|
||||||
gradeStreak++;
|
gradeStreak++;
|
||||||
} else if (grade.value.value !=0) {
|
} else if (grade.value.value != 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ name: refilc_kreta_api
|
|||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.2 <=3.6.0"
|
sdk: ^3.6.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
@ -17,4 +17,4 @@ dependencies:
|
|||||||
uuid: ^4.3.3
|
uuid: ^4.3.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^4.0.0
|
flutter_lints: ^5.0.0
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ActionButton extends StatelessWidget {
|
class ActionButton extends StatelessWidget {
|
||||||
const ActionButton({super.key, required this.label, this.activeColor, this.onTap});
|
const ActionButton(
|
||||||
|
{super.key, required this.label, this.activeColor, this.onTap});
|
||||||
|
|
||||||
final Color? activeColor;
|
final Color? activeColor;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
@ -19,7 +20,8 @@ class ActionButton extends StatelessWidget {
|
|||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
height: 32.0,
|
height: 32.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: (activeColor ?? Theme.of(context).colorScheme.secondary).withOpacity(0.25),
|
color: (activeColor ?? Theme.of(context).colorScheme.secondary)
|
||||||
|
.withValues(alpha: 0.25),
|
||||||
borderRadius: BorderRadius.circular(6.0),
|
borderRadius: BorderRadius.circular(6.0),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 12.0),
|
padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 12.0),
|
||||||
@ -28,7 +30,11 @@ class ActionButton extends StatelessWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w600, color: activeColor ?? Theme.of(context).colorScheme.secondary))),
|
style: TextStyle(
|
||||||
|
fontSize: 15.0,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: activeColor ??
|
||||||
|
Theme.of(context).colorScheme.secondary))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,7 @@ class AverageDisplay extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color color = average == 0.0
|
Color color = average == 0.0
|
||||||
? AppColors.of(context).text.withOpacity(.8)
|
? AppColors.of(context).text.withValues(alpha: .8)
|
||||||
: gradeColor(context: context, value: average);
|
: gradeColor(context: context, value: average);
|
||||||
|
|
||||||
String averageText = average.toStringAsFixed(2);
|
String averageText = average.toStringAsFixed(2);
|
||||||
@ -49,16 +49,18 @@ class AverageDisplay extends StatelessWidget {
|
|||||||
borderRadius:
|
borderRadius:
|
||||||
(border && dashed) ? null : BorderRadius.circular(45.0 * scale),
|
(border && dashed) ? null : BorderRadius.circular(45.0 * scale),
|
||||||
border: border && !dashed
|
border: border && !dashed
|
||||||
? Border.fromBorderSide(
|
? Border.fromBorderSide(BorderSide(
|
||||||
BorderSide(color: color.withOpacity(.5), width: 1.0 * scale))
|
color: color.withValues(alpha: .5), width: 1.0 * scale))
|
||||||
|
: null,
|
||||||
|
color: !border
|
||||||
|
? color.withValues(alpha: average == 0.0 ? .15 : .25)
|
||||||
: null,
|
: null,
|
||||||
color: !border ? color.withOpacity(average == 0.0 ? .15 : .25) : null,
|
|
||||||
),
|
),
|
||||||
child: (border && dashed)
|
child: (border && dashed)
|
||||||
? DottedBorder(
|
? DottedBorder(
|
||||||
strokeWidth: 1.0 * scale,
|
strokeWidth: 1.0 * scale,
|
||||||
padding: EdgeInsets.all(4.0 * scale),
|
padding: EdgeInsets.all(4.0 * scale),
|
||||||
color: color.withOpacity(.5),
|
color: color.withValues(alpha: .5),
|
||||||
dashPattern: const [6, 6],
|
dashPattern: const [6, 6],
|
||||||
radius: Radius.circular(45.0 * scale),
|
radius: Radius.circular(45.0 * scale),
|
||||||
borderType: BorderType.RRect,
|
borderType: BorderType.RRect,
|
||||||
|
@ -25,7 +25,7 @@ class BottomCard extends StatelessWidget {
|
|||||||
margin: const EdgeInsets.only(top: 12.0, bottom: 4.0),
|
margin: const EdgeInsets.only(top: 12.0, bottom: 4.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(45.0),
|
borderRadius: BorderRadius.circular(45.0),
|
||||||
color: AppColors.of(context).text.withOpacity(0.10),
|
color: AppColors.of(context).text.withValues(alpha: 0.10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (child != null) child!,
|
if (child != null) child!,
|
||||||
|
@ -38,7 +38,7 @@ class RoundedBottomSheet extends StatelessWidget {
|
|||||||
margin: const EdgeInsets.only(top: 12.0, bottom: 4.0),
|
margin: const EdgeInsets.only(top: 12.0, bottom: 4.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(45.0),
|
borderRadius: BorderRadius.circular(45.0),
|
||||||
color: AppColors.of(context).text.withOpacity(0.10),
|
color: AppColors.of(context).text.withValues(alpha: 0.10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (child != null) child!,
|
if (child != null) child!,
|
||||||
|
@ -16,7 +16,7 @@ class BetaChip extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: !disabled
|
color: !disabled
|
||||||
? Theme.of(context).colorScheme.secondary
|
? Theme.of(context).colorScheme.secondary
|
||||||
: AppColors.of(context).text.withOpacity(.25),
|
: AppColors.of(context).text.withValues(alpha: .25),
|
||||||
borderRadius: BorderRadius.circular(40),
|
borderRadius: BorderRadius.circular(40),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -28,7 +28,7 @@ class BetaChip extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
color: disabled
|
color: disabled
|
||||||
? AppColors.of(context).text.withOpacity(.5)
|
? AppColors.of(context).text.withValues(alpha: .5)
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
@ -11,8 +11,9 @@ class NewChip extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color: disabled
|
||||||
disabled ? AppColors.of(context).text.withOpacity(.25) : Colors.red,
|
? AppColors.of(context).text.withValues(alpha: .25)
|
||||||
|
: Colors.red,
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
),
|
),
|
||||||
padding:
|
padding:
|
||||||
@ -23,7 +24,7 @@ class NewChip extends StatelessWidget {
|
|||||||
Icon(
|
Icon(
|
||||||
Icons.hotel_class_rounded,
|
Icons.hotel_class_rounded,
|
||||||
color: disabled
|
color: disabled
|
||||||
? AppColors.of(context).text.withOpacity(.5)
|
? AppColors.of(context).text.withValues(alpha: .5)
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
size: 14.0,
|
size: 14.0,
|
||||||
),
|
),
|
||||||
@ -32,7 +33,7 @@ class NewChip extends StatelessWidget {
|
|||||||
'new'.i18n,
|
'new'.i18n,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: disabled
|
color: disabled
|
||||||
? AppColors.of(context).text.withOpacity(.5)
|
? AppColors.of(context).text.withValues(alpha: .5)
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
fontSize: 12.0,
|
fontSize: 12.0,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
@ -24,7 +24,7 @@ SnackBar CustomSnackBar({
|
|||||||
color: _backgroundColor,
|
color: _backgroundColor,
|
||||||
borderRadius: BorderRadius.circular(6.0),
|
borderRadius: BorderRadius.circular(6.0),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(color: Colors.black.withOpacity(.15), blurRadius: 4.0)
|
BoxShadow(color: Colors.black.withValues(alpha: .15), blurRadius: 4.0)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
@ -26,7 +26,7 @@ class Detail extends StatelessWidget {
|
|||||||
text: description,
|
text: description,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: AppColors.of(context).text.withOpacity(0.85)),
|
color: AppColors.of(context).text.withValues(alpha: 0.85)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -34,7 +34,7 @@ class Empty extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 32.0,
|
fontSize: 32.0,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: AppColors.of(context).text.withOpacity(.75)),
|
color: AppColors.of(context).text.withValues(alpha: .75)),
|
||||||
children: subtitle != null
|
children: subtitle != null
|
||||||
? [
|
? [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
@ -42,7 +42,9 @@ class Empty extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18.0,
|
fontSize: 18.0,
|
||||||
height: 2.0,
|
height: 2.0,
|
||||||
color: AppColors.of(context).text.withOpacity(.5)))
|
color: AppColors.of(context)
|
||||||
|
.text
|
||||||
|
.withValues(alpha: .5)))
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
|
@ -56,12 +56,12 @@ class _FilterBarState extends State<FilterBar> {
|
|||||||
),
|
),
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
|
labelPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
|
||||||
labelColor: Theme.of(context).colorScheme.secondary,
|
labelColor: Theme.of(context).colorScheme.secondary,
|
||||||
unselectedLabelColor: AppColors.of(context).text.withOpacity(0.65),
|
unselectedLabelColor: AppColors.of(context).text.withValues(alpha: 0.65),
|
||||||
// indicator
|
// indicator
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
indicatorPadding: const EdgeInsets.symmetric(vertical: 8.0),
|
indicatorPadding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
indicator: BoxDecoration(
|
indicator: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.tertiary.withOpacity(.2),
|
color: Theme.of(context).colorScheme.tertiary.withValues(alpha: .2),
|
||||||
borderRadius: BorderRadius.circular(45.0),
|
borderRadius: BorderRadius.circular(45.0),
|
||||||
),
|
),
|
||||||
overlayColor: WidgetStateProperty.all(const Color(0x00000000)),
|
overlayColor: WidgetStateProperty.all(const Color(0x00000000)),
|
||||||
@ -77,7 +77,7 @@ class _FilterBarState extends State<FilterBar> {
|
|||||||
width: e,
|
width: e,
|
||||||
height: 15,
|
height: 15,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.of(context).text.withOpacity(.45),
|
color: AppColors.of(context).text.withValues(alpha: .45),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -79,7 +79,7 @@ class HeroScrollViewState extends State<HeroScrollView> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(widget.icon,
|
Icon(widget.icon,
|
||||||
color: AppColors.of(context).text.withOpacity(.8)),
|
color: AppColors.of(context).text.withValues(alpha: .8)),
|
||||||
const SizedBox(width: 8.0),
|
const SizedBox(width: 8.0),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -123,7 +123,7 @@ class HeroScrollViewState extends State<HeroScrollView> {
|
|||||||
Theme.of(context).colorScheme.secondary,
|
Theme.of(context).colorScheme.secondary,
|
||||||
amount: 0.1,
|
amount: 0.1,
|
||||||
)
|
)
|
||||||
.withOpacity(0.4),
|
.withValues(alpha: 0.4),
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
@ -135,10 +135,10 @@ class HeroScrollViewState extends State<HeroScrollView> {
|
|||||||
Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
Theme.of(context)
|
Theme.of(context)
|
||||||
.scaffoldBackgroundColor
|
.scaffoldBackgroundColor
|
||||||
.withOpacity(0.1),
|
.withValues(alpha: 0.1),
|
||||||
Theme.of(context)
|
Theme.of(context)
|
||||||
.scaffoldBackgroundColor
|
.scaffoldBackgroundColor
|
||||||
.withOpacity(0.1),
|
.withValues(alpha: 0.1),
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
],
|
],
|
||||||
stops: const [0.1, 0.5, 0.7, 0.98],
|
stops: const [0.1, 0.5, 0.7, 0.98],
|
||||||
@ -164,7 +164,7 @@ class HeroScrollViewState extends State<HeroScrollView> {
|
|||||||
Theme.of(context).colorScheme.secondary,
|
Theme.of(context).colorScheme.secondary,
|
||||||
amount: 0.1,
|
amount: 0.1,
|
||||||
)
|
)
|
||||||
.withOpacity(0.9),
|
.withValues(alpha: 0.9),
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
padding: 12.0,
|
padding: 12.0,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
@ -175,7 +175,7 @@ class HeroScrollViewState extends State<HeroScrollView> {
|
|||||||
Theme.of(context).colorScheme.secondary,
|
Theme.of(context).colorScheme.secondary,
|
||||||
amount: 0.1,
|
amount: 0.1,
|
||||||
)
|
)
|
||||||
.withOpacity(0.8),
|
.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -192,7 +192,7 @@ class HeroScrollViewState extends State<HeroScrollView> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 26.0,
|
fontSize: 26.0,
|
||||||
color: AppColors.of(context).text.withOpacity(.8),
|
color: AppColors.of(context).text.withValues(alpha: .8),
|
||||||
fontStyle: widget.italic ? FontStyle.italic : null,
|
fontStyle: widget.italic ? FontStyle.italic : null,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
@ -19,7 +19,8 @@ class MaterialActionButton extends StatelessWidget {
|
|||||||
return RawMaterialButton(
|
return RawMaterialButton(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
shape: const StadiumBorder(),
|
shape: const StadiumBorder(),
|
||||||
fillColor: backgroundColor ?? AppColors.of(context).text.withOpacity(.15),
|
fillColor:
|
||||||
|
backgroundColor ?? AppColors.of(context).text.withValues(alpha: .15),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
highlightElevation: 0,
|
highlightElevation: 0,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
@ -24,7 +24,8 @@ class OutlinedRoundButton extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Theme.of(context).colorScheme.secondary.withOpacity(0.1),
|
color:
|
||||||
|
Theme.of(context).colorScheme.secondary.withValues(alpha: 0.1),
|
||||||
width: 1.1,
|
width: 1.1,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
@ -67,7 +67,7 @@ class PanelTitle extends StatelessWidget {
|
|||||||
child: DefaultTextStyle(
|
child: DefaultTextStyle(
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: AppColors.of(context).text.withOpacity(0.65)),
|
color: AppColors.of(context).text.withValues(alpha: 0.65)),
|
||||||
child: title,
|
child: title,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -23,18 +23,28 @@ class PanelActionButton extends StatelessWidget {
|
|||||||
padding: padding,
|
padding: padding,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
side: BorderSide(color: Theme.of(context).colorScheme.secondary.withOpacity(.6), width: 2),
|
side: BorderSide(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.secondary.withValues(alpha: .6),
|
||||||
|
width: 2),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: leading != null
|
leading: leading != null
|
||||||
? Theme(
|
? Theme(
|
||||||
data: Theme.of(context).copyWith(iconTheme: IconThemeData(color: Theme.of(context).colorScheme.secondary)),
|
data: Theme.of(context).copyWith(
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: Theme.of(context).colorScheme.secondary)),
|
||||||
child: leading!,
|
child: leading!,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
title: title != null
|
title: title != null
|
||||||
? DefaultTextStyle(style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, fontSize: 15.0), child: title!)
|
? DefaultTextStyle(
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontWeight: FontWeight.w500, fontSize: 15.0),
|
||||||
|
child: title!)
|
||||||
: null,
|
: null,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
|
@ -36,8 +36,9 @@ class PanelButton extends StatelessWidget {
|
|||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: borderRadius ?? BorderRadius.circular(12.0)),
|
borderRadius: borderRadius ?? BorderRadius.circular(12.0)),
|
||||||
fillColor: background
|
fillColor: background
|
||||||
? Colors.white.withOpacity(
|
? Colors.white.withValues(
|
||||||
Theme.of(context).brightness == Brightness.light ? .35 : .2)
|
alpha:
|
||||||
|
Theme.of(context).brightness == Brightness.light ? .35 : .2)
|
||||||
: null,
|
: null,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: leading != null
|
leading: leading != null
|
||||||
@ -57,7 +58,7 @@ class PanelButton extends StatelessWidget {
|
|||||||
width: 2.0,
|
width: 2.0,
|
||||||
height: 32.0,
|
height: 32.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.of(context).text.withOpacity(.15),
|
color: AppColors.of(context).text.withValues(alpha: .15),
|
||||||
borderRadius: BorderRadius.circular(45.0),
|
borderRadius: BorderRadius.circular(45.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -44,7 +44,7 @@ class _EmptyCardState extends State<EmptyCard> {
|
|||||||
if (Provider.of<SettingsProvider>(context, listen: false)
|
if (Provider.of<SettingsProvider>(context, listen: false)
|
||||||
.shadowEffect)
|
.shadowEffect)
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.08),
|
color: Colors.black.withValues(alpha: 0.08),
|
||||||
offset: const Offset(0, 5),
|
offset: const Offset(0, 5),
|
||||||
blurRadius: 20,
|
blurRadius: 20,
|
||||||
spreadRadius: 10,
|
spreadRadius: 10,
|
||||||
@ -52,7 +52,7 @@ class _EmptyCardState extends State<EmptyCard> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: DottedBorder(
|
child: DottedBorder(
|
||||||
color: Colors.black.withOpacity(0.9),
|
color: Colors.black.withValues(alpha: 0.9),
|
||||||
dashPattern: const [12, 12],
|
dashPattern: const [12, 12],
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
|
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
|
||||||
@ -60,7 +60,7 @@ class _EmptyCardState extends State<EmptyCard> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
widget.text,
|
widget.text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white.withOpacity(0.9),
|
color: Colors.white.withValues(alpha: 0.9),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -301,7 +301,7 @@ class _PersonalityCardState extends State<PersonalityCard> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
height: 1.2,
|
height: 1.2,
|
||||||
color: Colors.white.withOpacity(0.8),
|
color: Colors.white.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
@ -354,7 +354,7 @@ class _PersonalityCardState extends State<PersonalityCard> {
|
|||||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.08),
|
color: Colors.black.withValues(alpha: 0.08),
|
||||||
offset: const Offset(0, 5),
|
offset: const Offset(0, 5),
|
||||||
blurRadius: 20,
|
blurRadius: 20,
|
||||||
spreadRadius: 10,
|
spreadRadius: 10,
|
||||||
@ -362,7 +362,7 @@ class _PersonalityCardState extends State<PersonalityCard> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: DottedBorder(
|
child: DottedBorder(
|
||||||
color: Colors.black.withOpacity(0.9),
|
color: Colors.black.withValues(alpha: 0.9),
|
||||||
dashPattern: const [12, 12],
|
dashPattern: const [12, 12],
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
|
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
|
||||||
|
@ -91,10 +91,10 @@ class _ProfileImageState extends State<ProfileImage> {
|
|||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
color: widget.name != null && widget.name! == 'Rendszerüzenet'
|
color: widget.name != null && widget.name! == 'Rendszerüzenet'
|
||||||
? widget.backgroundColor?.withOpacity(0.8) ??
|
? widget.backgroundColor?.withValues(alpha: 0.8) ??
|
||||||
AppColors.of(context).text.withOpacity(0.5)
|
AppColors.of(context).text.withValues(alpha: 0.5)
|
||||||
: widget.backgroundColor ??
|
: widget.backgroundColor ??
|
||||||
AppColors.of(context).text.withOpacity(.15),
|
AppColors.of(context).text.withValues(alpha: .15),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
onDoubleTap: widget.onDoubleTap,
|
onDoubleTap: widget.onDoubleTap,
|
||||||
@ -114,7 +114,7 @@ class _ProfileImageState extends State<ProfileImage> {
|
|||||||
width: 15,
|
width: 15,
|
||||||
height: 15,
|
height: 15,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color.withOpacity(.5),
|
color: color.withValues(alpha: .5),
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -206,7 +206,7 @@ class _ProfileImageState extends State<ProfileImage> {
|
|||||||
color: profilePicture != null
|
color: profilePicture != null
|
||||||
? Colors.transparent
|
? Colors.transparent
|
||||||
: widget.backgroundColor ??
|
: widget.backgroundColor ??
|
||||||
AppColors.of(context).text.withOpacity(.15),
|
AppColors.of(context).text.withValues(alpha: .15),
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
height: widget.radius * 2,
|
height: widget.radius * 2,
|
||||||
|
@ -2,7 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class ProgressBar extends StatelessWidget {
|
class ProgressBar extends StatelessWidget {
|
||||||
const ProgressBar(
|
const ProgressBar(
|
||||||
{super.key, required this.value, this.backgroundColor, this.height = 8.0});
|
{super.key,
|
||||||
|
required this.value,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.height = 8.0});
|
||||||
|
|
||||||
final double value;
|
final double value;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
@ -16,8 +19,8 @@ class ProgressBar extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).brightness == Brightness.light
|
color: Theme.of(context).brightness == Brightness.light
|
||||||
? Colors.black.withOpacity(0.1)
|
? Colors.black.withValues(alpha: 0.1)
|
||||||
: Colors.white.withOpacity(0.1),
|
: Colors.white.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(45.0),
|
borderRadius: BorderRadius.circular(45.0),
|
||||||
),
|
),
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
@ -19,7 +19,7 @@ class RoundBorderIcon extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: color ??
|
color: color ??
|
||||||
Theme.of(context).colorScheme.secondary.withOpacity(0.25),
|
Theme.of(context).colorScheme.secondary.withValues(alpha: 0.25),
|
||||||
width: width),
|
width: width),
|
||||||
borderRadius: BorderRadius.circular(50.0),
|
borderRadius: BorderRadius.circular(50.0),
|
||||||
),
|
),
|
||||||
|
@ -56,7 +56,7 @@ class SplittedPanel extends StatelessWidget {
|
|||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondary
|
.secondary
|
||||||
.withOpacity(.25),
|
.withValues(alpha: .25),
|
||||||
width: 1.0)
|
width: 1.0)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
@ -121,7 +121,7 @@ class SplittedPanelTitle extends StatelessWidget {
|
|||||||
child: DefaultTextStyle(
|
child: DefaultTextStyle(
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: AppColors.of(context).text.withOpacity(0.65)),
|
color: AppColors.of(context).text.withValues(alpha: 0.65)),
|
||||||
child: title,
|
child: title,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -15,7 +15,7 @@ class AbsenceDisplay extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(top: 5.0),
|
padding: const EdgeInsets.only(top: 5.0),
|
||||||
// padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 6.0),
|
// padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 6.0),
|
||||||
// decoration: BoxDecoration(
|
// decoration: BoxDecoration(
|
||||||
// color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.2),
|
// color: Theme.of(context).scaffoldBackgroundColor.withValues(alpha: .2),
|
||||||
// borderRadius: BorderRadius.circular(12.0),
|
// borderRadius: BorderRadius.circular(12.0),
|
||||||
// ),
|
// ),
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
|
@ -81,7 +81,7 @@ class AbsenceSubjectTile extends StatelessWidget {
|
|||||||
// fontFamily: "monospace",
|
// fontFamily: "monospace",
|
||||||
color:
|
color:
|
||||||
getColorByPercentage(percentage, context: context)
|
getColorByPercentage(percentage, context: context)
|
||||||
.withOpacity(0.8),
|
.withValues(alpha: 0.8),
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
@ -109,5 +109,5 @@ Color getColorByPercentage(double percentage, {required BuildContext context}) {
|
|||||||
color = AppColors.of(context).yellow;
|
color = AppColors.of(context).yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
return color.withOpacity(.8);
|
return color.withValues(alpha: .8);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ class AbsenceTile extends StatelessWidget {
|
|||||||
width: 39,
|
width: 39,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: !group ? color.withOpacity(.25) : null,
|
color: !group ? color.withValues(alpha: .25) : null,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Icon(justificationIcon(absence.state), color: color)),
|
child: Icon(justificationIcon(absence.state), color: color)),
|
||||||
|
@ -51,7 +51,7 @@ class AbsenceView extends StatelessWidget {
|
|||||||
height: 44.0,
|
height: 44.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: color.withOpacity(.25),
|
color: color.withValues(alpha: .25),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user