Merge remote-tracking branch 'origin/HEAD' into less_memory_test_branch

# Conflicts:
#	app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
#	app/src/main/java/com/vitorpamplona/amethyst/model/User.kt
This commit is contained in:
Vitor Pamplona
2023-03-13 14:51:44 -04:00
58 changed files with 683 additions and 785 deletions
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst
import android.content.Context
import androidx.lifecycle.LiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -14,7 +13,7 @@ import java.util.concurrent.atomic.AtomicBoolean
object NotificationCache {
val lastReadByRoute = mutableMapOf<String, Long>()
fun markAsRead(route: String, timestampInSecs: Long, context: Context) {
fun markAsRead(route: String, timestampInSecs: Long) {
val lastTime = lastReadByRoute[route]
if (lastTime == null || timestampInSecs > lastTime) {
lastReadByRoute.put(route, timestampInSecs)
@@ -27,7 +26,7 @@ object NotificationCache {
}
}
fun load(route: String, context: Context): Long {
fun load(route: String): Long {
var lastTime = lastReadByRoute[route]
if (lastTime == null) {
lastTime = LocalPreferences.loadLastRead(route)
@@ -23,6 +23,7 @@ import com.vitorpamplona.amethyst.service.relays.FeedType
import com.vitorpamplona.amethyst.service.relays.Relay
import com.vitorpamplona.amethyst.service.relays.RelayPool
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
@@ -48,6 +49,7 @@ fun getLanguagesSpokenByUser(): Set<String> {
return codedList
}
@OptIn(DelicateCoroutinesApi::class)
class Account(
val loggedIn: Persona,
var followingChannels: Set<String> = DefaultChannels,
@@ -283,8 +283,9 @@ object LocalCache {
refreshObservers()
}
@Suppress("UNUSED_PARAMETER")
fun consume(event: RecommendRelayEvent) {
// Log.d("RR", event.toJson())
// // Log.d("RR", event.toJson())
}
fun consume(event: ContactListEvent) {
@@ -542,9 +543,11 @@ object LocalCache {
refreshObservers()
}
@Suppress("UNUSED_PARAMETER")
fun consume(event: ChannelHideMessageEvent) {
}
@Suppress("UNUSED_PARAMETER")
fun consume(event: ChannelMuteUserEvent) {
}
@@ -7,6 +7,7 @@ import com.vitorpamplona.amethyst.service.model.TextNoteEvent
import com.vitorpamplona.amethyst.service.relays.FeedType
import com.vitorpamplona.amethyst.service.relays.JsonFilter
import com.vitorpamplona.amethyst.service.relays.TypedFilter
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -18,6 +19,7 @@ object NostrHomeDataSource : NostrDataSource("HomeFeed") {
invalidateFilters()
}
@OptIn(DelicateCoroutinesApi::class)
override fun start() {
if (this::account.isInitialized) {
GlobalScope.launch(Dispatchers.Main) {
@@ -27,6 +29,7 @@ object NostrHomeDataSource : NostrDataSource("HomeFeed") {
super.start()
}
@OptIn(DelicateCoroutinesApi::class)
override fun stop() {
super.stop()
if (this::account.isInitialized) {
@@ -133,9 +133,9 @@ class LightningAddressResolver {
fetchLightningAddressJson(
lnaddress,
onSuccess = {
onSuccess = { lnAddressJson ->
val lnurlp = try {
mapper.readTree(it)
mapper.readTree(lnAddressJson)
} catch (t: Throwable) {
onError("Error Parsing JSON from Lightning Address. Check the user's lightning setup")
null
@@ -149,9 +149,9 @@ class LightningAddressResolver {
val allowsNostr = lnurlp?.get("allowsNostr")?.asBoolean() ?: false
callback?.let { callback ->
callback?.let { cb ->
fetchLightningInvoice(
callback,
cb,
milliSats,
message,
if (allowsNostr) nostrRequest else null,
@@ -18,17 +18,16 @@ class ChannelHideMessageEvent(
companion object {
const val kind = 43
fun create(reason: String, messagesToHide: List<String>?, mentions: List<String>?, privateKey: ByteArray, createdAt: Long = Date().time / 1000): ChannelHideMessageEvent {
val content = reason
fun create(reason: String, messagesToHide: List<String>?, privateKey: ByteArray, createdAt: Long = Date().time / 1000): ChannelHideMessageEvent {
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
val tags =
messagesToHide?.map {
listOf("e", it)
} ?: emptyList()
val id = generateId(pubKey, createdAt, kind, tags, content)
val id = generateId(pubKey, createdAt, kind, tags, reason)
val sig = Utils.sign(id, privateKey)
return ChannelHideMessageEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
return ChannelHideMessageEvent(id.toHexKey(), pubKey, createdAt, tags, reason, sig.toHexKey())
}
}
}
@@ -28,9 +28,7 @@ abstract class IdentityClaim(
companion object {
fun create(platformIdentity: String, proof: String): IdentityClaim? {
val platformIdentity = platformIdentity.split(':')
val platform = platformIdentity[0]
val identity = platformIdentity[1]
val (platform, identity) = platformIdentity.split(':')
return when (platform.lowercase()) {
GitHubIdentity.platform -> GitHubIdentity(identity, proof)
@@ -169,16 +167,16 @@ class MetadataEvent(
}
fun create(contactMetaData: String, identities: List<IdentityClaim>, privateKey: ByteArray, createdAt: Long = Date().time / 1000): MetadataEvent {
val content = contactMetaData
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
val tags = mutableListOf<List<String>>()
identities?.forEach {
identities.forEach {
tags.add(listOf("i", it.platformIdentity(), it.proof))
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val id = generateId(pubKey, createdAt, kind, tags, contactMetaData)
val sig = Utils.sign(id, privateKey)
return MetadataEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
return MetadataEvent(id.toHexKey(), pubKey, createdAt, tags, contactMetaData, sig.toHexKey())
}
}
}
@@ -19,9 +19,9 @@ class ReportEvent(
private fun defaultReportType(): ReportType {
// Works with old and new structures for report.
var reportType = tags.filter { it.firstOrNull() == "report" }.mapNotNull { it.getOrNull(1) }.map { ReportType.valueOf(it.toUpperCase()) }.firstOrNull()
var reportType = tags.filter { it.firstOrNull() == "report" }.mapNotNull { it.getOrNull(1) }.map { ReportType.valueOf(it.uppercase()) }.firstOrNull()
if (reportType == null) {
reportType = tags.mapNotNull { it.getOrNull(2) }.map { ReportType.valueOf(it.toUpperCase()) }.firstOrNull()
reportType = tags.mapNotNull { it.getOrNull(2) }.map { ReportType.valueOf(it.uppercase()) }.firstOrNull()
}
if (reportType == null) {
reportType = ReportType.SPAM
@@ -34,7 +34,7 @@ class ReportEvent(
.map {
ReportedKey(
it[1],
it.getOrNull(2)?.toUpperCase()?.let { it1 -> ReportType.valueOf(it1) } ?: defaultReportType()
it.getOrNull(2)?.uppercase()?.let { it1 -> ReportType.valueOf(it1) } ?: defaultReportType()
)
}
@@ -43,7 +43,7 @@ class ReportEvent(
.map {
ReportedKey(
it[1],
it.getOrNull(2)?.toUpperCase()?.let { it1 -> ReportType.valueOf(it1) } ?: defaultReportType()
it.getOrNull(2)?.uppercase()?.let { it1 -> ReportType.valueOf(it1) } ?: defaultReportType()
)
}
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.service.relays
import com.vitorpamplona.amethyst.service.model.Event
import com.vitorpamplona.amethyst.service.model.EventInterface
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -78,6 +79,7 @@ object Client : RelayPool.Listener {
RelayPool.unloadRelays()
}
@OptIn(DelicateCoroutinesApi::class)
override fun onEvent(event: Event, subscriptionId: String, relay: Relay) {
// Releases the Web thread for the new payload.
// May need to add a processing queue if processing new events become too costly.
@@ -86,6 +88,7 @@ object Client : RelayPool.Listener {
}
}
@OptIn(DelicateCoroutinesApi::class)
override fun onError(error: Error, subscriptionId: String, relay: Relay) {
// Releases the Web thread for the new payload.
// May need to add a processing queue if processing new events become too costly.
@@ -94,6 +97,7 @@ object Client : RelayPool.Listener {
}
}
@OptIn(DelicateCoroutinesApi::class)
override fun onRelayStateChange(type: Relay.Type, relay: Relay, channel: String?) {
// Releases the Web thread for the new payload.
// May need to add a processing queue if processing new events become too costly.
@@ -102,6 +106,7 @@ object Client : RelayPool.Listener {
}
}
@OptIn(DelicateCoroutinesApi::class)
override fun onSendResponse(eventId: String, success: Boolean, message: String, relay: Relay) {
// Releases the Web thread for the new payload.
// May need to add a processing queue if processing new events become too costly.
@@ -27,13 +27,13 @@ class TypedFilter(
fun filterToJson(filter: JsonFilter): JsonObject {
val jsonObject = JsonObject()
filter.ids?.run {
jsonObject.add("ids", JsonArray().apply { filter.ids?.forEach { add(it) } })
jsonObject.add("ids", JsonArray().apply { filter.ids.forEach { add(it) } })
}
filter.authors?.run {
jsonObject.add("authors", JsonArray().apply { filter.authors?.forEach { add(it) } })
jsonObject.add("authors", JsonArray().apply { filter.authors.forEach { add(it) } })
}
filter.kinds?.run {
jsonObject.add("kinds", JsonArray().apply { filter.kinds?.forEach { add(it) } })
jsonObject.add("kinds", JsonArray().apply { filter.kinds.forEach { add(it) } })
}
filter.tags?.run {
entries.forEach { kv ->
@@ -21,6 +21,7 @@ import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.screen.AccountScreen
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -67,6 +68,7 @@ class MainActivity : FragmentActivity() {
Client.lenient = true
}
@OptIn(DelicateCoroutinesApi::class)
override fun onResume() {
super.onResume()
@@ -3,12 +3,11 @@ package com.vitorpamplona.amethyst.ui.actions
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.media.MediaScannerConnection
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import androidx.core.net.toUri
import okhttp3.*
import okio.BufferedSource
import okio.IOException
@@ -135,12 +134,7 @@ object ImageSaver {
// Call the media scanner manually, so the image
// appears in the gallery faster.
context.sendBroadcast(
Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
outputFile.toUri()
)
)
MediaScannerConnection.scanFile(context, arrayOf(outputFile.toString()), null, null)
}
private const val PICTURES_SUBDIRECTORY = "Amethyst"
@@ -16,7 +16,6 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.style.TextDirection
@@ -31,8 +30,6 @@ import com.vitorpamplona.amethyst.model.Channel
@Composable
fun NewChannelView(onClose: () -> Unit, account: Account, channel: Channel? = null) {
val postViewModel: NewChannelViewModel = viewModel()
val context = LocalContext.current.applicationContext
postViewModel.load(account, channel)
Dialog(
@@ -58,7 +55,7 @@ fun NewChannelView(onClose: () -> Unit, account: Account, channel: Channel? = nu
PostButton(
onPost = {
postViewModel.create(context)
postViewModel.create()
onClose()
},
postViewModel.channelName.value.text.isNotBlank()
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst.ui.actions
import android.content.Context
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
@@ -25,7 +24,7 @@ class NewChannelViewModel : ViewModel() {
}
}
fun create(context: Context) {
fun create() {
this.account?.let { account ->
if (originalChannel == null) {
account.sendCreateNewChannel(
@@ -208,7 +208,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
itemsIndexed(
userSuggestions,
key = { _, item -> item.pubkeyHex }
) { index, item ->
) { _, item ->
UserLine(item, account) {
postViewModel.autocompleteWithUser(item)
}
@@ -40,7 +40,6 @@ 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.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
@@ -58,12 +57,10 @@ import java.lang.Math.round
@Composable
fun NewRelayListView(onClose: () -> Unit, account: Account, relayToAdd: String = "") {
val postViewModel: NewRelayListViewModel = viewModel()
val ctx = LocalContext.current.applicationContext
val feedState by postViewModel.relays.collectAsState()
LaunchedEffect(Unit) {
postViewModel.load(account, ctx)
postViewModel.load(account)
}
Dialog(
@@ -83,13 +80,13 @@ fun NewRelayListView(onClose: () -> Unit, account: Account, relayToAdd: String =
verticalAlignment = Alignment.CenterVertically
) {
CloseButton(onCancel = {
postViewModel.clear(ctx)
postViewModel.clear()
onClose()
})
PostButton(
onPost = {
postViewModel.create(ctx)
postViewModel.create()
onClose()
},
true
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst.ui.actions
import android.content.Context
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.RelaySetupInfo
@@ -18,20 +17,20 @@ class NewRelayListViewModel : ViewModel() {
private val _relays = MutableStateFlow<List<RelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow()
fun load(account: Account, ctx: Context) {
fun load(account: Account) {
this.account = account
clear(ctx)
clear()
}
fun create(ctx: Context) {
fun create() {
relays.let {
account.saveRelayList(it.value)
}
clear(ctx)
clear()
}
fun clear(ctx: Context) {
fun clear() {
_relays.update {
var relayFile = account.userProfile().latestContactList?.relays()
@@ -218,7 +218,7 @@ fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgro
val index = try {
matcher.find()
matcher.group(1).toInt()
matcher.group(1)?.toInt()
} catch (e: Exception) {
println("Couldn't link tag $word")
null
@@ -26,7 +26,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
@@ -60,8 +59,6 @@ fun TranslateableRichTextViewer(
var showOriginal by remember { mutableStateOf(false) }
var langSettingsPopupExpanded by remember { mutableStateOf(false) }
val context = LocalContext.current
val accountState by accountViewModel.accountLanguagesLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -154,7 +151,7 @@ fun TranslateableRichTextViewer(
onDismissRequest = { langSettingsPopupExpanded = false }
) {
DropdownMenuItem(onClick = {
accountViewModel.dontTranslateFrom(source, context)
accountViewModel.dontTranslateFrom(source)
langSettingsPopupExpanded = false
}) {
if (source in account.dontTranslateFrom) {
@@ -169,7 +166,7 @@ fun TranslateableRichTextViewer(
Spacer(modifier = Modifier.size(10.dp))
Text(stringResource(R.string.never_translate_from) + "${Locale(source).displayName}")
Text(stringResource(R.string.never_translate_from) + Locale(source).displayName)
}
Divider()
DropdownMenuItem(onClick = {
@@ -223,11 +220,11 @@ fun TranslateableRichTextViewer(
Divider()
val languageList =
ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration())
ConfigurationCompat.getLocales(Resources.getSystem().configuration)
for (i in 0 until languageList.size()) {
languageList.get(i)?.let { lang ->
DropdownMenuItem(onClick = {
accountViewModel.translateTo(lang, context)
accountViewModel.translateTo(lang)
langSettingsPopupExpanded = false
}) {
if (lang.language in account.translateTo) {
@@ -18,14 +18,14 @@ import kotlinx.coroutines.withContext
@Composable
fun UrlPreview(url: String, urlText: String) {
val default = UrlCachedPreviewer.cache[url]?.let {
if (it.url == url) {
UrlPreviewState.Loaded(it)
} else {
UrlPreviewState.Empty
}
} ?: UrlPreviewState.Loading
var context = LocalContext.current
// val default = UrlCachedPreviewer.cache[url]?.let {
// if (it.url == url) {
// UrlPreviewState.Loaded(it)
// } else {
// UrlPreviewState.Empty
// }
// } ?: UrlPreviewState.Loading
val context = LocalContext.current
var urlPreviewState by remember { mutableStateOf<UrlPreviewState>(UrlPreviewState.Loading) }
@@ -1,9 +1,9 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.calculatePan
import androidx.compose.foundation.gestures.calculateZoom
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -32,17 +32,15 @@ fun ZoomableAsyncImage(imageUrl: String) {
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
awaitFirstDown()
do {
val event = awaitPointerEvent()
scale *= event.calculateZoom()
val offset = event.calculatePan()
offsetX += offset.x
offsetY += offset.y
} while (event.changes.any { it.pressed })
}
awaitEachGesture {
awaitFirstDown()
do {
val event = awaitPointerEvent()
scale *= event.calculateZoom()
val offset = event.calculatePan()
offsetX += offset.x
offsetY += offset.y
} while (event.changes.any { it.pressed })
}
}
) {
@@ -38,7 +38,6 @@ 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.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -60,9 +59,7 @@ fun AccountSwitchBottomSheet(
accountViewModel: AccountViewModel,
accountStateViewModel: AccountStateViewModel
) {
val context = LocalContext.current
val accounts = LocalPreferences.allSavedAccounts()
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -30,7 +30,6 @@ 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.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
@@ -141,7 +140,7 @@ private fun NotifiableIcon(route: Route, selected: Boolean, accountViewModel: Ac
Box(Modifier.size(if ("Home" == route.base) 25.dp else 23.dp)) {
Icon(
painter = painterResource(id = route.icon),
null,
contentDescription = null,
modifier = Modifier.size(if ("Home" == route.base) 24.dp else 20.dp),
tint = if (selected) MaterialTheme.colors.primary else Color.Unspecified
)
@@ -158,17 +157,15 @@ private fun NotifiableIcon(route: Route, selected: Boolean, accountViewModel: Ac
var hasNewItems by remember { mutableStateOf<Boolean>(false) }
val context = LocalContext.current.applicationContext
LaunchedEffect(key1 = notif) {
withContext(Dispatchers.IO) {
hasNewItems = route.hasNewItems(account, notif.cache, context)
hasNewItems = route.hasNewItems(account, notif.cache)
}
}
LaunchedEffect(key1 = db) {
withContext(Dispatchers.IO) {
hasNewItems = route.hasNewItems(account, notif.cache, context)
hasNewItems = route.hasNewItems(account, notif.cache)
}
}
@@ -12,7 +12,6 @@ import com.google.accompanist.pager.rememberPagerState
import com.vitorpamplona.amethyst.ui.dal.GlobalFeedFilter
import com.vitorpamplona.amethyst.ui.dal.HomeConversationsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrGlobalFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel
@@ -32,7 +31,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.ThreadScreen
fun AppNavigation(
navController: NavHostController,
accountViewModel: AccountViewModel,
accountStateViewModel: AccountStateViewModel,
nextPage: String? = null
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
@@ -111,7 +109,6 @@ fun AppNavigation(
ChannelScreen(
channelId = it.arguments?.getString("id"),
accountViewModel = accountViewModel,
accountStateViewModel = accountStateViewModel,
navController = navController
)
})
@@ -71,6 +71,7 @@ fun AppTopBar(navController: NavHostController, scaffoldState: ScaffoldState, ac
}
}
@OptIn(coil.annotation.ExperimentalCoilApi::class)
@Composable
fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
val accountState by accountViewModel.accountLiveData.observeAsState()
@@ -86,7 +87,6 @@ fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel)
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val ctx = LocalContext.current.applicationContext
var wantsToEditRelays by remember {
mutableStateOf(false)
@@ -99,7 +99,6 @@ fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel)
Column() {
TopAppBar(
elevation = 0.dp,
backgroundColor = Color(0xFFFFFF),
title = {
Column(
modifier = Modifier.fillMaxWidth(),
@@ -36,7 +36,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -110,11 +109,9 @@ fun ProfileContent(baseAccountUser: User, modifier: Modifier = Modifier, scaffol
val accountUserFollowsState by baseAccountUser.live().follows.observeAsState()
val accountUserFollows = accountUserFollowsState?.user ?: return
val ctx = LocalContext.current.applicationContext
Box {
val banner = accountUser.info?.banner
if (banner != null && banner.isNotBlank()) {
if (!banner.isNullOrBlank()) {
AsyncImage(
model = banner,
contentDescription = stringResource(id = R.string.profile_image),
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst.ui.navigation
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.navigation.NamedNavArgument
@@ -18,7 +17,7 @@ import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
sealed class Route(
val route: String,
val icon: Int,
val hasNewItems: (Account, NotificationCache, Context) -> Boolean = { _, _, _ -> false },
val hasNewItems: (Account, NotificationCache) -> Boolean = { _, _ -> false },
val arguments: List<NamedNavArgument> = emptyList()
) {
val base: String
@@ -28,7 +27,7 @@ sealed class Route(
route = "Home?scrollToTop={scrollToTop}",
icon = R.drawable.ic_home,
arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }),
hasNewItems = { accountViewModel, cache, context -> homeHasNewItems(accountViewModel, cache, context) }
hasNewItems = { accountViewModel, cache -> homeHasNewItems(accountViewModel, cache) }
)
object Search : Route(
@@ -40,17 +39,13 @@ sealed class Route(
object Notification : Route(
route = "Notification",
icon = R.drawable.ic_notifications,
hasNewItems = { accountViewModel, cache, context ->
notificationHasNewItems(accountViewModel, cache, context)
}
hasNewItems = { accountViewModel, cache -> notificationHasNewItems(accountViewModel, cache) }
)
object Message : Route(
route = "Message",
icon = R.drawable.ic_dm,
hasNewItems = { accountViewModel, cache, context ->
messagesHasNewItems(accountViewModel, cache, context)
}
hasNewItems = { accountViewModel, cache -> messagesHasNewItems(accountViewModel, cache) }
)
object Filters : Route(
@@ -92,8 +87,8 @@ fun currentRoute(navController: NavHostController): String? {
return navBackStackEntry?.destination?.route
}
private fun homeHasNewItems(account: Account, cache: NotificationCache, context: Context): Boolean {
val lastTime = cache.load("HomeFollows", context)
private fun homeHasNewItems(account: Account, cache: NotificationCache): Boolean {
val lastTime = cache.load("HomeFollows")
HomeNewThreadFeedFilter.account = account
@@ -103,12 +98,8 @@ private fun homeHasNewItems(account: Account, cache: NotificationCache, context:
) > lastTime
}
private fun notificationHasNewItems(
account: Account,
cache: NotificationCache,
context: Context
): Boolean {
val lastTime = cache.load("Notification", context)
private fun notificationHasNewItems(account: Account, cache: NotificationCache): Boolean {
val lastTime = cache.load("Notification")
NotificationFeedFilter.account = account
@@ -118,18 +109,14 @@ private fun notificationHasNewItems(
) > lastTime
}
private fun messagesHasNewItems(
account: Account,
cache: NotificationCache,
context: Context
): Boolean {
private fun messagesHasNewItems(account: Account, cache: NotificationCache): Boolean {
ChatroomListKnownFeedFilter.account = account
val note = ChatroomListKnownFeedFilter.feed().firstOrNull {
it.createdAt() != null && it.channel() == null && it.author != account.userProfile()
} ?: return false
val lastTime = cache.load("Room/${note.author?.pubkeyHex}", context)
val lastTime = cache.load("Room/${note.author?.pubkeyHex}")
return (note.createdAt() ?: 0) > lastTime
}
@@ -42,13 +42,10 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BadgeCompose(likeSetCard: BadgeCard, modifier: Modifier = Modifier, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
val noteState by likeSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val context = LocalContext.current.applicationContext
val noteEvent = note?.event
@@ -61,13 +58,13 @@ fun BadgeCompose(likeSetCard: BadgeCard, modifier: Modifier = Modifier, isInnerN
LaunchedEffect(key1 = likeSetCard) {
withContext(Dispatchers.IO) {
isNew = likeSetCard.createdAt() > NotificationCache.load(routeForLastRead, context)
isNew = likeSetCard.createdAt() > NotificationCache.load(routeForLastRead)
NotificationCache.markAsRead(routeForLastRead, likeSetCard.createdAt(), context)
NotificationCache.markAsRead(routeForLastRead, likeSetCard.createdAt())
}
}
var backgroundColor = if (isNew) {
val backgroundColor = if (isNew) {
MaterialTheme.colors.primary.copy(0.12f).compositeOver(MaterialTheme.colors.background)
} else {
MaterialTheme.colors.background
@@ -22,7 +22,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@@ -44,8 +43,6 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val context = LocalContext.current.applicationContext
val noteEvent = note?.event
var popupExpanded by remember { mutableStateOf(false) }
@@ -56,13 +53,13 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
LaunchedEffect(key1 = boostSetCard) {
withContext(Dispatchers.IO) {
isNew = boostSetCard.createdAt > NotificationCache.load(routeForLastRead, context)
isNew = boostSetCard.createdAt > NotificationCache.load(routeForLastRead)
NotificationCache.markAsRead(routeForLastRead, boostSetCard.createdAt, context)
NotificationCache.markAsRead(routeForLastRead, boostSetCard.createdAt)
}
}
var backgroundColor = if (isNew) {
val backgroundColor = if (isNew) {
MaterialTheme.colors.primary.copy(0.12f).compositeOver(MaterialTheme.colors.background)
} else {
MaterialTheme.colors.background
@@ -64,8 +64,6 @@ fun ChatroomCompose(
val notificationCacheState = NotificationCache.live.observeAsState()
val notificationCache = notificationCacheState.value ?: return
val context = LocalContext.current.applicationContext
if (note?.event == null) {
BlankNote(Modifier)
} else if (note.channel() != null) {
@@ -84,22 +82,22 @@ fun ChatroomCompose(
} else {
noteEvent?.content()
}
channel?.let { channel ->
channel?.let { chan ->
var hasNewMessages by remember { mutableStateOf<Boolean>(false) }
LaunchedEffect(key1 = notificationCache, key2 = note) {
withContext(Dispatchers.IO) {
note.createdAt()?.let {
note.createdAt()?.let { timestamp ->
hasNewMessages =
it > notificationCache.cache.load("Channel/${channel.idHex}", context)
timestamp > notificationCache.cache.load("Channel/${chan.idHex}")
}
}
}
ChannelName(
channelIdHex = channel.idHex,
channelPicture = channel.profilePicture(),
channelTitle = {
channelIdHex = chan.idHex,
channelPicture = chan.profilePicture(),
channelTitle = { modifier ->
Text(
text = buildAnnotatedString {
withStyle(
@@ -107,7 +105,7 @@ fun ChatroomCompose(
fontWeight = FontWeight.Bold
)
) {
append(channel.info.name)
append(chan.info.name)
}
withStyle(
@@ -120,14 +118,14 @@ fun ChatroomCompose(
}
},
fontWeight = FontWeight.Bold,
modifier = it,
modifier = modifier,
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
},
channelLastTime = note.createdAt(),
channelLastContent = "${author?.toBestDisplayName()}: " + description,
hasNewMessages = hasNewMessages,
onClick = { navController.navigate("Channel/${channel.idHex}") }
onClick = { navController.navigate("Channel/${chan.idHex}") }
)
}
} else {
@@ -153,8 +151,7 @@ fun ChatroomCompose(
withContext(Dispatchers.IO) {
noteEvent?.let {
hasNewMessages = it.createdAt() > notificationCache.cache.load(
"Room/${userToComposeOn.pubkeyHex}",
context
"Room/${userToComposeOn.pubkeyHex}"
)
}
}
@@ -132,11 +132,11 @@ fun ChatroomMessageCompose(
LaunchedEffect(key1 = routeForLastRead) {
routeForLastRead?.let {
withContext(Dispatchers.IO) {
val lastTime = NotificationCache.load(it, context)
val lastTime = NotificationCache.load(it)
val createdAt = note.createdAt()
if (createdAt != null) {
NotificationCache.markAsRead(it, createdAt, context)
NotificationCache.markAsRead(it, createdAt)
isNew = createdAt > lastTime
}
}
@@ -206,17 +206,17 @@ fun ChatroomMessageCompose(
.height(25.dp)
.clip(shape = CircleShape)
.clickable(onClick = {
author?.let {
author.let {
navController.navigate("User/${it.pubkeyHex}")
}
})
)
Text(
" ${author?.toBestDisplayName()}",
" ${author.toBestDisplayName()}",
fontWeight = FontWeight.Bold,
modifier = Modifier.clickable(onClick = {
author?.let {
author.let {
navController.navigate("User/${it.pubkeyHex}")
}
})
@@ -225,9 +225,9 @@ fun ChatroomMessageCompose(
}
val replyTo = note.replyTo
if (!innerQuote && replyTo != null && replyTo.isNotEmpty()) {
if (!innerQuote && !replyTo.isNullOrEmpty()) {
Row(verticalAlignment = Alignment.CenterVertically) {
replyTo.toSet().mapIndexed { index, note ->
replyTo.toSet().mapIndexed { _, note ->
if (note.event != null) {
ChatroomMessageCompose(
note,
@@ -360,7 +360,6 @@ private fun RelayBadges(baseNote: Note) {
val relaysToDisplay = if (expanded) noteRelays else noteRelays.take(3)
val uri = LocalUriHandler.current
val ctx = LocalContext.current.applicationContext
FlowRow(Modifier.padding(start = 10.dp)) {
relaysToDisplay.forEach {
@@ -22,7 +22,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@@ -37,15 +36,13 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LikeSetCompose(likeSetCard: LikeSetCard, modifier: Modifier = Modifier, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
val noteState by likeSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val context = LocalContext.current.applicationContext
val noteEvent = note?.event
var popupExpanded by remember { mutableStateOf(false) }
@@ -56,13 +53,13 @@ fun LikeSetCompose(likeSetCard: LikeSetCard, modifier: Modifier = Modifier, isIn
LaunchedEffect(key1 = likeSetCard) {
withContext(Dispatchers.IO) {
isNew = likeSetCard.createdAt > NotificationCache.load(routeForLastRead, context)
isNew = likeSetCard.createdAt > NotificationCache.load(routeForLastRead)
NotificationCache.markAsRead(routeForLastRead, likeSetCard.createdAt, context)
NotificationCache.markAsRead(routeForLastRead, likeSetCard.createdAt)
}
}
var backgroundColor = if (isNew) {
val backgroundColor = if (isNew) {
MaterialTheme.colors.primary.copy(0.12f).compositeOver(MaterialTheme.colors.background)
} else {
MaterialTheme.colors.background
@@ -21,7 +21,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@@ -39,11 +38,6 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, isInnerNote: Boolean = fal
val noteState by messageSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val context = LocalContext.current.applicationContext
val noteEvent = note?.event
var popupExpanded by remember { mutableStateOf(false) }
@@ -55,13 +49,13 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, isInnerNote: Boolean = fal
LaunchedEffect(key1 = messageSetCard) {
withContext(Dispatchers.IO) {
isNew =
messageSetCard.createdAt() > NotificationCache.load(routeForLastRead, context)
messageSetCard.createdAt() > NotificationCache.load(routeForLastRead)
NotificationCache.markAsRead(routeForLastRead, messageSetCard.createdAt(), context)
NotificationCache.markAsRead(routeForLastRead, messageSetCard.createdAt())
}
}
var backgroundColor = if (isNew) {
val backgroundColor = if (isNew) {
MaterialTheme.colors.primary.copy(0.12f).compositeOver(MaterialTheme.colors.background)
} else {
MaterialTheme.colors.background
@@ -25,7 +25,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@@ -41,15 +40,13 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MultiSetCompose(multiSetCard: MultiSetCard, modifier: Modifier = Modifier, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
val noteState by multiSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val context = LocalContext.current.applicationContext
val noteEvent = note?.event
var popupExpanded by remember { mutableStateOf(false) }
@@ -60,13 +57,13 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, modifier: Modifier = Modifier, r
LaunchedEffect(key1 = multiSetCard) {
withContext(Dispatchers.IO) {
isNew = multiSetCard.createdAt > NotificationCache.load(routeForLastRead, context)
isNew = multiSetCard.createdAt > NotificationCache.load(routeForLastRead)
NotificationCache.markAsRead(routeForLastRead, multiSetCard.createdAt, context)
NotificationCache.markAsRead(routeForLastRead, multiSetCard.createdAt)
}
}
var backgroundColor = if (isNew) {
val backgroundColor = if (isNew) {
MaterialTheme.colors.primary.copy(0.12f).compositeOver(MaterialTheme.colors.background)
} else {
MaterialTheme.colors.background
@@ -124,18 +124,18 @@ fun NoteCompose(
LaunchedEffect(key1 = routeForLastRead) {
withContext(Dispatchers.IO) {
routeForLastRead?.let {
val lastTime = NotificationCache.load(it, context)
val lastTime = NotificationCache.load(it)
val createdAt = note.createdAt()
if (createdAt != null) {
NotificationCache.markAsRead(it, createdAt, context)
NotificationCache.markAsRead(it, createdAt)
isNew = createdAt > lastTime
}
}
}
}
var backgroundColor = if (isNew) {
val backgroundColor = if (isNew) {
val newColor = MaterialTheme.colors.primary.copy(0.12f)
if (parentBackgroundColor != null) {
newColor.compositeOver(parentBackgroundColor)
@@ -350,7 +350,7 @@ fun NoteCompose(
// Reposts have trash in their contents.
if (noteEvent is ReactionEvent) {
val refactorReactionText =
if (noteEvent.content == "+") "" else noteEvent.content ?: " "
if (noteEvent.content == "+") "" else noteEvent.content
Text(
text = refactorReactionText
@@ -365,7 +365,6 @@ fun NoteCompose(
ReportEvent.ReportType.SPAM -> stringResource(R.string.spam)
ReportEvent.ReportType.IMPERSONATION -> stringResource(R.string.impersonation)
ReportEvent.ReportType.ILLEGAL -> stringResource(R.string.illegal_behavior)
else -> stringResource(R.string.unknown)
}
}.toSet().joinToString(", ")
@@ -600,7 +599,6 @@ private fun RelayBadges(baseNote: Note) {
val relaysToDisplay = if (expanded) noteRelays else noteRelays.take(3)
val uri = LocalUriHandler.current
val ctx = LocalContext.current.applicationContext
FlowRow(Modifier.padding(top = 10.dp, start = 5.dp, end = 4.dp)) {
relaysToDisplay.forEach {
@@ -666,7 +664,7 @@ fun NoteAuthorPicture(
baseNote: Note,
baseUserAccount: User,
size: Dp,
pictureModifier: Modifier = Modifier,
modifier: Modifier = Modifier,
onClick: ((User) -> Unit)? = null
) {
val noteState by baseNote.live().metadata.observeAsState()
@@ -674,8 +672,6 @@ fun NoteAuthorPicture(
val author = note.author
val ctx = LocalContext.current.applicationContext
Box(
Modifier
.width(size)
@@ -685,13 +681,13 @@ fun NoteAuthorPicture(
RobohashAsyncImage(
robot = "authornotfound",
contentDescription = stringResource(R.string.unknown_author),
modifier = pictureModifier
modifier = modifier
.fillMaxSize(1f)
.clip(shape = CircleShape)
.background(MaterialTheme.colors.background)
)
} else {
UserPicture(author, baseUserAccount, size, pictureModifier, onClick)
UserPicture(author, baseUserAccount, size, modifier, onClick)
}
}
}
@@ -715,15 +711,13 @@ fun UserPicture(
baseUser: User,
baseUserAccount: User,
size: Dp,
pictureModifier: Modifier = Modifier,
modifier: Modifier = Modifier,
onClick: ((User) -> Unit)? = null,
onLongClick: ((User) -> Unit)? = null
) {
val userState by baseUser.live().metadata.observeAsState()
val user = userState?.user ?: return
val ctx = LocalContext.current.applicationContext
Box(
Modifier
.width(size)
@@ -733,7 +727,7 @@ fun UserPicture(
robot = user.pubkeyHex,
model = ResizeImage(user.profilePicture(), size),
contentDescription = stringResource(id = R.string.profile_image),
modifier = pictureModifier
modifier = modifier
.fillMaxSize(1f)
.clip(shape = CircleShape)
.background(MaterialTheme.colors.background)
@@ -803,7 +797,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit,
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(accountViewModel.decrypt(note) ?: "")); onDismiss() }) {
Text(stringResource(R.string.copy_text))
}
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}" ?: "")); onDismiss() }) {
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}")); onDismiss() }) {
Text(stringResource(R.string.copy_user_pubkey))
}
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(note.idNote())); onDismiss() }) {
@@ -840,10 +834,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit,
Divider()
DropdownMenuItem(onClick = {
note.author?.let {
accountViewModel.hide(
it,
appContext
)
accountViewModel.hide(it)
}; onDismiss()
}) {
Text(stringResource(R.string.block_hide_user))
@@ -851,35 +842,35 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit,
Divider()
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.SPAM)
note.author?.let { accountViewModel.hide(it, appContext) }
note.author?.let { accountViewModel.hide(it) }
onDismiss()
}) {
Text(stringResource(R.string.report_spam_scam))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.PROFANITY)
note.author?.let { accountViewModel.hide(it, appContext) }
note.author?.let { accountViewModel.hide(it) }
onDismiss()
}) {
Text(stringResource(R.string.report_hateful_speech))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.IMPERSONATION)
note.author?.let { accountViewModel.hide(it, appContext) }
note.author?.let { accountViewModel.hide(it) }
onDismiss()
}) {
Text(stringResource(R.string.report_impersonation))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.NUDITY)
note.author?.let { accountViewModel.hide(it, appContext) }
note.author?.let { accountViewModel.hide(it) }
onDismiss()
}) {
Text(stringResource(R.string.report_nudity_porn))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.ILLEGAL)
note.author?.let { accountViewModel.hide(it, appContext) }
note.author?.let { accountViewModel.hide(it) }
onDismiss()
}) {
Text(stringResource(R.string.report_illegal_behaviour))
@@ -124,7 +124,7 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni
}
VerticalDivider(primaryLight)
NoteQuickActionItem(Icons.Default.AlternateEmail, stringResource(R.string.quick_action_copy_user_id)) {
clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}" ?: ""))
clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}"))
showToast(R.string.copied_user_id_to_clipboard)
onDismiss()
}
@@ -39,12 +39,10 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -280,7 +278,7 @@ fun LikeReaction(
}
}
) {
if (reactedNote?.isReactedBy(accountViewModel.userProfile()) == true) {
if (reactedNote.isReactedBy(accountViewModel.userProfile())) {
Icon(
painter = painterResource(R.drawable.ic_liked),
null,
@@ -298,7 +296,7 @@ fun LikeReaction(
}
Text(
" ${showCount(reactedNote?.reactions?.size)}",
" ${showCount(reactedNote.reactions.size)}",
fontSize = 14.sp,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
modifier = textModifier
@@ -590,15 +588,13 @@ class UpdateZapAmountViewModel : ViewModel() {
}
}
@OptIn(ExperimentalComposeUiApi::class, ExperimentalLayoutApi::class)
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun UpdateZapAmountDialog(onClose: () -> Unit, account: Account) {
val postViewModel: UpdateZapAmountViewModel = viewModel()
val ctx = LocalContext.current.applicationContext
// initialize focus reference to be able to request focus programmatically
val keyboardController = LocalSoftwareKeyboardController.current
// val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(account) {
postViewModel.load(account)
@@ -22,7 +22,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.RelayInfo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -34,7 +33,6 @@ import java.time.format.DateTimeFormatter
fun RelayCompose(
relay: RelayInfo,
accountViewModel: AccountViewModel,
navController: NavController,
onAddRelay: () -> Unit,
onRemoveRelay: () -> Unit
) {
@@ -48,8 +46,6 @@ fun RelayCompose(
modifier = Modifier
.padding(start = 12.dp, end = 12.dp, top = 10.dp)
) {
// UserPicture(user, navController, account.userProfile(), 55.dp)
Column(
modifier = Modifier
.padding(start = 10.dp)
@@ -13,7 +13,6 @@ 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.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@@ -33,7 +32,6 @@ fun UserCompose(baseUser: User, accountViewModel: AccountViewModel, navControlle
val userState by account.userProfile().live().follows.observeAsState()
val userFollows = userState?.user ?: return
val ctx = LocalContext.current.applicationContext
val coroutineScope = rememberCoroutineScope()
Column(
@@ -58,8 +56,8 @@ fun UserCompose(baseUser: User, accountViewModel: AccountViewModel, navControlle
UsernameDisplay(baseUser)
}
val userState by baseUser.live().metadata.observeAsState()
val user = userState?.user ?: return
val baseUserState by baseUser.live().metadata.observeAsState()
val user = baseUserState?.user ?: return
Text(
user.info?.about ?: "",
@@ -18,7 +18,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.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -54,7 +53,6 @@ fun ZapNoteCompose(baseNote: Pair<Note, Note>, accountViewModel: AccountViewMode
val baseAuthor = noteZapRequest.author
val ctx = LocalContext.current.applicationContext
val coroutineScope = rememberCoroutineScope()
if (baseAuthor == null) {
@@ -83,8 +81,8 @@ fun ZapNoteCompose(baseNote: Pair<Note, Note>, accountViewModel: AccountViewMode
UsernameDisplay(baseAuthor)
}
val userState by baseAuthor.live().metadata.observeAsState()
val user = userState?.user ?: return
val baseAuthorState by baseAuthor.live().metadata.observeAsState()
val user = baseAuthorState?.user ?: return
Text(
user.info?.about ?: "",
@@ -23,7 +23,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@@ -39,15 +38,13 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ZapSetCompose(zapSetCard: ZapSetCard, modifier: Modifier = Modifier, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
val noteState by zapSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val context = LocalContext.current.applicationContext
val noteEvent = note?.event
var popupExpanded by remember { mutableStateOf(false) }
@@ -58,9 +55,9 @@ fun ZapSetCompose(zapSetCard: ZapSetCard, modifier: Modifier = Modifier, isInner
LaunchedEffect(key1 = zapSetCard) {
withContext(Dispatchers.IO) {
isNew = zapSetCard.createdAt > NotificationCache.load(routeForLastRead, context)
isNew = zapSetCard.createdAt > NotificationCache.load(routeForLastRead)
NotificationCache.markAsRead(routeForLastRead, zapSetCard.createdAt, context)
NotificationCache.markAsRead(routeForLastRead, zapSetCard.createdAt)
}
}
@@ -26,7 +26,6 @@ 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.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -44,8 +43,6 @@ import com.vitorpamplona.amethyst.ui.qrcode.QrCodeScanner
fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
var presenting by remember { mutableStateOf(true) }
val ctx = LocalContext.current.applicationContext
Dialog(
onDismissRequest = onClose,
properties = DialogProperties(usePlatformDefaultWidth = false)
@@ -6,6 +6,7 @@ import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account
import fr.acinq.secp256k1.Hex
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
@@ -67,6 +68,7 @@ class AccountStateViewModel() : ViewModel() {
login(account)
}
@OptIn(DelicateCoroutinesApi::class)
fun login(account: Account) {
LocalPreferences.updatePrefsForLogin(account)
@@ -86,16 +88,16 @@ class AccountStateViewModel() : ViewModel() {
}
}
@OptIn(DelicateCoroutinesApi::class)
private val saveListener: (com.vitorpamplona.amethyst.model.AccountState) -> Unit = {
GlobalScope.launch(Dispatchers.IO) {
LocalPreferences.saveToEncryptedStorage(it.account)
}
}
@OptIn(DelicateCoroutinesApi::class)
private fun prepareLogoutOrSwitch() {
val state = accountContent.value
when (state) {
when (val state = accountContent.value) {
is AccountState.LoggedIn -> {
GlobalScope.launch(Dispatchers.Main) {
state.account.saveable.removeObserver(saveListener)
@@ -90,7 +90,7 @@ private fun FeedLoaded(
),
state = listState
) {
itemsIndexed(state.feed.value, key = { _, item -> item.id() }) { index, item ->
itemsIndexed(state.feed.value, key = { _, item -> item.id() }) { _, item ->
when (item) {
is NoteCard -> NoteCompose(
item.note,
@@ -22,7 +22,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
@@ -94,7 +93,6 @@ private fun FeedLoaded(
val account = accountState?.account ?: return
val notificationCacheState = NotificationCache.live.observeAsState()
val notificationCache = notificationCacheState.value ?: return
val context = LocalContext.current.applicationContext
LaunchedEffect(key1 = markAsRead.value) {
if (markAsRead.value) {
@@ -118,7 +116,7 @@ private fun FeedLoaded(
"Room/${userToComposeOn.pubkeyHex}"
}
notificationCache.cache.markAsRead(route, it.createdAt(), context)
notificationCache.cache.markAsRead(route, it.createdAt())
}
}
markAsRead.value = false
@@ -78,7 +78,7 @@ private fun LnZapFeedLoaded(
),
state = listState
) {
itemsIndexed(state.feed.value, key = { _, item -> item.second.idHex }) { index, item ->
itemsIndexed(state.feed.value, key = { _, item -> item.second.idHex }) { _, item ->
ZapNoteCompose(item, accountViewModel = accountViewModel, navController = navController)
}
}
@@ -22,7 +22,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.model.RelayInfo
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.UserState
@@ -107,7 +106,7 @@ class RelayFeedViewModel : ViewModel() {
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun RelayFeedView(viewModel: RelayFeedViewModel, accountViewModel: AccountViewModel, navController: NavController) {
fun RelayFeedView(viewModel: RelayFeedViewModel, accountViewModel: AccountViewModel) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -136,11 +135,10 @@ fun RelayFeedView(viewModel: RelayFeedViewModel, accountViewModel: AccountViewMo
),
state = listState
) {
itemsIndexed(feedState, key = { _, item -> item.url }) { index, item ->
itemsIndexed(feedState, key = { _, item -> item.url }) { _, item ->
RelayCompose(
item,
accountViewModel = accountViewModel,
navController = navController,
onAddRelay = { wantsToAddRelay = item.url },
onRemoveRelay = { wantsToAddRelay = item.url }
)
@@ -78,7 +78,7 @@ private fun FeedLoaded(
),
state = listState
) {
itemsIndexed(state.feed.value, key = { _, item -> item.pubkeyHex }) { index, item ->
itemsIndexed(state.feed.value, key = { _, item -> item.pubkeyHex }) { _, item ->
UserCompose(item, accountViewModel = accountViewModel, navController = navController)
}
}
@@ -167,6 +167,7 @@ private fun authenticatedCopyNSec(
return
}
@Suppress("DEPRECATION")
fun keyguardPrompt() {
val intent = keyguardManager.createConfirmDeviceCredentialIntent(
context.getString(R.string.app_name_release),
@@ -97,19 +97,19 @@ class AccountViewModel(private val account: Account) : ViewModel() {
return account.decryptContent(note)
}
fun hide(user: User, ctx: Context) {
fun hide(user: User) {
account.hideUser(user.pubkeyHex)
}
fun show(user: User, ctx: Context) {
fun show(user: User) {
account.showUser(user.pubkeyHex)
}
fun translateTo(lang: Locale, ctx: Context) {
fun translateTo(lang: Locale) {
account.updateTranslateTo(lang.language)
}
fun dontTranslateFrom(lang: String, ctx: Context) {
fun dontTranslateFrom(lang: String) {
account.addDontTranslateFrom(lang)
}
@@ -71,7 +71,6 @@ import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.dal.ChannelFeedFilter
import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.ChatroomFeedView
import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
@@ -79,7 +78,6 @@ import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
fun ChannelScreen(
channelId: String?,
accountViewModel: AccountViewModel,
accountStateViewModel: AccountStateViewModel,
navController: NavController
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
@@ -103,7 +101,7 @@ fun ChannelScreen(
}
DisposableEffect(channelId) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Channel Start")
NostrChannelDataSource.start()
@@ -145,7 +145,7 @@ fun TabKnown(
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrChatroomListDataSource.resetFilters()
feedViewModel.refresh()
@@ -186,7 +186,7 @@ fun TabNew(
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrChatroomListDataSource.resetFilters()
feedViewModel.refresh()
@@ -36,7 +36,6 @@ 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.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
@@ -81,7 +80,7 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr
}
DisposableEffect(userId) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Private Message Start")
NostrChatroomDataSource.start()
@@ -101,11 +100,7 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr
Column(Modifier.fillMaxHeight()) {
NostrChatroomDataSource.withUser?.let {
ChatroomHeader(
it,
accountViewModel = accountViewModel,
navController = navController
)
ChatroomHeader(it, navController = navController)
}
Column(
@@ -198,9 +193,7 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr
}
@Composable
fun ChatroomHeader(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
val ctx = LocalContext.current.applicationContext
fun ChatroomHeader(baseUser: User, navController: NavController) {
Column(
modifier = Modifier.clickable(
onClick = { navController.navigate("User/${baseUser.pubkeyHex}") }
@@ -69,7 +69,7 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
scaffoldState = scaffoldState
) {
Column(modifier = Modifier.padding(bottom = it.calculateBottomPadding())) {
AppNavigation(navController, accountViewModel, accountStateViewModel, startingPage)
AppNavigation(navController, accountViewModel, startingPage)
}
}
}
@@ -34,7 +34,7 @@ fun NotificationScreen(accountViewModel: AccountViewModel, navController: NavCon
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
feedViewModel.refresh()
}
@@ -27,7 +27,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalUriHandler
@@ -111,7 +110,7 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Profile Start")
NostrUserProfileDataSource.loadUserProfile(userId)
@@ -259,13 +258,13 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
}
) {
when (pagerState.currentPage) {
0 -> TabNotesNewThreads(baseUser, accountViewModel, navController)
1 -> TabNotesConversations(baseUser, accountViewModel, navController)
0 -> TabNotesNewThreads(accountViewModel, navController)
1 -> TabNotesConversations(accountViewModel, navController)
2 -> TabFollows(baseUser, accountViewModel, navController)
3 -> TabFollowers(baseUser, accountViewModel, navController)
4 -> TabReceivedZaps(baseUser, accountViewModel, navController)
5 -> TabReports(baseUser, accountViewModel, navController)
6 -> TabRelays(baseUser, accountViewModel, navController)
6 -> TabRelays(baseUser, accountViewModel)
}
}
}
@@ -335,7 +334,7 @@ private fun ProfileHeader(
baseUser = baseUser,
baseUserAccount = account.userProfile(),
size = 100.dp,
pictureModifier = Modifier.border(
modifier = Modifier.border(
3.dp,
MaterialTheme.colors.background,
CircleShape
@@ -576,8 +575,6 @@ fun BadgeThumb(
val event = (note.event as? BadgeDefinitionEvent)
val image = event?.thumb() ?: event?.image()
val ctx = LocalContext.current.applicationContext
Box(
Modifier
.width(size)
@@ -656,7 +653,7 @@ private fun DrawBanner(baseUser: User) {
}
@Composable
fun TabNotesNewThreads(user: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabNotesNewThreads(accountViewModel: AccountViewModel, navController: NavController) {
val accountState by accountViewModel.accountLiveData.observeAsState()
if (accountState != null) {
val feedViewModel: NostrUserProfileNewThreadsFeedViewModel = viewModel()
@@ -676,7 +673,7 @@ fun TabNotesNewThreads(user: User, accountViewModel: AccountViewModel, navContro
}
@Composable
fun TabNotesConversations(user: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabNotesConversations(accountViewModel: AccountViewModel, navController: NavController) {
val accountState by accountViewModel.accountLiveData.observeAsState()
if (accountState != null) {
val feedViewModel: NostrUserProfileConversationsFeedViewModel = viewModel()
@@ -772,13 +769,13 @@ fun TabReports(baseUser: User, accountViewModel: AccountViewModel, navController
}
@Composable
fun TabRelays(user: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabRelays(user: User, accountViewModel: AccountViewModel) {
val feedViewModel: RelayFeedViewModel = viewModel()
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(user) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Profile Relay Start")
feedViewModel.subscribeTo(user)
@@ -801,7 +798,7 @@ fun TabRelays(user: User, accountViewModel: AccountViewModel, navController: Nav
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
RelayFeedView(feedViewModel, accountViewModel, navController)
RelayFeedView(feedViewModel, accountViewModel)
}
}
}
@@ -943,8 +940,6 @@ fun ShowUserButton(onClick: () -> Unit) {
@Composable
fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () -> Unit, accountViewModel: AccountViewModel) {
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current.applicationContext
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -960,52 +955,51 @@ fun UserProfileDropDownMenu(user: User, popupExpanded: Boolean, onDismiss: () ->
Divider()
if (account.isHidden(user)) {
DropdownMenuItem(onClick = {
user.let {
accountViewModel.show(
it,
context
)
}; onDismiss()
accountViewModel.show(user)
onDismiss()
}) {
Text(stringResource(R.string.unblock_user))
}
} else {
DropdownMenuItem(onClick = { user.let { accountViewModel.hide(it, context) }; onDismiss() }) {
DropdownMenuItem(onClick = {
accountViewModel.hide(user)
onDismiss()
}) {
Text(stringResource(id = R.string.block_hide_user))
}
}
Divider()
DropdownMenuItem(onClick = {
accountViewModel.report(user, ReportEvent.ReportType.SPAM)
user.let { accountViewModel.hide(it, context) }
accountViewModel.hide(user)
onDismiss()
}) {
Text(stringResource(id = R.string.report_spam_scam))
}
DropdownMenuItem(onClick = {
accountViewModel.report(user, ReportEvent.ReportType.PROFANITY)
user.let { accountViewModel.hide(it, context) }
accountViewModel.hide(user)
onDismiss()
}) {
Text(stringResource(R.string.report_hateful_speech))
}
DropdownMenuItem(onClick = {
accountViewModel.report(user, ReportEvent.ReportType.IMPERSONATION)
user.let { accountViewModel.hide(it, context) }
accountViewModel.hide(user)
onDismiss()
}) {
Text(stringResource(id = R.string.report_impersonation))
}
DropdownMenuItem(onClick = {
accountViewModel.report(user, ReportEvent.ReportType.NUDITY)
user.let { accountViewModel.hide(it, context) }
accountViewModel.hide(user)
onDismiss()
}) {
Text(stringResource(R.string.report_nudity_porn))
}
DropdownMenuItem(onClick = {
accountViewModel.report(user, ReportEvent.ReportType.ILLEGAL)
user.let { accountViewModel.hide(it, context) }
accountViewModel.hide(user)
onDismiss()
}) {
Text(stringResource(id = R.string.report_illegal_behaviour))
@@ -36,7 +36,6 @@ 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.LocalLifecycleOwner
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -88,7 +87,7 @@ fun SearchScreen(
}
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Global Start")
NostrGlobalDataSource.start()
@@ -127,8 +126,6 @@ private fun SearchBar(accountViewModel: AccountViewModel, navController: NavCont
val onlineSearch = NostrSearchEventOrUserDataSource
val ctx = LocalContext.current.applicationContext
val isTrailingIconVisible by remember {
derivedStateOf {
searchValue.isNotBlank()
@@ -237,11 +234,11 @@ private fun SearchBar(accountViewModel: AccountViewModel, navController: NavCont
bottom = 10.dp
)
) {
itemsIndexed(searchResults.value, key = { _, item -> "u" + item.pubkeyHex }) { index, item ->
itemsIndexed(searchResults.value, key = { _, item -> "u" + item.pubkeyHex }) { _, item ->
UserCompose(item, accountViewModel = accountViewModel, navController = navController)
}
itemsIndexed(searchResultsChannels.value, key = { _, item -> "c" + item.idHex }) { index, item ->
itemsIndexed(searchResultsChannels.value, key = { _, item -> "c" + item.idHex }) { _, item ->
ChannelName(
channelIdHex = item.idHex,
channelPicture = item.profilePicture(),
@@ -258,7 +255,7 @@ private fun SearchBar(accountViewModel: AccountViewModel, navController: NavCont
)
}
itemsIndexed(searchResultsNotes.value, key = { _, item -> "n" + item.idHex }) { index, item ->
itemsIndexed(searchResultsNotes.value, key = { _, item -> "n" + item.idHex }) { _, item ->
NoteCompose(item, accountViewModel = accountViewModel, navController = navController)
}
}
@@ -36,7 +36,7 @@ fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, navControl
}
DisposableEffect(accountViewModel) {
val observer = LifecycleEventObserver { source, event ->
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Thread Start")
ThreadFeedFilter.loadThread(noteId)