mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-06 11:28:22 +00:00
Refactor permission management and UI components
- Add `updateExpiredPermissions` to `ApplicationDao` to reset expired temporary permissions. - Implement `AmberToggles` and `ToggleOption` reusable UI components. - Refactor `EditPermission` to use the new toggle components and ensure expired permissions are updated on load. - Update `ConnectivityService` to periodically clear expired permissions across all accounts. - Clean up redundant build flavor checks and logic in `ConnectivityService`.
This commit is contained in:
@@ -42,7 +42,10 @@ interface ApplicationDao {
|
||||
@Transaction
|
||||
suspend fun getBySecret(secret: String): ApplicationWithPermissions?
|
||||
|
||||
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key and rememberType = 4")
|
||||
@Query("UPDATE applicationPermission set acceptUntil = 0, rejectUntil = 0, rememberType = 0 where (acceptUntil < :time OR rejectUntil < :time) AND rememberType <> 4")
|
||||
fun updateExpiredPermissions(time: Long)
|
||||
|
||||
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key")
|
||||
suspend fun getAllByKey(key: String): List<ApplicationPermissionsEntity>
|
||||
|
||||
@Query("SELECT `key`, `name` FROM application WHERE name <> ''")
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.greenart7c3.nostrsigner.Amber
|
||||
import com.greenart7c3.nostrsigner.BuildFlavorChecker
|
||||
import com.greenart7c3.nostrsigner.LocalPreferences
|
||||
import com.greenart7c3.nostrsigner.database.LogEntity
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -109,11 +110,10 @@ class ConnectivityService : Service() {
|
||||
timer.schedule(
|
||||
object : TimerTask() {
|
||||
override fun run() {
|
||||
if (Amber.instance.settings.killSwitch.value) return
|
||||
|
||||
scope.launch {
|
||||
LocalPreferences.allSavedAccounts(Amber.instance).forEach { accountInfo ->
|
||||
val now = System.currentTimeMillis() / 1000
|
||||
Amber.instance.getDatabase(accountInfo.npub).dao().updateExpiredPermissions(TimeUtils.now())
|
||||
val deleted = Amber.instance.getDatabase(accountInfo.npub).dao().deleteOldApplications(now)
|
||||
if (deleted > 0) {
|
||||
Amber.instance.getLogDatabase(accountInfo.npub).dao().insertLog(
|
||||
@@ -125,9 +125,6 @@ class ConnectivityService : Service() {
|
||||
time = System.currentTimeMillis(),
|
||||
),
|
||||
)
|
||||
if (!BuildFlavorChecker.isOfflineFlavor()) {
|
||||
Amber.instance.notificationSubscription.updateFilter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,8 +132,11 @@ class ConnectivityService : Service() {
|
||||
if (BuildFlavorChecker.isOfflineFlavor()) {
|
||||
return
|
||||
}
|
||||
if (Amber.instance.settings.killSwitch.value) return
|
||||
|
||||
scope.launch {
|
||||
Amber.instance.notificationSubscription.updateFilter()
|
||||
|
||||
if (!Amber.instance.client.isActive()) {
|
||||
Amber.instance.client.connect()
|
||||
}
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
package com.greenart7c3.nostrsigner.ui
|
||||
|
||||
import android.content.ClipData
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -39,7 +30,6 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.ClipEntry
|
||||
@@ -62,6 +52,8 @@ import com.greenart7c3.nostrsigner.models.Account
|
||||
import com.greenart7c3.nostrsigner.models.Permission
|
||||
import com.greenart7c3.nostrsigner.ui.actions.RemoveAllPermissionsDialog
|
||||
import com.greenart7c3.nostrsigner.ui.components.AmberButton
|
||||
import com.greenart7c3.nostrsigner.ui.components.AmberToggles
|
||||
import com.greenart7c3.nostrsigner.ui.components.ToggleOption
|
||||
import com.greenart7c3.nostrsigner.ui.theme.orange
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -98,6 +90,7 @@ fun EditPermission(
|
||||
LaunchedEffect(selectedPackage) {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
val dao = Amber.instance.getDatabase(account.npub).dao()
|
||||
dao.updateExpiredPermissions(TimeUtils.now())
|
||||
val perms = dao.getAllByKey(selectedPackage)
|
||||
.sortedBy { "${it.type}-${it.kind}" }
|
||||
|
||||
@@ -316,9 +309,7 @@ fun PermissionRow(
|
||||
}
|
||||
}
|
||||
val fixedSegmentWidth = 55.dp
|
||||
val padding = 2.dp
|
||||
val totalWidth = (fixedSegmentWidth * 3) + (padding * 2)
|
||||
val totalWidthRememberType = (fixedSegmentWidth * 4) + (padding * 2)
|
||||
|
||||
var optionIndex by remember {
|
||||
if (permission.acceptUntil > 0) {
|
||||
mutableIntStateOf(0)
|
||||
@@ -352,215 +343,125 @@ fun PermissionRow(
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
AmberToggles(
|
||||
count = 3,
|
||||
selectedIndex = optionIndex,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(totalWidth)
|
||||
.height(32.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(padding),
|
||||
) {
|
||||
val indicatorOffset by animateDpAsState(
|
||||
targetValue = fixedSegmentWidth * optionIndex,
|
||||
animationSpec = tween(durationMillis = 250),
|
||||
label = "indicatorOffset",
|
||||
)
|
||||
ToggleOption(
|
||||
text = "Allow",
|
||||
isSelected = optionIndex == 0,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
optionIndex = 0
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.offset(x = indicatorOffset)
|
||||
.width(fixedSegmentWidth)
|
||||
.fillMaxHeight()
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
)
|
||||
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
ToggleOption(
|
||||
text = "Allow",
|
||||
isSelected = optionIndex == 0,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
optionIndex = 0
|
||||
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
ToggleOption(
|
||||
text = "Deny",
|
||||
isSelected = optionIndex == 1,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
optionIndex = 1
|
||||
},
|
||||
)
|
||||
ToggleOption(
|
||||
text = "Deny",
|
||||
isSelected = optionIndex == 1,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
optionIndex = 1
|
||||
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
ToggleOption(
|
||||
text = "Ask",
|
||||
isSelected = optionIndex == 2,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
optionIndex = 2
|
||||
},
|
||||
)
|
||||
ToggleOption(
|
||||
text = "Ask",
|
||||
isSelected = optionIndex == 2,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
optionIndex = 2
|
||||
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (optionIndex != 2) {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(totalWidthRememberType)
|
||||
.height(32.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(padding),
|
||||
AmberToggles(
|
||||
selectedIndex = rememberTypeIndex,
|
||||
count = 4,
|
||||
content = {
|
||||
ToggleOption(
|
||||
text = "Always",
|
||||
isSelected = rememberTypeIndex == 0,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 0
|
||||
|
||||
) {
|
||||
val indicatorOffset by animateDpAsState(
|
||||
targetValue = fixedSegmentWidth * rememberTypeIndex,
|
||||
animationSpec = tween(durationMillis = 250),
|
||||
label = "indicatorOffset",
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.offset(x = indicatorOffset)
|
||||
.width(fixedSegmentWidth)
|
||||
.fillMaxHeight()
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
ToggleOption(
|
||||
text = "1m",
|
||||
isSelected = rememberTypeIndex == 1,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 1
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
ToggleOption(
|
||||
text = "Always",
|
||||
isSelected = rememberTypeIndex == 0,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 0
|
||||
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
ToggleOption(
|
||||
text = "1m",
|
||||
isSelected = rememberTypeIndex == 1,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 1
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
ToggleOption(
|
||||
text = "5m",
|
||||
isSelected = rememberTypeIndex == 2,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 2
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
ToggleOption(
|
||||
text = "10m",
|
||||
isSelected = rememberTypeIndex == 3,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 3
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
ToggleOption(
|
||||
text = "5m",
|
||||
isSelected = rememberTypeIndex == 2,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 2
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
ToggleOption(
|
||||
text = "10m",
|
||||
isSelected = rememberTypeIndex == 3,
|
||||
modifier = Modifier.width(fixedSegmentWidth),
|
||||
onClick = {
|
||||
rememberTypeIndex = 3
|
||||
onSetPermission(
|
||||
optionIndex,
|
||||
rememberTypeIndex,
|
||||
permission,
|
||||
onToggle,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleOption(
|
||||
text: String,
|
||||
isSelected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val textColor by animateColorAsState(
|
||||
targetValue = if (isSelected) Color.Black else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
animationSpec = tween(durationMillis = 200),
|
||||
label = "textColor",
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxHeight()
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.clickable(onClick = onClick),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = textColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RelayCard(
|
||||
modifier: Modifier = Modifier,
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.greenart7c3.nostrsigner.ui.components
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun AmberToggles(
|
||||
selectedIndex: Int,
|
||||
count: Int,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
val fixedSegmentWidth = 55.dp
|
||||
val padding = 2.dp
|
||||
val totalWidth = (fixedSegmentWidth * count) + (padding * 2)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(totalWidth)
|
||||
.height(32.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(padding),
|
||||
) {
|
||||
val indicatorOffset by animateDpAsState(
|
||||
targetValue = fixedSegmentWidth * selectedIndex,
|
||||
animationSpec = tween(durationMillis = 250),
|
||||
label = "indicatorOffset",
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.offset(x = indicatorOffset)
|
||||
.width(fixedSegmentWidth)
|
||||
.fillMaxHeight()
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
)
|
||||
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
this
|
||||
.content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.greenart7c3.nostrsigner.ui.components
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ToggleOption(
|
||||
text: String,
|
||||
isSelected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val textColor by animateColorAsState(
|
||||
targetValue = if (isSelected) Color.Black else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
animationSpec = tween(durationMillis = 200),
|
||||
label = "textColor",
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxHeight()
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.clickable(onClick = onClick),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = textColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user