This commit is contained in:
Hsy
2025-10-15 10:15:55 +08:00
parent ede72fdedc
commit 6e8529caad
48 changed files with 880 additions and 427 deletions

View File

@@ -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\"")

Binary file not shown.

View File

@@ -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">
<activity
android:exported="true"
@@ -27,15 +25,33 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="dl.taskttl.com"
android:pathPrefix="/"
android:scheme="https" />
</intent-filter>
</activity>
<!-- Facebook -->
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="${facebookAppId}" />
<meta-data
android:name="com.facebook.sdk.ClientToken"
android:value="${facebookClientToken}" />
</application>
</manifest>
<queries>
<!--&lt;!&ndash; TikTok 国际版 &ndash;&gt;-->
<!--<package android:name="com.zhiliaoapp.musically" />-->
<!--&lt;!&ndash; TikTok 部分地区版本 &ndash;&gt;-->
<!--<package android:name="com.ss.android.ugc.trill" />-->
<!--&lt;!&ndash; 抖音(中国版) &ndash;&gt;-->
<!--<package android:name="com.ss.android.ugc.aweme" />-->
<!-- 用于打开网页链接 -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
</manifest>

View File

@@ -67,7 +67,7 @@ actual object DeviceUtils {
uniqueId = getUniqueId(),
deviceInfo = Build.MODEL ?: "0",
deviceVersion = Build.VERSION.RELEASE,
language = ""
language = Localization.getDeviceLanguage()
)
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,313 +1,317 @@
<resources>
<!-- Onboarding -->
<string name="continue_text">继续</string>
<string name="skip_text">跳过</string>
<string name="get_start_text">开始使用</string>
<string name="continue_text">Continue</string>
<string name="skip_text">Skip</string>
<string name="get_start_text">Get Started</string>
<string name="onboarding_welcome_title">欢迎使用 TaskTTL</string>
<string name="onboarding_welcome_desc">一个简洁而强大的任务管理工具,帮助您高效管理日常任务和重要日期</string>
<string name="onboarding_smart_title">智能任务管理</string>
<string name="onboarding_smart_desc">创建、分类和跟踪您的任务。设置优先级,添加截止日期,让工作更有条理</string>
<string name="onboarding_dates_title">重要日期提醒</string>
<string name="onboarding_dates_desc">设置重要日期的倒数计时,永远不会错过生日、纪念日或重要的截止日期</string>
<string name="onboarding_ready_title">准备就绪!</string>
<string name="onboarding_ready_desc">现在您可以开始创建第一个任务,让我们一起提高工作效率吧!</string>
<string name="onboarding_welcome_title">Welcome to TaskTTL</string>
<string name="onboarding_welcome_desc">A simple yet powerful task management tool that helps you efficiently manage your daily tasks and important dates</string>
<string name="onboarding_smart_title">Smart Task Management</string>
<string name="onboarding_smart_desc">Create, categorize, and track your tasks. Set priorities and add due dates for better organization</string>
<string name="onboarding_dates_title">Important Date Reminders</string>
<string name="onboarding_dates_desc">Set countdowns to important dates and never miss a birthday, anniversary, or important deadline</string>
<string name="onboarding_ready_title">Ready! </string>
<string name="onboarding_ready_desc">Now you can start creating your first task. Let's improve your productivity together! </string>
<!-- 应用信息 -->
<!-- App Information -->
<string name="app_name">TaskTTL</string>
<string name="app_name_description">任务管理与倒数日应用</string>
<string name="app_name_remark">让每一天都更有意义</string>
<string name="version">版本</string>
<string name="build_version">构建版本</string>
<string name="app_name_description">Task Management and Countdown App</string>
<string name="app_name_remark">Make Every Day More Meaningful</string>
<string name="version">Version</string>
<string name="build_version">Build Version</string>
<!-- 导航栏 -->
<string name="nav_todo">待办</string>
<string name="nav_countdown">倒数日</string>
<string name="nav_statistics">统计</string>
<string name="nav_settings">设置</string>
<!-- Navigation Bar -->
<string name="nav_todo">To Do</string>
<string name="nav_countdown">Countdown</string>
<string name="nav_statistics">Statistics</string>
<string name="nav_settings">Settings</string>
<!-- 任务模块 -->
<string name="search_placeholder">搜索任务...</string>
<string name="title_task">我的任务</string>
<string name="title_task_info">任务详情</string>
<string name="title_add_task">添加任务</string>
<string name="title_edit_task">编辑任务</string>
<!-- Task Module -->
<string name="search_placeholder">Search Tasks...</string>
<string name="title_task">My Tasks</string>
<string name="title_task_info">Task Details</string>
<string name="title_add_task">Add Task</string>
<string name="label_task_list">任务列表</string>
<string name="label_show_completed">显示已完成</string>
<string name="text_no_tasks">暂无任务</string>
<string name="text_add_task_hint">点击右下角按钮添加新任务</string>
<string name="desc_completed">已完成</string>
<string name="desc_incomplete">未完成</string>
<string name="title_edit_task">Edit Task</string>
<string name="title_task_title">任务标题</string>
<string name="title_task_description">任务描述</string>
<string name="title_select_category">选择分类</string>
<string name="title_priority">优先级</string>
<string name="title_due_date">截止日期(可选)</string>
<string name="desc_select_date">选择日期</string>
<string name="title_tags">标签(用逗号分隔)</string>
<string name="hint_tags">例如:重要,紧急,工作</string>
<string name="label_task_list">Task List</string>
<string name="label_show_completed">Show Completed</string>
<string name="text_no_tasks">No Tasks</string>
<string name="text_add_task_hint">Click the button in the lower right corner to add a new task</string>
<string name="desc_completed">Completed</string>
<string name="desc_incomplete">Incomplete</string>
<string name="text_task_not_found">任务不存在</string>
<string name="label_due_date">截止日期:</string>
<string name="label_none"></string>
<string name="label_created_at">创建时间:</string>
<string name="label_description">任务描述</string>
<string name="title_task_title">Task Title</string>
<string name="title_task_description">Task Description</string>
<string name="title_select_category">Select Category</string>
<string name="title_priority">Priority</string>
<string name="title_due_date">Due Date (Optional)</string>
<string name="desc_select_date">Select a date</string>
<string name="title_tags">Tags (comma-separated)</string>
<string name="hint_tags">Example: Important, Urgent, Work</string>
<string name="task_add_success">任务添加成功</string>
<string name="task_add_failed">添加任务失败</string>
<string name="task_update_success">任务更新成功</string>
<string name="task_update_failed">更新任务失败</string>
<string name="task_delete_success">任务删除成功</string>
<string name="task_delete_failed">删除任务失败</string>
<string name="task_load_failed">加载任务失败</string>
<string name="task_query_failed">查询任务失败</string>
<string name="task_status_update_success">更新任务状态成功</string>
<string name="task_status_update_failed">更新任务状态失败</string>
<string name="text_task_not_found">Task does not exist</string>
<string name="label_due_date">Due date:</string>
<string name="label_none">None</string>
<string name="label_created_at">Creation time:</string>
<string name="label_description">Task description</string>
<!-- 倒数日模块 -->
<string name="title_countdown">倒数日</string>
<string name="title_countdown_info">倒数日详情</string>
<string name="title_add_countdown">添加倒数日</string>
<string name="title_edit_countdown">编辑倒数日</string>
<string name="task_add_success">Task added successfully</string>
<string name="task_add_failed">Task addition failed</string>
<string name="task_update_success">Task updated successfully</string>
<string name="task_update_failed">Task update failed</string>
<string name="task_delete_success">Task deleted successfully</string>
<string name="task_delete_failed">Task deletion failed</string>
<string name="task_load_failed">Task loading failed</string>
<string name="task_query_failed">Task query failed</string>
<string name="task_status_update_success">Task status update successful</string>
<string name="task_status_update_failed">Task status update failed</string>
<string name="label_countdown_list">倒数日列表</string>
<string name="label_days"></string>
<string name="text_no_countdowns">暂无倒数日</string>
<string name="text_add_countdown_tip">点击右下角按钮添加新的倒数日</string>
<string name="desc_add_countdown">添加倒数日</string>
<!-- Countdown Module -->
<string name="title_countdown">Countdown</string>
<string name="title_countdown_info">Countdown Details</string>
<string name="title_add_countdown">Add Countdown</string>
<string name="title_edit_countdown">Edit Countdown</string>
<string name="label_countdown_title">倒数日标题</string>
<string name="label_countdown_description">倒数日描述</string>
<string name="label_target_date">目标日期</string>
<string name="label_notification_setting">通知设置</string>
<string name="countdown_not_found">倒数日不存在</string>
<string name="event_description">事件描述</string>
<string name="detail_information">详细信息</string>
<string name="reminder">提醒</string>
<string name="label_countdown_list">Countdown List</string>
<string name="label_days">Days</string>
<string name="text_no_countdowns">No Countdown</string>
<string name="text_add_countdown_tip">Click the button in the lower right corner to add a new countdown</string>
<string name="desc_add_countdown">Add Countdown</string>
<string name="countdown_add_success">倒数日添加成功</string>
<string name="countdown_add_failed">添加倒数日失败</string>
<string name="countdown_update_success">倒数日更新成功</string>
<string name="countdown_update_failed">更新倒数日失败</string>
<string name="countdown_delete_success">倒数日删除成功</string>
<string name="countdown_delete_failed">删除倒数日失败</string>
<string name="countdown_load_failed">加载倒数日失败</string>
<string name="countdown_query_failed">查询倒数日失败</string>
<string name="label_countdown_title">Countdown Title</string>
<string name="label_countdown_description">Countdown Description</string>
<string name="label_target_date">Target Date</string>
<string name="label_notification_setting">Notification settings</string>
<string name="countdown_not_found">Countdown day does not exist</string>
<string name="event_description">Event description</string>
<string name="detail_information">Detailed information</string>
<string name="reminder">Reminder</string>
<!-- 统计模块 -->
<string name="title_statistics">统计</string>
<string name="overview">总览</string>
<string name="category_statistics">分类统计</string>
<string name="total_tasks">总任务</string>
<string name="completed">已完成</string>
<string name="completion_rate">完成率</string>
<string name="total_countdowns">倒数日总数</string>
<string name="active">活跃中</string>
<string name="countdown_add_success">Countdown day added successfully</string>
<string name="countdown_add_failed">Countdown day added failed</string>
<string name="countdown_update_success">Countdown day updated successfully</string>
<string name="countdown_update_failed">Countdown day updated failed</string>
<string name="countdown_delete_success">Countdown day deleted successfully</string>
<string name="countdown_delete_failed">Countdown day deleted failed</string>
<string name="countdown_load_failed">Countdown day loaded failed</string>
<string name="countdown_query_failed">Countdown query failed</string>
<!-- 分类模块 -->
<string name="category_task">任务</string>
<string name="category_countdown">倒数日</string>
<!-- Statistics Module -->
<string name="title_statistics">Statistics</string>
<string name="overview">Overview</string>
<string name="category_statistics">Category statistics</string>
<string name="total_tasks">Total tasks</string>
<string name="completed">Completed</string>
<string name="completion_rate">Completion rate</string>
<string name="total_countdowns">Total countdown days</string>
<string name="active">Active</string>
<string name="title_category">分类管理</string>
<string name="title_add_category">添加分类</string>
<string name="title_edit_category">编辑分类</string>
<string name="label_no_category">暂无分类</string>
<string name="label_add_category_hint">点击右下角按钮添加新分类</string>
<string name="label_category_name">分类名称</string>
<string name="placeholder_category_name">输入分类名称...</string>
<string name="label_category_type">分类类型</string>
<string name="label_select_color">选择颜色</string>
<string name="label_select_icon">选择图标</string>
<string name="label_task_category">任务分类</string>
<string name="label_countdown_category">倒数日分类</string>
<!-- Category Module -->
<string name="category_task">Task</string>
<string name="category_countdown">Countdown</string>
<string name="label_task_count">%1$d 个任务</string>
<string name="label_countdown_count">%1$d 个倒数日</string>
<string name="title_category">Category Management</string>
<string name="title_add_category">Add Category</string>
<string name="title_edit_category">Edit Category</string>
<string name="label_no_category">No Category</string>
<string name="label_add_category_hint">Click the button in the lower right corner to add a new category</string>
<string name="label_category_name">Category Name</string>
<string name="placeholder_category_name">Enter Category Name...</string>
<string name="label_category_type">Category Type</string>
<string name="label_select_color">Select Color</string>
<string name="label_select_icon">Select Icon</string>
<string name="label_task_category">Task Category</string>
<string name="label_countdown_category">Countdown Category</string>
<!-- 分类操作反馈 -->
<string name="category_add_success">分类添加成功</string>
<string name="category_add_failed">添加分类失败</string>
<string name="category_update_success">分类更新成功</string>
<string name="category_update_failed">更新分类失败</string>
<string name="category_delete_success">分类删除成功</string>
<string name="category_delete_failed">删除分类失败</string>
<string name="category_load_failed">加载分类失败</string>
<string name="category_stat_failed">加载统计数据失败</string>
<string name="category_init_success">默认分类初始化成功</string>
<string name="category_init_failed">初始化默认分类失败</string>
<string name="category_not_found">查询分类失败</string>
<string name="category_count_update_failed">更新分类计数失败</string>
<string name="label_task_count">%1$d tasks</string>
<string name="label_countdown_count">%1$d countdown days</string>
<!-- 通用操作 -->
<string name="enter">进入</string>
<string name="edit">编辑</string>
<string name="cancel">取消</string>
<string name="confirm">确定</string>
<string name="delete">删除</string>
<string name="export">导出</string>
<string name="import">导入</string>
<string name="back">返回</string>
<string name="action">操作</string>
<string name="search">搜索</string>
<string name="clear_text">清除</string>
<string name="all_text">全部</string>
<string name="retry">重试</string>
<string name="choose_file">选择文件</string>
<string name="error_title">错误</string>
<string name="webview_loading_error">加载失败,请检查网络连接</string>
<!-- Category Operation Feedback -->
<string name="category_add_success">Category added successfully</string>
<string name="category_add_failed">Category added failed</string>
<string name="category_update_success">Category updated successfully</string>
<string name="category_update_failed">Category updated failed</string>
<string name="category_delete_success">Category deleted successfully</string>
<string name="category_delete_failed">Category deleted failed</string>
<string name="category_load_failed">Category loaded failed</string>
<string name="category_stat_failed">Statistics loaded failed</string>
<string name="category_init_success">Default category initialized successfully</string>
<string name="category_init_failed">Default category initialized failed</string>
<string name="category_not_found">Category query failed</string>
<string name="category_count_update_failed">Category count update failed</string>
<!-- Common Operations -->
<string name="enter">Enter</string>
<string name="edit">Edit</string>
<string name="cancel">Cancel</string>
<string name="confirm">Confirm</string>
<string name="delete">Delete</string>
<string name="export">Export</string>
<string name="import">Import</string>
<string name="back">Back</string>
<string name="action">Action</string>
<string name="search">Search</string>
<string name="clear_text">Clear</string>
<string name="all_text">All</string>
<string name="retry">Retry</string>
<string name="choose_file">Choose File</string>
<string name="error_title">Error</string>
<string name="loading">Loading...</string>
<string name="webview_loading_error">Loading failed, please check your network connection</string>
<!-- 数据管理 -->
<string name="title_data_management">数据管理</string>
<string name="title_export_data">导出数据</string>
<string name="desc_export_data">将所有任务和倒数日导出为文件</string>
<string name="title_import_data">导入数据</string>
<string name="desc_import_data">从文件导入任务和倒数日</string>
<string name="title_auto_backup">自动备份</string>
<string name="desc_auto_backup">定期自动备份数据到云端</string>
<string name="title_clear_all_data">清除所有数据</string>
<string name="desc_clear_all_data">删除所有任务、倒数日和设置</string>
<string name="desc_clear_all_data_dialog">此操作将删除所有任务、倒数日和设置,且无法恢复。</string>
<string name="title_clear_completed_tasks">清理已完成任务</string>
<string name="title_clear_expired_countdowns">清理过期倒数日</string>
<string name="desc_clear_completed_tasks">删除所有已完成的任务</string>
<string name="desc_clear_expired_countdowns">删除所有已过期的倒数日</string>
<!-- Data Management -->
<string name="title_data_management">Data Management</string>
<string name="title_export_data">Export Data</string>
<string name="desc_export_data">Export all tasks and countdowns to a file</string>
<string name="title_import_data">Import Data</string>
<string name="desc_import_data">Import tasks and countdowns from a file</string>
<string name="title_auto_backup">Auto Backup</string>
<string name="desc_auto_backup">Regularly and automatically back up data to the cloud</string>
<string name="title_clear_all_data">Clear All Data</string>
<string name="desc_clear_all_data">Delete all tasks, countdowns, and settings</string>
<string name="desc_clear_all_data_dialog">This operation will delete all tasks, countdowns, and settings and cannot be restored. </string>
<string name="title_clear_completed_tasks">Clear completed tasks</string>
<string name="title_clear_expired_countdowns">Clear expired countdowns</string>
<string name="desc_clear_completed_tasks">Delete all completed tasks</string>
<string name="desc_clear_expired_countdowns">Delete all expired countdowns</string>
<string name="title_backup_restore">备份与恢复</string>
<string name="title_data_clean">数据清理</string>
<string name="label_select_import_file">选择要导入的文件</string>
<string name="label_select_file">选择文件</string>
<string name="label_select_export_format">选择导出格式</string>
<string name="label_json_format">JSON格式</string>
<string name="label_csv_format">CSV格式</string>
<string name="title_backup_restore">Backup and restore</string>
<string name="title_data_clean">Data cleanup</string>
<string name="label_select_import_file">Select the file to import</string>
<string name="label_select_file">Select a file</string>
<string name="label_select_export_format">Select the export format</string>
<string name="label_json_format">JSON format</string>
<string name="label_csv_format">CSV format</string>
<!-- 设置模块 -->
<string name="title_app_settings">应用设置</string>
<string name="section_general_settings">通用设置</string>
<string name="section_data_management">数据管理</string>
<string name="section_social_share">社交分享</string>
<string name="section_help_feedback">帮助与反馈</string>
<!-- Settings Module -->
<string name="title_app_settings">App Settings</string>
<string name="section_general_settings">General Settings</string>
<string name="section_data_management">Data Management</string>
<string name="section_social_share">Social Sharing</string>
<string name="section_help_feedback">Help and Feedback</string>
<string name="setting_push_notification">推送通知</string>
<string name="setting_push_notification_desc">接收任务和倒数日提醒</string>
<string name="setting_dark_mode">深色模式</string>
<string name="setting_dark_mode_desc">使用深色主题</string>
<string name="setting_language">语言设置</string>
<string name="setting_language_desc">简体中文</string>
<string name="setting_push_notification">Push Notifications</string>
<string name="setting_push_notification_desc">Receive Task and Countdown Reminders</string>
<string name="setting_dark_mode">Dark Mode</string>
<string name="setting_dark_mode_desc">Use Dark Theme</string>
<string name="setting_language">Language Settings</string>
<string name="setting_language_desc">Simplified Chinese</string>
<string name="setting_category_management">分类管理</string>
<string name="setting_category_management_desc">管理分类</string>
<string name="setting_data_management">数据管理</string>
<string name="setting_data_management_desc">备份和恢复数据</string>
<string name="setting_category_management">Management</string>
<string name="setting_category_management_desc">Manage categories</string>
<string name="setting_data_management">Data management</string>
<string name="setting_data_management_desc">Backup and restore data</string>
<string name="setting_share_achievement">分享成就</string>
<string name="setting_share_achievement_desc">分享任务完成成就</string>
<string name="setting_invite_friend">推荐给朋友</string>
<string name="setting_invite_friend_desc">邀请朋友使用 TaskMaster</string>
<string name="setting_share_achievement">Share achievements</string>
<string name="setting_share_achievement_desc">Share task completion achievements</string>
<string name="setting_invite_friend">Recommend to a friend</string>
<string name="setting_invite_friend_desc">Invite a friend to use TaskMaster</string>
<string name="setting_feedback">意见反馈</string>
<string name="setting_feedback_desc">告诉我们您的想法</string>
<string name="setting_privacy_policy">隐私政策</string>
<string name="setting_privacy_policy_desc">了解我们如何保护您的数据</string>
<string name="setting_about_app">关于应用</string>
<string name="setting_about_app_desc">版本 1.0.0</string>
<string name="setting_feedback">Feedback</string>
<string name="setting_feedback_desc">Tell us what you think</string>
<string name="setting_privacy_policy">Privacy Policy</string>
<string name="setting_privacy_policy_desc">Learn how we protect your data</string>
<string name="setting_privacy_rate">App Review</string>
<string name="setting_privacy_rate_desc">If you like it, please leave a five-star review in the store</string>
<string name="setting_about_app">About the App</string>
<string name="app_version_code">100001</string>
<string name="app_version_name">1.0.1</string>
<string name="setting_about_app_desc">Version 1.0.1</string>
<!-- 反馈与帮助 -->
<string name="title_feedback">意见反馈</string>
<string name="feedback_type">反馈类型</string>
<string name="feedback_issue">问题反馈</string>
<string name="feedback_suggestion">功能建议</string>
<string name="feedback_description">问题描述</string>
<string name="feedback_placeholder">请详细描述您遇到的问题或建议...</string>
<string name="feedback_contact">联系方式(可选)</string>
<string name="feedback_contact_placeholder">您的邮箱地址,方便我们回复</string>
<string name="feedback_submitted">感谢您的反馈!我们会尽快处理。</string>
<string name="feedback_error_empty">请填写反馈内容</string>
<string name="button_send_feedback">发送反馈</string>
<!-- Feedback & Help -->
<string name="title_feedback">Feedback</string>
<string name="feedback_type">Feedback Type</string>
<string name="feedback_issue">Issue Feedback</string>
<string name="feedback_suggestion">Feature Suggestion</string>
<string name="feedback_description">Issue Description</string>
<string name="feedback_placeholder">Please describe your problem or suggestion in detail...</string>
<string name="feedback_contact">Contact Information (optional)</string>
<string name="feedback_contact_placeholder">Your email address for our response</string>
<string name="feedback_submitted">Thank you for your feedback! We will address it as soon as possible.</string>
<string name="feedback_error_empty">Please fill in your feedback</string>
<string name="button_send_feedback">Send Feedback</string>
<!-- 关于页面 -->
<string name="title_about">关于</string>
<string name="app_intro_title">应用介绍</string>
<string name="app_intro_content">
TaskTTL 是一款现代化的任务管理与倒数日应用,
支持分类管理、优先级设置与统计分析,让生活更有条理。
</string>
<!-- About Page -->
<string name="title_about">About</string>
<string name="app_intro_title">App Introduction</string>
<string name="app_intro_content">TaskTTL is a modern task management and countdown app.\nIt supports category management, priority setting, and statistical analysis, making your life more organized. </string>
<string name="title_privacy">隐私协议</string>
<string name="title_privacy">Privacy Policy</string>
<string name="tech_stack">技术栈</string>
<string name="tech_stack_kmp">Kotlin Multiplatform(跨平台开发框架)</string>
<string name="tech_stack_compose">Jetpack Compose(现代化 UI 框架)</string>
<string name="tech_stack_room">Room Database(本地存储)</string>
<string name="tech_stack_koin">Koin(依赖注入框架)</string>
<string name="tech_stack_ktor">Ktor(网络请求)</string>
<string name="tech_stack_mvi">MVI Architecture(响应式架构模式)</string>
<string name="tech_stack">Technology Stack</string>
<string name="tech_stack_kmp">Kotlin Multiplatform (Cross-Platform Development Framework)</string>
<string name="tech_stack_compose">Jetpack Compose (Modern UI Framework)</string>
<string name="tech_stack_room">Room Database (Local Storage)</string>
<string name="tech_stack_koin">Koin (Dependency Injection Framework)</string>
<string name="tech_stack_ktor">Ktor (Network Request)</string>
<string name="tech_stack_mvi">MVI Architecture (Responsive Architecture Pattern)</string>
<string name="developer_text">开发者</string>
<string name="devttl_team">DevTTL 团队</string>
<string name="contact_us">联系我们</string>
<string name="email_text">电子邮箱</string>
<string name="developer_text">Developer</string>
<string name="devttl_team">DevTTL Team</string>
<string name="contact_us">Contact Us</string>
<string name="email_text">Email</string>
<string name="setting_privacy_email_uri">mailto:%1$s</string>
<string name="email">admin@devttl.com</string>
<string name="web_text">官方网站</string>
<string name="web_text">Official Website</string>
<string name="web_url">https://devttl.com</string>
<string name="copyright_year">2025</string>
<string name="all_rights_reserved">保留所有权利</string>
<string name="all_rights_reserved">All Rights Reserved</string>
<string name="priority_low"></string>
<string name="priority_medium"></string>
<string name="priority_high"></string>
<string name="priority_urgent">紧急</string>
<string name="priority_low">Low</string>
<string name="priority_medium">Medium</string>
<string name="priority_high">High</string>
<string name="priority_urgent">Urgent</string>
<string name="reminder_once">一次</string>
<string name="reminder_daily">每天</string>
<string name="reminder_weekly">每周</string>
<string name="reminder_monthly">每月</string>
<string name="reminder_off">关闭</string>
<string name="reminder_once">Once</string>
<string name="reminder_daily">Daily</string>
<string name="reminder_weekly">Weekly</string>
<string name="reminder_monthly">Monthly</string>
<string name="reminder_off">Off</string>
<!-- 工作与学习 -->
<string name="category_briefcase">工作</string>
<string name="category_book">学习</string>
<string name="category_exam">考试</string>
<string name="category_project">项目</string>
<!-- Work & Study -->
<string name="category_briefcase">Work</string>
<string name="category_book">Study</string>
<string name="category_exam">Exam</string>
<string name="category_project">Project</string>
<!-- 生活与家庭 -->
<string name="category_home">家庭</string>
<string name="category_coffee">休闲</string>
<string name="category_shopping">购物</string>
<string name="category_food">美食</string>
<string name="category_cleaning">家务</string>
<!-- Life & Family -->
<string name="category_home">Family</string>
<string name="category_coffee">Leisure</string>
<string name="category_shopping">Shopping</string>
<string name="category_food">Food</string>
<string name="category_cleaning">Housework</string>
<!-- 健康与运动 -->
<string name="category_heart">健康</string>
<string name="category_dumbbell">健身</string>
<string name="category_sleep">作息</string>
<!-- Health & Fitness -->
<string name="category_heart">Health</string>
<string name="category_dumbbell">Fitness</string>
<string name="category_sleep">Days and Nights</string>
<!-- 娱乐与兴趣 -->
<string name="category_music">音乐</string>
<string name="category_gamepad">娱乐</string>
<string name="category_camera">摄影</string>
<string name="category_movie">影视</string>
<!-- Entertainment & Hobbies -->
<string name="category_music">Music</string>
<string name="category_gamepad">Entertainment</string>
<string name="category_camera">Photography</string>
<string name="category_movie">Movies</string>
<!-- 出行与旅行 -->
<string name="category_car">出行</string>
<string name="category_plane">旅行</string>
<string name="category_walk">步行</string>
<!-- Travel & Tours -->
<string name="category_car">Travel</string>
<string name="category_plane">Travel</string>
<string name="category_walk">Walk</string>
<!-- 节日与纪念 -->
<string name="category_birthday">生日</string>
<string name="category_festival">节日</string>
<string name="category_anniversary">纪念日</string>
<!-- Holidays & Anniversaries -->
<string name="category_birthday">Birthday</string>
<string name="category_festival">Festival</string>
<string name="category_anniversary">Anniversary</string>
<!-- 财务与计划 -->
<string name="category_money">理财</string>
<string name="category_goal">目标</string>
<string name="category_reminder">提醒</string>
<!-- Finance & Planning -->
<string name="category_money">Financial Planning</string>
<string name="category_goal">Goal</string>
<string name="category_reminder">Reminder</string>
<string name="privacy_url">https://sites.google.com/view/taskttl/privacy</string>
<string name="feedback_success">反馈成功</string>
<string name="feedback_error">反馈失败,请检查网络连接或稍后重试</string>
<string name="feedback_success">Feedback successful</string>
<string name="feedback_error">Feedback failed. Please check your network connection or try again later.</string>
</resources>

View File

@@ -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<String, Any> = mapOf(
"eventName" to eventName,
"eventCode" to eventCode,
)
fun toMap(): Map<String, Any> = mapOf("eventName" to eventName, "eventCode" to eventCode)
}

View File

@@ -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"

View File

@@ -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<Color>,
) {
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)
}

View File

@@ -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()
}
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)
}
}
// enum class LogLevel { DEBUG, INFO, WARN, ERROR }

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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()
}

View File

@@ -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<SettingsState> = _state.asStateFlow()
private val _effects = MutableSharedFlow<SettingsEffect>()
val effects: SharedFlow<SettingsEffect> = _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)
}
}

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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)
) {

View File

@@ -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)
}
}

View File

@@ -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(),

View File

@@ -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))
}
}

View File

@@ -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)
) {

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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),

View File

@@ -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"
}
}

View File

@@ -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"