Add account switch bottom sheet and update LocalPrefs

This commit is contained in:
maxmoney21m
2023-03-11 23:13:04 +08:00
parent 907aa7b734
commit b7f8241a08
7 changed files with 384 additions and 110 deletions
@@ -11,21 +11,44 @@ import com.vitorpamplona.amethyst.service.model.Event
import com.vitorpamplona.amethyst.service.model.Event.Companion.getRefinedEvent
import nostr.postr.Persona
import nostr.postr.toHex
import nostr.postr.toNpub
import java.util.Locale
data class AccountInfo(val npub: String, val current: Boolean, val displayName: String?, val profilePicture: String?)
class LocalPreferences(context: Context) {
private fun prefKeysForAccount(npub: String) = object {
val NOSTR_PRIVKEY = "$npub/nostr_privkey"
val NOSTR_PUBKEY = "$npub/nostr_pubkey"
val DISPLAY_NAME = "$npub/display_name"
val PROFILE_PICTURE_URL = "$npub/profile_picture"
val FOLLOWING_CHANNELS = "$npub/following_channels"
val HIDDEN_USERS = "$npub/hidden_users"
val RELAYS = "$npub/relays"
val DONT_TRANSLATE_FROM = "$npub/dontTranslateFrom"
val LANGUAGE_PREFS = "$npub/languagePreferences"
val TRANSLATE_TO = "$npub/translateTo"
val ZAP_AMOUNTS = "$npub/zapAmounts"
val LATEST_CONTACT_LIST = "$npub/latestContactList"
val HIDE_DELETE_REQUEST_INFO = "$npub/hideDeleteRequestInfo"
// val LAST_READ: (String) -> String = { route -> "$npub/last_read_route_$route" }
}
private object PrefKeys {
const val NOSTR_PRIVKEY = "nostr_privkey"
const val NOSTR_PUBKEY = "nostr_pubkey"
const val FOLLOWING_CHANNELS = "following_channels"
const val HIDDEN_USERS = "hidden_users"
const val RELAYS = "relays"
const val DONT_TRANSLATE_FROM = "dontTranslateFrom"
const val LANGUAGE_PREFS = "languagePreferences"
const val TRANSLATE_TO = "translateTo"
const val ZAP_AMOUNTS = "zapAmounts"
const val LATEST_CONTACT_LIST = "latestContactList"
const val HIDE_DELETE_REQUEST_INFO = "hideDeleteRequestInfo"
const val CURRENT_ACCOUNT = "currentlyLoggedInAccount"
// val NOSTR_PRIVKEY = "nostr_privkey"
// val NOSTR_PUBKEY = "nostr_pubkey"
// val FOLLOWING_CHANNELS = "following_channels"
// val HIDDEN_USERS = "hidden_users"
// val RELAYS = "relays"
// val DONT_TRANSLATE_FROM = "dontTranslateFrom"
// val LANGUAGE_PREFS = "languagePreferences"
// val TRANSLATE_TO = "translateTo"
// val ZAP_AMOUNTS = "zapAmounts"
// val LATEST_CONTACT_LIST = "latestContactList"
// val HIDE_DELETE_REQUEST_INFO = "hideDeleteRequestInfo"
val LAST_READ: (String) -> String = { route -> "last_read_route_$route" }
}
@@ -34,47 +57,88 @@ class LocalPreferences(context: Context) {
fun clearEncryptedStorage() {
encryptedPreferences.edit().apply {
encryptedPreferences.all.keys.forEach { remove(it) }
encryptedPreferences.all.keys.forEach {
remove(it)
}
// encryptedPreferences.all.keys.filter {
// it.startsWith(npub)
// }.forEach {
// remove(it)
// }
}.apply()
}
fun findAllLocalAccounts(): List<AccountInfo> {
encryptedPreferences.apply {
val currentAccount = getString(PrefKeys.CURRENT_ACCOUNT, null)
return encryptedPreferences.all.keys.filter {
it.endsWith("nostr_pubkey")
}.map {
val npub = it.substringBefore("/")
val myPrefs = prefKeysForAccount(npub)
AccountInfo(
npub,
npub == currentAccount,
getString(myPrefs.DISPLAY_NAME, null),
getString(myPrefs.PROFILE_PICTURE_URL, null)
)
}
}
}
fun saveToEncryptedStorage(account: Account) {
val npub = account.loggedIn.pubKey.toNpub()
val myPrefs = prefKeysForAccount(npub)
encryptedPreferences.edit().apply {
account.loggedIn.privKey?.let { putString(PrefKeys.NOSTR_PRIVKEY, it.toHex()) }
account.loggedIn.pubKey.let { putString(PrefKeys.NOSTR_PUBKEY, it.toHex()) }
account.followingChannels.let { putStringSet(PrefKeys.FOLLOWING_CHANNELS, it) }
account.hiddenUsers.let { putStringSet(PrefKeys.HIDDEN_USERS, it) }
account.localRelays.let { putString(PrefKeys.RELAYS, gson.toJson(it)) }
account.dontTranslateFrom.let { putStringSet(PrefKeys.DONT_TRANSLATE_FROM, it) }
account.languagePreferences.let { putString(PrefKeys.LANGUAGE_PREFS, gson.toJson(it)) }
account.translateTo.let { putString(PrefKeys.TRANSLATE_TO, it) }
account.zapAmountChoices.let { putString(PrefKeys.ZAP_AMOUNTS, gson.toJson(it)) }
account.backupContactList.let { putString(PrefKeys.LATEST_CONTACT_LIST, Event.gson.toJson(it)) }
putBoolean(PrefKeys.HIDE_DELETE_REQUEST_INFO, account.hideDeleteRequestInfo)
putString(PrefKeys.CURRENT_ACCOUNT, npub)
account.loggedIn.privKey?.let { putString(myPrefs.NOSTR_PRIVKEY, it.toHex()) }
account.loggedIn.pubKey.let { putString(myPrefs.NOSTR_PUBKEY, it.toHex()) }
putStringSet(myPrefs.FOLLOWING_CHANNELS, account.followingChannels)
putStringSet(myPrefs.HIDDEN_USERS, account.hiddenUsers)
putString(myPrefs.RELAYS, gson.toJson(account.localRelays))
putStringSet(myPrefs.DONT_TRANSLATE_FROM, account.dontTranslateFrom)
putString(myPrefs.LANGUAGE_PREFS, gson.toJson(account.languagePreferences))
putString(myPrefs.TRANSLATE_TO, account.translateTo)
putString(myPrefs.ZAP_AMOUNTS, gson.toJson(account.zapAmountChoices))
putString(myPrefs.LATEST_CONTACT_LIST, Event.gson.toJson(account.backupContactList))
putBoolean(myPrefs.HIDE_DELETE_REQUEST_INFO, account.hideDeleteRequestInfo)
}.apply()
}
fun saveCurrentAccountMetadata(account: Account) {
val myPrefs = prefKeysForAccount(account.loggedIn.pubKey.toNpub())
encryptedPreferences.edit().apply {
putString(myPrefs.DISPLAY_NAME, account.userProfile().toBestDisplayName())
putString(myPrefs.PROFILE_PICTURE_URL, account.userProfile().profilePicture())
}.apply()
}
fun loadFromEncryptedStorage(): Account? {
encryptedPreferences.apply {
val privKey = getString(PrefKeys.NOSTR_PRIVKEY, null)
val pubKey = getString(PrefKeys.NOSTR_PUBKEY, null)
val followingChannels = getStringSet(PrefKeys.FOLLOWING_CHANNELS, null) ?: setOf()
val hiddenUsers = getStringSet(PrefKeys.HIDDEN_USERS, emptySet()) ?: setOf()
val npub = getString(PrefKeys.CURRENT_ACCOUNT, null) ?: return null
val myPrefs = prefKeysForAccount(npub)
val pubKey = getString(myPrefs.NOSTR_PUBKEY, null) ?: return null
val privKey = getString(myPrefs.NOSTR_PRIVKEY, null)
val followingChannels = getStringSet(myPrefs.FOLLOWING_CHANNELS, null) ?: setOf()
val hiddenUsers = getStringSet(myPrefs.HIDDEN_USERS, emptySet()) ?: setOf()
val localRelays = gson.fromJson(
getString(PrefKeys.RELAYS, "[]"),
getString(myPrefs.RELAYS, "[]"),
object : TypeToken<Set<RelaySetupInfo>>() {}.type
) ?: setOf<RelaySetupInfo>()
val dontTranslateFrom = getStringSet(PrefKeys.DONT_TRANSLATE_FROM, null) ?: setOf()
val translateTo = getString(PrefKeys.TRANSLATE_TO, null) ?: Locale.getDefault().language
val dontTranslateFrom = getStringSet(myPrefs.DONT_TRANSLATE_FROM, null) ?: setOf()
val translateTo = getString(myPrefs.TRANSLATE_TO, null) ?: Locale.getDefault().language
val zapAmountChoices = gson.fromJson(
getString(PrefKeys.ZAP_AMOUNTS, "[]"),
getString(myPrefs.ZAP_AMOUNTS, "[]"),
object : TypeToken<List<Long>>() {}.type
) ?: listOf(500L, 1000L, 5000L)
val latestContactList = try {
getString(PrefKeys.LATEST_CONTACT_LIST, null)?.let {
getString(myPrefs.LATEST_CONTACT_LIST, null)?.let {
Event.gson.fromJson(it, Event::class.java).getRefinedEvent(true) as ContactListEvent
}
} catch (e: Throwable) {
@@ -83,32 +147,28 @@ class LocalPreferences(context: Context) {
}
val languagePreferences = try {
getString(PrefKeys.LANGUAGE_PREFS, null)?.let {
getString(myPrefs.LANGUAGE_PREFS, null)?.let {
gson.fromJson(it, object : TypeToken<Map<String, String>>() {}.type) as Map<String, String>
} ?: mapOf<String, String>()
} ?: mapOf()
} catch (e: Throwable) {
e.printStackTrace()
mapOf<String, String>()
mapOf()
}
val hideDeleteRequestInfo = getBoolean(PrefKeys.HIDE_DELETE_REQUEST_INFO, false)
val hideDeleteRequestInfo = getBoolean(myPrefs.HIDE_DELETE_REQUEST_INFO, false)
if (pubKey != null) {
return Account(
Persona(privKey = privKey?.toByteArray(), pubKey = pubKey.toByteArray()),
followingChannels,
hiddenUsers,
localRelays,
dontTranslateFrom,
languagePreferences,
translateTo,
zapAmountChoices,
hideDeleteRequestInfo,
latestContactList
)
} else {
return null
}
return Account(
Persona(privKey = privKey?.toByteArray(), pubKey = pubKey.toByteArray()),
followingChannels,
hiddenUsers,
localRelays,
dontTranslateFrom,
languagePreferences,
translateTo,
zapAmountChoices,
hideDeleteRequestInfo,
latestContactList
)
}
}
@@ -0,0 +1,122 @@
package com.vitorpamplona.amethyst.ui.navigation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Logout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.RoboHashCache
import com.vitorpamplona.amethyst.ui.components.AsyncImageProxy
import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.note.toShortenHex
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun AccountSwitchBottomSheet(
accountViewModel: AccountViewModel,
sheetState: ModalBottomSheetState
) {
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val localPrefs = LocalPreferences(context)
val accounts = localPrefs.findAllLocalAccounts()
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val accountUserState by account.userProfile().live().metadata.observeAsState()
val accountUser = accountUserState?.user ?: return
LaunchedEffect(key1 = accountUser) {
localPrefs.saveCurrentAccountMetadata(account)
}
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text("Select Account", fontWeight = FontWeight.Bold)
}
accounts.forEach { acc ->
val current = accountUser.pubkeyNpub() == acc.npub
Row(
modifier = Modifier
.fillMaxWidth()
.padding(32.dp, 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AsyncImageProxy(
model = ResizeImage(acc.profilePicture, 64.dp),
placeholder = BitmapPainter(RoboHashCache.get(context, acc.npub)),
fallback = BitmapPainter(RoboHashCache.get(context, acc.npub)),
error = BitmapPainter(RoboHashCache.get(context, acc.npub)),
contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier
.width(64.dp)
.height(64.dp)
.clip(shape = CircleShape)
)
Spacer(modifier = Modifier.width(16.dp))
Column {
acc.displayName?.let {
Text(it)
}
Text(acc.npub.toShortenHex())
}
Spacer(modifier = Modifier.width(8.dp))
if (current) {
Text("")
}
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { /*TODO*/ }) {
Icon(imageVector = Icons.Default.Logout, "Logout")
}
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(onClick = { coroutineScope.launch { sheetState.hide() } }) {
Text("Add New Account")
}
}
}
}
@@ -16,9 +16,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Divider
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Surface
import androidx.compose.material.Text
@@ -56,10 +58,12 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun DrawerContent(
navController: NavHostController,
scaffoldState: ScaffoldState,
sheetState: ModalBottomSheetState,
accountViewModel: AccountViewModel,
accountStateViewModel: AccountStateViewModel
) {
@@ -88,6 +92,7 @@ fun DrawerContent(
account.userProfile(),
navController,
scaffoldState,
sheetState,
modifier = Modifier
.fillMaxWidth()
.weight(1F),
@@ -214,15 +219,18 @@ fun ProfileContent(baseAccountUser: User, modifier: Modifier = Modifier, scaffol
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ListContent(
accountUser: User?,
navController: NavHostController,
scaffoldState: ScaffoldState,
sheetState: ModalBottomSheetState,
modifier: Modifier,
accountViewModel: AccountStateViewModel,
account: Account
) {
val coroutineScope = rememberCoroutineScope()
var backupDialogOpen by remember { mutableStateOf(false) }
Column(modifier = modifier.fillMaxHeight()) {
@@ -260,11 +268,18 @@ fun ListContent(
Spacer(modifier = Modifier.weight(1f))
IconRow(
stringResource(R.string.log_out),
R.drawable.ic_logout,
MaterialTheme.colors.onBackground,
onClick = { accountViewModel.logOff() }
title = "Accounts",
icon = R.drawable.manage_accounts,
tint = MaterialTheme.colors.onBackground,
onClick = { coroutineScope.launch { sheetState.show() } }
)
// IconRow(
// title = stringResource(R.string.log_out),
// icon = R.drawable.ic_logout,
// tint = MaterialTheme.colors.onBackground,
// onClick = { accountViewModel.logOff() }
// )
}
if (backupDialogOpen) {
@@ -36,62 +36,95 @@ sealed class Route(
val buildScreen: (AccountViewModel, AccountStateViewModel, NavController) -> @Composable (NavBackStackEntry) -> Unit
) {
object Home : Route(
"Home",
R.drawable.ic_home,
hasNewItems = { acc, cache, ctx -> homeHasNewItems(acc, cache, ctx) },
buildScreen = { acc, accSt, nav -> { _ -> HomeScreen(acc, nav) } }
route = "Home",
icon = R.drawable.ic_home,
hasNewItems = { accountViewModel, cache, context ->
homeHasNewItems(accountViewModel, cache, context)
},
buildScreen = { accountViewModel, _, navController ->
{ HomeScreen(accountViewModel, navController) }
}
)
object Search : Route(
"Search",
R.drawable.ic_globe,
buildScreen = { acc, accSt, nav -> { _ -> SearchScreen(acc, nav) } }
route = "Search",
icon = R.drawable.ic_globe,
buildScreen = { accountViewModel, _, navController ->
{ SearchScreen(accountViewModel, navController) }
}
)
object Notification : Route(
"Notification",
R.drawable.ic_notifications,
hasNewItems = { acc, cache, ctx -> notificationHasNewItems(acc, cache, ctx) },
buildScreen = { acc, accSt, nav -> { _ -> NotificationScreen(acc, nav) } }
route = "Notification",
icon = R.drawable.ic_notifications,
hasNewItems = { accountViewModel, cache, context ->
notificationHasNewItems(accountViewModel, cache, context)
},
buildScreen = { accountViewModel, _, navController ->
{ NotificationScreen(accountViewModel, navController) }
}
)
object Message : Route(
"Message",
R.drawable.ic_dm,
hasNewItems = { acc, cache, ctx -> messagesHasNewItems(acc, cache, ctx) },
buildScreen = { acc, accSt, nav -> { _ -> ChatroomListScreen(acc, nav) } }
route = "Message",
icon = R.drawable.ic_dm,
hasNewItems = { accountViewModel, cache, context ->
messagesHasNewItems(accountViewModel, cache, context)
},
buildScreen = { accountViewModel, _, navController ->
{ ChatroomListScreen(accountViewModel, navController) }
}
)
object Filters : Route(
"Filters",
R.drawable.ic_security,
buildScreen = { acc, accSt, nav -> { _ -> FiltersScreen(acc, nav) } }
route = "Filters",
icon = R.drawable.ic_security,
buildScreen = { accountViewModel, _, navController ->
{ FiltersScreen(accountViewModel, navController) }
}
)
object Profile : Route(
"User/{id}",
R.drawable.ic_profile,
route = "User/{id}",
icon = R.drawable.ic_profile,
arguments = listOf(navArgument("id") { type = NavType.StringType }),
buildScreen = { acc, accSt, nav -> { ProfileScreen(it.arguments?.getString("id"), acc, nav) } }
buildScreen = { accountViewModel, _, navController ->
{ ProfileScreen(it.arguments?.getString("id"), accountViewModel, navController) }
}
)
object Note : Route(
"Note/{id}",
R.drawable.ic_moments,
route = "Note/{id}",
icon = R.drawable.ic_moments,
arguments = listOf(navArgument("id") { type = NavType.StringType }),
buildScreen = { acc, accSt, nav -> { ThreadScreen(it.arguments?.getString("id"), acc, nav) } }
buildScreen = { accountViewModel, _, navController ->
{ ThreadScreen(it.arguments?.getString("id"), accountViewModel, navController) }
}
)
object Room : Route(
"Room/{id}",
R.drawable.ic_moments,
route = "Room/{id}",
icon = R.drawable.ic_moments,
arguments = listOf(navArgument("id") { type = NavType.StringType }),
buildScreen = { acc, accSt, nav -> { ChatroomScreen(it.arguments?.getString("id"), acc, nav) } }
buildScreen = { accountViewModel, _, navController ->
{ ChatroomScreen(it.arguments?.getString("id"), accountViewModel, navController) }
}
)
object Channel : Route(
"Channel/{id}",
R.drawable.ic_moments,
route = "Channel/{id}",
icon = R.drawable.ic_moments,
arguments = listOf(navArgument("id") { type = NavType.StringType }),
buildScreen = { acc, accSt, nav -> { ChannelScreen(it.arguments?.getString("id"), acc, accSt, nav) } }
buildScreen = { accountViewModel, accountStateViewModel, navController ->
{
ChannelScreen(
it.arguments?.getString("id"),
accountViewModel,
accountStateViewModel,
navController
)
}
}
)
}
@@ -124,18 +157,32 @@ private fun homeHasNewItems(account: Account, cache: NotificationCache, context:
HomeNewThreadFeedFilter.account = account
return (HomeNewThreadFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt() ?: 0) > lastTime
return (
HomeNewThreadFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt()
?: 0
) > lastTime
}
private fun notificationHasNewItems(account: Account, cache: NotificationCache, context: Context): Boolean {
private fun notificationHasNewItems(
account: Account,
cache: NotificationCache,
context: Context
): Boolean {
val lastTime = cache.load("Notification", context)
NotificationFeedFilter.account = account
return (NotificationFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt() ?: 0) > lastTime
return (
NotificationFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt()
?: 0
) > lastTime
}
private fun messagesHasNewItems(account: Account, cache: NotificationCache, context: Context): Boolean {
private fun messagesHasNewItems(
account: Account,
cache: NotificationCache,
context: Context
): Boolean {
ChatroomListKnownFeedFilter.account = account
val note = ChatroomListKnownFeedFilter.feed().firstOrNull {
@@ -78,7 +78,12 @@ import com.vitorpamplona.amethyst.ui.screen.ChatroomFeedView
import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
@Composable
fun ChannelScreen(channelId: String?, accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel, navController: NavController) {
fun ChannelScreen(
channelId: String?,
accountViewModel: AccountViewModel,
accountStateViewModel: AccountStateViewModel,
navController: NavController
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account
@@ -7,9 +7,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material.DrawerValue
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Scaffold
import androidx.compose.material.rememberDrawerState
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -19,6 +23,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.vitorpamplona.amethyst.buttons.NewChannelButton
import com.vitorpamplona.amethyst.buttons.NewNoteButton
import com.vitorpamplona.amethyst.ui.navigation.AccountSwitchBottomSheet
import com.vitorpamplona.amethyst.ui.navigation.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
import com.vitorpamplona.amethyst.ui.navigation.AppTopBar
@@ -28,31 +33,44 @@ import com.vitorpamplona.amethyst.ui.navigation.currentRoute
import com.vitorpamplona.amethyst.ui.screen.AccountState
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel, startingPage: String? = null) {
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
val sheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
confirmValueChange = { it != ModalBottomSheetValue.HalfExpanded },
skipHalfExpanded = true
)
Scaffold(
modifier = Modifier
.background(MaterialTheme.colors.primaryVariant)
.statusBarsPadding(),
bottomBar = {
AppBottomBar(navController, accountViewModel)
},
topBar = {
AppTopBar(navController, scaffoldState, accountViewModel)
},
drawerContent = {
DrawerContent(navController, scaffoldState, accountViewModel, accountStateViewModel)
},
floatingActionButton = {
FloatingButton(navController, accountStateViewModel)
},
scaffoldState = scaffoldState
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
AccountSwitchBottomSheet(accountViewModel = accountViewModel, sheetState = sheetState)
}
) {
Column(modifier = Modifier.padding(bottom = it.calculateBottomPadding())) {
AppNavigation(navController, accountViewModel, accountStateViewModel, startingPage)
Scaffold(
modifier = Modifier
.background(MaterialTheme.colors.primaryVariant)
.statusBarsPadding(),
bottomBar = {
AppBottomBar(navController, accountViewModel)
},
topBar = {
AppTopBar(navController, scaffoldState, accountViewModel)
},
drawerContent = {
DrawerContent(navController, scaffoldState, sheetState, accountViewModel, accountStateViewModel)
},
floatingActionButton = {
FloatingButton(navController, accountStateViewModel)
},
scaffoldState = scaffoldState
) {
Column(modifier = Modifier.padding(bottom = it.calculateBottomPadding())) {
AppNavigation(navController, accountViewModel, accountStateViewModel, startingPage)
}
}
}
}
@@ -0,0 +1,7 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10,8m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"/>
<path android:fillColor="@android:color/white" android:pathData="M10.67,13.02C10.45,13.01 10.23,13 10,13c-2.42,0 -4.68,0.67 -6.61,1.82C2.51,15.34 2,16.32 2,17.35V20h9.26C10.47,18.87 10,17.49 10,16C10,14.93 10.25,13.93 10.67,13.02z"/>
<path android:fillColor="@android:color/white" android:pathData="M20.75,16c0,-0.22 -0.03,-0.42 -0.06,-0.63l1.14,-1.01l-1,-1.73l-1.45,0.49c-0.32,-0.27 -0.68,-0.48 -1.08,-0.63L18,11h-2l-0.3,1.49c-0.4,0.15 -0.76,0.36 -1.08,0.63l-1.45,-0.49l-1,1.73l1.14,1.01c-0.03,0.21 -0.06,0.41 -0.06,0.63s0.03,0.42 0.06,0.63l-1.14,1.01l1,1.73l1.45,-0.49c0.32,0.27 0.68,0.48 1.08,0.63L16,21h2l0.3,-1.49c0.4,-0.15 0.76,-0.36 1.08,-0.63l1.45,0.49l1,-1.73l-1.14,-1.01C20.72,16.42 20.75,16.22 20.75,16zM17,18c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S18.1,18 17,18z"/>
</vector>