diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index a3411d2..a1c7b31 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -62,7 +62,7 @@ kotlin { implementation(project.dependencies.platform(libs.firebase.bom)) implementation(libs.firebase.analytics) // facebook - implementation(libs.android.facebook.android.sdk) + // implementation(libs.android.facebook.android.sdk) // mmkv implementation(libs.android.mmkv) @@ -143,8 +143,8 @@ android { applicationId = "com.taskttl" minSdk = libs.versions.android.minSdk.get().toInt() targetSdk = libs.versions.android.targetSdk.get().toInt() - versionCode = 1 - versionName = "1.0" + versionCode = libs.versions.android.versionCode.get().toInt() + versionName = libs.versions.android.versionName.get().toString() buildConfigField("String", "APP_NAME", "\"TaskTTL\"") diff --git a/composeApp/release/composeApp-release.aab b/composeApp/release/composeApp-release.aab new file mode 100644 index 0000000..10660f9 Binary files /dev/null and b/composeApp/release/composeApp-release.aab differ diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index 1d92678..1e6bea8 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -15,8 +15,6 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:usesCleartextTraffic="true" - android:networkSecurityConfig="@xml/network_security_config" android:theme="@android:style/Theme.Material.Light.NoActionBar"> + + + + + + + + + - - - - \ No newline at end of file + + + + + + + + + + + + + + diff --git a/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/DeviceUtils.android.kt b/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/DeviceUtils.android.kt index 4b95135..2943523 100644 --- a/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/DeviceUtils.android.kt +++ b/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/DeviceUtils.android.kt @@ -67,7 +67,7 @@ actual object DeviceUtils { uniqueId = getUniqueId(), deviceInfo = Build.MODEL ?: "0", deviceVersion = Build.VERSION.RELEASE, - language = "" + language = Localization.getDeviceLanguage() ) } diff --git a/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/ExternalAppLauncher.android.kt b/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/ExternalAppLauncher.android.kt new file mode 100644 index 0000000..e739b4c --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/ExternalAppLauncher.android.kt @@ -0,0 +1,78 @@ +package com.taskttl.core.utils + +import android.content.Context +import android.content.Intent +import androidx.core.net.toUri +import com.taskttl.MainApplication + +actual object ExternalAppLauncher { + private val appContext: Context + get() = MainApplication.instance.applicationContext + + private val packageName = appContext.packageName + + /** + * 打开应用程序评级 + */ + actual suspend fun openAppRating() { + val webUrl = "https://play.google.com/store/apps/details?id=$packageName" + try { + // 尝试打开 Google Play 应用 + val marketUri = "market://details?id=$packageName" + if (openUriWithFallback(marketUri, webUrl)) { + return + } + + // 如果前面的方法都失败,直接打开网页版 + openWebUrl(webUrl) + } catch (e: Exception) { + // Log.e(TAG, getString(Res.string.external_app_launcher_open_rating_failed), e) + openWebUrl(webUrl) + } + } + + actual suspend fun openUrl(url: String) { + openWebUrl(url) + } + + /** + * 尝试打开URI,打开指定的应用包名 + * @return 是否成功打开 + */ + private fun openUriWithFallback(uri: String, packageName: String): Boolean { + try { + val intent = Intent(Intent.ACTION_VIEW, uri.toUri()) + intent.setPackage(packageName) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + // 检查是否有应用可以处理此Intent + if (isIntentResolvable(intent)) { + appContext.startActivity(intent) + return true + } + } catch (e: Exception) { + // Log.w(TAG, getString(Res.string.external_app_launcher_open_uri_failed, uri), e) + } + return false + } + + /** + * 打开网页URL + */ + private fun openWebUrl(url: String) { + try { + val intent = Intent(Intent.ACTION_VIEW, url.toUri()) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + appContext.startActivity(intent) + } catch (e: Exception) { + // Log.e(TAG, getString(Res.string.external_app_launcher_open_web_failed, url), e) + } + } + + /** + * 检查Intent是否可以被解析 + */ + private fun isIntentResolvable(intent: Intent): Boolean { + return intent.resolveActivity(appContext.packageManager) != null + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/Localization.android.kt b/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/Localization.android.kt new file mode 100644 index 0000000..bcc6537 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/taskttl/core/utils/Localization.android.kt @@ -0,0 +1,32 @@ +package com.taskttl.core.utils + +import android.content.Context +import android.os.LocaleList +import com.taskttl.MainApplication +import java.util.Locale + +/** + * 本地化 + * @author DevTTL + * @date 2025/10/14 + */ +actual object Localization { + private val appContext: Context + get() = MainApplication.instance.applicationContext + + actual fun applyLanguage(iso: String) { + val locale = Locale.forLanguageTag(iso) + Locale.setDefault(locale) + val config = appContext.resources.configuration + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + config.setLocales(LocaleList(locale)) + // } else { + // config.setLocale(locale) + // } + appContext.createConfigurationContext(config) + } + + actual fun getDeviceLanguage(): String { + return Locale.getDefault().language + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cf..0000000 --- a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a571e60..0000000 Binary files a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 61da551..0000000 Binary files a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png index c41dd28..3a91ec6 100644 Binary files a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png and b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png index db5080a..4f8e01c 100644 Binary files a/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png and b/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png index 6dba46d..ffb521a 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png and b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png index da31a87..2fb7570 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png and b/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png index 15ac681..d41d6c2 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png and b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png index b216f2d..cf60cf1 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png and b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png index f25a419..bac6ce2 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png and b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png index e96783c..b7e48a8 100644 Binary files a/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png and b/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 6d453db..1f53858 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -1,313 +1,317 @@ - 继续 - 跳过 - 开始使用 + Continue + Skip + Get Started - 欢迎使用 TaskTTL - 一个简洁而强大的任务管理工具,帮助您高效管理日常任务和重要日期 - 智能任务管理 - 创建、分类和跟踪您的任务。设置优先级,添加截止日期,让工作更有条理 - 重要日期提醒 - 设置重要日期的倒数计时,永远不会错过生日、纪念日或重要的截止日期 - 准备就绪! - 现在您可以开始创建第一个任务,让我们一起提高工作效率吧! + Welcome to TaskTTL + A simple yet powerful task management tool that helps you efficiently manage your daily tasks and important dates + Smart Task Management + Create, categorize, and track your tasks. Set priorities and add due dates for better organization + Important Date Reminders + Set countdowns to important dates and never miss a birthday, anniversary, or important deadline + Ready! + Now you can start creating your first task. Let's improve your productivity together! - + TaskTTL - 任务管理与倒数日应用 - 让每一天都更有意义 - 版本 - 构建版本 + Task Management and Countdown App + Make Every Day More Meaningful + Version + Build Version - - 待办 - 倒数日 - 统计 - 设置 + + To Do + Countdown + Statistics + Settings - - 搜索任务... - 我的任务 - 任务详情 - 添加任务 - 编辑任务 + + Search Tasks... + My Tasks + Task Details + Add Task - 任务列表 - 显示已完成 - 暂无任务 - 点击右下角按钮添加新任务 - 已完成 - 未完成 + Edit Task - 任务标题 - 任务描述 - 选择分类 - 优先级 - 截止日期(可选) - 选择日期 - 标签(用逗号分隔) - 例如:重要,紧急,工作 + Task List + Show Completed + No Tasks + Click the button in the lower right corner to add a new task + Completed + Incomplete - 任务不存在 - 截止日期: - - 创建时间: - 任务描述 + Task Title + Task Description + Select Category + Priority + Due Date (Optional) + Select a date + Tags (comma-separated) + Example: Important, Urgent, Work - 任务添加成功 - 添加任务失败 - 任务更新成功 - 更新任务失败 - 任务删除成功 - 删除任务失败 - 加载任务失败 - 查询任务失败 - 更新任务状态成功 - 更新任务状态失败 + Task does not exist + Due date: + None + Creation time: + Task description - - 倒数日 - 倒数日详情 - 添加倒数日 - 编辑倒数日 + Task added successfully + Task addition failed + Task updated successfully + Task update failed + Task deleted successfully + Task deletion failed + Task loading failed + Task query failed + Task status update successful + Task status update failed - 倒数日列表 - - 暂无倒数日 - 点击右下角按钮添加新的倒数日 - 添加倒数日 + + Countdown + Countdown Details + Add Countdown + Edit Countdown - 倒数日标题 - 倒数日描述 - 目标日期 - 通知设置 - 倒数日不存在 - 事件描述 - 详细信息 - 提醒 + Countdown List + Days + No Countdown + Click the button in the lower right corner to add a new countdown + Add Countdown - 倒数日添加成功 - 添加倒数日失败 - 倒数日更新成功 - 更新倒数日失败 - 倒数日删除成功 - 删除倒数日失败 - 加载倒数日失败 - 查询倒数日失败 + Countdown Title + Countdown Description + Target Date + Notification settings + Countdown day does not exist + Event description + Detailed information + Reminder - - 统计 - 总览 - 分类统计 - 总任务 - 已完成 - 完成率 - 倒数日总数 - 活跃中 + Countdown day added successfully + Countdown day added failed + Countdown day updated successfully + Countdown day updated failed + Countdown day deleted successfully + Countdown day deleted failed + Countdown day loaded failed + Countdown query failed - - 任务 - 倒数日 + + Statistics + Overview + Category statistics + Total tasks + Completed + Completion rate + Total countdown days + Active - 分类管理 - 添加分类 - 编辑分类 - 暂无分类 - 点击右下角按钮添加新分类 - 分类名称 - 输入分类名称... - 分类类型 - 选择颜色 - 选择图标 - 任务分类 - 倒数日分类 + + Task + Countdown - %1$d 个任务 - %1$d 个倒数日 + Category Management + Add Category + Edit Category + No Category + Click the button in the lower right corner to add a new category + Category Name + Enter Category Name... + Category Type + Select Color + Select Icon + Task Category + Countdown Category - - 分类添加成功 - 添加分类失败 - 分类更新成功 - 更新分类失败 - 分类删除成功 - 删除分类失败 - 加载分类失败 - 加载统计数据失败 - 默认分类初始化成功 - 初始化默认分类失败 - 查询分类失败 - 更新分类计数失败 + %1$d tasks + %1$d countdown days - - 进入 - 编辑 - 取消 - 确定 - 删除 - 导出 - 导入 - 返回 - 操作 - 搜索 - 清除 - 全部 - 重试 - 选择文件 - 错误 - 加载失败,请检查网络连接 + + Category added successfully + Category added failed + Category updated successfully + Category updated failed + Category deleted successfully + Category deleted failed + Category loaded failed + Statistics loaded failed + Default category initialized successfully + Default category initialized failed + Category query failed + Category count update failed + + + Enter + Edit + Cancel + Confirm + Delete + Export + Import + Back + Action + Search + Clear + All + Retry + Choose File + Error + Loading... + Loading failed, please check your network connection - - 数据管理 - 导出数据 - 将所有任务和倒数日导出为文件 - 导入数据 - 从文件导入任务和倒数日 - 自动备份 - 定期自动备份数据到云端 - 清除所有数据 - 删除所有任务、倒数日和设置 - 此操作将删除所有任务、倒数日和设置,且无法恢复。 - 清理已完成任务 - 清理过期倒数日 - 删除所有已完成的任务 - 删除所有已过期的倒数日 + + Data Management + Export Data + Export all tasks and countdowns to a file + Import Data + Import tasks and countdowns from a file + Auto Backup + Regularly and automatically back up data to the cloud + Clear All Data + Delete all tasks, countdowns, and settings + This operation will delete all tasks, countdowns, and settings and cannot be restored. + Clear completed tasks + Clear expired countdowns + Delete all completed tasks + Delete all expired countdowns - 备份与恢复 - 数据清理 - 选择要导入的文件 - 选择文件 - 选择导出格式 - JSON格式 - CSV格式 + Backup and restore + Data cleanup + Select the file to import + Select a file + Select the export format + JSON format + CSV format - - 应用设置 - 通用设置 - 数据管理 - 社交分享 - 帮助与反馈 + + App Settings + General Settings + Data Management + Social Sharing + Help and Feedback - 推送通知 - 接收任务和倒数日提醒 - 深色模式 - 使用深色主题 - 语言设置 - 简体中文 + Push Notifications + Receive Task and Countdown Reminders + Dark Mode + Use Dark Theme + Language Settings + Simplified Chinese - 分类管理 - 管理分类 - 数据管理 - 备份和恢复数据 + Management + Manage categories + Data management + Backup and restore data - 分享成就 - 分享任务完成成就 - 推荐给朋友 - 邀请朋友使用 TaskMaster + Share achievements + Share task completion achievements + Recommend to a friend + Invite a friend to use TaskMaster - 意见反馈 - 告诉我们您的想法 - 隐私政策 - 了解我们如何保护您的数据 - 关于应用 - 版本 1.0.0 + Feedback + Tell us what you think + Privacy Policy + Learn how we protect your data + App Review + If you like it, please leave a five-star review in the store + About the App + 100001 + 1.0.1 + Version 1.0.1 - - 意见反馈 - 反馈类型 - 问题反馈 - 功能建议 - 问题描述 - 请详细描述您遇到的问题或建议... - 联系方式(可选) - 您的邮箱地址,方便我们回复 - 感谢您的反馈!我们会尽快处理。 - 请填写反馈内容 - 发送反馈 + + Feedback + Feedback Type + Issue Feedback + Feature Suggestion + Issue Description + Please describe your problem or suggestion in detail... + Contact Information (optional) + Your email address for our response + Thank you for your feedback! We will address it as soon as possible. + Please fill in your feedback + Send Feedback - - 关于 - 应用介绍 - - TaskTTL 是一款现代化的任务管理与倒数日应用, - 支持分类管理、优先级设置与统计分析,让生活更有条理。 - + + About + App Introduction + TaskTTL is a modern task management and countdown app.\nIt supports category management, priority setting, and statistical analysis, making your life more organized. - 隐私协议 + Privacy Policy - 技术栈 - Kotlin Multiplatform(跨平台开发框架) - Jetpack Compose(现代化 UI 框架) - Room Database(本地存储) - Koin(依赖注入框架) - Ktor(网络请求) - MVI Architecture(响应式架构模式) + Technology Stack + Kotlin Multiplatform (Cross-Platform Development Framework) + Jetpack Compose (Modern UI Framework) + Room Database (Local Storage) + Koin (Dependency Injection Framework) + Ktor (Network Request) + MVI Architecture (Responsive Architecture Pattern) - 开发者 - DevTTL 团队 - 联系我们 - 电子邮箱 + Developer + DevTTL Team + Contact Us + Email + mailto:%1$s admin@devttl.com - 官方网站 + Official Website https://devttl.com 2025 - 保留所有权利 + All Rights Reserved - - - - 紧急 + Low + Medium + High + Urgent - 一次 - 每天 - 每周 - 每月 - 关闭 + Once + Daily + Weekly + Monthly + Off - - 工作 - 学习 - 考试 - 项目 + + Work + Study + Exam + Project - - 家庭 - 休闲 - 购物 - 美食 - 家务 + + Family + Leisure + Shopping + Food + Housework - - 健康 - 健身 - 作息 + + Health + Fitness + Days and Nights - - 音乐 - 娱乐 - 摄影 - 影视 + + Music + Entertainment + Photography + Movies - - 出行 - 旅行 - 步行 + + Travel + Travel + Walk - - 生日 - 节日 - 纪念日 + + Birthday + Festival + Anniversary - - 理财 - 目标 - 提醒 + + Financial Planning + Goal + Reminder https://sites.google.com/view/taskttl/privacy - 反馈成功 - 反馈失败,请检查网络连接或稍后重试 + Feedback successful + Feedback failed. Please check your network connection or try again later. diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/core/domain/constant/PointEvent.kt b/composeApp/src/commonMain/kotlin/com/taskttl/core/domain/constant/PointEvent.kt index dbf499b..a2a38db 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/core/domain/constant/PointEvent.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/core/domain/constant/PointEvent.kt @@ -41,8 +41,5 @@ enum class PointEvent(val eventName: String, val eventCode: Int) { AdShownBanner("ad_shown_banner", 9001), AdClickReward("ad_click_reward", 9002); - fun toMap(): Map = mapOf( - "eventName" to eventName, - "eventCode" to eventCode, - ) + fun toMap(): Map = mapOf("eventName" to eventName, "eventCode" to eventCode) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/core/network/ApiConfig.kt b/composeApp/src/commonMain/kotlin/com/taskttl/core/network/ApiConfig.kt index beef5b8..666fdaa 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/core/network/ApiConfig.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/core/network/ApiConfig.kt @@ -7,8 +7,8 @@ package com.taskttl.core.network */ object ApiConfig { /** 基本地址 */ - const val BASE_URL = "http://10.0.0.5:8888/api/v1" - // const val BASE_URL = "https://api.tikttl.com/api/v1" + // const val BASE_URL = "http://10.0.0.5:8888/api/v1" + const val BASE_URL = "https://api.taskttl.com/api/v1" /** 反馈地址 */ const val FEEDBACK_URL = "$BASE_URL/feedback" diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/core/ui/LoadingOverlay.kt b/composeApp/src/commonMain/kotlin/com/taskttl/core/ui/LoadingOverlay.kt new file mode 100644 index 0000000..6f363ad --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/taskttl/core/ui/LoadingOverlay.kt @@ -0,0 +1,131 @@ +package com.taskttl.core.ui + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.blur +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.coroutines.delay +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.ui.tooling.preview.Preview +import taskttl.composeapp.generated.resources.Res +import taskttl.composeapp.generated.resources.loading + +@Composable +fun LoadingOverlay( + visible: Boolean, + messageRes: StringResource = Res.string.loading, +) { + AnimatedVisibility( + visible = visible, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(300)) + ) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.4f)) + .blur(8.dp) + // 拦截所有点击事件,防止点击到底层 + .clickable( + indication = null, // 无点击动画 + interactionSource = remember { MutableInteractionSource() } + ) { }, + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + RotatingGradientRing( + size = 56.dp, + stroke = 6.dp, + gradientColors = listOf( + Color(0xFF4285F4), + Color(0xFF73A7F9), + Color(0xFF4285F4) + ) + ) + Spacer(Modifier.height(16.dp)) + Text( + text = stringResource(messageRes), + color = MaterialTheme.colorScheme.onSurface, + fontSize = 15.sp + ) + } + + // CircularProgressIndicator( + // modifier = Modifier.size(48.dp), + // strokeWidth = 4.dp, + // color = MaterialTheme.colorScheme.primary + // ) + } + } +} + +@Composable +private fun RotatingGradientRing( + size: Dp, + stroke: Dp, + gradientColors: List, +) { + val rotation by rememberInfiniteTransition(label = "").animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable( + animation = tween(1200, easing = LinearEasing) + ), label = "" + ) + + Canvas( + modifier = Modifier + .size(size) + .graphicsLayer(rotationZ = rotation) + ) { + drawCircle( + brush = Brush.sweepGradient(gradientColors), + style = Stroke(width = stroke.toPx(), cap = StrokeCap.Round) + ) + } +} + +@Preview +@Composable +private fun PreviewTaskTTLLoading() { + var show by remember { mutableStateOf(true) } + LaunchedEffect(Unit) { + delay(3000) + show = false + } + LoadingOverlay(visible = show) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/core/ui/LoadingScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/core/ui/LoadingScreen.kt deleted file mode 100644 index 0090c4b..0000000 --- a/composeApp/src/commonMain/kotlin/com/taskttl/core/ui/LoadingScreen.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.taskttl.core.ui - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color - -@Composable -fun LoadingScreen() { - Box( - modifier = Modifier - .fillMaxSize() - .background(Color(0x88000000)) - // 拦截所有点击事件,防止点击到底层 - .clickable( - indication = null, // 无点击动画 - interactionSource = remember { MutableInteractionSource() } - ) { }, - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator() - } -} diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/ExternalAppLauncher.kt b/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/ExternalAppLauncher.kt new file mode 100644 index 0000000..c5295df --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/ExternalAppLauncher.kt @@ -0,0 +1,22 @@ +package com.taskttl.core.utils + +/** + * 外部应用程序启动器 + * @author DevTTL + * @date 2025/03/08 + * @constructor 创建[ExternalAppLauncher] + */ +expect object ExternalAppLauncher { + + /** + * 打开应用程序评级 + */ + suspend fun openAppRating() + + /** + * 打开网址 + * @param [url] 网址 + */ + suspend fun openUrl(url: String) + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/Localization.kt b/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/Localization.kt new file mode 100644 index 0000000..660e9af --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/Localization.kt @@ -0,0 +1,20 @@ +package com.taskttl.core.utils + +/** + * 本地化 + * @author DevTTL + * @date 2025/10/14 + */ +expect object Localization { + /** + * 应用语言 + * @param [iso] + */ + fun applyLanguage(iso: String) + + /** + * 获取设备当前语言 + * @return 设备语言代码,例如"zh"、"en"等 + */ + fun getDeviceLanguage(): String +} diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/LogUtils.kt b/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/LogUtils.kt index 4a9c1c5..8954770 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/LogUtils.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/core/utils/LogUtils.kt @@ -1,8 +1,5 @@ package com.taskttl.core.utils -import io.ktor.client.plugins.logging.Logger - -enum class LogLevel { DEBUG, INFO, WARN, ERROR } /** * 日志工具类 @@ -14,4 +11,6 @@ expect object LogUtils { fun i(tag: String, message: String) fun w(tag: String, message: String) fun e(tag: String, message: String, throwable: Throwable? = null) -} \ No newline at end of file +} + +// enum class LogLevel { DEBUG, INFO, WARN, ERROR } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/data/di/KoinModels.kt b/composeApp/src/commonMain/kotlin/com/taskttl/data/di/KoinModels.kt index e332bee..35fc2ab 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/data/di/KoinModels.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/data/di/KoinModels.kt @@ -8,6 +8,7 @@ import com.taskttl.data.viewmodel.CategoryViewModel import com.taskttl.data.viewmodel.CountdownViewModel import com.taskttl.data.viewmodel.FeedbackViewModel import com.taskttl.data.viewmodel.OnboardingViewModel +import com.taskttl.data.viewmodel.SettingsViewModel import com.taskttl.data.viewmodel.SplashViewModel import com.taskttl.data.viewmodel.TaskViewModel import org.koin.core.KoinApplication @@ -44,5 +45,6 @@ val viewModelModule = module { viewModelOf(::TaskViewModel) viewModelOf(::CategoryViewModel) viewModelOf(::CountdownViewModel) + viewModelOf(::SettingsViewModel) viewModelOf(::FeedbackViewModel) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/data/repository/impl/CategoryRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/taskttl/data/repository/impl/CategoryRepositoryImpl.kt index 762a3ad..878c2a6 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/data/repository/impl/CategoryRepositoryImpl.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/data/repository/impl/CategoryRepositoryImpl.kt @@ -3,18 +3,28 @@ package com.taskttl.data.repository.impl import com.taskttl.data.local.dao.CategoryDao import com.taskttl.data.local.dao.CountdownDao import com.taskttl.data.local.dao.TaskDao -import com.taskttl.data.mapper.CategoryMapper import com.taskttl.data.local.model.Category import com.taskttl.data.local.model.CategoryColor import com.taskttl.data.local.model.CategoryIcon import com.taskttl.data.local.model.CategoryStatistics import com.taskttl.data.local.model.CategoryType +import com.taskttl.data.mapper.CategoryMapper import com.taskttl.data.repository.CategoryRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime +import org.jetbrains.compose.resources.getString +import taskttl.composeapp.generated.resources.Res +import taskttl.composeapp.generated.resources.category_birthday +import taskttl.composeapp.generated.resources.category_book +import taskttl.composeapp.generated.resources.category_briefcase +import taskttl.composeapp.generated.resources.category_exam +import taskttl.composeapp.generated.resources.category_festival +import taskttl.composeapp.generated.resources.category_goal +import taskttl.composeapp.generated.resources.category_heart +import taskttl.composeapp.generated.resources.category_home import kotlin.time.Clock import kotlin.time.ExperimentalTime import kotlin.uuid.ExperimentalUuidApi @@ -123,7 +133,7 @@ class CategoryRepositoryImpl( val defaultTaskCategories = listOf( Category( id = Uuid.random().toString(), - name = "工作", + name = getString(Res.string.category_briefcase), color = CategoryColor.BLUE, icon = CategoryIcon.BRIEFCASE, type = CategoryType.TASK, @@ -132,7 +142,7 @@ class CategoryRepositoryImpl( ), Category( id = Uuid.random().toString(), - name = "生活", + name = getString(Res.string.category_home), color = CategoryColor.GREEN, icon = CategoryIcon.HOME, type = CategoryType.TASK, @@ -141,7 +151,7 @@ class CategoryRepositoryImpl( ), Category( id = Uuid.random().toString(), - name = "健康", + name = getString(Res.string.category_heart), color = CategoryColor.ORANGE, icon = CategoryIcon.HEART, type = CategoryType.TASK, @@ -150,7 +160,7 @@ class CategoryRepositoryImpl( ), Category( id = Uuid.random().toString(), - name = "学习", + name = getString(Res.string.category_book), color = CategoryColor.PURPLE, icon = CategoryIcon.BOOK, type = CategoryType.TASK, @@ -163,7 +173,7 @@ class CategoryRepositoryImpl( val defaultCountdownCategories = listOf( Category( id = Uuid.random().toString(), - name = "生日", + name = getString(Res.string.category_birthday), color = CategoryColor.PINK, icon = CategoryIcon.BIRTHDAY, type = CategoryType.COUNTDOWN, @@ -172,7 +182,7 @@ class CategoryRepositoryImpl( ), Category( id = Uuid.random().toString(), - name = "节日", + name = getString(Res.string.category_festival), color = CategoryColor.CYAN, icon = CategoryIcon.FESTIVAL, type = CategoryType.COUNTDOWN, @@ -181,7 +191,7 @@ class CategoryRepositoryImpl( ), Category( id = Uuid.random().toString(), - name = "目标", + name = getString(Res.string.category_goal), color = CategoryColor.YELLOW, icon = CategoryIcon.GOAL, type = CategoryType.COUNTDOWN, @@ -190,7 +200,7 @@ class CategoryRepositoryImpl( ), Category( id = Uuid.random().toString(), - name = "考试", + name = getString(Res.string.category_exam), color = CategoryColor.GREEN, icon = CategoryIcon.EXAM, type = CategoryType.COUNTDOWN, diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/data/state/SettingsState.kt b/composeApp/src/commonMain/kotlin/com/taskttl/data/state/SettingsState.kt new file mode 100644 index 0000000..6ab1314 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/taskttl/data/state/SettingsState.kt @@ -0,0 +1,63 @@ +package com.taskttl.data.state + +/** + * 设置状态 + * @author DevTTL + * @date 2025/10/14 + * @constructor 创建[SettingsState] + * @param [isLoading] 正在加载 + * @param [error] 错误 + */ +data class SettingsState( + val isLoading: Boolean = false, + val error: String? = null, +) + +/** + * 设置意图 + * @author DevTTL + * @date 2025/10/14 + * @constructor 创建[SettingsIntent] + */ +sealed class SettingsIntent { + /** + * 打开应用评分 + * @author DevTTL + * @date 2025/10/14 + */ + object OpenAppRating: SettingsIntent() + + /** + * 打开网址 + * @author DevTTL + * @date 2025/10/14 + * @constructor 创建[OpenUrl] + * @param [url] 网址 + */ + class OpenUrl(val url:String): SettingsIntent() +} + + +/** + * 设置效果 + * @author DevTTL + * @date 2025/10/14 + * @constructor 创建[SettingsEffect] + */ +sealed class SettingsEffect { + /** + * 导航返回 + * @author admin + * @date 2025/10/12 + */ + object NavigateBack : SettingsEffect() + + /** + * 显示消息 + * @author admin + * @date 2025/10/12 + * @constructor 创建[ShowMessage] + * @param [message] 消息 + */ + data class ShowMessage(val message: String) : SettingsEffect() +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/data/viewmodel/SettingsViewModel.kt b/composeApp/src/commonMain/kotlin/com/taskttl/data/viewmodel/SettingsViewModel.kt new file mode 100644 index 0000000..669989f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/taskttl/data/viewmodel/SettingsViewModel.kt @@ -0,0 +1,58 @@ +package com.taskttl.data.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.taskttl.core.utils.ExternalAppLauncher +import com.taskttl.data.state.SettingsEffect +import com.taskttl.data.state.SettingsIntent +import com.taskttl.data.state.SettingsState +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +/** + * 反馈视图模型 + * @author admin + * @date 2025/10/12 + * @constructor 创建[SettingsViewModel] + */ +class SettingsViewModel() : ViewModel() { + + private val _state = MutableStateFlow(SettingsState()) + val state: StateFlow = _state.asStateFlow() + + private val _effects = MutableSharedFlow() + val effects: SharedFlow = _effects.asSharedFlow() + + + fun handleIntent(intent: SettingsIntent) { + when (intent) { + is SettingsIntent.OpenAppRating -> openAppRating() + is SettingsIntent.OpenUrl -> openUrl(intent.url) + } + } + + private fun openAppRating() { + viewModelScope.launch { + ExternalAppLauncher.openAppRating() + } + } + + private fun openUrl(url:String) { + viewModelScope.launch { + ExternalAppLauncher.openUrl(url) + } + } + + /** + * 清除错误 + */ + private fun clearError() { + _state.value = _state.value.copy(error = null) + } + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryEditorScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryEditorScreen.kt index 5693d72..6b5eae4 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryEditorScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryEditorScreen.kt @@ -17,9 +17,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -48,7 +45,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay import com.taskttl.core.utils.ToastUtils import com.taskttl.data.local.model.Category import com.taskttl.data.local.model.CategoryColor @@ -56,7 +53,6 @@ import com.taskttl.data.local.model.CategoryIcon import com.taskttl.data.local.model.CategoryType import com.taskttl.data.state.CategoryEffect import com.taskttl.data.state.CategoryIntent -import com.taskttl.data.state.TaskEffect import com.taskttl.data.viewmodel.CategoryViewModel import com.taskttl.ui.components.AppHeader import kotlinx.datetime.TimeZone @@ -89,7 +85,7 @@ import kotlin.uuid.Uuid fun CategoryEditScreen( categoryId: String? = null, onNavigateBack: () -> Unit, - viewModel: CategoryViewModel = koinViewModel() + viewModel: CategoryViewModel = koinViewModel(), ) { LaunchedEffect(Unit) { viewModel.effects.collect { effect -> @@ -173,6 +169,7 @@ fun CategoryEditScreen( LazyColumn( modifier = Modifier .fillMaxSize() + .background(MaterialTheme.colorScheme.background) .padding(16.dp), verticalArrangement = Arrangement.spacedBy(24.dp) ) { @@ -257,7 +254,7 @@ fun CategoryEditScreen( verticalArrangement = Arrangement.spacedBy(4.dp), modifier = Modifier.fillMaxWidth() ) { - CategoryIcon.entries.forEach {item -> + CategoryIcon.entries.forEach { item -> IconOption( item = item, selected = icon == item, @@ -283,7 +280,7 @@ fun CategoryEditScreen( } } } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } @@ -296,7 +293,7 @@ private fun CategoryTypeOption( icon: ImageVector, text: String, selected: Boolean, - onClick: () -> Unit + onClick: () -> Unit, ) { Card( modifier = modifier @@ -367,7 +364,7 @@ private fun ColorOption( colorLong: CategoryColor, selected: Boolean, onClick: () -> Unit, - modifier: Modifier + modifier: Modifier, ) { Box( modifier = modifier diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryScreen.kt index ffd5793..a057b69 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/category/CategoryScreen.kt @@ -56,7 +56,8 @@ import androidx.navigation.NavHostController import com.taskttl.core.routes.Routes.Main import com.taskttl.core.ui.ActionButtonListItem import com.taskttl.core.ui.ErrorDialog -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay + import com.taskttl.core.utils.ToastUtils import com.taskttl.data.local.model.Category import com.taskttl.data.local.model.CategoryType @@ -127,7 +128,7 @@ fun CategoryScreen( Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .padding(16.dp) ) { @@ -210,7 +211,7 @@ fun CategoryScreen( contentDescription = stringResource(Res.string.title_add_category) ) } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownDetailScreen.kt index 94fd294..5308d34 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownDetailScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.material.icons.filled.CalendarToday import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -33,7 +34,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.taskttl.core.ui.Chip -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay + import com.taskttl.core.utils.DateUtils import com.taskttl.data.state.CountdownEffect import com.taskttl.data.viewmodel.CountdownViewModel @@ -104,7 +106,7 @@ fun CountdownDetailScreen( Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .padding(16.dp) .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally @@ -234,7 +236,7 @@ fun CountdownDetailScreen( } } } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownEditorScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownEditorScreen.kt index 66ec587..4e0d4f7 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownEditorScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownEditorScreen.kt @@ -1,5 +1,6 @@ package com.taskttl.presentation.countdown +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -40,7 +41,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay + import com.taskttl.core.utils.ToastUtils import com.taskttl.data.local.model.Category import com.taskttl.data.local.model.Countdown @@ -163,6 +165,7 @@ fun CountdownEditScreen( Column( modifier = Modifier .fillMaxSize() + .background(MaterialTheme.colorScheme.background) .verticalScroll(rememberScrollState()) .padding(16.dp), ) { @@ -283,6 +286,6 @@ fun CountdownEditScreen( } } } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownScreen.kt index 079bb70..6e51e65 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/countdown/CountdownScreen.kt @@ -58,12 +58,10 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController import com.taskttl.core.routes.Routes import com.taskttl.core.ui.ErrorDialog -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay import com.taskttl.core.utils.DateUtils import com.taskttl.core.utils.ToastUtils import com.taskttl.data.local.model.Countdown -import com.taskttl.data.state.CategoryEffect -import com.taskttl.data.state.CategoryIntent import com.taskttl.data.state.CountdownEffect import com.taskttl.data.state.CountdownIntent import com.taskttl.data.viewmodel.CountdownViewModel @@ -75,19 +73,18 @@ import org.koin.compose.viewmodel.koinViewModel import taskttl.composeapp.generated.resources.Res import taskttl.composeapp.generated.resources.delete import taskttl.composeapp.generated.resources.desc_add_countdown +import taskttl.composeapp.generated.resources.edit import taskttl.composeapp.generated.resources.label_countdown_list import taskttl.composeapp.generated.resources.label_days -import taskttl.composeapp.generated.resources.edit import taskttl.composeapp.generated.resources.text_add_countdown_tip import taskttl.composeapp.generated.resources.text_no_countdowns import taskttl.composeapp.generated.resources.title_countdown -import taskttl.composeapp.generated.resources.title_edit_countdown @Composable @Preview fun CountdownScreen( navController: NavHostController, - viewModel: CountdownViewModel = koinViewModel() + viewModel: CountdownViewModel = koinViewModel(), ) { val state by viewModel.state.collectAsState() @@ -97,6 +94,7 @@ fun CountdownScreen( is CountdownEffect.ShowMessage -> { ToastUtils.show(effect.message) } + is CountdownEffect.NavigateBack -> { navController.popBackStack() } @@ -125,7 +123,7 @@ fun CountdownScreen( Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .padding(16.dp) ) { // 分类筛选 @@ -220,7 +218,7 @@ fun CountdownScreen( contentDescription = stringResource(Res.string.desc_add_countdown) ) } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } @@ -229,7 +227,7 @@ fun CountdownCard( countdown: Countdown, onEdit: () -> Unit = {}, onDelete: () -> Unit = {}, - onCardClick: () -> Unit = {} + onCardClick: () -> Unit = {}, ) { val countdownTime = DateUtils.calculateCountdownTime(countdown.targetDate) countdown.category @@ -422,7 +420,7 @@ fun IconBut(onClick: () -> Unit = {}, icon: ImageVector) { @Composable fun ReminderDialog( onDismiss: () -> Unit, - onSave: (String) -> Unit + onSave: (String) -> Unit, ) { var isEnabled by remember { mutableStateOf(true) } var timeBefore by remember { mutableStateOf("1天前") } @@ -490,7 +488,7 @@ fun MoreActionsDialog( onDismiss: () -> Unit, onEdit: () -> Unit, onShare: () -> Unit, - onDelete: () -> Unit + onDelete: () -> Unit, ) { AlertDialog( onDismissRequest = { onDismiss() }, @@ -521,7 +519,7 @@ private fun ActionItem( text: String, icon: ImageVector, danger: Boolean = false, - onClick: () -> Unit + onClick: () -> Unit, ) { Row( modifier = Modifier diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/AboutScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/AboutScreen.kt index 0f087b5..80fefda 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/AboutScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/AboutScreen.kt @@ -1,6 +1,7 @@ package com.taskttl.presentation.settings import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -31,15 +32,20 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.taskttl.data.state.SettingsIntent +import com.taskttl.data.viewmodel.SettingsViewModel import com.taskttl.ui.components.AppHeader import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource +import org.koin.compose.viewmodel.koinViewModel import taskttl.composeapp.generated.resources.Res import taskttl.composeapp.generated.resources.all_rights_reserved import taskttl.composeapp.generated.resources.app_intro_content import taskttl.composeapp.generated.resources.app_intro_title import taskttl.composeapp.generated.resources.app_name import taskttl.composeapp.generated.resources.app_name_description +import taskttl.composeapp.generated.resources.app_version_code +import taskttl.composeapp.generated.resources.app_version_name import taskttl.composeapp.generated.resources.build_version import taskttl.composeapp.generated.resources.contact_us import taskttl.composeapp.generated.resources.copyright_year @@ -47,6 +53,7 @@ import taskttl.composeapp.generated.resources.developer_text import taskttl.composeapp.generated.resources.devttl_team import taskttl.composeapp.generated.resources.email import taskttl.composeapp.generated.resources.email_text +import taskttl.composeapp.generated.resources.setting_privacy_email_uri import taskttl.composeapp.generated.resources.tech_stack import taskttl.composeapp.generated.resources.tech_stack_compose import taskttl.composeapp.generated.resources.tech_stack_kmp @@ -62,7 +69,8 @@ import taskttl.composeapp.generated.resources.web_url @OptIn(ExperimentalMaterial3Api::class) @Composable fun AboutScreen( - onNavigateBack: () -> Unit + onNavigateBack: () -> Unit, + viewModel: SettingsViewModel = koinViewModel(), ) { Box(modifier = Modifier.fillMaxSize()) { Column( @@ -77,7 +85,7 @@ fun AboutScreen( Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .verticalScroll(rememberScrollState()) .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally @@ -118,9 +126,15 @@ fun AboutScreen( Column( modifier = Modifier.padding(16.dp) ) { - AboutInfoRow(labelRes = Res.string.version, value = "1.0.0") + AboutInfoRow( + labelRes = Res.string.version, + valueRes = Res.string.app_version_name + ) Spacer(modifier = Modifier.height(8.dp)) - AboutInfoRow(labelRes = Res.string.build_version, value = "1") + AboutInfoRow( + labelRes = Res.string.build_version, + valueRes = Res.string.app_version_code + ) } } Spacer(modifier = Modifier.height(16.dp)) @@ -148,29 +162,29 @@ fun AboutScreen( } Spacer(modifier = Modifier.height(16.dp)) // 技术栈 - Card( - modifier = Modifier.fillMaxWidth() - ) { - Column( - modifier = Modifier.padding(16.dp) - ) { - Text( - text = stringResource(Res.string.tech_stack), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Medium - ) - - Spacer(modifier = Modifier.height(12.dp)) - - TechStackItem("Kotlin Multiplatform", Res.string.tech_stack_kmp) - TechStackItem("Jetpack Compose", Res.string.tech_stack_compose) - TechStackItem("Room Database", Res.string.tech_stack_room) - TechStackItem("Koin", Res.string.tech_stack_koin) - TechStackItem("Ktor", Res.string.tech_stack_ktor) - TechStackItem("MVI Architecture", Res.string.tech_stack_mvi) - } - } - Spacer(modifier = Modifier.height(16.dp)) + // Card( + // modifier = Modifier.fillMaxWidth() + // ) { + // Column( + // modifier = Modifier.padding(16.dp) + // ) { + // Text( + // text = stringResource(Res.string.tech_stack), + // style = MaterialTheme.typography.titleMedium, + // fontWeight = FontWeight.Medium + // ) + // + // Spacer(modifier = Modifier.height(12.dp)) + // + // TechStackItem("Kotlin Multiplatform", Res.string.tech_stack_kmp) + // TechStackItem("Jetpack Compose", Res.string.tech_stack_compose) + // TechStackItem("Room Database", Res.string.tech_stack_room) + // TechStackItem("Koin", Res.string.tech_stack_koin) + // TechStackItem("Ktor", Res.string.tech_stack_ktor) + // TechStackItem("MVI Architecture", Res.string.tech_stack_mvi) + // } + // } + // Spacer(modifier = Modifier.height(16.dp)) // 开发者信息 Card( modifier = Modifier.fillMaxWidth() @@ -208,18 +222,22 @@ fun AboutScreen( Spacer(modifier = Modifier.height(12.dp)) + val email = stringResource(Res.string.email) + val emailUrl = stringResource(Res.string.setting_privacy_email_uri, email) ContactItem( icon = Icons.Default.Email, labelRes = Res.string.email_text, - valueRes = Res.string.email + valueRes = Res.string.email, + onClick = { viewModel.handleIntent(SettingsIntent.OpenUrl(emailUrl)) } ) Spacer(modifier = Modifier.height(8.dp)) - + val url = stringResource(Res.string.web_url) ContactItem( icon = Icons.Default.Language, labelRes = Res.string.web_text, - valueRes = Res.string.web_url + valueRes = Res.string.web_url, + onClick = { viewModel.handleIntent(SettingsIntent.OpenUrl(url)) } ) } } @@ -243,7 +261,7 @@ fun AboutScreen( @Composable private fun AboutInfoRow( labelRes: StringResource, - value: String + valueRes: StringResource, ) { Row( modifier = Modifier.fillMaxWidth(), @@ -255,7 +273,7 @@ private fun AboutInfoRow( color = MaterialTheme.colorScheme.onSurfaceVariant ) Text( - text = value, + text = stringResource(valueRes), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium ) @@ -265,7 +283,7 @@ private fun AboutInfoRow( @Composable private fun TechStackItem( name: String, - descriptionRes: StringResource + descriptionRes: StringResource, ) { Column { Text( @@ -286,10 +304,13 @@ private fun TechStackItem( private fun ContactItem( icon: ImageVector, labelRes: StringResource, - valueRes: StringResource + valueRes: StringResource, + onClick: (() -> Unit)? = null, ) { Row( - verticalAlignment = Alignment.CenterVertically + modifier = Modifier.fillMaxWidth() + .clickable(enabled = onClick != null) { onClick?.invoke() }, + verticalAlignment = Alignment.CenterVertically, ) { Icon( imageVector = icon, diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/DataManagementScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/DataManagementScreen.kt index 1fb8de8..380d30c 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/DataManagementScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/DataManagementScreen.kt @@ -100,7 +100,7 @@ fun DataManagementScreen( LazyColumn( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/FeedbackScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/FeedbackScreen.kt index 84e1abb..a90e46d 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/FeedbackScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/FeedbackScreen.kt @@ -42,7 +42,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.taskttl.core.domain.FeedbackType import com.taskttl.core.ui.ErrorDialog -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay + import com.taskttl.core.utils.ToastUtils import com.taskttl.data.network.domain.req.FeedbackReq import com.taskttl.data.state.FeedbackEffect @@ -110,7 +111,7 @@ fun FeedbackScreen( Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .verticalScroll(rememberScrollState()) .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) @@ -218,7 +219,7 @@ fun FeedbackScreen( Spacer(modifier = Modifier.height(32.dp)) } } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/PrivacyScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/PrivacyScreen.kt index 33b0ff7..5737786 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/PrivacyScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/PrivacyScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -29,7 +30,7 @@ fun PrivacyScreen(onNavigateBack: () -> Unit) { Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)), + .background(MaterialTheme.colorScheme.background), ) { DevTTLWebView( modifier = Modifier.fillMaxSize(), diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/SettingsScreen.kt index 449f1ba..26b47b1 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/settings/SettingsScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Storage import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -40,10 +41,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController import com.taskttl.core.routes.Routes +import com.taskttl.core.utils.ExternalAppLauncher +import com.taskttl.data.state.SettingsIntent +import com.taskttl.data.viewmodel.SettingsViewModel import com.taskttl.ui.components.AppHeader import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview +import org.koin.compose.viewmodel.koinViewModel import taskttl.composeapp.generated.resources.Res import taskttl.composeapp.generated.resources.section_data_management import taskttl.composeapp.generated.resources.section_general_settings @@ -65,6 +70,8 @@ import taskttl.composeapp.generated.resources.setting_language import taskttl.composeapp.generated.resources.setting_language_desc import taskttl.composeapp.generated.resources.setting_privacy_policy import taskttl.composeapp.generated.resources.setting_privacy_policy_desc +import taskttl.composeapp.generated.resources.setting_privacy_rate +import taskttl.composeapp.generated.resources.setting_privacy_rate_desc import taskttl.composeapp.generated.resources.setting_push_notification import taskttl.composeapp.generated.resources.setting_push_notification_desc import taskttl.composeapp.generated.resources.setting_share_achievement @@ -79,6 +86,7 @@ import taskttl.composeapp.generated.resources.title_app_settings @Preview fun SettingsScreen( navController: NavHostController, + viewModel: SettingsViewModel = koinViewModel() ) { Box( modifier = Modifier @@ -96,7 +104,7 @@ fun SettingsScreen( Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .verticalScroll(rememberScrollState()) .padding(16.dp) ) { @@ -218,13 +226,18 @@ fun SettingsScreen( showArrow = true, onClick = { navController.navigate(Routes.Main.Settings.Privacy) } ) + SettingItem( + titleRes = Res.string.setting_privacy_rate, + descriptionRes = Res.string.setting_privacy_rate_desc, + showArrow = true, + onClick = { viewModel.handleIntent(SettingsIntent.OpenAppRating) } + ) SettingItem( titleRes = Res.string.setting_about_app, descriptionRes = Res.string.setting_about_app_desc, showArrow = true, onClick = { navController.navigate(Routes.Main.Settings.About) } ) - Spacer(modifier = Modifier.height(24.dp)) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/statistics/StatisticsScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/statistics/StatisticsScreen.kt index e6eb763..894f70a 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/statistics/StatisticsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/statistics/StatisticsScreen.kt @@ -44,7 +44,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import com.taskttl.core.routes.Routes import com.taskttl.core.ui.Chip -import com.taskttl.core.ui.LoadingScreen + import com.taskttl.data.local.model.Category import com.taskttl.data.state.CountdownIntent import com.taskttl.data.state.TaskIntent @@ -95,7 +95,7 @@ fun StatisticsScreen( LazyColumn( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskDetailScreen.kt index 606dfca..a81e8a2 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskDetailScreen.kt @@ -37,7 +37,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay + import com.taskttl.data.state.TaskEffect import com.taskttl.data.state.TaskIntent import com.taskttl.data.viewmodel.TaskViewModel @@ -111,6 +112,7 @@ fun TaskDetailScreen( Column( modifier = Modifier .fillMaxSize() + .background(MaterialTheme.colorScheme.background) .verticalScroll(rememberScrollState()) .padding(16.dp), verticalArrangement = Arrangement.Top @@ -229,6 +231,6 @@ fun TaskDetailScreen( } } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskEditorScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskEditorScreen.kt index e2b42a7..8ff1d08 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskEditorScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskEditorScreen.kt @@ -1,5 +1,6 @@ package com.taskttl.presentation.task +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -39,7 +40,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.taskttl.core.routes.Routes -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay + import com.taskttl.core.utils.ToastUtils import com.taskttl.data.local.model.Category import com.taskttl.data.local.model.Task @@ -168,6 +170,7 @@ fun TaskEditorScreen( Column( modifier = Modifier .fillMaxSize() + .background(MaterialTheme.colorScheme.background) .verticalScroll(rememberScrollState()) .padding(16.dp), ) { @@ -292,6 +295,6 @@ fun TaskEditorScreen( ) } } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskScreen.kt b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskScreen.kt index cd0b7ea..34c4f88 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/presentation/task/TaskScreen.kt @@ -54,7 +54,8 @@ import androidx.navigation.NavHostController import com.taskttl.core.routes.Routes import com.taskttl.core.ui.ActionButtonListItem import com.taskttl.core.ui.ErrorDialog -import com.taskttl.core.ui.LoadingScreen +import com.taskttl.core.ui.LoadingOverlay + import com.taskttl.core.utils.ToastUtils import com.taskttl.data.local.model.Task import com.taskttl.data.state.TaskEffect @@ -129,7 +130,7 @@ fun TaskScreen( Column( modifier = Modifier .fillMaxSize() - .background(Color(0xFFF5F5F5)) + .background(MaterialTheme.colorScheme.background) .padding(16.dp) ) { if (state.isSearch) { @@ -258,7 +259,7 @@ fun TaskScreen( contentDescription = stringResource(Res.string.title_add_task) ) } - if (state.isLoading) LoadingScreen() + LoadingOverlay(state.isLoading) } } diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/ui/components/AppHeader.kt b/composeApp/src/commonMain/kotlin/com/taskttl/ui/components/AppHeader.kt index 4883abd..7a54f50 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/ui/components/AppHeader.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/ui/components/AppHeader.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -39,16 +40,19 @@ fun AppHeader( showBack: Boolean = false, onBackClick: (() -> Unit)? = null, trailingIcon: ImageVector? = null, - onTrailingClick: (() -> Unit)? = null + onTrailingClick: (() -> Unit)? = null, ) { + val gradient = Brush.linearGradient( + colors = listOf( + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.secondary + ) + ) + Row( modifier = Modifier .fillMaxWidth() - .background( - Brush.linearGradient( - colors = listOf(Color(0xFF667EEA), Color(0xFF764BA2)) - ) - ) + .background(brush = gradient) .padding(horizontal = 20.dp, vertical = 15.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically diff --git a/composeApp/src/commonMain/kotlin/com/taskttl/ui/theme/Theme.kt b/composeApp/src/commonMain/kotlin/com/taskttl/ui/theme/Theme.kt index d314011..b201f86 100644 --- a/composeApp/src/commonMain/kotlin/com/taskttl/ui/theme/Theme.kt +++ b/composeApp/src/commonMain/kotlin/com/taskttl/ui/theme/Theme.kt @@ -9,11 +9,11 @@ import androidx.compose.ui.graphics.Color /** 浅色方案 */ private val LightColorScheme = lightColorScheme( - primary = Color(0xFF6750A4), + primary = Color(0xFF667EEA), onPrimary = Color(0xFFFFFFFF), primaryContainer = Color(0xFFEADDFF), onPrimaryContainer = Color(0xFF21005D), - secondary = Color(0xFF625B71), + secondary = Color(0xFF764BA2), onSecondary = Color(0xFFFFFFFF), secondaryContainer = Color(0xFFE8DEF8), onSecondaryContainer = Color(0xFF1D192B), @@ -25,7 +25,7 @@ private val LightColorScheme = lightColorScheme( onError = Color(0xFFFFFFFF), errorContainer = Color(0xFFFFDAD6), onErrorContainer = Color(0xFF410002), - background = Color(0xFFFFFBFE), + background = Color(0xFFF5F5F5), onBackground = Color(0xFF1C1B1F), surface = Color(0xFFFFFBFE), onSurface = Color(0xFF1C1B1F), diff --git a/composeApp/src/iosMain/kotlin/com/taskttl/core/utils/Localization.ios.kt b/composeApp/src/iosMain/kotlin/com/taskttl/core/utils/Localization.ios.kt new file mode 100644 index 0000000..c1bec28 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/com/taskttl/core/utils/Localization.ios.kt @@ -0,0 +1,14 @@ +package com.taskttl.core.utils + +import platform.Foundation.NSLocale + +@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") +actual object Localization { + actual fun applyLanguage(iso: String) { + NSUserDefaults.standardUserDefaults.setObject(arrayListOf(iso), "AppleLanguages") + } + + actual fun getDeviceLanguage(): String { + return NSLocale.currentLocale.languageCode ?: "en" + } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eafaac5..c975786 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,8 +37,8 @@ android-compileSdk = "36" android-minSdk = "24" android-targetSdk = "36" -android-versionCode = "100000" -android-versionName = "1.0.0" +android-versionCode = "100001" +android-versionName = "1.0.1" android-facebookAppId = "1203530117944408" android-facebookClientToken = "1ee2da9430c1a589e8aa623bfaaaa586"