瀏覽代碼

Added all keys on hello page

Vadik Sirekanyan 2 年之前
父節點
當前提交
54122d4d00

+ 18 - 6
app/src/main/java/org/sirekanyan/outline/MainContent.kt

@@ -62,23 +62,35 @@ fun MainContent(state: MainState) {
         val insets = WindowInsets.systemBars.asPaddingValues() + PaddingValues(top = 64.dp)
         when (val page = state.page) {
             is HelloPage -> {
-                Box(Modifier.fillMaxSize().padding(insets), Alignment.Center) {
-                    TextButton(onClick = { state.dialog = AddServerDialog }) {
-                        Icon(Icons.Default.Add, null)
-                        Spacer(Modifier.size(8.dp))
-                        Text("Add server")
+                val allKeys by rememberFlowAsState(initial = null) { state.keys.observeAllKeys() }
+                allKeys?.let { keys ->
+                    if (keys.isNotEmpty()) {
+                        KeysContent(insets, state, keys, sorting)
+                    } else {
+                        Box(Modifier.fillMaxSize().padding(insets), Alignment.Center) {
+                            TextButton(onClick = { state.dialog = AddServerDialog }) {
+                                Icon(Icons.Default.Add, null)
+                                Spacer(Modifier.size(8.dp))
+                                Text("Add server")
+                            }
+                        }
                     }
                 }
                 MainTopAppBar(
                     title = stringResource(R.string.outln_app_name),
                     onMenuClick = state::openDrawer,
+                    items = listOf(
+                        MenuItem("Sort by…", IconSort) {
+                            isSortingVisible = true
+                        },
+                    )
                 )
             }
             is SelectedPage -> {
                 val keys by rememberFlowAsState(listOf(), page.server.id) {
                     state.keys.observeKeys(page.server)
                 }
-                KeysContent(insets, state, keys, sorting)
+                KeysContent(insets + PaddingValues(bottom = 88.dp), state, keys, sorting)
                 val hasKeys = keys.isNotEmpty()
                 Box(Modifier.fillMaxSize().padding(insets).alpha(0.95f)) {
                     AnimatedVisibility(

+ 10 - 0
app/src/main/java/org/sirekanyan/outline/api/model/Key.kt

@@ -1,6 +1,7 @@
 package org.sirekanyan.outline.api.model
 
 import org.sirekanyan.outline.db.model.KeyEntity
+import org.sirekanyan.outline.db.model.SelectAllKeys
 import org.sirekanyan.outline.db.model.ServerEntity
 
 fun List<Key>.toEntities(): List<KeyEntity> =
@@ -14,4 +15,13 @@ fun List<KeyEntity>.fromEntities(server: ServerEntity): List<Key> =
         Key(server, AccessKey(entity.id, entity.url, entity.name), entity.traffic)
     }
 
+fun List<SelectAllKeys>.fromEntities(): List<Key> =
+    map { entity ->
+        Key(
+            ServerEntity(entity.serverId, entity.insecure, entity.serverName, entity.serverTraffic),
+            AccessKey(entity.id, entity.url, entity.name),
+            entity.traffic,
+        )
+    }
+
 class Key(val server: ServerEntity, val accessKey: AccessKey, val traffic: Long?)

+ 4 - 0
app/src/main/java/org/sirekanyan/outline/db/KeyDao.kt

@@ -8,6 +8,7 @@ import app.cash.sqldelight.coroutines.asFlow
 import kotlinx.coroutines.flow.Flow
 import org.sirekanyan.outline.app
 import org.sirekanyan.outline.db.model.KeyEntity
+import org.sirekanyan.outline.db.model.SelectAllKeys
 import org.sirekanyan.outline.db.model.ServerEntity
 
 @Composable
@@ -23,6 +24,9 @@ class KeyDao(database: OutlineDatabase) {
     fun observe(server: ServerEntity): Flow<Query<KeyEntity>> =
         queries.selectKeys(server.id).asFlow()
 
+    fun observeAll(): Flow<Query<SelectAllKeys>> =
+        queries.selectAllKeys().asFlow()
+
     fun update(server: ServerEntity, keys: List<KeyEntity>) {
         queries.transaction {
             queries.deleteKeys(server.id)

+ 22 - 3
app/src/main/java/org/sirekanyan/outline/feature/keys/KeyContent.kt

@@ -2,8 +2,10 @@ package org.sirekanyan.outline.feature.keys
 
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
@@ -13,19 +15,36 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis
 import androidx.compose.ui.unit.dp
 import org.sirekanyan.outline.api.model.Key
+import org.sirekanyan.outline.api.model.getHost
 import org.sirekanyan.outline.text.formatTraffic
 
 @Composable
-fun KeyContent(key: Key, onClick: () -> Unit) {
+fun KeyContent(key: Key, withServer: Boolean, onClick: () -> Unit) {
     Row(
         Modifier
             .clickable(onClick = onClick)
             .fillMaxWidth()
-            .padding(16.dp),
+            .heightIn(min = if (withServer) 72.dp else 56.dp)
+            .padding(horizontal = 16.dp, vertical = 8.dp),
         Arrangement.SpaceBetween,
         Alignment.CenterVertically,
     ) {
-        Text(key.accessKey.nameOrDefault, Modifier.weight(1f), overflow = Ellipsis, maxLines = 1)
+        Column(Modifier.weight(1f), Arrangement.Center) {
+            Text(
+                text = key.accessKey.nameOrDefault,
+                overflow = Ellipsis,
+                maxLines = 1,
+            )
+            if (withServer) {
+                Text(
+                    key.server.name.ifEmpty { key.server.getHost() },
+                    overflow = Ellipsis,
+                    maxLines = 1,
+                    style = MaterialTheme.typography.bodyMedium,
+                    color = MaterialTheme.colorScheme.onSurfaceVariant,
+                )
+            }
+        }
         key.traffic?.let { traffic ->
             Text(
                 text = formatTraffic(traffic),

+ 3 - 2
app/src/main/java/org/sirekanyan/outline/feature/keys/KeysContent.kt

@@ -10,6 +10,7 @@ import androidx.compose.runtime.produceState
 import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import org.sirekanyan.outline.HelloPage
 import org.sirekanyan.outline.MainState
 import org.sirekanyan.outline.api.model.Key
 import org.sirekanyan.outline.ext.plus
@@ -22,7 +23,7 @@ fun KeysContent(insets: PaddingValues, state: MainState, keys: List<Key>, sortin
             keys.sortedWith(sorting.comparator)
         }
     }
-    LazyColumn(contentPadding = insets + PaddingValues(top = 4.dp, bottom = 88.dp)) {
+    LazyColumn(contentPadding = insets + PaddingValues(top = 4.dp, bottom = 4.dp)) {
         sortedKeys.forEach { key ->
             item {
                 val isDeleting = key.accessKey.accessUrl == state.deletingKey?.accessKey?.accessUrl
@@ -30,7 +31,7 @@ fun KeysContent(insets: PaddingValues, state: MainState, keys: List<Key>, sortin
                 CompositionLocalProvider(
                     LocalContentColor provides LocalContentColor.current.copy(alpha = alpha)
                 ) {
-                    KeyContent(key, onClick = { state.selectedKey = key })
+                    KeyContent(key, state.page is HelloPage, onClick = { state.selectedKey = key })
                 }
             }
         }

+ 4 - 0
app/src/main/java/org/sirekanyan/outline/repository/KeyRepository.kt

@@ -11,6 +11,7 @@ import org.sirekanyan.outline.api.model.Key
 import org.sirekanyan.outline.api.model.fromEntities
 import org.sirekanyan.outline.api.model.toEntities
 import org.sirekanyan.outline.db.KeyDao
+import org.sirekanyan.outline.db.model.SelectAllKeys
 import org.sirekanyan.outline.db.model.ServerEntity
 
 class KeyRepository(private val api: OutlineApi, private val keyDao: KeyDao) {
@@ -18,6 +19,9 @@ class KeyRepository(private val api: OutlineApi, private val keyDao: KeyDao) {
     fun observeKeys(server: ServerEntity): Flow<List<Key>> =
         keyDao.observe(server).mapToList(IO).map { it.fromEntities(server) }
 
+    fun observeAllKeys(): Flow<List<Key>> =
+        keyDao.observeAll().mapToList(IO).map(List<SelectAllKeys>::fromEntities)
+
     suspend fun updateKeys(server: ServerEntity) {
         withContext(IO) {
             val keys = api.getKeys(server)

+ 13 - 1
app/src/main/java/org/sirekanyan/outline/ui/DrawerContent.kt

@@ -16,6 +16,7 @@ import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Done
+import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.Warning
 import androidx.compose.material3.Divider
 import androidx.compose.material3.Icon
@@ -34,6 +35,7 @@ import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.launch
 import org.sirekanyan.outline.AddServerDialog
+import org.sirekanyan.outline.HelloPage
 import org.sirekanyan.outline.MainState
 import org.sirekanyan.outline.R
 import org.sirekanyan.outline.SelectedPage
@@ -101,8 +103,18 @@ private fun DrawerSheetContent(state: MainState, insets: PaddingValues) {
                 state.dialog = AddServerDialog
             },
         )
+        Spacer(Modifier.weight(1f))
+        if (servers.isNotEmpty()) {
+            DrawerItem(
+                icon = Icons.Default.Search,
+                label = "All servers",
+                onClick = {
+                    state.page = HelloPage
+                    state.closeDrawer()
+                },
+            )
+        }
         if (isPlayFlavor() || isDebugBuild()) {
-            Spacer(Modifier.weight(1f))
             Divider(Modifier.padding(vertical = 8.dp))
             val context = LocalContext.current
             if (isDebugBuild()) {

+ 5 - 0
app/src/main/sqldelight/org/sirekanyan/outline/db/model/KeyEntity.sq

@@ -10,6 +10,11 @@ CREATE TABLE IF NOT EXISTS KeyEntity (
 selectKeys:
 SELECT * FROM KeyEntity WHERE serverId = ?;
 
+selectAllKeys:
+SELECT k.*, s.insecure, s.name serverName, s.traffic serverTraffic
+  FROM KeyEntity k, ServerEntity s
+  WHERE k.serverId = s.id;
+
 insertKey:
 INSERT OR REPLACE INTO KeyEntity VALUES ?;