diff --git a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AndroidFileLocationsProvider.kt b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AndroidFileLocationsProvider.kt
index 2625c59d..639d80b7 100644
--- a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AndroidFileLocationsProvider.kt
+++ b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/services/AndroidFileLocationsProvider.kt
@@ -20,4 +20,37 @@ class AndroidFileLocationsProvider(
override fun setExecutableIfNeeded(path: String) {
// No-op on Android
}
+
+ override fun getCacheSizeBytes(): Long {
+ val dir = File(appDownloadsDir())
+ return calculateDirSize(dir)
+ }
+
+ override fun clearCacheFiles(): Boolean {
+ val dir = File(appDownloadsDir())
+ return deleteDirectoryContents(dir)
+ }
+
+ private fun calculateDirSize(dir: File): Long {
+ if (!dir.exists()) return 0L
+ var size = 0L
+ dir.listFiles()?.forEach { file ->
+ size += if (file.isDirectory) calculateDirSize(file) else file.length()
+ }
+ return size
+ }
+
+ private fun deleteDirectoryContents(dir: File): Boolean {
+ if (!dir.exists()) return true
+ var allDeleted = true
+ dir.listFiles()?.forEach { file ->
+ if (file.isDirectory) {
+ if (!deleteDirectoryContents(file)) allDeleted = false
+ if (!file.delete()) allDeleted = false
+ } else {
+ if (!file.delete()) allDeleted = false
+ }
+ }
+ return allDeleted
+ }
}
\ No newline at end of file
diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/services/FileLocationsProvider.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/services/FileLocationsProvider.kt
index 3c5b27da..44caac6f 100644
--- a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/services/FileLocationsProvider.kt
+++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/services/FileLocationsProvider.kt
@@ -4,4 +4,6 @@ interface FileLocationsProvider {
fun appDownloadsDir(): String
fun userDownloadsDir(): String
fun setExecutableIfNeeded(path: String)
+ fun getCacheSizeBytes(): Long
+ fun clearCacheFiles(): Boolean
}
diff --git a/core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopFileLocationsProvider.kt b/core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopFileLocationsProvider.kt
index 92a3ecd6..167a1960 100644
--- a/core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopFileLocationsProvider.kt
+++ b/core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopFileLocationsProvider.kt
@@ -95,6 +95,43 @@ class DesktopFileLocationsProvider(
return downloadsDir.absolutePath
}
+ override fun getCacheSizeBytes(): Long {
+ val appDir = File(appDownloadsDir())
+ val userDir = File(userDownloadsDir())
+ return calculateDirSize(appDir) + calculateDirSize(userDir)
+ }
+
+ override fun clearCacheFiles(): Boolean {
+ val appDir = File(appDownloadsDir())
+ val userDir = File(userDownloadsDir())
+ val appCleared = deleteDirectoryContents(appDir)
+ val userCleared = deleteDirectoryContents(userDir)
+ return appCleared && userCleared
+ }
+
+ private fun calculateDirSize(dir: File): Long {
+ if (!dir.exists()) return 0L
+ var size = 0L
+ dir.listFiles()?.forEach { file ->
+ size += if (file.isDirectory) calculateDirSize(file) else file.length()
+ }
+ return size
+ }
+
+ private fun deleteDirectoryContents(dir: File): Boolean {
+ if (!dir.exists()) return true
+ var allDeleted = true
+ dir.listFiles()?.forEach { file ->
+ if (file.isDirectory) {
+ if (!deleteDirectoryContents(file)) allDeleted = false
+ if (!file.delete()) allDeleted = false
+ } else {
+ if (!file.delete()) allDeleted = false
+ }
+ }
+ return allDeleted
+ }
+
private fun getXdgDownloadsDir(): String? {
return try {
val userDirsFile = File(
diff --git a/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml b/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
index 33255e9b..bac9882a 100644
--- a/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
+++ b/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
@@ -126,6 +126,7 @@
সফলভাবে লগআউট হয়েছে, রিডাইরেক্ট করা হচ্ছে...
+ ক্যাশ সফলভাবে পরিষ্কার করা হয়েছে
সতর্কতা!
@@ -279,9 +280,9 @@
ইনস্টলযোগ্য রিলিজ থাকা রিপোজিটরি GitHub-এ স্টার করুন
শেষ সিঙ্ক
এইমাত্র
- %d মিনিট আগে
- %d ঘণ্টা আগে
- %d দিন আগে
+ %1$d মিনিট আগে
+ %1$d ঘণ্টা আগে
+ %1$d দিন আগে
বন্ধ করুন
স্টার করা রিপোজিটরি সিঙ্ক করতে ব্যর্থ হয়েছে
@@ -453,4 +454,8 @@
অ্যাপে খুলুন
ক্লিপবোর্ডে কোনো GitHub লিংক পাওয়া যায়নি
+ স্টোরেজ
+ ক্যাশে পরিষ্কার করুন
+ বর্তমান আকার:
+ পরিষ্কার করুন
diff --git a/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml b/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
index da64435b..6049b2df 100644
--- a/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
+++ b/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
@@ -104,6 +104,7 @@
Cerrar sesión
Sesión cerrada correctamente, redirigiendo…
+ Caché borrada con éxito
¡Advertencia!
¿Estás seguro de que deseas cerrar sesión?
@@ -418,4 +419,8 @@
Abrir en la app
No se encontró enlace de GitHub en el portapapeles
+ Almacenamiento
+ Borrar caché
+ Tamaño actual:
+ Borrar
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml b/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
index 25201a19..4fce5704 100644
--- a/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
+++ b/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
@@ -104,6 +104,7 @@
Se déconnecter
Déconnexion réussie, redirection…
+ Cache vidé avec succès
Attention !
Voulez-vous vraiment vous déconnecter ?
@@ -418,4 +419,8 @@
Ouvrir dans l\'app
Aucun lien GitHub trouvé dans le presse-papiers
+ Stockage
+ Vider le cache
+ Taille actuelle :
+ Vider
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml b/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
index 157aa9f1..3432c922 100644
--- a/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
+++ b/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
@@ -126,6 +126,7 @@
सफलतापूर्वक लॉग आउट हो गए, रीडायरेक्ट किया जा रहा है...
+ कैश सफलतापूर्वक साफ़ किया गया
चेतावनी!
@@ -277,9 +278,9 @@
उन्हें यहां देखने के लिए इंस्टॉलेबल रिलीज़ वाले GitHub पर रिपॉजिटरी को स्टार करें।
अंतिम सिंक
अभी-अभी
- %d मिनट पहले
- %d घंटे पहले
- %d दिन पहले
+ %1$d मिनट पहले
+ %1$d घंटे पहले
+ %1$d दिन पहले
हटाएं
तारांकित रिपॉजिटरी को सिंक करने में विफल रहा
@@ -453,4 +454,8 @@
ऐप में खोलें
क्लिपबोर्ड में कोई GitHub लिंक नहीं मिला
+ संग्रहण
+ कैश साफ़ करें
+ वर्तमान आकार:
+ साफ़ करें
diff --git a/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml b/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
index 42edc300..d1fafd6b 100644
--- a/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
+++ b/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
@@ -126,6 +126,7 @@
Uscito con successo, reindirizzamento…
+ Cache cancellata con successo
Attenzione!
@@ -454,4 +455,8 @@
Apri nell\'app
Nessun link GitHub trovato negli appunti
+ Archiviazione
+ Pulisci cache
+ Dimensione attuale:
+ Pulisci
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml b/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
index 00c71a92..81edb858 100644
--- a/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
+++ b/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
@@ -104,6 +104,7 @@
ログアウト
ログアウトしました。リダイレクト中…
+ キャッシュを正常にクリアしました
警告!
ログアウトしてもよろしいですか?
@@ -417,4 +418,8 @@
アプリで開く
クリップボードにGitHubリンクが見つかりません
+ ストレージ
+ キャッシュをクリア
+ 現在のサイズ:
+ クリア
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml b/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
index d93812bb..3a3353a6 100644
--- a/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
+++ b/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
@@ -124,6 +124,7 @@
성공적으로 로그아웃되었습니다. 이동 중...
+ 캐시가 성공적으로 삭제되었습니다
경고!
@@ -277,9 +278,9 @@
설치 가능한 릴리스가 있는 저장소에 별표를 추가하세요
마지막 동기화
방금
- %d분 전
- %d시간 전
- %d일 전
+ %1$d분 전
+ %1$d시간 전
+ %1$d일 전
닫기
별표 저장소 동기화에 실패했습니다
@@ -450,4 +451,8 @@
앱에서 열기
클립보드에서 GitHub 링크를 찾을 수 없습니다
+ 저장 공간
+ 캐시 지우기
+ 현재 크기:
+ 지우기
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml b/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
index 48fa3948..6ac85b77 100644
--- a/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
+++ b/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
@@ -105,6 +105,7 @@
Wyloguj się
Wylogowano pomyślnie, przekierowywanie...
+ Pamięć podręczna wyczyszczona pomyślnie
Ostrzeżenie!
Czy na pewno chcesz się wylogować?
@@ -243,9 +244,9 @@
Oznacz repozytoria z instalowalnymi wydaniami na GitHubie
Ostatnia synchronizacja
Przed chwilą
- %d min temu
- %d h temu
- %d d temu
+ %1$d min temu
+ %1$d h temu
+ %1$d d temu
Zamknij
Nie udało się zsynchronizować oznaczonych gwiazdką repozytoriów
@@ -416,4 +417,8 @@
Otwórz w aplikacji
Nie znaleziono linku GitHub w schowku
+ Przechowywanie
+ Wyczyść pamięć podręczną
+ Aktualny rozmiar:
+ Wyczyść
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml b/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
index d3879e39..5a405544 100644
--- a/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
+++ b/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
@@ -104,6 +104,7 @@
Выйти
Вы успешно вышли, перенаправление...
+ Кэш успешно очищен
Внимание!
Вы уверены, что хотите выйти?
@@ -244,9 +245,9 @@
Отмечайте репозитории с установочными релизами на GitHub
Последняя синхронизация
Только что
- %d мин назад
- %d ч назад
- %d д назад
+ %1$d мин назад
+ %1$d ч назад
+ %1$d д назад
Закрыть
Не удалось синхронизировать избранные репозитории
@@ -418,4 +419,8 @@
Открыть в приложении
Ссылка GitHub не найдена в буфере обмена
+ Хранение
+ Очистить кэш
+ Текущий размер:
+ Очистить
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml b/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
index 99ad849c..e857b4f0 100644
--- a/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
+++ b/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
@@ -125,6 +125,7 @@
Başarılı şekilde çıkış yapıldı, yönlendiriliyor...
+ Önbellek başarıyla temizlendi
Uyarı!
@@ -278,9 +279,9 @@
Yüklenebilir sürümlü repoları görmek için GitHub'da yıldızlayın
Son eşitleme
Şimdi
- %d dakika önce
- %d saat önce
- %d gün önce
+ %1$d dakika önce
+ %1$d saat önce
+ %1$d gün önce
Kapat
Yıldızlı repoları eşitlerken hata
@@ -450,4 +451,8 @@
Uygulamada aç
Panoda GitHub bağlantısı bulunamadı
+ Depolama
+ Önbelleği Temizle
+ Geçerli boyut:
+ Temizle
diff --git a/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml b/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
index bb136c26..e567ed5b 100644
--- a/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
+++ b/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
@@ -106,6 +106,7 @@
退出登录
已成功退出,正在跳转…
+ 缓存已成功清除
警告!
确定要退出登录吗?
@@ -227,9 +228,9 @@
在 GitHub 上收藏有可安装版本的仓库以在此查看
上次同步
刚刚
- %d 分钟前
- %d 小时前
- %d 天前
+ %1$d 分钟前
+ %1$d 小时前
+ %1$d 天前
关闭
同步收藏的仓库失败
@@ -418,4 +419,8 @@
在应用中打开
剪贴板中未找到 GitHub 链接
+ 存储
+ 清除缓存
+ 当前大小:
+ 清除
\ No newline at end of file
diff --git a/core/presentation/src/commonMain/composeResources/values/strings.xml b/core/presentation/src/commonMain/composeResources/values/strings.xml
index 5c1a64fe..49cbc82d 100644
--- a/core/presentation/src/commonMain/composeResources/values/strings.xml
+++ b/core/presentation/src/commonMain/composeResources/values/strings.xml
@@ -147,6 +147,7 @@
Logged out successfully, redirecting...
+ Cache cleared successfully
Warning!
@@ -313,9 +314,9 @@
Star repositories on GitHub with installable releases to see them here
Last synced
Just now
- %d min ago
- %d h ago
- %d d ago
+ %1$d min ago
+ %1$d h ago
+ %1$d d ago
Dismiss
Failed to sync starred repos
@@ -453,4 +454,9 @@
Detected Links
Open in app
No GitHub link found in clipboard
+
+ Storage
+ Clear Cache
+ Current size:
+ Clear
\ No newline at end of file
diff --git a/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/FavouritesRoot.kt b/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/FavouritesRoot.kt
index 7752e9de..9ef108cf 100644
--- a/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/FavouritesRoot.kt
+++ b/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/FavouritesRoot.kt
@@ -109,7 +109,7 @@ fun FavouritesScreen(
onDevProfileClick = {
onAction(FavouritesAction.OnDeveloperProfileClick(repo.repoOwner))
},
- modifier = Modifier.Companion.animateItem()
+ modifier = Modifier.animateItem()
)
}
}
diff --git a/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/components/FavouriteRepositoryItem.kt b/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/components/FavouriteRepositoryItem.kt
index 007bfd68..9b35e61c 100644
--- a/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/components/FavouriteRepositoryItem.kt
+++ b/feature/favourites/presentation/src/commonMain/kotlin/zed/rainxch/favourites/presentation/components/FavouriteRepositoryItem.kt
@@ -1,7 +1,7 @@
package zed.rainxch.favourites.presentation.components
-import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -12,19 +12,25 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.CalendarToday
+import androidx.compose.material.icons.filled.Code
import androidx.compose.material.icons.filled.Favorite
-import androidx.compose.material3.Card
-import androidx.compose.material3.CardDefaults
+import androidx.compose.material.icons.filled.NewReleases
+import androidx.compose.material3.AssistChip
+import androidx.compose.material3.AssistChipDefaults
import androidx.compose.material3.CircularWavyProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialShapes
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.toShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -35,9 +41,11 @@ import androidx.compose.ui.unit.dp
import com.skydoves.landscapist.coil3.CoilImage
import com.skydoves.landscapist.components.rememberImageComponent
import com.skydoves.landscapist.crossfade.CrossfadePlugin
-import zed.rainxch.githubstore.core.presentation.res.*
import org.jetbrains.compose.resources.stringResource
+import zed.rainxch.core.presentation.components.ExpressiveCard
import zed.rainxch.favourites.presentation.model.FavouriteRepository
+import zed.rainxch.githubstore.core.presentation.res.Res
+import zed.rainxch.githubstore.core.presentation.res.remove_from_favourites
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@@ -46,15 +54,11 @@ fun FavouriteRepositoryItem(
onToggleFavouriteClick: () -> Unit,
onItemClick: () -> Unit,
onDevProfileClick: () -> Unit,
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
) {
- Card(
- modifier = modifier.fillMaxWidth(),
+ ExpressiveCard(
+ modifier = modifier,
onClick = onItemClick,
- colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceContainer
- ),
- shape = RoundedCornerShape(24.dp)
) {
Column(
modifier = Modifier
@@ -63,9 +67,9 @@ fun FavouriteRepositoryItem(
) {
Row(
modifier = Modifier
- .clickable(onClick = {
- onDevProfileClick()
- }),
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(24.dp))
+ .clickable(onClick = onDevProfileClick),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
@@ -92,12 +96,12 @@ fun FavouriteRepositoryItem(
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.outline,
maxLines = 1,
- softWrap = false,
- overflow = TextOverflow.Ellipsis
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.weight(1f)
)
}
- Spacer(modifier = Modifier.height(4.dp))
+ Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
@@ -113,34 +117,28 @@ fun FavouriteRepositoryItem(
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 1,
- softWrap = false,
overflow = TextOverflow.Ellipsis
)
favouriteRepository.repoDescription?.let {
- Spacer(Modifier.height(4.dp))
+ Spacer(modifier = Modifier.height(4.dp))
Text(
text = it,
fontWeight = FontWeight.Medium,
- style = MaterialTheme.typography.titleMedium,
+ style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 2,
- softWrap = true,
overflow = TextOverflow.Ellipsis
)
}
}
IconButton(
- onClick = {
- onToggleFavouriteClick()
- },
- colors = IconButtonDefaults.iconButtonColors(
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- ),
- shapes = IconButtonDefaults.shapes()
+ onClick = onToggleFavouriteClick,
+ colors = IconButtonDefaults.filledTonalIconButtonColors(),
+ modifier = Modifier.align(Alignment.CenterVertically),
+ shape = MaterialShapes.Cookie6Sided.toShape()
) {
Icon(
imageVector = Icons.Default.Favorite,
@@ -149,50 +147,80 @@ fun FavouriteRepositoryItem(
}
}
- favouriteRepository.primaryLanguage?.let {
- Spacer(modifier = Modifier.height(6.dp))
+ Spacer(modifier = Modifier.height(12.dp))
- Text(
- text = it,
- fontWeight = FontWeight.Medium,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onPrimaryContainer,
- maxLines = 1,
- softWrap = false,
- overflow = TextOverflow.Ellipsis,
- modifier = Modifier
- .background(
- color = MaterialTheme.colorScheme.primaryContainer,
- shape = RoundedCornerShape(12.dp)
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .horizontalScroll(rememberScrollState()),
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ favouriteRepository.primaryLanguage?.let { language ->
+ AssistChip(
+ onClick = { /* No action */ },
+ label = {
+ Text(
+ text = language,
+ style = MaterialTheme.typography.titleSmall,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ },
+ leadingIcon = {
+ Icon(
+ imageVector = Icons.Default.Code,
+ contentDescription = null,
+ modifier = Modifier.size(AssistChipDefaults.IconSize)
+ )
+ },
+ colors = AssistChipDefaults.assistChipColors(
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ labelColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ leadingIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
- .padding(8.dp)
- )
- }
- favouriteRepository.latestRelease?.let {
- Spacer(modifier = Modifier.height(6.dp))
+ )
+ }
- Text(
- text = it,
- fontWeight = FontWeight.Medium,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface,
- maxLines = 1,
- softWrap = false,
- overflow = TextOverflow.Ellipsis
+ favouriteRepository.latestRelease?.let { release ->
+ AssistChip(
+ onClick = { /* No action */ },
+ label = {
+ Text(
+ text = release,
+ style = MaterialTheme.typography.titleSmall,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ },
+ leadingIcon = {
+ Icon(
+ imageVector = Icons.Default.NewReleases,
+ contentDescription = null,
+ modifier = Modifier.size(AssistChipDefaults.IconSize)
+ )
+ }
+ )
+ }
+
+ AssistChip(
+ onClick = { /* No action */ },
+ label = {
+ Text(
+ text = favouriteRepository.addedAtFormatter,
+ style = MaterialTheme.typography.titleSmall,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ },
+ leadingIcon = {
+ Icon(
+ imageVector = Icons.Default.CalendarToday,
+ contentDescription = null,
+ modifier = Modifier.size(AssistChipDefaults.IconSize)
+ )
+ }
)
}
-
- Spacer(modifier = Modifier.height(6.dp))
-
- Text(
- text = favouriteRepository.addedAtFormatter,
- fontWeight = FontWeight.Medium,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface,
- maxLines = 1,
- softWrap = false,
- overflow = TextOverflow.Ellipsis
- )
}
}
}
\ No newline at end of file
diff --git a/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt b/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt
index 37648651..18bec024 100644
--- a/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt
+++ b/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt
@@ -11,7 +11,8 @@ val settingsModule = module {
tokenStore = get(),
httpClient = get(),
cacheManager = get(),
- logger = get()
+ logger = get(),
+ fileLocationsProvider = get()
)
}
}
\ No newline at end of file
diff --git a/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt b/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt
index 653bed76..39979bf6 100644
--- a/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt
+++ b/feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt
@@ -5,6 +5,7 @@ import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.http.HttpHeaders
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
@@ -13,6 +14,7 @@ import zed.rainxch.core.data.cache.CacheManager.CacheTtl.USER_PROFILE
import zed.rainxch.core.data.data_source.TokenStore
import zed.rainxch.core.data.dto.UserProfileNetwork
import zed.rainxch.core.data.network.executeRequest
+import zed.rainxch.core.data.services.FileLocationsProvider
import zed.rainxch.core.domain.logging.GitHubStoreLogger
import zed.rainxch.core.domain.repository.AuthenticationState
import zed.rainxch.feature.profile.data.BuildKonfig
@@ -25,7 +27,8 @@ class ProfileRepositoryImpl(
private val tokenStore: TokenStore,
private val httpClient: HttpClient,
private val cacheManager: CacheManager,
- private val logger: GitHubStoreLogger
+ private val logger: GitHubStoreLogger,
+ private val fileLocationsProvider: FileLocationsProvider
) : ProfileRepository {
companion object {
@@ -84,4 +87,15 @@ class ProfileRepositoryImpl(
tokenStore.clear()
cacheManager.clearAll()
}
+
+ override fun observeCacheSize(): Flow = flow {
+ val sizeBytes = fileLocationsProvider.getCacheSizeBytes()
+ emit(sizeBytes)
+ }.flowOn(Dispatchers.IO)
+
+ override suspend fun clearCache() {
+ fileLocationsProvider.clearCacheFiles()
+ cacheManager.clearAll()
+ logger.debug("Cache cleared successfully")
+ }
}
diff --git a/feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt b/feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt
index 369fa931..70365aaa 100644
--- a/feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt
+++ b/feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt
@@ -8,4 +8,6 @@ interface ProfileRepository {
fun getUser(): Flow
fun getVersionName(): String
suspend fun logout()
+ fun observeCacheSize(): Flow
+ suspend fun clearCache()
}
\ No newline at end of file
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt
index 921cef90..5ce3cd85 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt
@@ -2,6 +2,7 @@ package zed.rainxch.profile.presentation
import zed.rainxch.core.domain.model.AppTheme
import zed.rainxch.core.domain.model.FontTheme
+import zed.rainxch.profile.presentation.model.ProxyType
sealed interface ProfileAction {
data object OnNavigateBackClick : ProfileAction
@@ -15,6 +16,7 @@ sealed interface ProfileAction {
data object OnLogoutDismiss : ProfileAction
data object OnHelpClick : ProfileAction
data object OnLoginClick : ProfileAction
+ data object OnClearCacheClick : ProfileAction
data class OnFontThemeSelected(val fontTheme: FontTheme) : ProfileAction
data class OnProxyTypeSelected(val type: ProxyType) : ProfileAction
data class OnProxyHostChanged(val host: String) : ProfileAction
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt
index 9bb5f0ae..45438522 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt
@@ -5,4 +5,6 @@ sealed interface ProfileEvent {
data class OnLogoutError(val message: String) : ProfileEvent
data object OnProxySaved : ProfileEvent
data class OnProxySaveError(val message: String) : ProfileEvent
+ data object OnCacheCleared : ProfileEvent
+ data class OnCacheClearError(val message: String) : ProfileEvent
}
\ No newline at end of file
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt
index 33e72f53..56928c01 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt
@@ -36,6 +36,7 @@ import zed.rainxch.profile.presentation.components.LogoutDialog
import zed.rainxch.profile.presentation.components.sections.about
import zed.rainxch.profile.presentation.components.sections.logout
import zed.rainxch.profile.presentation.components.sections.networkSection
+import zed.rainxch.profile.presentation.components.sections.othersSection
import zed.rainxch.profile.presentation.components.sections.profile
import zed.rainxch.profile.presentation.components.sections.settings
@@ -79,6 +80,18 @@ fun ProfileRoot(
snackbarState.showSnackbar(event.message)
}
}
+
+ ProfileEvent.OnCacheCleared -> {
+ coroutineScope.launch {
+ snackbarState.showSnackbar(getString(Res.string.cache_cleared))
+ }
+ }
+
+ is ProfileEvent.OnCacheClearError -> {
+ coroutineScope.launch {
+ snackbarState.showSnackbar(event.message)
+ }
+ }
}
}
@@ -160,7 +173,7 @@ fun ProfileScreen(
)
item {
- Spacer(Modifier.height(16.dp))
+ Spacer(Modifier.height(32.dp))
}
settings(
@@ -169,7 +182,7 @@ fun ProfileScreen(
)
item {
- Spacer(Modifier.height(16.dp))
+ Spacer(Modifier.height(32.dp))
}
networkSection(
@@ -178,7 +191,16 @@ fun ProfileScreen(
)
item {
- Spacer(Modifier.height(16.dp))
+ Spacer(Modifier.height(32.dp))
+ }
+
+ othersSection(
+ state = state,
+ onAction = onAction
+ )
+
+ item {
+ Spacer(Modifier.height(32.dp))
}
about(
@@ -187,7 +209,7 @@ fun ProfileScreen(
)
item {
- Spacer(Modifier.height(16.dp))
+ Spacer(Modifier.height(32.dp))
}
if (state.isUserLoggedIn) {
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt
index c78f4b7c..66d24075 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt
@@ -4,6 +4,7 @@ import zed.rainxch.core.domain.model.AppTheme
import zed.rainxch.core.domain.model.FontTheme
import zed.rainxch.core.domain.model.ProxyConfig
import zed.rainxch.profile.domain.model.UserProfile
+import zed.rainxch.profile.presentation.model.ProxyType
data class ProfileState(
val userProfile: UserProfile? = null,
@@ -21,17 +22,5 @@ data class ProfileState(
val proxyPassword: String = "",
val isProxyPasswordVisible: Boolean = false,
val autoDetectClipboardLinks: Boolean = true,
-)
-
-enum class ProxyType {
- NONE, SYSTEM, HTTP, SOCKS;
-
- companion object {
- fun fromConfig(config: ProxyConfig): ProxyType = when (config) {
- is ProxyConfig.None -> NONE
- is ProxyConfig.System -> SYSTEM
- is ProxyConfig.Http -> HTTP
- is ProxyConfig.Socks -> SOCKS
- }
- }
-}
\ No newline at end of file
+ val cacheSize: String = ""
+)
\ No newline at end of file
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt
index 49d675c7..7d75685e 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt
@@ -12,7 +12,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.getString
-import org.jetbrains.compose.resources.stringResource
import zed.rainxch.core.domain.model.ProxyConfig
import zed.rainxch.core.domain.repository.ProxyRepository
import zed.rainxch.core.domain.repository.ThemesRepository
@@ -22,6 +21,7 @@ import zed.rainxch.githubstore.core.presentation.res.failed_to_save_proxy_settin
import zed.rainxch.githubstore.core.presentation.res.invalid_proxy_port
import zed.rainxch.githubstore.core.presentation.res.proxy_host_required
import zed.rainxch.profile.domain.repository.ProfileRepository
+import zed.rainxch.profile.presentation.model.ProxyType
class ProfileViewModel(
private val browserHelper: BrowserHelper,
@@ -43,6 +43,7 @@ class ProfileViewModel(
loadUserProfile()
loadVersionName()
loadProxyConfig()
+ observeCacheSize()
hasLoadedInitialData = true
}
@@ -56,6 +57,32 @@ class ProfileViewModel(
private val _events = Channel()
val events = _events.receiveAsFlow()
+ private fun observeCacheSize() {
+ viewModelScope.launch {
+ profileRepository.observeCacheSize().collect { sizeBytes ->
+ _state.update {
+ it.copy(cacheSize = formatCacheSize(sizeBytes))
+ }
+ }
+ }
+ }
+
+ private fun formatCacheSize(bytes: Long): String {
+ if (bytes <= 0) return "0 B"
+ val units = arrayOf("B", "KB", "MB", "GB")
+ var size = bytes.toDouble()
+ var unitIndex = 0
+ while (size >= 1024 && unitIndex < units.lastIndex) {
+ size /= 1024
+ unitIndex++
+ }
+ return if (size == size.toLong().toDouble()) {
+ "${size.toLong()} ${units[unitIndex]}"
+ } else {
+ "${"%.1f".format(size)} ${units[unitIndex]}"
+ }
+ }
+
private fun loadVersionName() {
viewModelScope.launch {
_state.update {
@@ -172,6 +199,23 @@ class ProfileViewModel(
)
}
+ ProfileAction.OnClearCacheClick -> {
+ viewModelScope.launch {
+ runCatching {
+ profileRepository.clearCache()
+ }.onSuccess {
+ observeCacheSize()
+ _events.send(ProfileEvent.OnCacheCleared)
+ }.onFailure { error ->
+ _events.send(
+ ProfileEvent.OnCacheClearError(
+ error.message ?: "Failed to clear cache"
+ )
+ )
+ }
+ }
+ }
+
is ProfileAction.OnThemeColorSelected -> {
viewModelScope.launch {
themesRepository.setThemeColor(action.themeColor)
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt
index cfef4968..99db94b3 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt
@@ -31,6 +31,7 @@ import androidx.compose.ui.unit.dp
import zed.rainxch.githubstore.core.presentation.res.*
import org.jetbrains.compose.resources.stringResource
import zed.rainxch.profile.presentation.ProfileAction
+import zed.rainxch.profile.presentation.components.SectionHeader
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
fun LazyListScope.about(
@@ -38,12 +39,8 @@ fun LazyListScope.about(
onAction: (ProfileAction) -> Unit,
) {
item {
- Text(
- text = stringResource(Res.string.section_about),
- style = MaterialTheme.typography.titleSmall,
- color = MaterialTheme.colorScheme.outline,
- fontWeight = FontWeight.Bold,
- modifier = Modifier.padding(start = 8.dp)
+ SectionHeader(
+ text = stringResource(Res.string.section_about)
)
Spacer(Modifier.height(8.dp))
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt
index 651ab587..85369a11 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt
@@ -106,7 +106,7 @@ fun LazyListScope.appearanceSection(
}
)
- VerticalSpacer(16.dp)
+ VerticalSpacer(8.dp)
}
ToggleSettingCard(
@@ -124,7 +124,7 @@ fun LazyListScope.appearanceSection(
}
)
- VerticalSpacer(16.dp)
+ VerticalSpacer(8.dp)
ToggleSettingCard(
title = stringResource(Res.string.auto_detect_clipboard_links),
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Network.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Network.kt
index cec3b1d6..02f95aad 100644
--- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Network.kt
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Network.kt
@@ -45,8 +45,8 @@ import org.jetbrains.compose.resources.stringResource
import zed.rainxch.githubstore.core.presentation.res.*
import zed.rainxch.profile.presentation.ProfileAction
import zed.rainxch.profile.presentation.ProfileState
-import zed.rainxch.profile.presentation.ProxyType
import zed.rainxch.profile.presentation.components.SectionHeader
+import zed.rainxch.profile.presentation.model.ProxyType
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
fun LazyListScope.networkSection(
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Others.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Others.kt
new file mode 100644
index 00000000..b374b448
--- /dev/null
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Others.kt
@@ -0,0 +1,101 @@
+package zed.rainxch.profile.presentation.components.sections
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Storage
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.resources.stringResource
+import zed.rainxch.core.presentation.components.ExpressiveCard
+import zed.rainxch.githubstore.core.presentation.res.*
+import zed.rainxch.profile.presentation.ProfileAction
+import zed.rainxch.profile.presentation.ProfileState
+import zed.rainxch.profile.presentation.components.SectionHeader
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+fun LazyListScope.othersSection(
+ state: ProfileState,
+ onAction: (ProfileAction) -> Unit
+) {
+ item {
+ SectionHeader(
+ text = stringResource(Res.string.storage).uppercase()
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ ExpressiveCard {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Storage,
+ contentDescription = null,
+ modifier = Modifier
+ .size(44.dp)
+ .clip(RoundedCornerShape(36.dp))
+ .background(MaterialTheme.colorScheme.surfaceContainerLow)
+ .padding(8.dp)
+ )
+
+ Column (
+ modifier = Modifier.weight(1f),
+ verticalArrangement = Arrangement.spacedBy(2.dp),
+ horizontalAlignment = Alignment.Start
+ ) {
+ Text(
+ text = stringResource(Res.string.clear_cache),
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface
+ )
+
+ Text(
+ text = "${stringResource(Res.string.current_size)} ${state.cacheSize}",
+ style = MaterialTheme.typography.titleSmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+
+ Button(
+ onClick = {
+ onAction(ProfileAction.OnClearCacheClick)
+ },
+ shape = RoundedCornerShape(12.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ contentColor = MaterialTheme.colorScheme.onSurface
+ )
+ ) {
+ Text(
+ text = stringResource(Res.string.clear),
+ style = MaterialTheme.typography.titleMediumEmphasized,
+ fontWeight = FontWeight.Bold
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/model/ProxyType.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/model/ProxyType.kt
new file mode 100644
index 00000000..336cc207
--- /dev/null
+++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/model/ProxyType.kt
@@ -0,0 +1,16 @@
+package zed.rainxch.profile.presentation.model;
+
+import zed.rainxch.core.domain.model.ProxyConfig
+
+enum class ProxyType {
+ NONE, SYSTEM, HTTP, SOCKS;
+
+ companion object {
+ fun fromConfig(config: ProxyConfig): ProxyType = when (config) {
+ is ProxyConfig.None -> NONE
+ is ProxyConfig.System -> SYSTEM
+ is ProxyConfig.Http -> HTTP
+ is ProxyConfig.Socks -> SOCKS
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/starred/presentation/src/commonMain/kotlin/zed/rainxch/starred/presentation/components/StarredRepositoryItem.kt b/feature/starred/presentation/src/commonMain/kotlin/zed/rainxch/starred/presentation/components/StarredRepositoryItem.kt
index 914fd2ec..1339e5e7 100644
--- a/feature/starred/presentation/src/commonMain/kotlin/zed/rainxch/starred/presentation/components/StarredRepositoryItem.kt
+++ b/feature/starred/presentation/src/commonMain/kotlin/zed/rainxch/starred/presentation/components/StarredRepositoryItem.kt
@@ -25,9 +25,11 @@ import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledIconToggleButton
import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialShapes
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.Text
+import androidx.compose.material3.toShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -42,6 +44,7 @@ import com.skydoves.landscapist.coil3.CoilImage
import zed.rainxch.githubstore.core.presentation.res.*
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.tooling.preview.Preview
+import zed.rainxch.core.presentation.components.ExpressiveCard
import zed.rainxch.core.presentation.theme.GithubStoreTheme
import zed.rainxch.core.presentation.utils.formatCount
import zed.rainxch.starred.presentation.model.StarredRepositoryUi
@@ -55,9 +58,9 @@ fun StarredRepositoryItem(
onDevProfileClick: () -> Unit,
modifier: Modifier = Modifier
) {
- Card(
- modifier = modifier.fillMaxWidth(),
- onClick = onItemClick
+ ExpressiveCard(
+ onClick = onItemClick,
+ modifier = modifier.fillMaxWidth()
) {
Column(
modifier = Modifier
@@ -110,7 +113,8 @@ fun StarredRepositoryItem(
FilledIconToggleButton(
checked = repository.isFavorite,
onCheckedChange = { onToggleFavoriteClick() },
- modifier = Modifier.size(40.dp)
+ modifier = Modifier.size(40.dp),
+ shape = MaterialShapes.Cookie6Sided.toShape()
) {
Icon(
imageVector = if (repository.isFavorite) {