Procházet zdrojové kódy

Added draft page state

Vadik Sirekanyan před 2 roky
rodič
revize
25610fa2a3

+ 8 - 1
app/src/main/java/org/sirekanyan/outline/MainActivity.kt

@@ -13,6 +13,8 @@ import androidx.compose.ui.Modifier
 import androidx.core.view.WindowCompat
 import org.sirekanyan.outline.api.OutlineApi
 import org.sirekanyan.outline.api.model.Key
+import org.sirekanyan.outline.db.rememberApiUrlDao
+import org.sirekanyan.outline.ui.DraftContent
 import org.sirekanyan.outline.ui.theme.OutlineTheme
 
 class MainActivity : ComponentActivity() {
@@ -21,13 +23,18 @@ class MainActivity : ComponentActivity() {
         WindowCompat.setDecorFitsSystemWindows(window, false)
         setContent {
             val api = remember { OutlineApi() }
+            val dao = rememberApiUrlDao()
             val state = rememberMainState()
             val keys by produceState(listOf<Key>(), state.selected) {
                 value = state.selected?.let { api.getKeys(it) } ?: listOf()
             }
             OutlineTheme {
                 Surface(Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
-                    MainContent(api, state, keys)
+                    if (state.page is DraftPage) {
+                        DraftContent(api, dao, state)
+                    } else {
+                        MainContent(api, dao, state, keys)
+                    }
                 }
             }
         }

+ 3 - 2
app/src/main/java/org/sirekanyan/outline/MainContent.kt

@@ -9,13 +9,14 @@ import androidx.compose.runtime.Composable
 import kotlinx.coroutines.launch
 import org.sirekanyan.outline.api.OutlineApi
 import org.sirekanyan.outline.api.model.Key
+import org.sirekanyan.outline.db.ApiUrlDao
 import org.sirekanyan.outline.ui.AddKeyButton
 import org.sirekanyan.outline.ui.DrawerContent
 import org.sirekanyan.outline.ui.KeyContent
 
 @Composable
-fun MainContent(api: OutlineApi, state: MainState, keys: List<Key>) {
-    ModalNavigationDrawer({ DrawerContent(api, state) }, drawerState = state.drawer) {
+fun MainContent(api: OutlineApi, dao: ApiUrlDao, state: MainState, keys: List<Key>) {
+    ModalNavigationDrawer({ DrawerContent(api, dao, state) }, drawerState = state.drawer) {
         LazyColumn(contentPadding = WindowInsets.systemBars.asPaddingValues()) {
             keys.sortedByDescending(Key::traffic).forEach { key ->
                 item {

+ 20 - 4
app/src/main/java/org/sirekanyan/outline/MainState.kt

@@ -3,6 +3,7 @@ package org.sirekanyan.outline
 import androidx.compose.material3.DrawerState
 import androidx.compose.material3.DrawerValue
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -20,10 +21,25 @@ fun rememberMainState(): MainState {
 class MainState(val scope: CoroutineScope) {
 
     val drawer = DrawerState(DrawerValue.Closed)
-    var selected by mutableStateOf<String?>(null)
+    var page by mutableStateOf<Page>(HelloPage)
+    val selected by derivedStateOf { (page as? SelectedPage)?.selected }
 
-    fun closeDrawer() {
-        scope.launch { drawer.close() }
+    fun closeDrawer(animated: Boolean = true) {
+        scope.launch {
+            if (animated) {
+                drawer.close()
+            } else {
+                drawer.snapTo(DrawerValue.Closed)
+            }
+        }
     }
 
-}
+}
+
+sealed class Page
+
+data object HelloPage : Page()
+
+data object DraftPage : Page()
+
+data class SelectedPage(val selected: String) : Page()

+ 1 - 1
app/src/main/java/org/sirekanyan/outline/db/ApiUrlDao.kt

@@ -21,7 +21,7 @@ class ApiUrlDao(app: Application) {
     private val queries = OutlineDatabase(driver).apiUrlQueries
 
     fun observeUrls(): Flow<List<String>> =
-        queries.selectUrl().asFlow().mapToList()
+        queries.selectUrls().asFlow().mapToList()
 
     fun insertUrl(id: String) {
         queries.insertUrl(id)

+ 95 - 0
app/src/main/java/org/sirekanyan/outline/ui/DraftContent.kt

@@ -0,0 +1,95 @@
+package org.sirekanyan.outline.ui
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.surfaceColorAtElevation
+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.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.launch
+import org.sirekanyan.outline.HelloPage
+import org.sirekanyan.outline.MainState
+import org.sirekanyan.outline.SelectedPage
+import org.sirekanyan.outline.api.OutlineApi
+import org.sirekanyan.outline.db.ApiUrlDao
+
+@Composable
+fun DraftContent(api: OutlineApi, dao: ApiUrlDao, state: MainState) {
+    var draft by remember { mutableStateOf("") }
+    var error by remember(draft) { mutableStateOf("") }
+    suspend fun onAddClick() {
+        try {
+            api.getServerName(draft)
+            dao.insertUrl(draft)
+            state.page = SelectedPage(draft)
+            state.closeDrawer(animated = false)
+        } catch (exception: Exception) {
+            exception.printStackTrace()
+            error = "Check URL or try again"
+        }
+    }
+    Column {
+        DraftTopAppBar(
+            onCloseClick = { state.page = HelloPage },
+            onAddClick = { state.scope.launch { onAddClick() } },
+        )
+        val focusRequester = remember { FocusRequester() }
+        OutlinedTextField(
+            value = draft,
+            onValueChange = { draft = it.trim() },
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(horizontal = 16.dp, vertical = 24.dp)
+                .focusRequester(focusRequester),
+            label = { Text("Management API URL") },
+            placeholder = { Text("https://xx.xx.xx.xx:xxx/xxxxx") },
+            isError = error.isNotEmpty(),
+            supportingText = { Text(error) },
+            maxLines = 4,
+            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+            keyboardActions = KeyboardActions(onDone = { state.scope.launch { onAddClick() } })
+        )
+        LaunchedEffect(Unit) {
+            focusRequester.requestFocus()
+        }
+    }
+}
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun DraftTopAppBar(onCloseClick: () -> Unit, onAddClick: () -> Unit) {
+    TopAppBar(
+        title = { Text("Add server") },
+        navigationIcon = {
+            IconButton({ onCloseClick() }) { Icon(Icons.Default.Close, null) }
+        },
+        actions = {
+            TextButton({ onAddClick() }) { Text("Add") }
+        },
+        colors = TopAppBarDefaults.topAppBarColors(
+            containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
+        ),
+    )
+}

+ 6 - 7
app/src/main/java/org/sirekanyan/outline/ui/DrawerContent.kt

@@ -18,14 +18,15 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import org.sirekanyan.outline.DraftPage
 import org.sirekanyan.outline.MainState
 import org.sirekanyan.outline.R
+import org.sirekanyan.outline.SelectedPage
 import org.sirekanyan.outline.api.OutlineApi
-import org.sirekanyan.outline.db.rememberApiUrlDao
+import org.sirekanyan.outline.db.ApiUrlDao
 
 @Composable
-fun DrawerContent(api: OutlineApi, state: MainState) {
-    val dao = rememberApiUrlDao()
+fun DrawerContent(api: OutlineApi, dao: ApiUrlDao, state: MainState) {
     ModalDrawerSheet {
         Text(
             text = stringResource(R.string.app_name),
@@ -44,7 +45,7 @@ fun DrawerContent(api: OutlineApi, state: MainState) {
                 modifier = Modifier.padding(horizontal = 12.dp),
                 selected = selected,
                 onClick = {
-                    state.selected = apiUrl
+                    state.page = SelectedPage(apiUrl)
                     state.closeDrawer()
                 },
             )
@@ -55,9 +56,7 @@ fun DrawerContent(api: OutlineApi, state: MainState) {
             modifier = Modifier.padding(horizontal = 12.dp),
             selected = false,
             onClick = {
-                // TODO: do something useful
-                state.selected = null
-                state.closeDrawer()
+                state.page = DraftPage
             },
         )
     }

+ 2 - 2
app/src/main/sqldelight/org/sirekanyan/outline/db/model/ApiUrl.sq

@@ -2,11 +2,11 @@ CREATE TABLE IF NOT EXISTS ApiUrl (
   id TEXT NOT NULL PRIMARY KEY
 );
 
-selectUrl:
+selectUrls:
 SELECT * FROM ApiUrl;
 
 insertUrl:
-INSERT INTO ApiUrl (id) VALUES (?);
+INSERT OR IGNORE INTO ApiUrl (id) VALUES (?);
 
 deleteUrl:
 DELETE FROM ApiUrl WHERE id = ?;