Fixed (almost) all test warnings

This commit is contained in:
maxmoney21m 2023-03-14 01:47:44 +08:00
parent 9c4f9fce37
commit 2820197905
59 changed files with 686 additions and 788 deletions

View File

@ -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)

View File

@ -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,

View File

@ -334,8 +334,9 @@ object LocalCache {
}
}
@Suppress("UNUSED_PARAMETER")
fun consume(event: RecommendRelayEvent) {
// Log.d("RR", event.toJson())
// // Log.d("RR", event.toJson())
}
fun consume(event: ContactListEvent) {
@ -650,9 +651,11 @@ object LocalCache {
refreshObservers()
}
@Suppress("UNUSED_PARAMETER")
fun consume(event: ChannelHideMessageEvent) {
}
@Suppress("UNUSED_PARAMETER")
fun consume(event: ChannelMuteUserEvent) {
}

View File

@ -89,7 +89,7 @@ class User(val pubkeyHex: String) {
return info?.picture
}
fun follow(user: User, followedAt: Long) {
fun follow(user: User) {
follows = follows + user
user.followers = user.followers + this
@ -105,7 +105,7 @@ class User(val pubkeyHex: String) {
user.liveSet?.follows?.invalidateData()
}
fun follow(users: Set<User>, followedAt: Long) {
fun follow(users: Set<User>) {
follows = follows + users
users.forEach {
if (this !in it.followers && it.liveSet?.isInUse() == true) {
@ -258,7 +258,7 @@ class User(val pubkeyHex: String) {
val toBeAdded = newFollows - follows
val toBeRemoved = follows - newFollows
follow(toBeAdded, updateAt)
follow(toBeAdded)
unfollow(toBeRemoved)
updatedFollowsAt = updateAt

View File

@ -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) {

View File

@ -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,

View File

@ -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())
}
}
}

View File

@ -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())
}
}
}

View File

@ -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()
)
}

View File

@ -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.

View File

@ -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 ->

View File

@ -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()

View File

@ -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"

View File

@ -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()

View File

@ -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(

View File

@ -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)
}

View File

@ -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

View File

@ -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().relays

View File

@ -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

View File

@ -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) {

View File

@ -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) }

View File

@ -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 })
}
}
) {

View File

@ -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

View File

@ -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)
}
}

View File

@ -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
)
})

View File

@ -1,253 +1,252 @@
package com.vitorpamplona.amethyst.ui.navigation
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
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.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import coil.Coil
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
import com.vitorpamplona.amethyst.service.NostrGlobalDataSource
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.service.relays.RelayPool
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.RelayPoolViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch
@Composable
fun AppTopBar(navController: NavHostController, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
when (currentRoute(navController)) {
// Route.Profile.route -> TopBarWithBackButton(navController)
else -> MainTopBar(scaffoldState, accountViewModel)
}
}
@Composable
fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val accountUserState by account.userProfile().live().metadata.observeAsState()
val accountUser = accountUserState?.user ?: return
val relayViewModel: RelayPoolViewModel = viewModel { RelayPoolViewModel() }
val connectedRelaysLiveData by relayViewModel.connectedRelaysLiveData.observeAsState()
val availableRelaysLiveData by relayViewModel.availableRelaysLiveData.observeAsState()
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val ctx = LocalContext.current.applicationContext
var wantsToEditRelays by remember {
mutableStateOf(false)
}
if (wantsToEditRelays) {
NewRelayListView({ wantsToEditRelays = false }, account)
}
Column() {
TopAppBar(
elevation = 0.dp,
backgroundColor = Color(0xFFFFFF),
title = {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(Modifier) {
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(start = 0.dp, end = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
IconButton(
onClick = {
Client.allSubscriptions().map {
"$it ${
Client.getSubscriptionFilters(it)
.joinToString { it.filter.toJson() }
}"
}.forEach {
Log.d("STATE DUMP", it)
}
NostrAccountDataSource.printCounter()
NostrChannelDataSource.printCounter()
NostrChatroomDataSource.printCounter()
NostrChatroomListDataSource.printCounter()
NostrGlobalDataSource.printCounter()
NostrHomeDataSource.printCounter()
NostrSingleEventDataSource.printCounter()
NostrSearchEventOrUserDataSource.printCounter()
NostrSingleChannelDataSource.printCounter()
NostrSingleUserDataSource.printCounter()
NostrThreadDataSource.printCounter()
NostrUserProfileDataSource.printCounter()
Log.d("STATE DUMP", "Connected Relays: " + RelayPool.connectedRelays())
val imageLoader = Coil.imageLoader(context)
Log.d("STATE DUMP", "Image Disk Cache ${(imageLoader.diskCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.diskCache?.maxSize ?: 0) / (1024 * 1024)} MB")
Log.d("STATE DUMP", "Image Memory Cache ${(imageLoader.memoryCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.memoryCache?.maxSize ?: 0) / (1024 * 1024)} MB")
Log.d("STATE DUMP", "Notes: " + LocalCache.notes.filter { it.value.event != null }.size + "/" + LocalCache.notes.size)
Log.d("STATE DUMP", "Users: " + LocalCache.users.filter { it.value.info?.latestMetadata != null }.size + "/" + LocalCache.users.size)
}
) {
Icon(
painter = painterResource(R.drawable.amethyst),
null,
modifier = Modifier.size(40.dp),
tint = Color.Unspecified
)
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
horizontalAlignment = Alignment.End
) {
Row(
modifier = Modifier.fillMaxHeight(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
"${connectedRelaysLiveData ?: "--"}/${availableRelaysLiveData ?: "--"}",
color = if (connectedRelaysLiveData == 0) Color.Red else MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
style = MaterialTheme.typography.subtitle1,
modifier = Modifier.clickable(
onClick = {
wantsToEditRelays = true
}
)
)
}
}
}
}
},
navigationIcon = {
IconButton(
onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
},
modifier = Modifier
) {
RobohashAsyncImageProxy(
robot = accountUser.pubkeyHex,
model = ResizeImage(accountUser.profilePicture(), 34.dp),
contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier
.width(34.dp)
.height(34.dp)
.clip(shape = CircleShape)
)
}
},
actions = {
IconButton(
onClick = { wantsToEditRelays = true },
modifier = Modifier
) {
Icon(
painter = painterResource(R.drawable.ic_trends),
null,
modifier = Modifier.size(24.dp),
tint = Color.Unspecified
)
}
}
)
Divider(thickness = 0.25.dp)
}
}
@Composable
fun TopBarWithBackButton(navController: NavHostController) {
Column() {
TopAppBar(
elevation = 0.dp,
backgroundColor = Color(0xFFFFFF),
title = {},
navigationIcon = {
IconButton(
onClick = {
navController.popBackStack()
},
modifier = Modifier
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colors.primary
)
}
},
actions = {}
)
Divider(thickness = 0.25.dp)
}
}
package com.vitorpamplona.amethyst.ui.navigation
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
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.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import coil.Coil
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
import com.vitorpamplona.amethyst.service.NostrGlobalDataSource
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.service.relays.RelayPool
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.RelayPoolViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch
@Composable
fun AppTopBar(navController: NavHostController, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
when (currentRoute(navController)) {
// Route.Profile.route -> TopBarWithBackButton(navController)
else -> MainTopBar(scaffoldState, accountViewModel)
}
}
@OptIn(coil.annotation.ExperimentalCoilApi::class)
@Composable
fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val accountUserState by account.userProfile().live().metadata.observeAsState()
val accountUser = accountUserState?.user ?: return
val relayViewModel: RelayPoolViewModel = viewModel { RelayPoolViewModel() }
val connectedRelaysLiveData by relayViewModel.connectedRelaysLiveData.observeAsState()
val availableRelaysLiveData by relayViewModel.availableRelaysLiveData.observeAsState()
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
var wantsToEditRelays by remember {
mutableStateOf(false)
}
if (wantsToEditRelays) {
NewRelayListView({ wantsToEditRelays = false }, account)
}
Column() {
TopAppBar(
elevation = 0.dp,
title = {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(Modifier) {
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(start = 0.dp, end = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
IconButton(
onClick = {
Client.allSubscriptions().map {
"$it ${
Client.getSubscriptionFilters(it)
.joinToString { it.filter.toJson() }
}"
}.forEach {
Log.d("STATE DUMP", it)
}
NostrAccountDataSource.printCounter()
NostrChannelDataSource.printCounter()
NostrChatroomDataSource.printCounter()
NostrChatroomListDataSource.printCounter()
NostrGlobalDataSource.printCounter()
NostrHomeDataSource.printCounter()
NostrSingleEventDataSource.printCounter()
NostrSearchEventOrUserDataSource.printCounter()
NostrSingleChannelDataSource.printCounter()
NostrSingleUserDataSource.printCounter()
NostrThreadDataSource.printCounter()
NostrUserProfileDataSource.printCounter()
Log.d("STATE DUMP", "Connected Relays: " + RelayPool.connectedRelays())
val imageLoader = Coil.imageLoader(context)
Log.d("STATE DUMP", "Image Disk Cache ${(imageLoader.diskCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.diskCache?.maxSize ?: 0) / (1024 * 1024)} MB")
Log.d("STATE DUMP", "Image Memory Cache ${(imageLoader.memoryCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.memoryCache?.maxSize ?: 0) / (1024 * 1024)} MB")
Log.d("STATE DUMP", "Notes: " + LocalCache.notes.filter { it.value.event != null }.size + "/" + LocalCache.notes.size)
Log.d("STATE DUMP", "Users: " + LocalCache.users.filter { it.value.info?.latestMetadata != null }.size + "/" + LocalCache.users.size)
}
) {
Icon(
painter = painterResource(R.drawable.amethyst),
null,
modifier = Modifier.size(40.dp),
tint = Color.Unspecified
)
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
horizontalAlignment = Alignment.End
) {
Row(
modifier = Modifier.fillMaxHeight(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
"${connectedRelaysLiveData ?: "--"}/${availableRelaysLiveData ?: "--"}",
color = if (connectedRelaysLiveData == 0) Color.Red else MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
style = MaterialTheme.typography.subtitle1,
modifier = Modifier.clickable(
onClick = {
wantsToEditRelays = true
}
)
)
}
}
}
}
},
navigationIcon = {
IconButton(
onClick = {
coroutineScope.launch {
scaffoldState.drawerState.open()
}
},
modifier = Modifier
) {
RobohashAsyncImageProxy(
robot = accountUser.pubkeyHex,
model = ResizeImage(accountUser.profilePicture(), 34.dp),
contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier
.width(34.dp)
.height(34.dp)
.clip(shape = CircleShape)
)
}
},
actions = {
IconButton(
onClick = { wantsToEditRelays = true },
modifier = Modifier
) {
Icon(
painter = painterResource(R.drawable.ic_trends),
null,
modifier = Modifier.size(24.dp),
tint = Color.Unspecified
)
}
}
)
Divider(thickness = 0.25.dp)
}
}
@Composable
fun TopBarWithBackButton(navController: NavHostController) {
Column() {
TopAppBar(
elevation = 0.dp,
backgroundColor = Color(0xFFFFFF),
title = {},
navigationIcon = {
IconButton(
onClick = {
navController.popBackStack()
},
modifier = Modifier
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colors.primary
)
}
},
actions = {}
)
Divider(thickness = 0.25.dp)
}
}

View File

@ -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),

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -62,8 +62,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) {
@ -82,22 +80,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(
@ -105,7 +103,7 @@ fun ChatroomCompose(
fontWeight = FontWeight.Bold
)
) {
append(channel.info.name)
append(chan.info.name)
}
withStyle(
@ -118,14 +116,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 {
@ -148,8 +146,7 @@ fun ChatroomCompose(
withContext(Dispatchers.IO) {
noteEvent?.let {
hasNewMessages = it.createdAt() > notificationCache.cache.load(
"Room/${userToComposeOn.pubkeyHex}",
context
"Room/${userToComposeOn.pubkeyHex}"
)
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
@ -342,7 +342,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
@ -357,7 +357,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(", ")
@ -590,7 +589,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 {
@ -656,7 +654,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()
@ -664,8 +662,6 @@ fun NoteAuthorPicture(
val author = note.author
val ctx = LocalContext.current.applicationContext
Box(
Modifier
.width(size)
@ -675,13 +671,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)
}
}
}
@ -705,15 +701,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)
@ -723,7 +717,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)
@ -793,7 +787,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() }) {
@ -830,10 +824,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))
@ -841,35 +832,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))

View File

@ -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()
}

View File

@ -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)

View File

@ -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)

View File

@ -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 ?: "",

View File

@ -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 ?: "",

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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
@ -92,7 +91,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) {
@ -112,7 +110,7 @@ private fun FeedLoaded(
"Room/${userToComposeOn.pubkeyHex}"
}
notificationCache.cache.markAsRead(route, it.createdAt(), context)
notificationCache.cache.markAsRead(route, it.createdAt())
}
}
markAsRead.value = false

View File

@ -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)
}
}

View File

@ -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 }
)

View File

@ -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)
}
}

View File

@ -167,6 +167,7 @@ private fun authenticatedCopyNSec(
return
}
@Suppress("DEPRECATION")
fun keyguardPrompt() {
val intent = keyguardManager.createConfirmDeviceCredentialIntent(
context.getString(R.string.app_name_release),

View File

@ -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)
}

View File

@ -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()

View File

@ -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()

View File

@ -1,241 +1,234 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
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.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.dal.ChatroomFeedFilter
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
import com.vitorpamplona.amethyst.ui.screen.ChatroomFeedView
import com.vitorpamplona.amethyst.ui.screen.NostrChatRoomFeedViewModel
@Composable
fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navController: NavController) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account
if (account != null && userId != null) {
val newPost = remember { mutableStateOf(TextFieldValue("")) }
val replyTo = remember { mutableStateOf<Note?>(null) }
ChatroomFeedFilter.loadMessagesBetween(account, userId)
NostrChatroomDataSource.loadMessagesBetween(account, userId)
val feedViewModel: NostrChatRoomFeedViewModel = viewModel()
val lifeCycleOwner = LocalLifecycleOwner.current
LaunchedEffect(userId) {
feedViewModel.refresh()
}
DisposableEffect(userId) {
val observer = LifecycleEventObserver { source, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Private Message Start")
NostrChatroomDataSource.start()
feedViewModel.refresh()
}
if (event == Lifecycle.Event.ON_PAUSE) {
println("Private Message Stop")
NostrChatroomDataSource.stop()
}
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
}
}
Column(Modifier.fillMaxHeight()) {
NostrChatroomDataSource.withUser?.let {
ChatroomHeader(
it,
accountViewModel = accountViewModel,
navController = navController
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.padding(vertical = 0.dp)
.weight(1f, true)
) {
ChatroomFeedView(feedViewModel, accountViewModel, navController, "Room/$userId") {
replyTo.value = it
}
}
Spacer(modifier = Modifier.height(10.dp))
Row(Modifier.padding(horizontal = 10.dp).animateContentSize(), verticalAlignment = Alignment.CenterVertically) {
val replyingNote = replyTo.value
if (replyingNote != null) {
Column(Modifier.weight(1f)) {
ChatroomMessageCompose(
baseNote = replyingNote,
null,
innerQuote = true,
accountViewModel = accountViewModel,
navController = navController,
onWantsToReply = {
replyTo.value = it
}
)
}
Column(Modifier.padding(end = 10.dp)) {
IconButton(
modifier = Modifier.size(30.dp),
onClick = { replyTo.value = null }
) {
Icon(
imageVector = Icons.Default.Cancel,
null,
modifier = Modifier.padding(end = 5.dp).size(30.dp),
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
}
}
}
}
// LAST ROW
Row(
modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 10.dp, top = 5.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
TextField(
value = newPost.value,
onValueChange = { newPost.value = it },
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences
),
modifier = Modifier.weight(1f, true),
shape = RoundedCornerShape(25.dp),
placeholder = {
Text(
text = stringResource(id = R.string.reply_here),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
trailingIcon = {
PostButton(
onPost = {
account.sendPrivateMeesage(newPost.value.text, userId, replyTo.value)
newPost.value = TextFieldValue("")
replyTo.value = null
feedViewModel.refresh() // Don't wait a full second before updating
},
newPost.value.text.isNotBlank(),
modifier = Modifier.padding(end = 10.dp)
)
},
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
)
)
}
}
}
}
@Composable
fun ChatroomHeader(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
val ctx = LocalContext.current.applicationContext
Column(
modifier = Modifier.clickable(
onClick = { navController.navigate("User/${baseUser.pubkeyHex}") }
)
) {
Column(modifier = Modifier.padding(12.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
val authorState by baseUser.live().metadata.observeAsState()
val author = authorState?.user!!
RobohashAsyncImageProxy(
robot = author.pubkeyHex,
model = ResizeImage(author.profilePicture(), 35.dp),
contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier
.width(35.dp)
.height(35.dp)
.clip(shape = CircleShape)
)
Column(modifier = Modifier.padding(start = 10.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
UsernameDisplay(baseUser)
}
Row(verticalAlignment = Alignment.CenterVertically) {
ObserveDisplayNip05Status(baseUser)
}
}
}
}
Divider(
modifier = Modifier.padding(start = 12.dp, end = 12.dp),
thickness = 0.25.dp
)
}
}
package com.vitorpamplona.amethyst.ui.screen.loggedIn
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
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.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.components.ResizeImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.dal.ChatroomFeedFilter
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
import com.vitorpamplona.amethyst.ui.screen.ChatroomFeedView
import com.vitorpamplona.amethyst.ui.screen.NostrChatRoomFeedViewModel
@Composable
fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navController: NavController) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account
if (account != null && userId != null) {
val newPost = remember { mutableStateOf(TextFieldValue("")) }
val replyTo = remember { mutableStateOf<Note?>(null) }
ChatroomFeedFilter.loadMessagesBetween(account, userId)
NostrChatroomDataSource.loadMessagesBetween(account, userId)
val feedViewModel: NostrChatRoomFeedViewModel = viewModel()
val lifeCycleOwner = LocalLifecycleOwner.current
LaunchedEffect(userId) {
feedViewModel.refresh()
}
DisposableEffect(userId) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Private Message Start")
NostrChatroomDataSource.start()
feedViewModel.refresh()
}
if (event == Lifecycle.Event.ON_PAUSE) {
println("Private Message Stop")
NostrChatroomDataSource.stop()
}
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
}
}
Column(Modifier.fillMaxHeight()) {
NostrChatroomDataSource.withUser?.let {
ChatroomHeader(it, navController = navController)
}
Column(
modifier = Modifier
.fillMaxHeight()
.padding(vertical = 0.dp)
.weight(1f, true)
) {
ChatroomFeedView(feedViewModel, accountViewModel, navController, "Room/$userId") {
replyTo.value = it
}
}
Spacer(modifier = Modifier.height(10.dp))
Row(Modifier.padding(horizontal = 10.dp).animateContentSize(), verticalAlignment = Alignment.CenterVertically) {
val replyingNote = replyTo.value
if (replyingNote != null) {
Column(Modifier.weight(1f)) {
ChatroomMessageCompose(
baseNote = replyingNote,
null,
innerQuote = true,
accountViewModel = accountViewModel,
navController = navController,
onWantsToReply = {
replyTo.value = it
}
)
}
Column(Modifier.padding(end = 10.dp)) {
IconButton(
modifier = Modifier.size(30.dp),
onClick = { replyTo.value = null }
) {
Icon(
imageVector = Icons.Default.Cancel,
null,
modifier = Modifier.padding(end = 5.dp).size(30.dp),
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
}
}
}
}
// LAST ROW
Row(
modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 10.dp, top = 5.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
TextField(
value = newPost.value,
onValueChange = { newPost.value = it },
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences
),
modifier = Modifier.weight(1f, true),
shape = RoundedCornerShape(25.dp),
placeholder = {
Text(
text = stringResource(id = R.string.reply_here),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
trailingIcon = {
PostButton(
onPost = {
account.sendPrivateMeesage(newPost.value.text, userId, replyTo.value)
newPost.value = TextFieldValue("")
replyTo.value = null
feedViewModel.refresh() // Don't wait a full second before updating
},
newPost.value.text.isNotBlank(),
modifier = Modifier.padding(end = 10.dp)
)
},
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
)
)
}
}
}
}
@Composable
fun ChatroomHeader(baseUser: User, navController: NavController) {
Column(
modifier = Modifier.clickable(
onClick = { navController.navigate("User/${baseUser.pubkeyHex}") }
)
) {
Column(modifier = Modifier.padding(12.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
val authorState by baseUser.live().metadata.observeAsState()
val author = authorState?.user!!
RobohashAsyncImageProxy(
robot = author.pubkeyHex,
model = ResizeImage(author.profilePicture(), 35.dp),
contentDescription = stringResource(id = R.string.profile_image),
modifier = Modifier
.width(35.dp)
.height(35.dp)
.clip(shape = CircleShape)
)
Column(modifier = Modifier.padding(start = 10.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
UsernameDisplay(baseUser)
}
Row(verticalAlignment = Alignment.CenterVertically) {
ObserveDisplayNip05Status(baseUser)
}
}
}
}
Divider(
modifier = Modifier.padding(start = 12.dp, end = 12.dp),
thickness = 0.25.dp
)
}
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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))

View File

@ -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)
}
}

View File

@ -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)