This commit is contained in:
greenart7c3
2023-09-23 12:40:17 -03:00
43 changed files with 358 additions and 227 deletions
@@ -145,6 +145,8 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
}
override fun consume(event: Event, relay: Relay) {
checkNotInMainThread()
if (LocalCache.justVerify(event)) {
if (event is GiftWrapEvent) {
val privateKey = account.keyPair.privKey
@@ -194,7 +194,7 @@ private fun RenderSearch(
}
}
DisposableEffect(Unit) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Join Start")
@@ -32,7 +32,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
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
@@ -206,34 +205,34 @@ fun VideoViewInner(
)
}
val mediaItem = remember(videoUri) {
MediaItem.Builder()
.setMediaId(videoUri)
.setUri(videoUri)
.setMediaMetadata(
MediaMetadata.Builder()
.setArtist(authorName?.ifBlank { null })
.setTitle(title?.ifBlank { null } ?: videoUri)
.setArtworkUri(
try {
if (artworkUri != null) {
Uri.parse(artworkUri)
} else {
null
}
} catch (e: Exception) {
null
}
)
.build()
)
.build()
}
if (!automaticallyStartPlayback.value) {
ImageUrlWithDownloadButton(url = videoUri, showImage = automaticallyStartPlayback)
} else {
VideoPlayerActiveMutex(videoUri) { activeOnScreen ->
val mediaItem = remember(videoUri) {
MediaItem.Builder()
.setMediaId(videoUri)
.setUri(videoUri)
.setMediaMetadata(
MediaMetadata.Builder()
.setArtist(authorName?.ifBlank { null })
.setTitle(title?.ifBlank { null } ?: videoUri)
.setArtworkUri(
try {
if (artworkUri != null) {
Uri.parse(artworkUri)
} else {
null
}
} catch (e: Exception) {
null
}
)
.build()
)
.build()
}
GetVideoController(
mediaItem = mediaItem,
videoUri = videoUri,
@@ -282,56 +281,52 @@ fun GetVideoController(
UUID.randomUUID().toString()
}
val scope = rememberCoroutineScope()
// Prepares a VideoPlayer from the foreground service.
DisposableEffect(key1 = videoUri) {
// If it is not null, the user might have come back from a playing video, like clicking on
// the notification of the video player.
if (controller.value == null) {
scope.launch(Dispatchers.IO) {
PlaybackClientController.prepareController(
uid,
videoUri,
nostrUriCallback,
context
) {
// checks again because of race conditions.
if (controller.value == null) { // still prone to race conditions.
controller.value = it
PlaybackClientController.prepareController(
uid,
videoUri,
nostrUriCallback,
context
) {
// checks again because of race conditions.
if (controller.value == null) { // still prone to race conditions.
controller.value = it
if (!it.isPlaying) {
if (keepPlayingMutex?.isPlaying == true) {
if (!it.isPlaying) {
if (keepPlayingMutex?.isPlaying == true) {
// There is a video playing, start this one on mute.
controller.value?.volume = 0f
} else {
// There is no other video playing. Use the default mute state to
// decide if sound is on or not.
controller.value?.volume = if (defaultToStart) 0f else 1f
}
}
controller.value?.setMediaItem(mediaItem)
controller.value?.prepare()
} else if (controller.value != it) {
// discards the new controller because there is an existing one
it.stop()
it.release()
controller.value?.let {
if (it.playbackState == Player.STATE_IDLE || it.playbackState == Player.STATE_ENDED) {
if (it.isPlaying) {
// There is a video playing, start this one on mute.
controller.value?.volume = 0f
it.volume = 0f
} else {
// There is no other video playing. Use the default mute state to
// decide if sound is on or not.
controller.value?.volume = if (defaultToStart) 0f else 1f
it.volume = if (defaultToStart) 0f else 1f
}
}
controller.value?.setMediaItem(mediaItem)
controller.value?.prepare()
} else if (controller.value != it) {
// discards the new controller because there is an existing one
it.stop()
it.release()
controller.value?.let {
if (it.playbackState == Player.STATE_IDLE || it.playbackState == Player.STATE_ENDED) {
if (it.isPlaying) {
// There is a video playing, start this one on mute.
it.volume = 0f
} else {
// There is no other video playing. Use the default mute state to
// decide if sound is on or not.
it.volume = if (defaultToStart) 0f else 1f
}
it.setMediaItem(mediaItem)
it.prepare()
}
it.setMediaItem(mediaItem)
it.prepare()
}
}
}
@@ -372,35 +367,33 @@ fun GetVideoController(
// if the controller is null, restarts the controller with a new one
// if the controller is not null, just continue playing what the controller was playing
if (controller.value == null) {
scope.launch(Dispatchers.IO) {
PlaybackClientController.prepareController(
uid,
videoUri,
nostrUriCallback,
context
) {
// checks again to make sure no other thread has created a controller.
if (controller.value == null) {
controller.value = it
PlaybackClientController.prepareController(
uid,
videoUri,
nostrUriCallback,
context
) {
// checks again to make sure no other thread has created a controller.
if (controller.value == null) {
controller.value = it
if (!it.isPlaying) {
if (keepPlayingMutex?.isPlaying == true) {
// There is a video playing, start this one on mute.
controller.value?.volume = 0f
} else {
// There is no other video playing. Use the default mute state to
// decide if sound is on or not.
controller.value?.volume = if (defaultToStart) 0f else 1f
}
if (!it.isPlaying) {
if (keepPlayingMutex?.isPlaying == true) {
// There is a video playing, start this one on mute.
controller.value?.volume = 0f
} else {
// There is no other video playing. Use the default mute state to
// decide if sound is on or not.
controller.value?.volume = if (defaultToStart) 0f else 1f
}
controller.value?.setMediaItem(mediaItem)
controller.value?.prepare()
} else if (controller.value != it) {
// discards the new controller because there is an existing one
it.stop()
it.release()
}
controller.value?.setMediaItem(mediaItem)
controller.value?.prepare()
} else if (controller.value != it) {
// discards the new controller because there is an existing one
it.stop()
it.release()
}
}
}
@@ -697,7 +690,7 @@ fun ControlWhenPlayerIsActive(
val view = LocalView.current
// Keeps the screen on while playing and viewing videos.
DisposableEffect(key1 = controller) {
DisposableEffect(key1 = controller, key2 = view) {
val listener = object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
// doesn't consider the mutex because the screen can turn off if the video
@@ -614,7 +614,7 @@ fun ZoomableImageDialog(
) {
val view = LocalView.current
DisposableEffect(key1 = Unit) {
DisposableEffect(key1 = view) {
if (Build.VERSION.SDK_INT >= 30) {
view.windowInsetsController?.hide(
android.view.WindowInsets.Type.systemBars()
@@ -284,7 +284,7 @@ fun AppNavigation(
actionableNextPage = null
}
DisposableEffect(navController) {
DisposableEffect(activity) {
val consumer = Consumer<Intent> { intent ->
val uri = intent?.data?.toString()
val newPage = uriToRoute(uri)
@@ -64,7 +64,6 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS
import com.vitorpamplona.amethyst.model.LiveActivitiesChannel
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
@@ -110,7 +109,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.LongChannelHeader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LongRoomHeader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.RoomNameOnlyDisplay
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShortChannelHeader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShowVideoStreaming
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
@@ -355,11 +353,6 @@ private fun ChannelTopBar(
) {
LoadChannel(baseChannelHex = id, accountViewModel) { baseChannel ->
FlexibleTopBarWithBackButton(
prefixRow = {
if (baseChannel is LiveActivitiesChannel) {
ShowVideoStreaming(baseChannel, accountViewModel)
}
},
title = {
ShortChannelHeader(
baseChannel = baseChannel,
@@ -3101,26 +3101,28 @@ fun DisplayUncitedHashtags(
eventContent: String,
nav: (String) -> Unit
) {
val hasHashtags = remember {
val hasHashtags = remember(eventContent) {
hashtags.isNotEmpty()
}
if (hasHashtags) {
val unusedHashtags = remember(eventContent) {
hashtags.filter { !eventContent.contains(it, true) }
}
FlowRow(
modifier = remember { Modifier.padding(top = 5.dp) }
) {
hashtags.forEach { hashtag ->
if (!eventContent.contains(hashtag, true)) {
ClickableText(
text = AnnotatedString("#$hashtag "),
onClick = { nav("Hashtag/$hashtag") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
alpha = 0.52f
)
unusedHashtags.forEach { hashtag ->
ClickableText(
text = remember { AnnotatedString("#$hashtag ") },
onClick = { nav("Hashtag/$hashtag") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
alpha = 0.52f
)
)
}
)
}
}
}
@@ -33,9 +33,12 @@ class PollNoteViewModel : ViewModel() {
private var pollEvent: PollNoteEvent? = null
private var pollOptions: Map<Int, String>? = null
private var valueMaximum: Int? = null
private var valueMinimum: Int? = null
private var closedAt: Int? = null
private var valueMaximum: Long? = null
private var valueMinimum: Long? = null
private var valueMaximumBD: BigDecimal? = null
private var valueMinimumBD: BigDecimal? = null
private var closedAt: Long? = null
private var consensusThreshold: BigDecimal? = null
private var totalZapped: BigDecimal = BigDecimal.ZERO
@@ -49,10 +52,12 @@ class PollNoteViewModel : ViewModel() {
pollNote = note
pollEvent = pollNote?.event as PollNoteEvent
pollOptions = pollEvent?.pollOptions()
valueMaximum = pollEvent?.getTagInt(VALUE_MAXIMUM)
valueMinimum = pollEvent?.getTagInt(VALUE_MINIMUM)
consensusThreshold = pollEvent?.getTagInt(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
closedAt = pollEvent?.getTagInt(CLOSED_AT)
valueMaximum = pollEvent?.getTagLong(VALUE_MAXIMUM)
valueMinimum = pollEvent?.getTagLong(VALUE_MINIMUM)
valueMinimumBD = valueMinimum?.let { BigDecimal(it) }
valueMaximumBD = valueMaximum?.let { BigDecimal(it) }
consensusThreshold = pollEvent?.getTagLong(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
closedAt = pollEvent?.getTagLong(CLOSED_AT)
}
fun refreshTallies() {
@@ -110,6 +115,29 @@ class PollNoteViewModel : ViewModel() {
} catch (e: Exception) { null }
}
fun isValidInputVoteAmount(amount: BigDecimal?): Boolean {
if (amount == null) {
return false
} else if (valueMinimum == null && valueMaximum == null) {
if (amount > BigDecimal.ZERO) {
return true
}
} else if (valueMinimum == null) {
if (amount > BigDecimal.ZERO && amount <= valueMaximumBD!!) {
return true
}
} else if (valueMaximum == null) {
if (amount >= valueMinimumBD!!) {
return true
}
} else {
if ((valueMinimumBD!! <= amount) && (amount <= valueMaximumBD!!)) {
return true
}
}
return false
}
fun isValidInputVoteAmount(amount: Long?): Boolean {
if (amount == null) {
return false
@@ -145,24 +173,29 @@ class PollNoteViewModel : ViewModel() {
private fun zappedPollOptionAmount(option: Int): BigDecimal {
return pollNote?.zaps?.values?.sumOf {
val event = it?.event as? LnZapEvent
if (event?.zappedPollOption() == option) {
event.amount ?: BigDecimal(0)
val zapAmount = event?.amount ?: BigDecimal.ZERO
val isValidAmount = isValidInputVoteAmount(event?.amount)
if (isValidAmount && event?.zappedPollOption() == option) {
zapAmount
} else {
BigDecimal(0)
BigDecimal.ZERO
}
} ?: BigDecimal(0)
} ?: BigDecimal.ZERO
}
private fun totalZapped(): BigDecimal {
return pollNote?.zaps?.values?.sumOf {
val zapEvent = (it?.event as? LnZapEvent)
val zapAmount = zapEvent?.amount ?: BigDecimal.ZERO
val isValidAmount = isValidInputVoteAmount(zapEvent?.amount)
if (zapEvent?.zappedPollOption() != null) {
zapEvent.amount ?: BigDecimal(0)
if (isValidAmount && zapEvent?.zappedPollOption() != null) {
zapAmount
} else {
BigDecimal(0)
BigDecimal.ZERO
}
} ?: BigDecimal(0)
} ?: BigDecimal.ZERO
}
fun createZapOptionsThatMatchThePollingParameters(): List<Long> {
@@ -176,8 +209,8 @@ class PollNoteViewModel : ViewModel() {
}
}
}
valueMinimum?.let { options.add(it.toLong()) }
valueMaximum?.let { options.add(it.toLong()) }
valueMinimum?.let { options.add(it) }
valueMaximum?.let { options.add(it) }
return options.toSet().sorted()
}
@@ -386,6 +386,19 @@ fun UpdateZapAmountDialog(
Modifier.weight(1f)
)
/* TODO: Find a way to open this in the PWA
IconButton(onClick = {
onClose()
runCatching { uri.openUri("https://app.mutinywallet.com/settings/connections?callbackUri=nostr+walletconnect&name=Amethyst") }
}) {
Icon(
painter = painterResource(R.mipmap.mutiny),
null,
modifier = Modifier.size(24.dp),
tint = Color.Unspecified
)
}*/
IconButton(onClick = {
onClose()
runCatching { uri.openUri("https://nwc.getalby.com/apps/new?c=Amethyst") }
@@ -4,7 +4,6 @@ import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import com.google.zxing.client.android.Intents
import com.journeyapps.barcodescanner.ScanContract
@@ -40,8 +39,6 @@ fun NIP19QrCodeScanner(onScan: (String?) -> Unit) {
@Composable
fun SimpleQrCodeScanner(onScan: (String?) -> Unit) {
val lifecycleOwner = LocalLifecycleOwner.current
val qrLauncher =
rememberLauncherForActivityResult(ScanContract()) {
if (it.contents != null) {
@@ -59,7 +56,7 @@ fun SimpleQrCodeScanner(onScan: (String?) -> Unit) {
addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN)
}
DisposableEffect(lifecycleOwner) {
DisposableEffect(Unit) {
qrLauncher.launch(scanOptions)
onDispose { }
}
@@ -87,6 +87,8 @@ open class LnZapFeedViewModel(val dataSource: FeedFilter<ZapReqResponse>) : View
checkNotInMainThread()
LocalCache.live.newEventBundles.collect { newNotes ->
checkNotInMainThread()
invalidateData()
}
}
@@ -70,16 +70,20 @@ class RelayFeedViewModel : ViewModel() {
}
fun subscribeTo(user: User) {
currentUser = user
user.live().relays.observeForever(listener)
user.live().relayInfo.observeForever(listener)
invalidateData()
if (currentUser != user) {
currentUser = user
user.live().relays.observeForever(listener)
user.live().relayInfo.observeForever(listener)
invalidateData()
}
}
fun unsubscribeTo(user: User) {
user.live().relays.removeObserver(listener)
user.live().relayInfo.removeObserver(listener)
currentUser = null
if (currentUser == user) {
user.live().relays.removeObserver(listener)
user.live().relayInfo.removeObserver(listener)
currentUser = null
}
}
private val bundler = BundledUpdate(250, Dispatchers.IO)
@@ -47,7 +47,7 @@ fun rememberForeverLazyListState(
savedOffset.roundToInt()
)
}
DisposableEffect(Unit) {
DisposableEffect(scrollState) {
onDispose {
val lastIndex = scrollState.firstVisibleItemIndex
val lastOffset = scrollState.firstVisibleItemScrollOffset
@@ -118,6 +118,8 @@ open class UserFeedViewModel(val dataSource: FeedFilter<User>) : ViewModel(), In
checkNotInMainThread()
LocalCache.live.newEventBundles.collect { newNotes ->
checkNotInMainThread()
invalidateData()
}
}
@@ -196,9 +196,6 @@ fun ChannelScreen(
val lifeCycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
NostrChannelDataSource.start()
feedViewModel.invalidateData(true)
launch(Dispatchers.IO) {
newPostModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) {
@@ -209,6 +206,17 @@ fun ChannelScreen(
}
DisposableEffect(accountViewModel) {
NostrChannelDataSource.loadMessagesBetween(accountViewModel.account, channel)
NostrChannelDataSource.start()
feedViewModel.invalidateData(true)
onDispose {
NostrChannelDataSource.clear()
NostrChannelDataSource.stop()
}
}
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Channel Start")
@@ -240,6 +248,9 @@ fun ChannelScreen(
.weight(1f, true)
}
) {
if (channel is LiveActivitiesChannel) {
ShowVideoStreaming(channel, accountViewModel)
}
RefreshingChatroomFeedView(
viewModel = feedViewModel,
accountViewModel = accountViewModel,
@@ -67,7 +67,7 @@ fun ChatroomListScreen(
WatchAccountForListScreen(knownFeedViewModel, newFeedViewModel, accountViewModel)
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrChatroomListDataSource.start()
@@ -234,9 +234,6 @@ fun ChatroomScreen(
LaunchedEffect(room, accountViewModel) {
launch(Dispatchers.IO) {
NostrChatroomDataSource.start()
feedViewModel.invalidateData()
newPostModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) {
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
@@ -246,6 +243,16 @@ fun ChatroomScreen(
}
DisposableEffect(room, accountViewModel) {
NostrChatroomDataSource.loadMessagesBetween(accountViewModel.account, room)
NostrChatroomDataSource.start()
feedViewModel.invalidateData()
onDispose {
NostrChatroomDataSource.stop()
}
}
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Private Message Start")
@@ -56,7 +56,7 @@ fun CommunityScreen(note: AddressableNote, feedViewModel: NostrCommunityFeedView
feedViewModel.invalidateData()
}
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Community Start")
@@ -88,7 +88,7 @@ fun DiscoverScreen(
accountViewModel = accountViewModel
)
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Discovery Start")
@@ -65,12 +65,16 @@ fun GeoHashScreen(tag: String, feedViewModel: NostrGeoHashFeedViewModel, account
NostrGeohashDataSource.loadHashtag(tag)
LaunchedEffect(tag) {
DisposableEffect(tag) {
NostrGeohashDataSource.start()
feedViewModel.invalidateData()
onDispose {
NostrGeohashDataSource.loadHashtag(null)
NostrGeohashDataSource.stop()
}
}
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Hashtag Start")
@@ -88,8 +92,6 @@ fun GeoHashScreen(tag: String, feedViewModel: NostrGeoHashFeedViewModel, account
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
NostrGeohashDataSource.loadHashtag(null)
NostrGeohashDataSource.stop()
}
}
@@ -12,7 +12,6 @@ import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@@ -61,12 +60,17 @@ fun HashtagScreen(tag: String, feedViewModel: NostrHashtagFeedViewModel, account
NostrHashtagDataSource.loadHashtag(tag)
LaunchedEffect(tag) {
DisposableEffect(tag) {
NostrHashtagDataSource.start()
feedViewModel.invalidateData()
onDispose {
NostrHashtagDataSource.loadHashtag(null)
NostrHashtagDataSource.stop()
}
}
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Hashtag Start")
@@ -84,8 +88,6 @@ fun HashtagScreen(tag: String, feedViewModel: NostrHashtagFeedViewModel, account
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
NostrHashtagDataSource.loadHashtag(null)
NostrHashtagDataSource.stop()
}
}
@@ -61,7 +61,7 @@ fun HiddenUsersScreen(
) {
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Hidden Users Start")
@@ -63,7 +63,7 @@ fun HomeScreen(
}
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrHomeDataSource.invalidateFilters()
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
@@ -33,7 +34,10 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavBackStackEntry
import androidx.navigation.compose.currentBackStackEntryAsState
@@ -323,6 +327,20 @@ fun WatchNavStateToUpdateBarVisibility(navState: State<NavBackStackEntry?>, bott
LaunchedEffect(key1 = navState.value) {
bottomBarOffsetHeightPx.value = 0f
}
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
bottomBarOffsetHeightPx.value = 0f
}
}
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
}
}
}
@Composable
@@ -75,7 +75,7 @@ fun NotificationScreen(
CheckifItNeedsToRequestNotificationPermission()
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NostrAccountDataSource.invalidateFilters()
@@ -238,11 +238,15 @@ fun ProfileScreen(
val lifeCycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
DisposableEffect(accountViewModel) {
NostrUserProfileDataSource.start()
onDispose {
NostrUserProfileDataSource.loadUserProfile(null)
NostrUserProfileDataSource.stop()
}
}
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Profidle Start")
@@ -259,9 +263,6 @@ fun ProfileScreen(
lifeCycleOwner.lifecycle.addObserver(observer)
onDispose {
lifeCycleOwner.lifecycle.removeObserver(observer)
println("Profile Dispose")
NostrUserProfileDataSource.loadUserProfile(null)
NostrUserProfileDataSource.stop()
}
}
@@ -954,7 +955,17 @@ private fun DrawAdditionalInfo(
ClickableText(
text = AnnotatedString(website.removePrefix("https://")),
onClick = { website.let { runCatching { uri.openUri(it) } } },
onClick = {
website.let {
runCatching {
if (it.contains("://")) {
uri.openUri(it)
} else {
uri.openUri("http://$it")
}
}
}
},
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary),
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp)
)
@@ -1567,6 +1578,13 @@ fun TabRelays(user: User, accountViewModel: AccountViewModel, nav: (String) -> U
val lifeCycleOwner = LocalLifecycleOwner.current
DisposableEffect(user) {
feedViewModel.subscribeTo(user)
onDispose {
feedViewModel.unsubscribeTo(user)
}
}
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Profile Relay Start")
@@ -104,7 +104,7 @@ fun SearchScreen(
WatchAccountForSearchScreen(accountViewModel)
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Search Start")
@@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
@@ -28,11 +27,15 @@ fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, nav: (Stri
NostrThreadDataSource.loadThread(noteId)
LaunchedEffect(noteId) {
DisposableEffect(noteId) {
feedViewModel.invalidateData(true)
onDispose {
NostrThreadDataSource.loadThread(null)
NostrThreadDataSource.stop()
}
}
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Thread Start")
@@ -89,7 +89,7 @@ fun VideoScreen(
WatchAccountForVideoScreen(videoFeedView = videoFeedView, accountViewModel = accountViewModel)
DisposableEffect(accountViewModel) {
DisposableEffect(lifeCycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
println("Video Start")