浏览代码

Renamed api url to server entity (refactoring)

Vadik Sirekanyan 2 年之前
父节点
当前提交
4ebe56b760

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

@@ -36,14 +36,14 @@ class MainActivity : ComponentActivity() {
                             is AddServerDialog -> Surface { AddServerContent(state) }
                             is EditKeyDialog -> Surface { EditKeyContent(state, dialog) }
                             is DeleteKeyDialog -> {
-                                val (apiUrl, key) = dialog
+                                val (server, key) = dialog
                                 DeleteKeyContent(
                                     key = key,
                                     onDismiss = { state.dialog = null },
                                     onConfirm = {
                                         state.scope.launch {
                                             state.deletingKey = key
-                                            state.api.deleteAccessKey(apiUrl, key.accessKey.id)
+                                            state.api.deleteAccessKey(server, key.accessKey.id)
                                             state.refreshCurrentKeys(showLoading = false)
                                         }.invokeOnCompletion {
                                             state.deletingKey = null
@@ -52,13 +52,13 @@ class MainActivity : ComponentActivity() {
                                 )
                             }
                             is DeleteServerDialog -> {
-                                val (apiUrl, serverName) = dialog
+                                val (server, serverName) = dialog
                                 DeleteServerContent(
                                     serverName = serverName,
                                     onDismiss = { state.dialog = null },
                                     onConfirm = {
                                         state.scope.launch {
-                                            state.dao.deleteUrl(apiUrl.id)
+                                            state.dao.deleteUrl(server.id)
                                         }
                                         state.page = HelloPage
                                         state.openDrawer()

+ 9 - 8
app/src/main/java/org/sirekanyan/outline/MainContent.kt

@@ -84,12 +84,13 @@ fun MainContent(state: MainState) {
                         KeysContent(insets, state, keys, sorting)
                     }
                 }
-                LaunchedEffect(page.apiUrl) {
+                LaunchedEffect(page.server) {
                     state.refreshCurrentKeys(showLoading = true)
                 }
-                val apiUrl = page.apiUrl
-                val server by produceState(state.servers.getCachedServer(apiUrl), apiUrl) {
-                    value = state.servers.getServer(apiUrl)
+                val serverEntity = page.server
+                val cachedServer = state.servers.getCachedServer(serverEntity)
+                val server by produceState(cachedServer, serverEntity) {
+                    value = state.servers.getServer(serverEntity)
                 }
                 MainTopAppBar(
                     title = server.name,
@@ -99,7 +100,7 @@ fun MainContent(state: MainState) {
                             isSortingVisible = true
                         },
                         MenuItem("Delete", Icons.Default.Delete) {
-                            state.dialog = DeleteServerDialog(page.apiUrl, server.name)
+                            state.dialog = DeleteServerDialog(page.server, server.name)
                         },
                     ),
                 )
@@ -112,7 +113,7 @@ fun MainContent(state: MainState) {
                 state.selectedPage?.let { page ->
                     state.scope.launch {
                         state.isFabLoading = true
-                        state.api.createAccessKey(page.apiUrl)
+                        state.api.createAccessKey(page.server)
                         state.refreshCurrentKeys(showLoading = false)
                     }.invokeOnCompletion {
                         state.isFabLoading = false
@@ -125,8 +126,8 @@ fun MainContent(state: MainState) {
                 KeyBottomSheet(
                     key = key,
                     onDismissRequest = { state.selectedKey = null },
-                    onEditClick = { state.dialog = EditKeyDialog(page.apiUrl, key) },
-                    onDeleteClick = { state.dialog = DeleteKeyDialog(page.apiUrl, key) },
+                    onEditClick = { state.dialog = EditKeyDialog(page.server, key) },
+                    onDeleteClick = { state.dialog = DeleteKeyDialog(page.server, key) },
                 )
             }
         }

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

@@ -19,11 +19,11 @@ import kotlinx.coroutines.launch
 import kotlinx.coroutines.plus
 import org.sirekanyan.outline.api.OutlineApi
 import org.sirekanyan.outline.api.model.Key
-import org.sirekanyan.outline.db.ApiUrlDao
 import org.sirekanyan.outline.db.KeyValueDao
-import org.sirekanyan.outline.db.model.ApiUrl
-import org.sirekanyan.outline.db.rememberApiUrlDao
+import org.sirekanyan.outline.db.ServerDao
+import org.sirekanyan.outline.db.model.ServerEntity
 import org.sirekanyan.outline.db.rememberKeyValueDao
+import org.sirekanyan.outline.db.rememberServerDao
 import org.sirekanyan.outline.ext.logError
 import org.sirekanyan.outline.feature.keys.KeysErrorState
 import org.sirekanyan.outline.feature.keys.KeysLoadingState
@@ -56,7 +56,7 @@ fun rememberMainState(): MainState {
     }
     val supervisor = remember { SupervisorJob() }
     val api = remember { OutlineApi() }
-    val dao = rememberApiUrlDao()
+    val dao = rememberServerDao()
     val prefs = rememberKeyValueDao()
     return remember { MainState(scope + supervisor, api, dao, prefs) }
 }
@@ -64,7 +64,7 @@ fun rememberMainState(): MainState {
 class MainState(
     val scope: CoroutineScope,
     val api: OutlineApi,
-    val dao: ApiUrlDao,
+    val dao: ServerDao,
     private val prefs: KeyValueDao,
 ) {
 
@@ -107,7 +107,7 @@ class MainState(
                 page.keys = KeysLoadingState
             }
             page.keys = try {
-                KeysSuccessState(api.getKeys(page.apiUrl))
+                KeysSuccessState(api.getKeys(page.server))
             } catch (exception: Exception) {
                 exception.printStackTrace()
                 KeysErrorState
@@ -121,7 +121,7 @@ sealed class Page
 
 data object HelloPage : Page()
 
-data class SelectedPage(val apiUrl: ApiUrl) : Page() {
+data class SelectedPage(val server: ServerEntity) : Page() {
     var keys by mutableStateOf<KeysState>(KeysLoadingState)
 }
 
@@ -129,8 +129,8 @@ sealed class Dialog
 
 data object AddServerDialog : Dialog()
 
-data class EditKeyDialog(val apiUrl: ApiUrl, val key: Key) : Dialog()
+data class EditKeyDialog(val server: ServerEntity, val key: Key) : Dialog()
 
-data class DeleteKeyDialog(val apiUrl: ApiUrl, val key: Key) : Dialog()
+data class DeleteKeyDialog(val server: ServerEntity, val key: Key) : Dialog()
 
-data class DeleteServerDialog(val apiUrl: ApiUrl, val serverName: String) : Dialog()
+data class DeleteServerDialog(val server: ServerEntity, val serverName: String) : Dialog()

+ 20 - 20
app/src/main/java/org/sirekanyan/outline/api/OutlineApi.kt

@@ -21,7 +21,7 @@ import org.sirekanyan.outline.api.model.RenameRequest
 import org.sirekanyan.outline.api.model.Server
 import org.sirekanyan.outline.api.model.ServerNameResponse
 import org.sirekanyan.outline.api.model.TransferMetricsResponse
-import org.sirekanyan.outline.db.model.ApiUrl
+import org.sirekanyan.outline.db.model.ServerEntity
 import org.sirekanyan.outline.ext.logDebug
 import java.security.SecureRandom
 import javax.net.ssl.SSLContext
@@ -54,50 +54,50 @@ class OutlineApi {
 
     private suspend fun request(
         httpMethod: HttpMethod,
-        apiUrl: ApiUrl,
+        server: ServerEntity,
         path: String,
         block: HttpRequestBuilder.() -> Unit = {},
     ): HttpResponse {
-        val client = if (apiUrl.insecure) insecureHttpClient else httpClient
-        return client.request(apiUrl.id + '/' + path) { method = httpMethod; block() }
+        val client = if (server.insecure) insecureHttpClient else httpClient
+        return client.request(server.id + '/' + path) { method = httpMethod; block() }
     }
 
-    suspend fun getServer(apiUrl: ApiUrl): Server {
-        val name = request(HttpMethod.Get, apiUrl, "server").body<ServerNameResponse>().name
-        val transferMetrics = getTransferMetrics(apiUrl)?.bytesTransferredByUserId
+    suspend fun getServer(server: ServerEntity): Server {
+        val name = request(HttpMethod.Get, server, "server").body<ServerNameResponse>().name
+        val transferMetrics = getTransferMetrics(server)?.bytesTransferredByUserId
         return Server(name, transferMetrics?.values?.sum())
     }
 
-    suspend fun getKeys(apiUrl: ApiUrl): List<Key> {
-        val accessKeys = getAccessKeys(apiUrl).accessKeys
-        val transferMetrics = getTransferMetrics(apiUrl)?.bytesTransferredByUserId
+    suspend fun getKeys(server: ServerEntity): List<Key> {
+        val accessKeys = getAccessKeys(server).accessKeys
+        val transferMetrics = getTransferMetrics(server)?.bytesTransferredByUserId
         return accessKeys.map { accessKey -> Key(accessKey, transferMetrics?.get(accessKey.id)) }
     }
 
-    private suspend fun getAccessKeys(apiUrl: ApiUrl): AccessKeysResponse =
-        request(HttpMethod.Get, apiUrl, "access-keys").body()
+    private suspend fun getAccessKeys(server: ServerEntity): AccessKeysResponse =
+        request(HttpMethod.Get, server, "access-keys").body()
 
-    private suspend fun getTransferMetrics(apiUrl: ApiUrl): TransferMetricsResponse? =
+    private suspend fun getTransferMetrics(server: ServerEntity): TransferMetricsResponse? =
         try {
-            request(HttpMethod.Get, apiUrl, "metrics/transfer").body()
+            request(HttpMethod.Get, server, "metrics/transfer").body()
         } catch (exception: Exception) {
             logDebug("Cannot fetch transfer metrics", exception)
             null
         }
 
-    suspend fun createAccessKey(apiUrl: ApiUrl) {
-        request(HttpMethod.Post, apiUrl, "access-keys")
+    suspend fun createAccessKey(server: ServerEntity) {
+        request(HttpMethod.Post, server, "access-keys")
     }
 
-    suspend fun renameAccessKey(apiUrl: ApiUrl, id: String, name: String) {
-        request(HttpMethod.Put, apiUrl, "access-keys/$id/name") {
+    suspend fun renameAccessKey(server: ServerEntity, id: String, name: String) {
+        request(HttpMethod.Put, server, "access-keys/$id/name") {
             contentType(ContentType.Application.Json)
             setBody(RenameRequest(name))
         }
     }
 
-    suspend fun deleteAccessKey(apiUrl: ApiUrl, id: String) {
-        request(HttpMethod.Delete, apiUrl, "access-keys/$id")
+    suspend fun deleteAccessKey(server: ServerEntity, id: String) {
+        request(HttpMethod.Delete, server, "access-keys/$id")
     }
 
 }

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

@@ -19,7 +19,7 @@ fun rememberKeyValueDao(): KeyValueDao {
 
 class KeyValueDao(database: OutlineDatabase) {
 
-    private val queries = database.keyValueQueries
+    private val queries = database.keyValueEntityQueries
 
     fun observe(key: String): Flow<String?> =
         queries.select(key).asFlow().mapToOneNotNull(Dispatchers.IO).map { it.content }

+ 8 - 8
app/src/main/java/org/sirekanyan/outline/db/ApiUrlDao.kt → app/src/main/java/org/sirekanyan/outline/db/ServerDao.kt

@@ -9,24 +9,24 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.withContext
 import org.sirekanyan.outline.app
-import org.sirekanyan.outline.db.model.ApiUrl
+import org.sirekanyan.outline.db.model.ServerEntity
 
 @Composable
-fun rememberApiUrlDao(): ApiUrlDao {
+fun rememberServerDao(): ServerDao {
     val database = LocalContext.current.app().database
-    return remember { ApiUrlDao(database) }
+    return remember { ServerDao(database) }
 }
 
-class ApiUrlDao(database: OutlineDatabase) {
+class ServerDao(database: OutlineDatabase) {
 
-    private val queries = database.apiUrlQueries
+    private val queries = database.serverEntityQueries
 
-    fun observeUrls(): Flow<List<ApiUrl>> =
+    fun observeUrls(): Flow<List<ServerEntity>> =
         queries.selectUrls().asFlow().mapToList(Dispatchers.IO)
 
-    suspend fun insertUrl(url: ApiUrl) =
+    suspend fun insertUrl(server: ServerEntity) =
         withContext(Dispatchers.IO) {
-            queries.insertUrl(url)
+            queries.insertUrl(server)
         }
 
     suspend fun deleteUrl(id: String) {

+ 10 - 10
app/src/main/java/org/sirekanyan/outline/repository/ServerRepository.kt

@@ -3,7 +3,7 @@ package org.sirekanyan.outline.repository
 import android.net.Uri
 import org.sirekanyan.outline.api.OutlineApi
 import org.sirekanyan.outline.api.model.Server
-import org.sirekanyan.outline.db.model.ApiUrl
+import org.sirekanyan.outline.db.model.ServerEntity
 import org.sirekanyan.outline.ext.logDebug
 import java.util.concurrent.ConcurrentHashMap
 
@@ -11,23 +11,23 @@ class ServerRepository(private val api: OutlineApi) {
 
     private val cache: MutableMap<String, Server> = ConcurrentHashMap()
 
-    fun getCachedServer(apiUrl: ApiUrl): Server =
-        cache[apiUrl.id] ?: Server(Uri.parse(apiUrl.id).host.orEmpty(), traffic = null)
+    fun getCachedServer(server: ServerEntity): Server =
+        cache[server.id] ?: Server(Uri.parse(server.id).host.orEmpty(), traffic = null)
 
-    suspend fun fetchServer(apiUrl: ApiUrl): Server =
-        api.getServer(apiUrl).also { fetched ->
-            cache[apiUrl.id] = fetched
+    suspend fun fetchServer(server: ServerEntity): Server =
+        api.getServer(server).also { fetched ->
+            cache[server.id] = fetched
         }
 
-    suspend fun getServer(apiUrl: ApiUrl): Server {
-        if (!cache.containsKey(apiUrl.id)) {
+    suspend fun getServer(server: ServerEntity): Server {
+        if (!cache.containsKey(server.id)) {
             try {
-                return fetchServer(apiUrl)
+                return fetchServer(server)
             } catch (exception: Exception) {
                 logDebug("Cannot fetch server name", exception)
             }
         }
-        return getCachedServer(apiUrl)
+        return getCachedServer(server)
     }
 
 }

+ 5 - 5
app/src/main/java/org/sirekanyan/outline/ui/AddServerContent.kt

@@ -27,7 +27,7 @@ import kotlinx.coroutines.launch
 import org.sirekanyan.outline.MainState
 import org.sirekanyan.outline.NotSupportedContent
 import org.sirekanyan.outline.SelectedPage
-import org.sirekanyan.outline.db.model.ApiUrl
+import org.sirekanyan.outline.db.model.ServerEntity
 import javax.net.ssl.SSLException
 
 @Composable
@@ -44,11 +44,11 @@ fun AddServerContent(state: MainState) {
         }
         try {
             isLoading = true
-            val apiUrl = ApiUrl(draft, insecure)
-            state.servers.fetchServer(apiUrl)
-            state.dao.insertUrl(apiUrl)
+            val serverEntity = ServerEntity(draft, insecure)
+            state.servers.fetchServer(serverEntity)
+            state.dao.insertUrl(serverEntity)
             state.dialog = null
-            state.page = SelectedPage(apiUrl)
+            state.page = SelectedPage(serverEntity)
             state.closeDrawer(animated = false)
         } catch (exception: SSLException) {
             exception.printStackTrace()

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

@@ -63,11 +63,12 @@ private fun DrawerSheetContent(state: MainState, insets: PaddingValues) {
             modifier = Modifier.padding(horizontal = 28.dp, vertical = 16.dp),
             style = MaterialTheme.typography.titleSmall,
         )
-        val apiUrls by remember { state.dao.observeUrls() }.collectAsState(listOf())
-        apiUrls.forEach { apiUrl ->
-            val isSelected = state.selectedPage?.apiUrl?.id == apiUrl.id
-            val server by produceState(state.servers.getCachedServer(apiUrl), state.drawer.isOpen) {
-                value = state.servers.getServer(apiUrl)
+        val serverEntities by remember { state.dao.observeUrls() }.collectAsState(listOf())
+        serverEntities.forEach { serverEntity ->
+            val isSelected = state.selectedPage?.server?.id == serverEntity.id
+            val cachedServer = state.servers.getCachedServer(serverEntity)
+            val server by produceState(cachedServer, state.drawer.isOpen) {
+                value = state.servers.getServer(serverEntity)
             }
             NavigationDrawerItem(
                 icon = { Icon(Icons.Default.Done, null) },
@@ -84,7 +85,7 @@ private fun DrawerSheetContent(state: MainState, insets: PaddingValues) {
                 modifier = Modifier.padding(horizontal = 12.dp),
                 selected = isSelected,
                 onClick = {
-                    state.page = SelectedPage(apiUrl)
+                    state.page = SelectedPage(serverEntity)
                     state.closeDrawer()
                 },
             )

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

@@ -38,7 +38,7 @@ fun EditKeyContent(state: MainState, dialog: EditKeyDialog) {
                     val isSuccess = try {
                         isLoading = true
                         val newName = draft.text.ifBlank { accessKey.defaultName }
-                        state.api.renameAccessKey(dialog.apiUrl, accessKey.id, newName)
+                        state.api.renameAccessKey(dialog.server, accessKey.id, newName)
                         state.dialog = null
                         true
                     } catch (exception: Exception) {

+ 2 - 0
app/src/main/sqldelight/migrations/2.sqm

@@ -0,0 +1,2 @@
+ALTER TABLE ApiUrl RENAME TO ServerEntity;
+ALTER TABLE KeyValue RENAME TO KeyValueEntity;

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

@@ -1,13 +0,0 @@
-CREATE TABLE IF NOT EXISTS ApiUrl (
-  id TEXT NOT NULL PRIMARY KEY,
-  insecure INTEGER AS kotlin.Boolean NOT NULL DEFAULT 0
-);
-
-selectUrls:
-SELECT * FROM ApiUrl;
-
-insertUrl:
-INSERT OR REPLACE INTO ApiUrl VALUES ?;
-
-deleteUrl:
-DELETE FROM ApiUrl WHERE id = ?;

+ 0 - 13
app/src/main/sqldelight/org/sirekanyan/outline/db/model/KeyValue.sq

@@ -1,13 +0,0 @@
-CREATE TABLE IF NOT EXISTS KeyValue (
-  id TEXT NOT NULL PRIMARY KEY,
-  content TEXT
-);
-
-select:
-SELECT content FROM KeyValue WHERE id = ?;
-
-insert:
-INSERT OR REPLACE INTO KeyValue (id, content) VALUES (?, ?);
-
-delete:
-DELETE FROM KeyValue WHERE id = ?;

+ 13 - 0
app/src/main/sqldelight/org/sirekanyan/outline/db/model/KeyValueEntity.sq

@@ -0,0 +1,13 @@
+CREATE TABLE IF NOT EXISTS KeyValueEntity (
+  id TEXT NOT NULL PRIMARY KEY,
+  content TEXT
+);
+
+select:
+SELECT content FROM KeyValueEntity WHERE id = ?;
+
+insert:
+INSERT OR REPLACE INTO KeyValueEntity (id, content) VALUES (?, ?);
+
+delete:
+DELETE FROM KeyValueEntity WHERE id = ?;

+ 13 - 0
app/src/main/sqldelight/org/sirekanyan/outline/db/model/ServerEntity.sq

@@ -0,0 +1,13 @@
+CREATE TABLE IF NOT EXISTS ServerEntity (
+  id TEXT NOT NULL PRIMARY KEY,
+  insecure INTEGER AS kotlin.Boolean NOT NULL DEFAULT 0
+);
+
+selectUrls:
+SELECT * FROM ServerEntity;
+
+insertUrl:
+INSERT OR REPLACE INTO ServerEntity VALUES ?;
+
+deleteUrl:
+DELETE FROM ServerEntity WHERE id = ?;