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