Created ActivitiesScreen
This commit is contained in:
@@ -162,6 +162,9 @@ interface ApplicationDao {
|
||||
@Query("SELECT * FROM history2 where pkKey = :pk ORDER BY time DESC")
|
||||
fun getAllHistory(pk: String): Flow<List<HistoryEntity2>>
|
||||
|
||||
@Query("SELECT * FROM history2 ORDER BY time DESC")
|
||||
fun getAllHistory(): Flow<List<HistoryEntity2>>
|
||||
|
||||
@Insert
|
||||
@Transaction
|
||||
suspend fun insertLog(logEntity: LogEntity)
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
package com.greenart7c3.nostrsigner.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.toLowerCase
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.greenart7c3.nostrsigner.Amber
|
||||
import com.greenart7c3.nostrsigner.R
|
||||
import com.greenart7c3.nostrsigner.database.AppDatabase
|
||||
import com.greenart7c3.nostrsigner.models.Account
|
||||
import com.greenart7c3.nostrsigner.models.Permission
|
||||
import com.greenart7c3.nostrsigner.models.TimeUtils
|
||||
import com.greenart7c3.nostrsigner.models.supportedKindNumbers
|
||||
import com.greenart7c3.nostrsigner.service.ApplicationNameCache
|
||||
import com.greenart7c3.nostrsigner.service.toShortenHex
|
||||
import com.greenart7c3.nostrsigner.ui.components.SimpleSearchBar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ActivitiesScreen(
|
||||
modifier: Modifier,
|
||||
paddingValues: PaddingValues,
|
||||
topPadding: Dp,
|
||||
account: Account,
|
||||
) {
|
||||
val database = Amber.instance.getDatabase(account.npub)
|
||||
val activities = database.applicationDao().getAllHistory().collectAsStateWithLifecycle(emptyList())
|
||||
val context = LocalContext.current
|
||||
// State for the search query
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
|
||||
// Filtered activities based on the search query
|
||||
var filteredActivities = activities.value.filter { activity ->
|
||||
if (searchQuery.isEmpty()) {
|
||||
true
|
||||
} else {
|
||||
val permission = Permission(activity.type.toLowerCase(Locale.current), activity.kind)
|
||||
permission.toLocalizedString(context, true).contains(searchQuery, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
val textFieldState by remember { mutableStateOf(TextFieldState(initialText = searchQuery)) }
|
||||
|
||||
Column(
|
||||
modifier = modifier.padding(top = topPadding),
|
||||
) {
|
||||
SimpleSearchBar(
|
||||
modifier = Modifier
|
||||
.padding(start = paddingValues.calculateLeftPadding(LayoutDirection.Ltr), end = paddingValues.calculateRightPadding(LayoutDirection.Ltr))
|
||||
.fillMaxWidth(),
|
||||
textFieldState = textFieldState,
|
||||
onSearch = {
|
||||
searchQuery = it
|
||||
},
|
||||
searchResults = supportedKindNumbers.map { it.toLocalizedString(context, true) },
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(
|
||||
start = paddingValues.calculateLeftPadding(LayoutDirection.Ltr),
|
||||
end = paddingValues.calculateRightPadding(LayoutDirection.Ltr),
|
||||
bottom = paddingValues.calculateBottomPadding(),
|
||||
),
|
||||
) {
|
||||
item {
|
||||
if (filteredActivities.isEmpty()) {
|
||||
Text(
|
||||
stringResource(R.string.no_activities_found),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 18.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(filteredActivities) { activity ->
|
||||
val permission =
|
||||
Permission(
|
||||
activity.type.toLowerCase(Locale.current),
|
||||
activity.kind,
|
||||
)
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.9f),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
ApplicationName(
|
||||
key = activity.pkKey,
|
||||
accepted = activity.accepted,
|
||||
database = database,
|
||||
account = account,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = if (permission.type == "connect") stringResource(R.string.connect) else permission.toLocalizedString(context),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = if (activity.accepted) Color.Unspecified else Color.Gray,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp),
|
||||
text = TimeUtils.formatLongToCustomDateTimeWithSeconds(activity.time * 1000),
|
||||
fontSize = 16.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = Color.Gray,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
if (activity.accepted) Icons.Default.Check else Icons.Default.Close,
|
||||
contentDescription = if (activity.accepted) stringResource(R.string.accepted) else stringResource(R.string.rejected),
|
||||
tint = if (activity.accepted) Color(0xFF1D8802) else Color(0xFFFF6B00),
|
||||
modifier = Modifier.padding(start = 10.dp, top = 4.dp, bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ApplicationName(
|
||||
key: String,
|
||||
accepted: Boolean,
|
||||
database: AppDatabase,
|
||||
account: Account,
|
||||
) {
|
||||
var name by remember { mutableStateOf("") }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
launch(Dispatchers.IO) {
|
||||
if (ApplicationNameCache.names["${account.npub.toShortenHex()}-$key"] == null) {
|
||||
val app = database.applicationDao().getByKey(key)
|
||||
app?.let {
|
||||
name = it.application.name
|
||||
ApplicationNameCache.names["${account.npub.toShortenHex()}-$key"] = it.application.name
|
||||
}
|
||||
} else {
|
||||
ApplicationNameCache.names["${account.npub.toShortenHex()}-$key"]?.let {
|
||||
name = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = name.ifBlank { key.toShortenHex() },
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = if (accepted) Color.Unspecified else Color.Gray,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
}
|
||||
+10
-1
@@ -39,10 +39,11 @@ import com.greenart7c3.nostrsigner.R
|
||||
import com.greenart7c3.nostrsigner.models.Account
|
||||
import com.greenart7c3.nostrsigner.models.TimeUtils
|
||||
import com.greenart7c3.nostrsigner.service.toShortenHex
|
||||
import com.greenart7c3.nostrsigner.ui.components.AmberButton
|
||||
import com.greenart7c3.nostrsigner.ui.navigation.Route
|
||||
|
||||
@Composable
|
||||
fun PermissionsScreen(
|
||||
fun ApplicationsScreen(
|
||||
modifier: Modifier,
|
||||
account: Account,
|
||||
navController: NavController,
|
||||
@@ -125,6 +126,14 @@ fun PermissionsScreen(
|
||||
},
|
||||
)
|
||||
} else {
|
||||
AmberButton(
|
||||
modifier = Modifier.padding(top = 20.dp),
|
||||
onClick = {
|
||||
navController.navigate(Route.Activities.route)
|
||||
},
|
||||
text = stringResource(R.string.activity),
|
||||
)
|
||||
|
||||
applications.value.forEach { applicationWithHistory ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -383,7 +383,7 @@ fun MainScreen(
|
||||
content = {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
PermissionsScreen(
|
||||
ApplicationsScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
@@ -647,6 +647,26 @@ fun MainScreen(
|
||||
},
|
||||
)
|
||||
|
||||
composable(
|
||||
Route.Activities.route,
|
||||
content = {
|
||||
ActivitiesScreen(
|
||||
account = account,
|
||||
paddingValues = PaddingValues(
|
||||
top = padding.calculateTopPadding() + (verticalPadding * 1.5f),
|
||||
bottom = padding.calculateBottomPadding(),
|
||||
start = padding.calculateStartPadding(LayoutDirection.Ltr) + verticalPadding,
|
||||
end = padding.calculateEndPadding(LayoutDirection.Ltr) + verticalPadding,
|
||||
),
|
||||
topPadding = padding.calculateTopPadding() + (verticalPadding * 1.5f),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.imePadding(),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
composable(
|
||||
Route.RelayLogScreen.route,
|
||||
arguments = listOf(navArgument("url") { type = NavType.StringType }),
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
package com.greenart7c3.nostrsigner.ui.actions
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.compose.foundation.background
|
||||
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.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -22,26 +17,18 @@ import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SearchBar
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.isTraversalGroup
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -51,8 +38,6 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.greenart7c3.nostrsigner.Amber
|
||||
import com.greenart7c3.nostrsigner.R
|
||||
@@ -60,85 +45,7 @@ import com.greenart7c3.nostrsigner.models.Account
|
||||
import com.greenart7c3.nostrsigner.models.Permission
|
||||
import com.greenart7c3.nostrsigner.models.TimeUtils
|
||||
import com.greenart7c3.nostrsigner.models.supportedKindNumbers
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SimpleSearchBar(
|
||||
textFieldState: TextFieldState,
|
||||
onSearch: (String) -> Unit,
|
||||
searchResults: List<String>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// Controls expansion state of the search bar
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
var localSearchResults by remember { mutableStateOf(searchResults) }
|
||||
|
||||
val view = LocalView.current
|
||||
val viewTreeObserver = view.viewTreeObserver
|
||||
DisposableEffect(viewTreeObserver) {
|
||||
val listener = ViewTreeObserver.OnGlobalLayoutListener {
|
||||
val isKeyboardOpen = ViewCompat.getRootWindowInsets(view)
|
||||
?.isVisible(WindowInsetsCompat.Type.ime()) != false
|
||||
if (!isKeyboardOpen) {
|
||||
onSearch(textFieldState.text.toString())
|
||||
expanded = false
|
||||
}
|
||||
}
|
||||
|
||||
viewTreeObserver.addOnGlobalLayoutListener(listener)
|
||||
onDispose {
|
||||
viewTreeObserver.removeOnGlobalLayoutListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier
|
||||
.semantics { isTraversalGroup = true },
|
||||
) {
|
||||
SearchBar(
|
||||
windowInsets = WindowInsets(0),
|
||||
inputField = {
|
||||
SearchBarDefaults.InputField(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
query = textFieldState.text.toString(),
|
||||
onQueryChange = {
|
||||
textFieldState.edit { replace(0, length, it) }
|
||||
localSearchResults = searchResults.filter { result ->
|
||||
result.contains(textFieldState.text.toString(), ignoreCase = true)
|
||||
}
|
||||
},
|
||||
onSearch = {
|
||||
onSearch(textFieldState.text.toString())
|
||||
expanded = false
|
||||
},
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it },
|
||||
placeholder = { Text("Search") },
|
||||
)
|
||||
},
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it },
|
||||
) {
|
||||
// Display search results in a scrollable column
|
||||
LazyColumn {
|
||||
items(localSearchResults) { result ->
|
||||
ListItem(
|
||||
headlineContent = { Text(result) },
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
textFieldState.edit { replace(0, length, result) }
|
||||
onSearch(textFieldState.text.toString())
|
||||
expanded = false
|
||||
}
|
||||
.background(Color.Red)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
import com.greenart7c3.nostrsigner.ui.components.SimpleSearchBar
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.greenart7c3.nostrsigner.ui.components
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.SearchBar
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.semantics.isTraversalGroup
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SimpleSearchBar(
|
||||
textFieldState: TextFieldState,
|
||||
onSearch: (String) -> Unit,
|
||||
searchResults: List<String>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// Controls expansion state of the search bar
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
var localSearchResults by remember { mutableStateOf(searchResults) }
|
||||
|
||||
val view = LocalView.current
|
||||
val viewTreeObserver = view.viewTreeObserver
|
||||
DisposableEffect(viewTreeObserver) {
|
||||
val listener = ViewTreeObserver.OnGlobalLayoutListener {
|
||||
val isKeyboardOpen = ViewCompat.getRootWindowInsets(view)
|
||||
?.isVisible(WindowInsetsCompat.Type.ime()) != false
|
||||
if (!isKeyboardOpen) {
|
||||
onSearch(textFieldState.text.toString())
|
||||
expanded = false
|
||||
}
|
||||
}
|
||||
|
||||
viewTreeObserver.addOnGlobalLayoutListener(listener)
|
||||
onDispose {
|
||||
viewTreeObserver.removeOnGlobalLayoutListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier
|
||||
.semantics { isTraversalGroup = true },
|
||||
) {
|
||||
SearchBar(
|
||||
windowInsets = WindowInsets(0),
|
||||
inputField = {
|
||||
SearchBarDefaults.InputField(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
query = textFieldState.text.toString(),
|
||||
onQueryChange = {
|
||||
textFieldState.edit { replace(0, length, it) }
|
||||
localSearchResults = searchResults.filter { result ->
|
||||
result.contains(textFieldState.text.toString(), ignoreCase = true)
|
||||
}
|
||||
},
|
||||
onSearch = {
|
||||
onSearch(textFieldState.text.toString())
|
||||
expanded = false
|
||||
},
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it },
|
||||
placeholder = { Text("Search") },
|
||||
)
|
||||
},
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it },
|
||||
) {
|
||||
// Display search results in a scrollable column
|
||||
LazyColumn {
|
||||
items(localSearchResults) { result ->
|
||||
ListItem(
|
||||
headlineContent = { Text(result) },
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
textFieldState.edit { replace(0, length, result) }
|
||||
onSearch(textFieldState.text.toString())
|
||||
expanded = false
|
||||
}
|
||||
.background(Color.Red)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,6 +178,12 @@ sealed class Route(
|
||||
route = "Feedback",
|
||||
icon = R.drawable.settings,
|
||||
)
|
||||
|
||||
data object Activities : Route(
|
||||
title = Amber.instance.getString(R.string.activities),
|
||||
route = "Activities",
|
||||
icon = R.drawable.incoming_request,
|
||||
)
|
||||
}
|
||||
|
||||
val routes = listOf(
|
||||
@@ -207,4 +213,5 @@ val routes = listOf(
|
||||
Route.TorSettings,
|
||||
Route.EditProfile,
|
||||
Route.Feedback,
|
||||
Route.Activities,
|
||||
)
|
||||
|
||||
@@ -537,4 +537,5 @@
|
||||
<string name="create_anonymously">Create anonymously</string>
|
||||
<string name="send">Send</string>
|
||||
<string name="feedback_sent">Feedback sent</string>
|
||||
<string name="activities">Activities</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user