mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
Migrating Lint to 1.3.1
This commit is contained in:
@@ -75,9 +75,21 @@ class ImageUploadTesting {
|
||||
)
|
||||
|
||||
val url = result.tags!!.first { it[0] == "url" }.get(1)
|
||||
val size = result.tags!!.firstOrNull { it[0] == "size" }?.get(1)?.ifBlank { null }
|
||||
val dim = result.tags!!.firstOrNull { it[0] == "dim" }?.get(1)?.ifBlank { null }
|
||||
val hash = result.tags!!.firstOrNull { it[0] == "x" }?.get(1)?.ifBlank { null }
|
||||
val size =
|
||||
result.tags!!
|
||||
.firstOrNull { it[0] == "size" }
|
||||
?.get(1)
|
||||
?.ifBlank { null }
|
||||
val dim =
|
||||
result.tags!!
|
||||
.firstOrNull { it[0] == "dim" }
|
||||
?.get(1)
|
||||
?.ifBlank { null }
|
||||
val hash =
|
||||
result.tags!!
|
||||
.firstOrNull { it[0] == "x" }
|
||||
?.get(1)
|
||||
?.ifBlank { null }
|
||||
val contentType = result.tags!!.first { it[0] == "m" }.get(1)
|
||||
val ox = result.tags!!.first { it[0] == "ox" }.get(1)
|
||||
|
||||
|
||||
@@ -79,8 +79,7 @@ class ThreadAssemblerTest {
|
||||
{"id":"512962dbada5fd5015fc727a107d5c3f569662de67eab8e5da5a8065012cf11e","pubkey":"7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194","created_at":1688194800,"kind":1,"tags":[["a","30023:6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93:1680612926599","","root"],["e","87a5bd25aa084cefb3357fc9c2a5b327254fab35fdd7b2d4bd0acddc63d0abe8","","reply"]],"content":"another one","sig":"b4bc4d8206a08de0a918043129b27c8863d17f60e4f58b4d2b535326625870d32640b04f247eb9eb5df1d62fe98c8e1b0341bdd119655553488300ec9d5b4036"},
|
||||
{"id":"ce6e32e3e17b6901d2cc70b60f3743e24f885bb6e9da6d88cff516079eac1883","pubkey":"726a1e261cc6474674e8285e3951b3bb139be9a773d1acf49dc868db861a1c11","created_at":1688234921,"kind":1,"tags":[["a","30023:6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93:1680612926599","","root"],["e","512962dbada5fd5015fc727a107d5c3f569662de67eab8e5da5a8065012cf11e","","reply"]],"content":"yet another one, testing 0.0.3","sig":"13bd41c4029c6a7ee41cb03d12e831e9e9b6e14d43a61c78a72657070b45385ba2ce98e8121049fbcbdb7b2dd777c36c7867ba70b5e6a4798a9b866910ae5b62"}
|
||||
]
|
||||
"""
|
||||
.trimIndent()
|
||||
""".trimIndent()
|
||||
|
||||
val header =
|
||||
"""
|
||||
@@ -111,8 +110,7 @@ class ThreadAssemblerTest {
|
||||
["p","b9e76546ba06456ed301d9e52bc49fa48e70a6bf2282be7a1ae72947612023dc"]
|
||||
]
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun threadOrderTest() =
|
||||
|
||||
+3
-8
@@ -55,15 +55,11 @@ object PushDistributorHandler : PushDistributorActions {
|
||||
endpointInternal = ""
|
||||
}
|
||||
|
||||
override fun getSavedDistributor(): String {
|
||||
return unifiedPush.getSavedDistributor(appContext) ?: ""
|
||||
}
|
||||
override fun getSavedDistributor(): String = unifiedPush.getSavedDistributor(appContext) ?: ""
|
||||
|
||||
fun savedDistributorExists(): Boolean = getSavedDistributor().isNotEmpty()
|
||||
|
||||
override fun getInstalledDistributors(): List<String> {
|
||||
return unifiedPush.getDistributors(appContext)
|
||||
}
|
||||
override fun getInstalledDistributors(): List<String> = unifiedPush.getDistributors(appContext)
|
||||
|
||||
fun formattedDistributorNames(): List<String> {
|
||||
val distributorsArray = getInstalledDistributors().toTypedArray()
|
||||
@@ -87,8 +83,7 @@ object PushDistributorHandler : PushDistributorActions {
|
||||
it
|
||||
}
|
||||
as String
|
||||
}
|
||||
.toTypedArray()
|
||||
}.toTypedArray()
|
||||
return distributorsNameArray.toList()
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -126,8 +126,7 @@ class PushMessageReceiver : MessagingReceiver() {
|
||||
pushHandler.removeEndpoint()
|
||||
}
|
||||
|
||||
fun notificationManager(): NotificationManager {
|
||||
return ContextCompat.getSystemService(appContext, NotificationManager::class.java)
|
||||
fun notificationManager(): NotificationManager =
|
||||
ContextCompat.getSystemService(appContext, NotificationManager::class.java)
|
||||
as NotificationManager
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,5 @@ private class PullRefreshNestedScrollConnection(
|
||||
else -> Offset.Zero
|
||||
}
|
||||
|
||||
override suspend fun onPreFling(available: Velocity): Velocity {
|
||||
return Velocity(0f, onRelease(available.y))
|
||||
}
|
||||
override suspend fun onPreFling(available: Velocity): Velocity = Velocity(0f, onRelease(available.y))
|
||||
}
|
||||
|
||||
+1
-2
@@ -63,8 +63,7 @@ fun Modifier.pullRefreshIndicatorTransform(
|
||||
) {
|
||||
this@drawWithContent.drawContent()
|
||||
}
|
||||
}
|
||||
.graphicsLayer {
|
||||
}.graphicsLayer {
|
||||
translationY = state.position - size.height
|
||||
|
||||
if (scale && !state.refreshing) {
|
||||
|
||||
@@ -61,7 +61,8 @@ class Amethyst : Application() {
|
||||
}
|
||||
|
||||
val coilCache: DiskCache by lazy {
|
||||
DiskCache.Builder()
|
||||
DiskCache
|
||||
.Builder()
|
||||
.directory(applicationContext.safeCacheDir.resolve("image_cache"))
|
||||
.maxSizePercent(0.2)
|
||||
.maximumMaxSizeBytes(500L * 1024 * 1024) // 250MB
|
||||
@@ -77,10 +78,18 @@ class Amethyst : Application() {
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
StrictMode.setThreadPolicy(
|
||||
ThreadPolicy.Builder().detectAll().penaltyLog().build(),
|
||||
ThreadPolicy
|
||||
.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build(),
|
||||
)
|
||||
StrictMode.setVmPolicy(
|
||||
VmPolicy.Builder().detectAll().penaltyLog().build(),
|
||||
VmPolicy
|
||||
.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,13 +103,9 @@ class Amethyst : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
fun imageLoaderBuilder(): ImageLoader.Builder {
|
||||
return ImageLoader.Builder(applicationContext).diskCache { coilCache }
|
||||
}
|
||||
fun imageLoaderBuilder(): ImageLoader.Builder = ImageLoader.Builder(applicationContext).diskCache { coilCache }
|
||||
|
||||
fun encryptedStorage(npub: String? = null): EncryptedSharedPreferences {
|
||||
return EncryptedStorage.preferences(instance, npub)
|
||||
}
|
||||
fun encryptedStorage(npub: String? = null): EncryptedSharedPreferences = EncryptedStorage.preferences(instance, npub)
|
||||
|
||||
companion object {
|
||||
lateinit var instance: Amethyst
|
||||
|
||||
@@ -29,16 +29,15 @@ class EncryptedStorage {
|
||||
private const val PREFERENCES_NAME = "secret_keeper"
|
||||
|
||||
// returns the preferences for each account or a global file if null.
|
||||
fun prefsFileName(npub: String? = null): String {
|
||||
return if (npub == null) PREFERENCES_NAME else "${PREFERENCES_NAME}_$npub"
|
||||
}
|
||||
fun prefsFileName(npub: String? = null): String = if (npub == null) PREFERENCES_NAME else "${PREFERENCES_NAME}_$npub"
|
||||
|
||||
fun preferences(
|
||||
applicationContext: Context,
|
||||
npub: String? = null,
|
||||
): EncryptedSharedPreferences {
|
||||
val masterKey: MasterKey =
|
||||
MasterKey.Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
|
||||
MasterKey
|
||||
.Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
|
||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
.build()
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@ import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
data class Spammer(val pubkeyHex: HexKey, var duplicatedMessages: Set<HexKey>)
|
||||
data class Spammer(
|
||||
val pubkeyHex: HexKey,
|
||||
var duplicatedMessages: Set<HexKey>,
|
||||
)
|
||||
|
||||
class AntiSpamFilter {
|
||||
val recentMessages = LruCache<Int, String>(1000)
|
||||
@@ -109,7 +112,9 @@ class AntiSpamFilter {
|
||||
}
|
||||
|
||||
@Stable
|
||||
class AntiSpamLiveData(val cache: AntiSpamFilter) : LiveData<AntiSpamState>(AntiSpamState(cache)) {
|
||||
class AntiSpamLiveData(
|
||||
val cache: AntiSpamFilter,
|
||||
) : LiveData<AntiSpamState>(AntiSpamState(cache)) {
|
||||
// Refreshes observers in batches.
|
||||
private val bundler = BundledUpdate(300, Dispatchers.IO)
|
||||
|
||||
@@ -126,4 +131,6 @@ class AntiSpamLiveData(val cache: AntiSpamFilter) : LiveData<AntiSpamState>(Anti
|
||||
}
|
||||
}
|
||||
|
||||
class AntiSpamState(val cache: AntiSpamFilter)
|
||||
class AntiSpamState(
|
||||
val cache: AntiSpamFilter,
|
||||
)
|
||||
|
||||
@@ -37,7 +37,9 @@ import com.vitorpamplona.quartz.events.LiveActivitiesEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
@Stable
|
||||
class PublicChatChannel(idHex: String) : Channel(idHex) {
|
||||
class PublicChatChannel(
|
||||
idHex: String,
|
||||
) : Channel(idHex) {
|
||||
var info = ChannelCreateEvent.ChannelData(null, null, null)
|
||||
|
||||
fun updateChannelInfo(
|
||||
@@ -49,26 +51,22 @@ class PublicChatChannel(idHex: String) : Channel(idHex) {
|
||||
super.updateChannelInfo(creator, updatedAt)
|
||||
}
|
||||
|
||||
override fun toBestDisplayName(): String {
|
||||
return info.name ?: super.toBestDisplayName()
|
||||
}
|
||||
override fun toBestDisplayName(): String = info.name ?: super.toBestDisplayName()
|
||||
|
||||
override fun summary(): String? {
|
||||
return info.about
|
||||
}
|
||||
override fun summary(): String? = info.about
|
||||
|
||||
override fun profilePicture(): String? {
|
||||
if (info.picture.isNullOrBlank()) return super.profilePicture()
|
||||
return info.picture ?: super.profilePicture()
|
||||
}
|
||||
|
||||
override fun anyNameStartsWith(prefix: String): Boolean {
|
||||
return listOfNotNull(info.name, info.about).filter { it.contains(prefix, true) }.isNotEmpty()
|
||||
}
|
||||
override fun anyNameStartsWith(prefix: String): Boolean = listOfNotNull(info.name, info.about).filter { it.contains(prefix, true) }.isNotEmpty()
|
||||
}
|
||||
|
||||
@Stable
|
||||
class LiveActivitiesChannel(val address: ATag) : Channel(address.toTag()) {
|
||||
class LiveActivitiesChannel(
|
||||
val address: ATag,
|
||||
) : Channel(address.toTag()) {
|
||||
var info: LiveActivitiesEvent? = null
|
||||
|
||||
override fun idNote() = address.toNAddr()
|
||||
@@ -86,27 +84,22 @@ class LiveActivitiesChannel(val address: ATag) : Channel(address.toTag()) {
|
||||
super.updateChannelInfo(creator, updatedAt)
|
||||
}
|
||||
|
||||
override fun toBestDisplayName(): String {
|
||||
return info?.title() ?: super.toBestDisplayName()
|
||||
}
|
||||
override fun toBestDisplayName(): String = info?.title() ?: super.toBestDisplayName()
|
||||
|
||||
override fun summary(): String? {
|
||||
return info?.summary()
|
||||
}
|
||||
override fun summary(): String? = info?.summary()
|
||||
|
||||
override fun profilePicture(): String? {
|
||||
return info?.image()?.ifBlank { null }
|
||||
}
|
||||
override fun profilePicture(): String? = info?.image()?.ifBlank { null }
|
||||
|
||||
override fun anyNameStartsWith(prefix: String): Boolean {
|
||||
return listOfNotNull(info?.title(), info?.summary())
|
||||
override fun anyNameStartsWith(prefix: String): Boolean =
|
||||
listOfNotNull(info?.title(), info?.summary())
|
||||
.filter { it.contains(prefix, true) }
|
||||
.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
abstract class Channel(val idHex: String) {
|
||||
abstract class Channel(
|
||||
val idHex: String,
|
||||
) {
|
||||
var creator: User? = null
|
||||
var updatedMetadataAt: Long = 0
|
||||
val notes = LargeCache<HexKey, Note>()
|
||||
@@ -118,21 +111,13 @@ abstract class Channel(val idHex: String) {
|
||||
|
||||
open fun idDisplayNote() = idNote().toShortenHex()
|
||||
|
||||
open fun toBestDisplayName(): String {
|
||||
return idDisplayNote()
|
||||
}
|
||||
open fun toBestDisplayName(): String = idDisplayNote()
|
||||
|
||||
open fun summary(): String? {
|
||||
return null
|
||||
}
|
||||
open fun summary(): String? = null
|
||||
|
||||
open fun creatorName(): String? {
|
||||
return creator?.toBestDisplayName()
|
||||
}
|
||||
open fun creatorName(): String? = creator?.toBestDisplayName()
|
||||
|
||||
open fun profilePicture(): String? {
|
||||
return creator?.info?.banner
|
||||
}
|
||||
open fun profilePicture(): String? = creator?.info?.banner
|
||||
|
||||
open fun updateChannelInfo(
|
||||
creator: User,
|
||||
@@ -167,10 +152,10 @@ abstract class Channel(val idHex: String) {
|
||||
|
||||
fun pruneOldAndHiddenMessages(account: Account): Set<Note> {
|
||||
val important =
|
||||
notes.filter { key, it ->
|
||||
it.author?.let { author -> account.isHidden(author) } == false
|
||||
}
|
||||
.sortedWith(DefaultFeedOrder)
|
||||
notes
|
||||
.filter { key, it ->
|
||||
it.author?.let { author -> account.isHidden(author) } == false
|
||||
}.sortedWith(DefaultFeedOrder)
|
||||
.take(500)
|
||||
.toSet()
|
||||
|
||||
@@ -182,7 +167,9 @@ abstract class Channel(val idHex: String) {
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelLiveData(val channel: Channel) : LiveData<ChannelState>(ChannelState(channel)) {
|
||||
class ChannelLiveData(
|
||||
val channel: Channel,
|
||||
) : LiveData<ChannelState>(ChannelState(channel)) {
|
||||
// Refreshes observers in batches.
|
||||
private val bundler = BundledUpdate(300, Dispatchers.IO)
|
||||
|
||||
@@ -208,4 +195,6 @@ class ChannelLiveData(val channel: Channel) : LiveData<ChannelState>(ChannelStat
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelState(val channel: Channel)
|
||||
class ChannelState(
|
||||
val channel: Channel,
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Stable
|
||||
class Chatroom() {
|
||||
class Chatroom {
|
||||
var authors: Set<User> = setOf()
|
||||
var roomMessages: Set<Note> = setOf()
|
||||
var subject: String? = null
|
||||
@@ -72,9 +72,7 @@ class Chatroom() {
|
||||
}
|
||||
}
|
||||
|
||||
fun senderIntersects(keySet: Set<HexKey>): Boolean {
|
||||
return authors.any { it.pubkeyHex in keySet }
|
||||
}
|
||||
fun senderIntersects(keySet: Set<HexKey>): Boolean = authors.any { it.pubkeyHex in keySet }
|
||||
|
||||
fun pruneMessagesToTheLatestOnly(): Set<Note> {
|
||||
val sorted = roomMessages.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
||||
|
||||
@@ -68,8 +68,8 @@ fun RenderHashTagIcons() {
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForHashtagWithIcon(tag: String): HashtagIcon? {
|
||||
return when (tag.lowercase()) {
|
||||
fun checkForHashtagWithIcon(tag: String): HashtagIcon? =
|
||||
when (tag.lowercase()) {
|
||||
"₿itcoin", "bitcoin", "btc", "timechain", "bitcoiner", "bitcoiners" -> bitcoin
|
||||
"nostr", "nostrich", "nostriches", "thenostr" -> nostr
|
||||
"lightning", "lightningnetwork" -> lightning
|
||||
@@ -86,7 +86,6 @@ fun checkForHashtagWithIcon(tag: String): HashtagIcon? {
|
||||
"weed", "weedstr", "420", "cannabis", "marijuana" -> weed
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val bitcoin = HashtagIcon(CustomHashTagIcons.Btc, "Bitcoin", Modifier.padding(start = 1.dp, bottom = 1.dp, top = 1.dp))
|
||||
val nostr = HashtagIcon(CustomHashTagIcons.Nostr, "Nostr", Modifier.padding(start = 1.dp, bottom = 1.dp, top = 1.dp))
|
||||
|
||||
@@ -66,9 +66,7 @@ class HumanName(
|
||||
var family: String? = null,
|
||||
var given: ArrayList<String> = arrayListOf(),
|
||||
) {
|
||||
fun assembleName(): String {
|
||||
return given.joinToString(" ") + " " + family
|
||||
}
|
||||
fun assembleName(): String = given.joinToString(" ") + " " + family
|
||||
}
|
||||
|
||||
class Bundle(
|
||||
|
||||
@@ -62,8 +62,7 @@ class ThreadAssembler {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
?.filterNotNull()
|
||||
}?.filterNotNull()
|
||||
|
||||
if (roots != null && roots.isNotEmpty()) {
|
||||
return roots[0]
|
||||
@@ -148,9 +147,7 @@ class OnlyLatestVersionSet : MutableSet<Note> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<Note>): Boolean {
|
||||
return elements.map { add(it) }.any()
|
||||
}
|
||||
override fun addAll(elements: Collection<Note>): Boolean = elements.map { add(it) }.any()
|
||||
|
||||
override val size: Int
|
||||
get() = set.size
|
||||
@@ -160,29 +157,17 @@ class OnlyLatestVersionSet : MutableSet<Note> {
|
||||
map.clear()
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return set.isEmpty()
|
||||
}
|
||||
override fun isEmpty(): Boolean = set.isEmpty()
|
||||
|
||||
override fun containsAll(elements: Collection<Note>): Boolean {
|
||||
return set.containsAll(elements)
|
||||
}
|
||||
override fun containsAll(elements: Collection<Note>): Boolean = set.containsAll(elements)
|
||||
|
||||
override fun contains(element: Note): Boolean {
|
||||
return set.contains(element)
|
||||
}
|
||||
override fun contains(element: Note): Boolean = set.contains(element)
|
||||
|
||||
override fun iterator(): MutableIterator<Note> {
|
||||
return set.iterator()
|
||||
}
|
||||
override fun iterator(): MutableIterator<Note> = set.iterator()
|
||||
|
||||
override fun retainAll(elements: Collection<Note>): Boolean {
|
||||
return set.retainAll(elements)
|
||||
}
|
||||
override fun retainAll(elements: Collection<Note>): Boolean = set.retainAll(elements)
|
||||
|
||||
override fun removeAll(elements: Collection<Note>): Boolean {
|
||||
return elements.map { remove(it) }.any()
|
||||
}
|
||||
override fun removeAll(elements: Collection<Note>): Boolean = elements.map { remove(it) }.any()
|
||||
|
||||
override fun remove(element: Note): Boolean {
|
||||
element.address()?.let {
|
||||
|
||||
@@ -27,16 +27,20 @@ import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
data class LevelSignature(val signature: String, val createdAt: Long?, val author: User?)
|
||||
data class LevelSignature(
|
||||
val signature: String,
|
||||
val createdAt: Long?,
|
||||
val author: User?,
|
||||
)
|
||||
|
||||
object ThreadLevelCalculator {
|
||||
val levelFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd-HH:mm:ss")
|
||||
|
||||
private fun formattedDateTime(timestamp: Long): String {
|
||||
return Instant.ofEpochSecond(timestamp)
|
||||
private fun formattedDateTime(timestamp: Long): String =
|
||||
Instant
|
||||
.ofEpochSecond(timestamp)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.format(levelFormatter)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method caches signatures during each execution to avoid recalculation in longer threads
|
||||
@@ -76,8 +80,7 @@ object ThreadLevelCalculator {
|
||||
accountFollowingSet,
|
||||
now,
|
||||
).apply { cachedSignatures.put(it, this) }
|
||||
}
|
||||
.maxByOrNull { it.signature.length }
|
||||
}.maxByOrNull { it.signature.length }
|
||||
)
|
||||
|
||||
val parentSignature = parent?.signature?.removeSuffix(";") ?: ""
|
||||
|
||||
@@ -74,7 +74,6 @@ object UrlCachedPreviewer {
|
||||
onReady(state)
|
||||
}
|
||||
},
|
||||
)
|
||||
.fetchUrlPreview()
|
||||
).fetchUrlPreview()
|
||||
}
|
||||
}
|
||||
|
||||
+23
-20
@@ -27,7 +27,10 @@ import com.vitorpamplona.quartz.events.Event
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class LatestByKindAndAuthor<T : Event>(private val kind: Int, private val pubkey: String) {
|
||||
class LatestByKindAndAuthor<T : Event>(
|
||||
private val kind: Int,
|
||||
private val pubkey: String,
|
||||
) {
|
||||
private val _latest = MutableStateFlow<T?>(null)
|
||||
val latest = _latest.asStateFlow()
|
||||
|
||||
@@ -41,30 +44,30 @@ class LatestByKindAndAuthor<T : Event>(private val kind: Int, private val pubkey
|
||||
}
|
||||
}
|
||||
|
||||
fun canDelete(): Boolean {
|
||||
return _latest.subscriptionCount.value == 0
|
||||
}
|
||||
fun canDelete(): Boolean = _latest.subscriptionCount.value == 0
|
||||
|
||||
suspend fun init() {
|
||||
val latestNote =
|
||||
if ((kind in 10000..19999) || (kind in 30000..39999)) {
|
||||
LocalCache.addressables.maxOrNullOf(
|
||||
filter = { idHex: String, note: AddressableNote ->
|
||||
note.event?.let {
|
||||
it.kind() == kind && it.pubKey() == pubkey
|
||||
} == true
|
||||
},
|
||||
comparator = CreatedAtComparatorAddresses,
|
||||
)?.event as? T
|
||||
LocalCache.addressables
|
||||
.maxOrNullOf(
|
||||
filter = { idHex: String, note: AddressableNote ->
|
||||
note.event?.let {
|
||||
it.kind() == kind && it.pubKey() == pubkey
|
||||
} == true
|
||||
},
|
||||
comparator = CreatedAtComparatorAddresses,
|
||||
)?.event as? T
|
||||
} else {
|
||||
LocalCache.notes.maxOrNullOf(
|
||||
filter = { idHex: String, note: Note ->
|
||||
note.event?.let {
|
||||
it.kind() == kind && it.pubKey() == pubkey
|
||||
} == true
|
||||
},
|
||||
comparator = CreatedAtComparator,
|
||||
)?.event as? T
|
||||
LocalCache.notes
|
||||
.maxOrNullOf(
|
||||
filter = { idHex: String, note: Note ->
|
||||
note.event?.let {
|
||||
it.kind() == kind && it.pubKey() == pubkey
|
||||
} == true
|
||||
},
|
||||
comparator = CreatedAtComparator,
|
||||
)?.event as? T
|
||||
}
|
||||
|
||||
_latest.tryEmit(latestNote)
|
||||
|
||||
+14
-12
@@ -26,7 +26,10 @@ import com.vitorpamplona.quartz.events.Event
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class LatestByKindWithETag<T : Event>(private val kind: Int, private val eTag: String) {
|
||||
class LatestByKindWithETag<T : Event>(
|
||||
private val kind: Int,
|
||||
private val eTag: String,
|
||||
) {
|
||||
private val _latest = MutableStateFlow<T?>(null)
|
||||
val latest = _latest.asStateFlow()
|
||||
|
||||
@@ -40,20 +43,19 @@ class LatestByKindWithETag<T : Event>(private val kind: Int, private val eTag: S
|
||||
}
|
||||
}
|
||||
|
||||
fun canDelete(): Boolean {
|
||||
return _latest.subscriptionCount.value == 0
|
||||
}
|
||||
fun canDelete(): Boolean = _latest.subscriptionCount.value == 0
|
||||
|
||||
suspend fun init() {
|
||||
val latestNote =
|
||||
LocalCache.notes.maxOrNullOf(
|
||||
filter = { idHex: String, note: Note ->
|
||||
note.event?.let {
|
||||
it.kind() == kind && it.isTaggedEvent(eTag)
|
||||
} == true
|
||||
},
|
||||
comparator = CreatedAtComparator,
|
||||
)?.event as? T
|
||||
LocalCache.notes
|
||||
.maxOrNullOf(
|
||||
filter = { idHex: String, note: Note ->
|
||||
note.event?.let {
|
||||
it.kind() == kind && it.isTaggedEvent(eTag)
|
||||
} == true
|
||||
},
|
||||
comparator = CreatedAtComparator,
|
||||
)?.event as? T
|
||||
|
||||
_latest.tryEmit(latestNote)
|
||||
}
|
||||
|
||||
+3
-1
@@ -26,7 +26,9 @@ import com.vitorpamplona.ammolite.relays.Relay
|
||||
import com.vitorpamplona.quartz.events.AddressableEvent
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
|
||||
abstract class AmethystNostrDataSource(debugName: String) : NostrDataSource(debugName) {
|
||||
abstract class AmethystNostrDataSource(
|
||||
debugName: String,
|
||||
) : NostrDataSource(debugName) {
|
||||
override fun consume(
|
||||
event: Event,
|
||||
relay: Relay,
|
||||
|
||||
@@ -59,9 +59,7 @@ class BlurHashFetcher(
|
||||
data: Uri,
|
||||
options: Options,
|
||||
imageLoader: ImageLoader,
|
||||
): Fetcher {
|
||||
return BlurHashFetcher(options, data)
|
||||
}
|
||||
): Fetcher = BlurHashFetcher(options, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +70,8 @@ object BlurHashRequester {
|
||||
): ImageRequest {
|
||||
val encodedMessage = URLEncoder.encode(message, "utf-8")
|
||||
|
||||
return ImageRequest.Builder(context)
|
||||
return ImageRequest
|
||||
.Builder(context)
|
||||
.data("bluehash:$encodedMessage")
|
||||
.fetcherFactory(BlurHashFetcher.Factory)
|
||||
.build()
|
||||
|
||||
@@ -22,9 +22,7 @@ package com.vitorpamplona.amethyst.service
|
||||
|
||||
import com.vitorpamplona.quartz.events.ImmutableListOfLists
|
||||
|
||||
fun String.isUTF16Char(pos: Int): Boolean {
|
||||
return Character.charCount(this.codePointAt(pos)) == 2
|
||||
}
|
||||
fun String.isUTF16Char(pos: Int): Boolean = Character.charCount(this.codePointAt(pos)) == 2
|
||||
|
||||
fun String.firstFullCharOld(): String {
|
||||
return when (this.length) {
|
||||
|
||||
@@ -236,10 +236,10 @@ fun MediaMetadataRetriever.prepareVideoHeight(): Int? {
|
||||
}
|
||||
}
|
||||
|
||||
class ByteArrayMediaDataSource(var imageData: ByteArray) : MediaDataSource() {
|
||||
override fun getSize(): Long {
|
||||
return imageData.size.toLong()
|
||||
}
|
||||
class ByteArrayMediaDataSource(
|
||||
var imageData: ByteArray,
|
||||
) : MediaDataSource() {
|
||||
override fun getSize(): Long = imageData.size.toLong()
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun readAt(
|
||||
|
||||
@@ -33,7 +33,9 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class LocationUtil(context: Context) {
|
||||
class LocationUtil(
|
||||
context: Context,
|
||||
) {
|
||||
companion object {
|
||||
const val MIN_TIME: Long = 1000L
|
||||
const val MIN_DISTANCE: Float = 0.0f
|
||||
@@ -96,9 +98,7 @@ class LocationUtil(context: Context) {
|
||||
object CachedGeoLocations {
|
||||
val locationNames = LruCache<String, String>(20)
|
||||
|
||||
fun cached(geoHashStr: String): String? {
|
||||
return locationNames[geoHashStr]
|
||||
}
|
||||
fun cached(geoHashStr: String): String? = locationNames[geoHashStr]
|
||||
|
||||
suspend fun geoLocate(
|
||||
geoHashStr: String,
|
||||
|
||||
@@ -31,4 +31,6 @@ fun checkNotInMainThread() {
|
||||
|
||||
fun isMainThread() = Looper.myLooper() == Looper.getMainLooper()
|
||||
|
||||
class OnMainThreadException(str: String) : RuntimeException(str)
|
||||
class OnMainThreadException(
|
||||
str: String,
|
||||
) : RuntimeException(str)
|
||||
|
||||
@@ -38,7 +38,10 @@ object Nip96MediaServers {
|
||||
ServerName("Void.cat", "https://void.cat"),
|
||||
)
|
||||
|
||||
data class ServerName(val name: String, val baseUrl: String)
|
||||
data class ServerName(
|
||||
val name: String,
|
||||
val baseUrl: String,
|
||||
)
|
||||
|
||||
val cache: MutableMap<String, Nip96Retriever.ServerInfo> = mutableMapOf()
|
||||
|
||||
@@ -83,7 +86,8 @@ class Nip96Retriever {
|
||||
checkNotInMainThread()
|
||||
|
||||
val request: Request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("Accept", "application/nostr+json")
|
||||
.url(baseUrl.removeSuffix("/") + "/.well-known/nostr/nip96.json")
|
||||
.build()
|
||||
|
||||
@@ -32,7 +32,10 @@ import okhttp3.Request
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@Immutable data class OnlineCheckResult(val timeInMs: Long, val online: Boolean)
|
||||
@Immutable data class OnlineCheckResult(
|
||||
val timeInMs: Long,
|
||||
val online: Boolean,
|
||||
)
|
||||
|
||||
object OnlineChecker {
|
||||
val checkOnlineCache = LruCache<String, OnlineCheckResult>(100)
|
||||
@@ -58,7 +61,8 @@ object OnlineChecker {
|
||||
val result =
|
||||
if (url.startsWith("wss")) {
|
||||
val request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.url(url.replace("wss+livekit://", "wss://"))
|
||||
.header("Upgrade", "websocket")
|
||||
@@ -69,7 +73,9 @@ object OnlineChecker {
|
||||
.build()
|
||||
|
||||
val client =
|
||||
HttpClientManager.getHttpClient().newBuilder()
|
||||
HttpClientManager
|
||||
.getHttpClient()
|
||||
.newBuilder()
|
||||
.eventListener(EventListener.NONE)
|
||||
.protocols(listOf(Protocol.HTTP_1_1))
|
||||
.build()
|
||||
@@ -80,7 +86,8 @@ object OnlineChecker {
|
||||
}
|
||||
} else {
|
||||
val request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.url(url)
|
||||
.get()
|
||||
|
||||
@@ -28,15 +28,12 @@ object PackageUtils {
|
||||
private fun isPackageInstalled(
|
||||
context: Context,
|
||||
target: String,
|
||||
): Boolean {
|
||||
return context.packageManager.getInstalledApplications(0).find { info ->
|
||||
): Boolean =
|
||||
context.packageManager.getInstalledApplications(0).find { info ->
|
||||
info.packageName == target
|
||||
} != null
|
||||
}
|
||||
|
||||
fun isOrbotInstalled(context: Context): Boolean {
|
||||
return isPackageInstalled(context, "org.torproject.android")
|
||||
}
|
||||
fun isOrbotInstalled(context: Context): Boolean = isPackageInstalled(context, "org.torproject.android")
|
||||
|
||||
fun isExternalSignerInstalled(context: Context): Boolean {
|
||||
val intent =
|
||||
|
||||
@@ -27,14 +27,15 @@ import kotlinx.coroutines.CancellationException
|
||||
import java.text.NumberFormat
|
||||
|
||||
@Stable
|
||||
data class InvoiceAmount(val invoice: String, val amount: String?)
|
||||
data class InvoiceAmount(
|
||||
val invoice: String,
|
||||
val amount: String?,
|
||||
)
|
||||
|
||||
object CachedLnInvoiceParser {
|
||||
val lnInvoicesCache = LruCache<String, InvoiceAmount>(20)
|
||||
|
||||
fun cached(lnurl: String): InvoiceAmount? {
|
||||
return lnInvoicesCache[lnurl]
|
||||
}
|
||||
fun cached(lnurl: String): InvoiceAmount? = lnInvoicesCache[lnurl]
|
||||
|
||||
fun parse(lnbcWord: String): InvoiceAmount? {
|
||||
val myInvoice = LnInvoiceUtil.findInvoice(lnbcWord)
|
||||
|
||||
+4
-2
@@ -42,7 +42,8 @@ class OkHttpBlockstreamExplorer : BitcoinExplorer {
|
||||
val url = "$BLOCKSTREAM_API_URL/block/$hash"
|
||||
|
||||
val request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.header("Accept", "application/json")
|
||||
.url(url)
|
||||
@@ -79,7 +80,8 @@ class OkHttpBlockstreamExplorer : BitcoinExplorer {
|
||||
val url = "$BLOCKSTREAM_API_URL/block-height/$height"
|
||||
|
||||
val request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.url(url)
|
||||
.get()
|
||||
|
||||
+6
-2
@@ -33,7 +33,10 @@ import java.util.concurrent.BlockingQueue
|
||||
/**
|
||||
* For making async calls to a calendar server
|
||||
*/
|
||||
class OkHttpCalendarAsyncSubmit(private val url: String, private val digest: ByteArray) : ICalendarAsyncSubmit {
|
||||
class OkHttpCalendarAsyncSubmit(
|
||||
private val url: String,
|
||||
private val digest: ByteArray,
|
||||
) : ICalendarAsyncSubmit {
|
||||
private var queue: BlockingQueue<Optional<Timestamp>>? = null
|
||||
|
||||
fun setQueue(queue: BlockingQueue<Optional<Timestamp>>?) {
|
||||
@@ -49,7 +52,8 @@ class OkHttpCalendarAsyncSubmit(private val url: String, private val digest: Byt
|
||||
val requestBody = digest.toRequestBody(mediaType)
|
||||
|
||||
val request =
|
||||
okhttp3.Request.Builder()
|
||||
okhttp3.Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.header("Accept", "application/vnd.opentimestamps.v1")
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
+2
-6
@@ -25,14 +25,10 @@ import com.vitorpamplona.quartz.ots.ICalendar
|
||||
import com.vitorpamplona.quartz.ots.ICalendarAsyncSubmit
|
||||
|
||||
class OkHttpCalendarBuilder : CalendarBuilder {
|
||||
override fun newSyncCalendar(url: String): ICalendar {
|
||||
return OkHttpCalendar(url)
|
||||
}
|
||||
override fun newSyncCalendar(url: String): ICalendar = OkHttpCalendar(url)
|
||||
|
||||
override fun newAsyncCalendar(
|
||||
url: String,
|
||||
digest: ByteArray,
|
||||
): ICalendarAsyncSubmit {
|
||||
return OkHttpCalendarAsyncSubmit(url, digest)
|
||||
}
|
||||
): ICalendarAsyncSubmit = OkHttpCalendarAsyncSubmit(url, digest)
|
||||
}
|
||||
|
||||
@@ -59,12 +59,12 @@ class VideoCache {
|
||||
// This method should be called when proxy setting changes.
|
||||
fun renewCacheFactory(client: OkHttpClient) {
|
||||
cacheDataSourceFactory =
|
||||
CacheDataSource.Factory()
|
||||
CacheDataSource
|
||||
.Factory()
|
||||
.setCache(simpleCache)
|
||||
.setUpstreamDataSourceFactory(
|
||||
OkHttpDataSource.Factory(client),
|
||||
)
|
||||
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
|
||||
).setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
|
||||
}
|
||||
|
||||
fun get(client: OkHttpClient): CacheDataSource.Factory {
|
||||
|
||||
+1
-3
@@ -32,7 +32,5 @@ class VideoViewedPositionCache {
|
||||
cachedPosition.put(uri, position)
|
||||
}
|
||||
|
||||
fun get(uri: String): Long? {
|
||||
return cachedPosition.get(uri)
|
||||
}
|
||||
fun get(uri: String): Long? = cachedPosition.get(uri)
|
||||
}
|
||||
|
||||
+2
-6
@@ -44,11 +44,7 @@ class WssDataStreamCollector : WebSocketListener() {
|
||||
wssData.removeAll(wssData)
|
||||
}
|
||||
|
||||
fun canStream(): Boolean {
|
||||
return wssData.size > 0
|
||||
}
|
||||
fun canStream(): Boolean = wssData.size > 0
|
||||
|
||||
fun getNextStream(): ByteString {
|
||||
return wssData.pollFirst()
|
||||
}
|
||||
fun getNextStream(): ByteString = wssData.pollFirst()
|
||||
}
|
||||
|
||||
+14
-7
@@ -32,7 +32,9 @@ import okhttp3.WebSocket
|
||||
import kotlin.math.min
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
class WssStreamDataSource(val httpClient: OkHttpClient) : BaseDataSource(true) {
|
||||
class WssStreamDataSource(
|
||||
val httpClient: OkHttpClient,
|
||||
) : BaseDataSource(true) {
|
||||
val dataStreamCollector: WssDataStreamCollector = WssDataStreamCollector()
|
||||
var webSocketClient: WebSocket? = null
|
||||
|
||||
@@ -46,11 +48,14 @@ class WssStreamDataSource(val httpClient: OkHttpClient) : BaseDataSource(true) {
|
||||
// which collects the data for us (Previous class).
|
||||
webSocketClient =
|
||||
httpClient.newWebSocket(
|
||||
Request.Builder().apply {
|
||||
dataSpec.httpRequestHeaders.forEach { entry ->
|
||||
addHeader(entry.key, entry.value)
|
||||
}
|
||||
}.url(dataSpec.uri.toString()).build(),
|
||||
Request
|
||||
.Builder()
|
||||
.apply {
|
||||
dataSpec.httpRequestHeaders.forEach { entry ->
|
||||
addHeader(entry.key, entry.value)
|
||||
}
|
||||
}.url(dataSpec.uri.toString())
|
||||
.build(),
|
||||
dataStreamCollector,
|
||||
)
|
||||
|
||||
@@ -106,7 +111,9 @@ class WssStreamDataSource(val httpClient: OkHttpClient) : BaseDataSource(true) {
|
||||
}
|
||||
|
||||
// Factory class for DataSource
|
||||
class Factory(val okHttpClient: OkHttpClient) : DataSource.Factory {
|
||||
class Factory(
|
||||
val okHttpClient: OkHttpClient,
|
||||
) : DataSource.Factory {
|
||||
override fun createDataSource(): DataSource = WssStreamDataSource(okHttpClient)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -22,7 +22,10 @@ package com.vitorpamplona.amethyst.service.previews
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) {
|
||||
class BahaUrlPreview(
|
||||
val url: String,
|
||||
var callback: IUrlPreviewCallback?,
|
||||
) {
|
||||
suspend fun fetchUrlPreview(timeOut: Int = 30000) =
|
||||
try {
|
||||
fetch(timeOut)
|
||||
|
||||
@@ -40,11 +40,7 @@ class UrlInfoItem(
|
||||
image
|
||||
}
|
||||
|
||||
fun fetchComplete(): Boolean {
|
||||
return url.isNotEmpty() && image.isNotEmpty()
|
||||
}
|
||||
fun fetchComplete(): Boolean = url.isNotEmpty() && image.isNotEmpty()
|
||||
|
||||
fun allFetchComplete(): Boolean {
|
||||
return title.isNotEmpty() && description.isNotEmpty() && image.isNotEmpty()
|
||||
}
|
||||
fun allFetchComplete(): Boolean = title.isNotEmpty() && description.isNotEmpty() && image.isNotEmpty()
|
||||
}
|
||||
|
||||
+10
-5
@@ -71,7 +71,12 @@ suspend fun getDocument(
|
||||
timeOut: Int = 30000,
|
||||
): UrlInfoItem =
|
||||
withContext(Dispatchers.IO) {
|
||||
val request: Request = Request.Builder().url(url).get().build()
|
||||
val request: Request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build()
|
||||
HttpClientManager.getHttpClient().newCall(request).execute().use {
|
||||
checkNotInMainThread()
|
||||
if (it.isSuccessful) {
|
||||
@@ -132,8 +137,8 @@ private val UNICODE_BOMS =
|
||||
"ffff0000".decodeHex(),
|
||||
)
|
||||
|
||||
private fun BufferedSource.readBomAsCharset(): Charset? {
|
||||
return when (select(UNICODE_BOMS)) {
|
||||
private fun BufferedSource.readBomAsCharset(): Charset? =
|
||||
when (select(UNICODE_BOMS)) {
|
||||
0 -> Charsets.UTF_8
|
||||
1 -> Charsets.UTF_16BE
|
||||
2 -> Charsets.UTF_16LE
|
||||
@@ -142,7 +147,6 @@ private fun BufferedSource.readBomAsCharset(): Charset? {
|
||||
-1 -> null
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
}
|
||||
|
||||
private val RE_CONTENT_TYPE_CHARSET = Regex("""charset=([^;]+)""")
|
||||
|
||||
@@ -158,7 +162,8 @@ private fun detectCharset(bodyBytes: ByteArray): Charset {
|
||||
}
|
||||
}
|
||||
if (meta.attr(ATTRIBUTE_VALUE_HTTP_EQUIV).lowercase() == "content-type") {
|
||||
RE_CONTENT_TYPE_CHARSET.find(meta.attr(CONTENT))
|
||||
RE_CONTENT_TYPE_CHARSET
|
||||
.find(meta.attr(CONTENT))
|
||||
?.let {
|
||||
runCatching { Charset.forName(it.groupValues[1]) }.getOrNull()
|
||||
}?.let {
|
||||
|
||||
@@ -23,7 +23,9 @@ package com.vitorpamplona.amethyst.service.relays
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.ammolite.relays.EOSETime
|
||||
|
||||
class EOSERelayList(var relayList: Map<String, EOSETime> = emptyMap()) {
|
||||
class EOSERelayList(
|
||||
var relayList: Map<String, EOSETime> = emptyMap(),
|
||||
) {
|
||||
fun addOrUpdate(
|
||||
relayUrl: String,
|
||||
time: Long,
|
||||
@@ -37,7 +39,9 @@ class EOSERelayList(var relayList: Map<String, EOSETime> = emptyMap()) {
|
||||
}
|
||||
}
|
||||
|
||||
class EOSEFollowList(var followList: Map<String, EOSERelayList> = emptyMap()) {
|
||||
class EOSEFollowList(
|
||||
var followList: Map<String, EOSERelayList> = emptyMap(),
|
||||
) {
|
||||
fun addOrUpdate(
|
||||
listCode: String,
|
||||
relayUrl: String,
|
||||
@@ -54,7 +58,9 @@ class EOSEFollowList(var followList: Map<String, EOSERelayList> = emptyMap()) {
|
||||
}
|
||||
}
|
||||
|
||||
class EOSEAccount(var users: Map<User, EOSEFollowList> = emptyMap()) {
|
||||
class EOSEAccount(
|
||||
var users: Map<User, EOSEFollowList> = emptyMap(),
|
||||
) {
|
||||
fun addOrUpdate(
|
||||
user: User,
|
||||
listCode: String,
|
||||
|
||||
+121
-119
@@ -28,150 +28,152 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.Locale
|
||||
|
||||
class TextToSpeechHelper private constructor(private val context: WeakReference<Context>) :
|
||||
LifecycleEventObserver {
|
||||
private val appContext
|
||||
get() = context.get()!!.applicationContext
|
||||
class TextToSpeechHelper private constructor(
|
||||
private val context: WeakReference<Context>,
|
||||
) : LifecycleEventObserver {
|
||||
private val appContext
|
||||
get() = context.get()!!.applicationContext
|
||||
|
||||
private var message: String? = null
|
||||
private var message: String? = null
|
||||
|
||||
private var ttsEngine: TextToSpeechEngine? = null
|
||||
private var ttsEngine: TextToSpeechEngine? = null
|
||||
|
||||
private var onStart: (() -> Unit)? = null
|
||||
private var onStart: (() -> Unit)? = null
|
||||
|
||||
private var onDoneListener: (() -> Unit)? = null
|
||||
private var onDoneListener: (() -> Unit)? = null
|
||||
|
||||
private var onErrorListener: ((String) -> Unit)? = null
|
||||
private var onErrorListener: ((String) -> Unit)? = null
|
||||
|
||||
private var onHighlightListener: ((Pair<Int, Int>) -> Unit)? = null
|
||||
private var onHighlightListener: ((Pair<Int, Int>) -> Unit)? = null
|
||||
|
||||
private var customActionForDestroy: (() -> Unit)? = null
|
||||
private var customActionForDestroy: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
Log.d("Init", "Init TTS")
|
||||
init {
|
||||
Log.d("Init", "Init TTS")
|
||||
initTTS()
|
||||
}
|
||||
|
||||
fun registerLifecycle(owner: LifecycleOwner): TextToSpeechHelper {
|
||||
owner.lifecycle.addObserver(this)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun initTTS() =
|
||||
context.get()?.run {
|
||||
ttsEngine =
|
||||
TextToSpeechEngine
|
||||
.getInstance()
|
||||
.setOnCompletionListener { onDoneListener?.invoke() }
|
||||
.setOnErrorListener { onErrorListener?.invoke(it) }
|
||||
.setOnStartListener { onStart?.invoke() }
|
||||
}
|
||||
|
||||
fun speak(message: String): TextToSpeechHelper {
|
||||
if (ttsEngine == null) {
|
||||
initTTS()
|
||||
}
|
||||
this.message = message
|
||||
|
||||
fun registerLifecycle(owner: LifecycleOwner): TextToSpeechHelper {
|
||||
owner.lifecycle.addObserver(this)
|
||||
return this
|
||||
ttsEngine?.initTTS(
|
||||
appContext,
|
||||
message,
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will highlight the text in the textView
|
||||
*
|
||||
* @exception Exception("Message can't be null for highlighting !! Call speak() first")
|
||||
*/
|
||||
fun highlight(): TextToSpeechHelper {
|
||||
if (message == null) {
|
||||
throw Exception("Message can't be null for highlighting !! Call speak() first")
|
||||
}
|
||||
ttsEngine?.setHighlightedMessage(message!!)
|
||||
ttsEngine?.setOnHighlightListener { i, i2 -> onHighlightListener?.invoke(Pair(i, i2)) }
|
||||
return this
|
||||
}
|
||||
|
||||
private fun initTTS() =
|
||||
context.get()?.run {
|
||||
ttsEngine =
|
||||
TextToSpeechEngine.getInstance()
|
||||
.setOnCompletionListener { onDoneListener?.invoke() }
|
||||
.setOnErrorListener { onErrorListener?.invoke(it) }
|
||||
.setOnStartListener { onStart?.invoke() }
|
||||
}
|
||||
fun removeHighlight(): TextToSpeechHelper {
|
||||
message = null
|
||||
onHighlightListener = null
|
||||
return this
|
||||
}
|
||||
|
||||
fun speak(message: String): TextToSpeechHelper {
|
||||
if (ttsEngine == null) {
|
||||
initTTS()
|
||||
}
|
||||
this.message = message
|
||||
fun destroy(action: (() -> Unit) = {}) {
|
||||
ttsEngine?.destroy()
|
||||
ttsEngine = null
|
||||
action.invoke()
|
||||
instance = null
|
||||
}
|
||||
|
||||
ttsEngine?.initTTS(
|
||||
appContext,
|
||||
message,
|
||||
)
|
||||
return this
|
||||
}
|
||||
fun onStart(onStartListener: () -> Unit): TextToSpeechHelper {
|
||||
this.onStart = onStartListener
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will highlight the text in the textView
|
||||
*
|
||||
* @exception Exception("Message can't be null for highlighting !! Call speak() first")
|
||||
*/
|
||||
fun highlight(): TextToSpeechHelper {
|
||||
if (message == null) {
|
||||
throw Exception("Message can't be null for highlighting !! Call speak() first")
|
||||
}
|
||||
ttsEngine?.setHighlightedMessage(message!!)
|
||||
ttsEngine?.setOnHighlightListener { i, i2 -> onHighlightListener?.invoke(Pair(i, i2)) }
|
||||
return this
|
||||
}
|
||||
fun onDone(onCompleteListener: () -> Unit): TextToSpeechHelper {
|
||||
this.onDoneListener = onCompleteListener
|
||||
return this
|
||||
}
|
||||
|
||||
fun removeHighlight(): TextToSpeechHelper {
|
||||
message = null
|
||||
onHighlightListener = null
|
||||
return this
|
||||
}
|
||||
fun onError(onErrorListener: (String) -> Unit): TextToSpeechHelper {
|
||||
this.onErrorListener = onErrorListener
|
||||
return this
|
||||
}
|
||||
|
||||
fun destroy(action: (() -> Unit) = {}) {
|
||||
ttsEngine?.destroy()
|
||||
ttsEngine = null
|
||||
action.invoke()
|
||||
instance = null
|
||||
}
|
||||
fun onHighlight(onHighlightListener: (Pair<Int, Int>) -> Unit): TextToSpeechHelper {
|
||||
this.onHighlightListener = onHighlightListener
|
||||
return this
|
||||
}
|
||||
|
||||
fun onStart(onStartListener: () -> Unit): TextToSpeechHelper {
|
||||
this.onStart = onStartListener
|
||||
return this
|
||||
}
|
||||
fun setCustomActionForDestroy(action: () -> Unit): TextToSpeechHelper {
|
||||
customActionForDestroy = action
|
||||
return this
|
||||
}
|
||||
|
||||
fun onDone(onCompleteListener: () -> Unit): TextToSpeechHelper {
|
||||
this.onDoneListener = onCompleteListener
|
||||
return this
|
||||
}
|
||||
fun setLanguage(locale: Locale): TextToSpeechHelper {
|
||||
ttsEngine?.setLanguage(locale)
|
||||
return this
|
||||
}
|
||||
|
||||
fun onError(onErrorListener: (String) -> Unit): TextToSpeechHelper {
|
||||
this.onErrorListener = onErrorListener
|
||||
return this
|
||||
}
|
||||
fun setPitchAndSpeed(
|
||||
pitch: Float = DEF_SPEECH_AND_PITCH,
|
||||
speed: Float = DEF_SPEECH_AND_PITCH,
|
||||
): TextToSpeechHelper {
|
||||
ttsEngine?.setPitchAndSpeed(pitch, speed)
|
||||
return this
|
||||
}
|
||||
|
||||
fun onHighlight(onHighlightListener: (Pair<Int, Int>) -> Unit): TextToSpeechHelper {
|
||||
this.onHighlightListener = onHighlightListener
|
||||
return this
|
||||
}
|
||||
fun resetPitchAndSpeed(): TextToSpeechHelper {
|
||||
ttsEngine?.resetPitchAndSpeed()
|
||||
return this
|
||||
}
|
||||
|
||||
fun setCustomActionForDestroy(action: () -> Unit): TextToSpeechHelper {
|
||||
customActionForDestroy = action
|
||||
return this
|
||||
}
|
||||
companion object {
|
||||
private var instance: TextToSpeechHelper? = null
|
||||
|
||||
fun setLanguage(locale: Locale): TextToSpeechHelper {
|
||||
ttsEngine?.setLanguage(locale)
|
||||
return this
|
||||
}
|
||||
|
||||
fun setPitchAndSpeed(
|
||||
pitch: Float = DEF_SPEECH_AND_PITCH,
|
||||
speed: Float = DEF_SPEECH_AND_PITCH,
|
||||
): TextToSpeechHelper {
|
||||
ttsEngine?.setPitchAndSpeed(pitch, speed)
|
||||
return this
|
||||
}
|
||||
|
||||
fun resetPitchAndSpeed(): TextToSpeechHelper {
|
||||
ttsEngine?.resetPitchAndSpeed()
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var instance: TextToSpeechHelper? = null
|
||||
|
||||
fun getInstance(context: Context): TextToSpeechHelper {
|
||||
synchronized(TextToSpeechHelper::class.java) {
|
||||
if (instance == null) {
|
||||
instance = TextToSpeechHelper(WeakReference(context))
|
||||
}
|
||||
return instance!!
|
||||
fun getInstance(context: Context): TextToSpeechHelper {
|
||||
synchronized(TextToSpeechHelper::class.java) {
|
||||
if (instance == null) {
|
||||
instance = TextToSpeechHelper(WeakReference(context))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStateChanged(
|
||||
source: LifecycleOwner,
|
||||
event: Lifecycle.Event,
|
||||
) {
|
||||
if (
|
||||
event == Lifecycle.Event.ON_DESTROY ||
|
||||
event == Lifecycle.Event.ON_STOP ||
|
||||
event == Lifecycle.Event.ON_PAUSE
|
||||
) {
|
||||
destroy { customActionForDestroy?.invoke() }
|
||||
return instance!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStateChanged(
|
||||
source: LifecycleOwner,
|
||||
event: Lifecycle.Event,
|
||||
) {
|
||||
if (
|
||||
event == Lifecycle.Event.ON_DESTROY ||
|
||||
event == Lifecycle.Event.ON_STOP ||
|
||||
event == Lifecycle.Event.ON_PAUSE
|
||||
) {
|
||||
destroy { customActionForDestroy?.invoke() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,8 @@ object ImageSaver {
|
||||
val client = HttpClientManager.getHttpClient()
|
||||
|
||||
val request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.get()
|
||||
.url(url)
|
||||
|
||||
+8
-5
@@ -35,12 +35,15 @@ import com.vitorpamplona.quartz.encoders.decodePublicKey
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
data class RangesChanges(val original: TextRange, val modified: TextRange)
|
||||
data class RangesChanges(
|
||||
val original: TextRange,
|
||||
val modified: TextRange,
|
||||
)
|
||||
|
||||
class UrlUserTagTransformation(val color: Color) : VisualTransformation {
|
||||
override fun filter(text: AnnotatedString): TransformedText {
|
||||
return buildAnnotatedStringWithUrlHighlighting(text, color)
|
||||
}
|
||||
class UrlUserTagTransformation(
|
||||
val color: Color,
|
||||
) : VisualTransformation {
|
||||
override fun filter(text: AnnotatedString): TransformedText = buildAnnotatedStringWithUrlHighlighting(text, color)
|
||||
}
|
||||
|
||||
fun buildAnnotatedStringWithUrlHighlighting(
|
||||
|
||||
+9
-6
@@ -78,12 +78,15 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
|
||||
_relays.update {
|
||||
val relayList = getRelayList() ?: emptyList()
|
||||
|
||||
relayList.map { relayUrl ->
|
||||
BasicRelaySetupInfo(
|
||||
RelayUrlFormatter.normalize(relayUrl),
|
||||
RelayStats.get(relayUrl),
|
||||
)
|
||||
}.distinctBy { it.url }.sortedBy { it.relayStat.receivedBytes }.reversed()
|
||||
relayList
|
||||
.map { relayUrl ->
|
||||
BasicRelaySetupInfo(
|
||||
RelayUrlFormatter.normalize(relayUrl),
|
||||
RelayStats.get(relayUrl),
|
||||
)
|
||||
}.distinctBy { it.url }
|
||||
.sortedBy { it.relayStat.receivedBytes }
|
||||
.reversed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -21,9 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.actions.relays
|
||||
|
||||
class DMRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||
override fun getRelayList(): List<String>? {
|
||||
return account.getDMRelayList()?.relays()
|
||||
}
|
||||
override fun getRelayList(): List<String>? = account.getDMRelayList()?.relays()
|
||||
|
||||
override fun saveRelayList(urlList: List<String>) {
|
||||
account.saveDMRelayList(urlList)
|
||||
|
||||
+1
-3
@@ -21,9 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.actions.relays
|
||||
|
||||
class LocalRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||
override fun getRelayList(): List<String>? {
|
||||
return account.localRelayServers.toList()
|
||||
}
|
||||
override fun getRelayList(): List<String>? = account.localRelayServers.toList()
|
||||
|
||||
override fun saveRelayList(urlList: List<String>) {
|
||||
account.updateLocalRelayServers(urlList.toSet())
|
||||
|
||||
+18
-12
@@ -105,23 +105,29 @@ class Nip65RelayListViewModel : ViewModel() {
|
||||
_homeRelays.update {
|
||||
val relayList = account.getNIP65RelayList()?.writeRelays() ?: emptyList()
|
||||
|
||||
relayList.map { relayUrl ->
|
||||
BasicRelaySetupInfo(
|
||||
RelayUrlFormatter.normalize(relayUrl),
|
||||
RelayStats.get(relayUrl),
|
||||
)
|
||||
}.distinctBy { it.url }.sortedBy { it.relayStat.receivedBytes }.reversed()
|
||||
relayList
|
||||
.map { relayUrl ->
|
||||
BasicRelaySetupInfo(
|
||||
RelayUrlFormatter.normalize(relayUrl),
|
||||
RelayStats.get(relayUrl),
|
||||
)
|
||||
}.distinctBy { it.url }
|
||||
.sortedBy { it.relayStat.receivedBytes }
|
||||
.reversed()
|
||||
}
|
||||
|
||||
_notificationRelays.update {
|
||||
val relayList = account.getNIP65RelayList()?.readRelays() ?: emptyList()
|
||||
|
||||
relayList.map { relayUrl ->
|
||||
BasicRelaySetupInfo(
|
||||
RelayUrlFormatter.normalize(relayUrl),
|
||||
RelayStats.get(relayUrl),
|
||||
)
|
||||
}.distinctBy { it.url }.sortedBy { it.relayStat.receivedBytes }.reversed()
|
||||
relayList
|
||||
.map { relayUrl ->
|
||||
BasicRelaySetupInfo(
|
||||
RelayUrlFormatter.normalize(relayUrl),
|
||||
RelayStats.get(relayUrl),
|
||||
)
|
||||
}.distinctBy { it.url }
|
||||
.sortedBy { it.relayStat.receivedBytes }
|
||||
.reversed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -21,9 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.actions.relays
|
||||
|
||||
class PrivateOutboxRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||
override fun getRelayList(): List<String>? {
|
||||
return account.getPrivateOutboxRelayList()?.relays()
|
||||
}
|
||||
override fun getRelayList(): List<String>? = account.getPrivateOutboxRelayList()?.relays()
|
||||
|
||||
override fun saveRelayList(urlList: List<String>) {
|
||||
account.savePrivateOutboxRelayList(urlList)
|
||||
|
||||
+1
-3
@@ -21,9 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.actions.relays
|
||||
|
||||
class SearchRelayListViewModel : BasicRelaySetupInfoModel() {
|
||||
override fun getRelayList(): List<String>? {
|
||||
return account.getSearchRelayList()?.relays()
|
||||
}
|
||||
override fun getRelayList(): List<String>? = account.getSearchRelayList()?.relays()
|
||||
|
||||
override fun saveRelayList(urlList: List<String>) {
|
||||
account.saveSearchRelayList(urlList)
|
||||
|
||||
+5
-8
@@ -101,11 +101,11 @@ fun AudioWaveformReadOnly(
|
||||
minHeight = MIN_SPIKE_HEIGHT,
|
||||
maxHeight = canvasSize.height.coerceAtLeast(MIN_SPIKE_HEIGHT),
|
||||
)
|
||||
}
|
||||
.map { animateFloatAsState(it, spikeAnimationSpec).value }
|
||||
}.map { animateFloatAsState(it, spikeAnimationSpec).value }
|
||||
Canvas(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(48.dp)
|
||||
.graphicsLayer(alpha = DEFAULT_GRAPHICS_LAYER_ALPHA)
|
||||
.then(modifier),
|
||||
@@ -161,8 +161,7 @@ private fun List<Int>.toDrawableAmplitudes(
|
||||
AmplitudeType.Avg -> data.average()
|
||||
AmplitudeType.Max -> data.max()
|
||||
AmplitudeType.Min -> data.min()
|
||||
}
|
||||
.toFloat()
|
||||
}.toFloat()
|
||||
.coerceIn(minHeight, maxHeight)
|
||||
}
|
||||
return when {
|
||||
@@ -198,9 +197,7 @@ internal fun <T> Iterable<T>.chunkToSize(
|
||||
internal fun Iterable<Float>.normalize(
|
||||
min: Float,
|
||||
max: Float,
|
||||
): List<Float> {
|
||||
return map { (max - min) * ((it - min()) / (max() - min())) + min }
|
||||
}
|
||||
): List<Float> = map { (max - min) * ((it - min()) / (max() - min())) + min }
|
||||
|
||||
private fun Int.safeDiv(value: Int): Float {
|
||||
return if (value == 0) return 0F else this / value.toFloat()
|
||||
|
||||
@@ -26,9 +26,13 @@ import androidx.compose.runtime.Immutable
|
||||
sealed class GenericLoadable<T> {
|
||||
@Immutable class Loading<T> : GenericLoadable<T>()
|
||||
|
||||
@Immutable class Loaded<T>(val loaded: T) : GenericLoadable<T>()
|
||||
@Immutable class Loaded<T>(
|
||||
val loaded: T,
|
||||
) : GenericLoadable<T>()
|
||||
|
||||
@Immutable class Empty<T> : GenericLoadable<T>()
|
||||
|
||||
@Immutable class Error<T>(val errorMessage: String) : GenericLoadable<T>()
|
||||
@Immutable class Error<T>(
|
||||
val errorMessage: String,
|
||||
) : GenericLoadable<T>()
|
||||
}
|
||||
|
||||
@@ -25,12 +25,14 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import kotlin.math.abs
|
||||
|
||||
class SplitItem<T>(val key: T) {
|
||||
class SplitItem<T>(
|
||||
val key: T,
|
||||
) {
|
||||
// 0 to 1
|
||||
var percentage by mutableStateOf(0f)
|
||||
}
|
||||
|
||||
class Split<T>() {
|
||||
class Split<T> {
|
||||
var items: List<SplitItem<T>> by mutableStateOf(emptyList())
|
||||
|
||||
fun addItem(
|
||||
|
||||
@@ -27,9 +27,13 @@ import com.vitorpamplona.amethyst.service.previews.UrlInfoItem
|
||||
sealed class UrlPreviewState {
|
||||
@Immutable object Loading : UrlPreviewState()
|
||||
|
||||
@Immutable class Loaded(val previewInfo: UrlInfoItem) : UrlPreviewState()
|
||||
@Immutable class Loaded(
|
||||
val previewInfo: UrlInfoItem,
|
||||
) : UrlPreviewState()
|
||||
|
||||
@Immutable object Empty : UrlPreviewState()
|
||||
|
||||
@Immutable class Error(val errorMessage: String) : UrlPreviewState()
|
||||
@Immutable class Error(
|
||||
val errorMessage: String,
|
||||
) : UrlPreviewState()
|
||||
}
|
||||
|
||||
+4
-4
@@ -24,10 +24,10 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
|
||||
class BookmarkPrivateFeedFilter(val account: Account) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().latestBookmarkList?.id ?: ""
|
||||
}
|
||||
class BookmarkPrivateFeedFilter(
|
||||
val account: Account,
|
||||
) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().latestBookmarkList?.id ?: ""
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val bookmarks = account.userProfile().latestBookmarkList
|
||||
|
||||
+4
-4
@@ -24,10 +24,10 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
|
||||
class BookmarkPublicFeedFilter(val account: Account) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().latestBookmarkList?.id ?: ""
|
||||
}
|
||||
class BookmarkPublicFeedFilter(
|
||||
val account: Account,
|
||||
) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().latestBookmarkList?.id ?: ""
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val bookmarks = account.userProfile().latestBookmarkList
|
||||
|
||||
@@ -24,27 +24,24 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Channel
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
|
||||
class ChannelFeedFilter(val channel: Channel, val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return channel.idHex
|
||||
}
|
||||
class ChannelFeedFilter(
|
||||
val channel: Channel,
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = channel.idHex
|
||||
|
||||
// returns the last Note of each user.
|
||||
override fun feed(): List<Note> {
|
||||
return sort(
|
||||
override fun feed(): List<Note> =
|
||||
sort(
|
||||
channel.notes.filterIntoSet { key, it ->
|
||||
account.isAcceptable(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return collection
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> =
|
||||
collection
|
||||
.filter { channel.notes.containsKey(it.idHex) && account.isAcceptable(it) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
|
||||
class ChatroomFeedFilter(val withUser: ChatroomKey, val account: Account) :
|
||||
AdditiveFeedFilter<Note>() {
|
||||
class ChatroomFeedFilter(
|
||||
val withUser: ChatroomKey,
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
// returns the last Note of each user.
|
||||
override fun feedKey(): String {
|
||||
return withUser.hashCode().toString()
|
||||
}
|
||||
override fun feedKey(): String = withUser.hashCode().toString()
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val messages = account.userProfile().privateChatrooms[withUser] ?: return emptyList()
|
||||
@@ -46,7 +46,5 @@ class ChatroomFeedFilter(val withUser: ChatroomKey, val account: Account) :
|
||||
return collection.filter { it in messages.roomMessages && account.isAcceptable(it) }.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
+14
-10
@@ -28,10 +28,10 @@ import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
import com.vitorpamplona.quartz.events.ChatroomKeyable
|
||||
|
||||
class ChatroomListKnownFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex
|
||||
}
|
||||
class ChatroomListKnownFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex
|
||||
|
||||
// returns the last Note of each user.
|
||||
override fun feed(): List<Note> {
|
||||
@@ -56,7 +56,8 @@ class ChatroomListKnownFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
.selectedChatsFollowList()
|
||||
.mapNotNull { LocalCache.getChannelIfExists(it) }
|
||||
.mapNotNull { it ->
|
||||
it.notes.filter { key, it -> account.isAcceptable(it) && it.event != null }
|
||||
it.notes
|
||||
.filter { key, it -> account.isAcceptable(it) && it.event != null }
|
||||
.sortedWith(DefaultFeedOrder)
|
||||
.firstOrNull()
|
||||
}
|
||||
@@ -136,7 +137,11 @@ class ChatroomListKnownFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
account: Account,
|
||||
): MutableMap<String, Note> {
|
||||
val followingChannels =
|
||||
account.userProfile().latestContactList?.taggedEvents()?.toSet() ?: emptySet()
|
||||
account
|
||||
.userProfile()
|
||||
.latestContactList
|
||||
?.taggedEvents()
|
||||
?.toSet() ?: emptySet()
|
||||
val newRelevantPublicMessages = mutableMapOf<String, Note>()
|
||||
newItems
|
||||
.filter { it.event is ChannelMessageEvent }
|
||||
@@ -177,7 +182,8 @@ class ChatroomListKnownFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
newNote.author?.pubkeyHex == me.pubkeyHex ||
|
||||
room.senderIntersects(followingKeySet) ||
|
||||
me.hasSentMessagesTo(roomKey)
|
||||
) && !account.isAllHidden(roomKey.users)
|
||||
) &&
|
||||
!account.isAllHidden(roomKey.users)
|
||||
) {
|
||||
val lastNote = newRelevantPrivateMessages.get(roomKey)
|
||||
if (lastNote != null) {
|
||||
@@ -193,7 +199,5 @@ class ChatroomListKnownFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
return newRelevantPrivateMessages
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
+5
-7
@@ -27,10 +27,10 @@ import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
import com.vitorpamplona.quartz.events.ChatroomKeyable
|
||||
import com.vitorpamplona.quartz.events.PrivateDmEvent
|
||||
|
||||
class ChatroomListNewFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex
|
||||
}
|
||||
class ChatroomListNewFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex
|
||||
|
||||
// returns the last Note of each user.
|
||||
override fun feed(): List<Note> {
|
||||
@@ -137,7 +137,5 @@ class ChatroomListNewFeedFilter(val account: Account) : AdditiveFeedFilter<Note>
|
||||
return newRelevantPrivateMessages
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
@@ -27,10 +27,11 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||
|
||||
class CommunityFeedFilter(val note: AddressableNote, val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + note.idHex
|
||||
}
|
||||
class CommunityFeedFilter(
|
||||
val note: AddressableNote,
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + note.idHex
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val myPubKey = account.userProfile().pubkeyHex
|
||||
@@ -42,23 +43,23 @@ class CommunityFeedFilter(val note: AddressableNote, val account: Account) : Add
|
||||
return sort(result)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val myPubKey = account.userProfile().pubkeyHex
|
||||
|
||||
return collection.mapNotNull {
|
||||
filterMap(it, myPubKey)
|
||||
}.flatten().toSet()
|
||||
return collection
|
||||
.mapNotNull {
|
||||
filterMap(it, myPubKey)
|
||||
}.flatten()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun filterMap(
|
||||
note: Note,
|
||||
myPubKey: HexKey,
|
||||
): List<Note>? {
|
||||
return if (
|
||||
): List<Note>? =
|
||||
if (
|
||||
// Only Approvals
|
||||
note.event is CommunityPostApprovalEvent &&
|
||||
// Of the given community
|
||||
@@ -74,9 +75,6 @@ class CommunityFeedFilter(val note: AddressableNote, val account: Account) : Add
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
+10
-15
@@ -29,17 +29,16 @@ import com.vitorpamplona.quartz.events.IsInPublicChatChannel
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
|
||||
open class DiscoverChatFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + account.defaultDiscoveryFollowList.value
|
||||
}
|
||||
open class DiscoverChatFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.defaultDiscoveryFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return account.defaultDiscoveryFollowList.value ==
|
||||
override fun showHiddenKey(): Boolean =
|
||||
account.defaultDiscoveryFollowList.value ==
|
||||
PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
account.defaultDiscoveryFollowList.value ==
|
||||
MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
@@ -63,18 +62,15 @@ open class DiscoverChatFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
return sort(allChannelNotes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
userHex = account.userProfile().pubkeyHex,
|
||||
selectedListName = account.defaultDiscoveryFollowList.value,
|
||||
followLists = account.liveDiscoveryFollowLists.value,
|
||||
hiddenUsers = account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
@@ -120,7 +116,6 @@ open class DiscoverChatFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
{ it.createdAt() },
|
||||
{ it.idHex },
|
||||
),
|
||||
)
|
||||
.reversed()
|
||||
).reversed()
|
||||
}
|
||||
}
|
||||
|
||||
+29
-31
@@ -29,17 +29,16 @@ import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
|
||||
open class DiscoverCommunityFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + account.defaultDiscoveryFollowList.value
|
||||
}
|
||||
open class DiscoverCommunityFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.defaultDiscoveryFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return account.defaultDiscoveryFollowList.value ==
|
||||
override fun showHiddenKey(): Boolean =
|
||||
account.defaultDiscoveryFollowList.value ==
|
||||
PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
account.defaultDiscoveryFollowList.value ==
|
||||
MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val filterParams =
|
||||
@@ -67,9 +66,7 @@ open class DiscoverCommunityFeedFilter(val account: Account) : AdditiveFeedFilte
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
// here, we need to look for CommunityDefinition in new collection AND new CommunityDefinition from Post Approvals
|
||||
@@ -81,28 +78,30 @@ open class DiscoverCommunityFeedFilter(val account: Account) : AdditiveFeedFilte
|
||||
hiddenUsers = account.flowHiddenUsers.value,
|
||||
)
|
||||
|
||||
return collection.mapNotNull { note ->
|
||||
// note event here will never be null
|
||||
val noteEvent = note.event
|
||||
if (noteEvent is CommunityDefinitionEvent && filterParams.match(noteEvent)) {
|
||||
listOf(note)
|
||||
} else if (noteEvent is CommunityPostApprovalEvent) {
|
||||
noteEvent.communities().mapNotNull {
|
||||
val definitionNote = LocalCache.getOrCreateAddressableNote(it)
|
||||
val definitionEvent = definitionNote.event
|
||||
return collection
|
||||
.mapNotNull { note ->
|
||||
// note event here will never be null
|
||||
val noteEvent = note.event
|
||||
if (noteEvent is CommunityDefinitionEvent && filterParams.match(noteEvent)) {
|
||||
listOf(note)
|
||||
} else if (noteEvent is CommunityPostApprovalEvent) {
|
||||
noteEvent.communities().mapNotNull {
|
||||
val definitionNote = LocalCache.getOrCreateAddressableNote(it)
|
||||
val definitionEvent = definitionNote.event
|
||||
|
||||
if (definitionEvent == null && shouldInclude(it, filterParams)) {
|
||||
definitionNote
|
||||
} else if (definitionEvent is CommunityDefinitionEvent && filterParams.match(definitionEvent)) {
|
||||
definitionNote
|
||||
} else {
|
||||
null
|
||||
if (definitionEvent == null && shouldInclude(it, filterParams)) {
|
||||
definitionNote
|
||||
} else if (definitionEvent is CommunityDefinitionEvent && filterParams.match(definitionEvent)) {
|
||||
definitionNote
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.flatten().toSet()
|
||||
}.flatten()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun shouldInclude(
|
||||
@@ -123,7 +122,6 @@ open class DiscoverCommunityFeedFilter(val account: Account) : AdditiveFeedFilte
|
||||
{ it.createdAt() },
|
||||
{ it.idHex },
|
||||
),
|
||||
)
|
||||
.reversed()
|
||||
).reversed()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,18 +34,13 @@ import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
open class DiscoverLiveFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + followList()
|
||||
}
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + followList()
|
||||
|
||||
open fun followList(): String {
|
||||
return account.defaultDiscoveryFollowList.value
|
||||
}
|
||||
open fun followList(): String = account.defaultDiscoveryFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
override fun showHiddenKey(): Boolean =
|
||||
followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
followList() == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val allChannelNotes = LocalCache.channels.mapNotNull { _, channel -> LocalCache.getNoteIfExists(channel.idHex) }
|
||||
@@ -56,9 +51,7 @@ open class DiscoverLiveFeedFilter(
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val filterParams =
|
||||
@@ -92,16 +85,14 @@ open class DiscoverLiveFeedFilter(
|
||||
{ (it.event as? LiveActivitiesEvent)?.starts() ?: it.createdAt() },
|
||||
{ it.idHex },
|
||||
),
|
||||
)
|
||||
.reversed()
|
||||
).reversed()
|
||||
}
|
||||
|
||||
fun convertStatusToOrder(status: String?): Int {
|
||||
return when (status) {
|
||||
fun convertStatusToOrder(status: String?): Int =
|
||||
when (status) {
|
||||
STATUS_LIVE -> 2
|
||||
STATUS_PLANNED -> 1
|
||||
STATUS_ENDED -> 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-18
@@ -30,18 +30,13 @@ import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
open class DiscoverMarketplaceFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + followList()
|
||||
}
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + followList()
|
||||
|
||||
open fun followList(): String {
|
||||
return account.defaultDiscoveryFollowList.value
|
||||
}
|
||||
open fun followList(): String = account.defaultDiscoveryFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
override fun showHiddenKey(): Boolean =
|
||||
followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
followList() == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
@@ -55,18 +50,15 @@ open class DiscoverMarketplaceFeedFilter(
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
account.userProfile().pubkeyHex,
|
||||
account.defaultDiscoveryFollowList.value,
|
||||
account.liveDiscoveryFollowLists.value,
|
||||
account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
@@ -77,7 +69,5 @@ open class DiscoverMarketplaceFeedFilter(
|
||||
}
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
||||
}
|
||||
|
||||
+10
-21
@@ -34,18 +34,13 @@ open class DiscoverNIP89FeedFilter(
|
||||
val lastAnnounced = 90 * 24 * 60 * 60 // 90 Days ago
|
||||
// TODO better than announced would be last active, as this requires the DVM provider to regularly update the NIP89 announcement
|
||||
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + followList()
|
||||
}
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + followList()
|
||||
|
||||
open fun followList(): String {
|
||||
return account.defaultDiscoveryFollowList.value
|
||||
}
|
||||
open fun followList(): String = account.defaultDiscoveryFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
override fun showHiddenKey(): Boolean =
|
||||
followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
followList() == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val filterParams = buildFilterParams(account)
|
||||
@@ -58,18 +53,15 @@ open class DiscoverNIP89FeedFilter(
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
account.userProfile().pubkeyHex,
|
||||
account.defaultDiscoveryFollowList.value,
|
||||
account.liveDiscoveryFollowLists.value,
|
||||
account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
fun acceptDVM(note: Note): Boolean {
|
||||
val noteEvent = note.event
|
||||
@@ -88,13 +80,10 @@ open class DiscoverNIP89FeedFilter(
|
||||
noteEvent.createdAt > TimeUtils.now() - lastAnnounced // && params.match(noteEvent)
|
||||
}
|
||||
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
return collection.filterTo(HashSet()) {
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> =
|
||||
collection.filterTo(HashSet()) {
|
||||
acceptDVM(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
||||
}
|
||||
|
||||
@@ -25,16 +25,15 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.events.DraftEvent
|
||||
|
||||
class DraftEventsFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex
|
||||
}
|
||||
class DraftEventsFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return collection.filterTo(HashSet()) {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> =
|
||||
collection.filterTo(HashSet()) {
|
||||
acceptableEvent(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val drafts =
|
||||
@@ -50,7 +49,5 @@ class DraftEventsFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
return noteEvent is DraftEvent && noteEvent.pubKey == account.userProfile().pubkeyHex
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
@@ -91,13 +91,12 @@ class FilterByListParams(
|
||||
selectedListName: String,
|
||||
followLists: Account.LiveFollowLists?,
|
||||
hiddenUsers: Account.LiveHiddenUsers,
|
||||
): FilterByListParams {
|
||||
return FilterByListParams(
|
||||
): FilterByListParams =
|
||||
FilterByListParams(
|
||||
isGlobal = selectedListName == GLOBAL_FOLLOWS,
|
||||
isHiddenList = showHiddenKey(selectedListName, userHex),
|
||||
followLists = followLists,
|
||||
hiddenLists = hiddenUsers,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,11 @@ import com.vitorpamplona.quartz.events.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.WikiNoteEvent
|
||||
|
||||
class GeoHashFeedFilter(val tag: String, val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + tag
|
||||
}
|
||||
class GeoHashFeedFilter(
|
||||
val tag: String,
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + tag
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val notes =
|
||||
@@ -45,19 +46,15 @@ class GeoHashFeedFilter(val tag: String, val account: Account) : AdditiveFeedFil
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
return collection.filterTo(HashSet<Note>()) { acceptableEvent(it, tag) }
|
||||
}
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet<Note>()) { acceptableEvent(it, tag) }
|
||||
|
||||
fun acceptableEvent(
|
||||
it: Note,
|
||||
geoTag: String,
|
||||
): Boolean {
|
||||
return (
|
||||
): Boolean =
|
||||
(
|
||||
it.event is TextNoteEvent ||
|
||||
it.event is LongTextNoteEvent ||
|
||||
it.event is WikiNoteEvent ||
|
||||
@@ -66,9 +63,6 @@ class GeoHashFeedFilter(val tag: String, val account: Account) : AdditiveFeedFil
|
||||
it.event is PollNoteEvent ||
|
||||
it.event is AudioHeaderEvent
|
||||
) && it.event?.isTaggedGeoHash(geoTag) == true && account.isAcceptable(it)
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
@@ -33,10 +33,11 @@ import com.vitorpamplona.quartz.events.RepostEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.WikiNoteEvent
|
||||
|
||||
class HashtagFeedFilter(val tag: String, val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + tag
|
||||
}
|
||||
class HashtagFeedFilter(
|
||||
val tag: String,
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + tag
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val notes =
|
||||
@@ -47,19 +48,15 @@ class HashtagFeedFilter(val tag: String, val account: Account) : AdditiveFeedFil
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
return collection.filterTo(HashSet<Note>()) { acceptableEvent(it, tag) }
|
||||
}
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet<Note>()) { acceptableEvent(it, tag) }
|
||||
|
||||
fun acceptableEvent(
|
||||
it: Note,
|
||||
hashTag: String,
|
||||
): Boolean {
|
||||
return (
|
||||
): Boolean =
|
||||
(
|
||||
it.event is TextNoteEvent ||
|
||||
it.event is RepostEvent ||
|
||||
it.event is GenericRepostEvent ||
|
||||
@@ -69,10 +66,9 @@ class HashtagFeedFilter(val tag: String, val account: Account) : AdditiveFeedFil
|
||||
it.event is PrivateDmEvent ||
|
||||
it.event is PollNoteEvent ||
|
||||
it.event is AudioHeaderEvent
|
||||
) && it.event?.isTaggedHash(hashTag) == true && account.isAcceptable(it)
|
||||
}
|
||||
) &&
|
||||
it.event?.isTaggedHash(hashTag) == true &&
|
||||
account.isAcceptable(it)
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
+15
-20
@@ -30,15 +30,14 @@ import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import com.vitorpamplona.quartz.events.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
|
||||
class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + account.defaultHomeFollowList.value
|
||||
}
|
||||
class HomeConversationsFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.defaultHomeFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
override fun showHiddenKey(): Boolean =
|
||||
account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
account.defaultHomeFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val filterParams = buildFilterParams(account)
|
||||
@@ -50,18 +49,15 @@ class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
userHex = account.userProfile().pubkeyHex,
|
||||
selectedListName = account.defaultHomeFollowList.value,
|
||||
followLists = account.liveHomeFollowLists.value,
|
||||
hiddenUsers = account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val filterParams = buildFilterParams(account)
|
||||
@@ -74,16 +70,15 @@ class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter<Not
|
||||
fun acceptableEvent(
|
||||
it: Note,
|
||||
filterParams: FilterByListParams,
|
||||
): Boolean {
|
||||
return (
|
||||
): Boolean =
|
||||
(
|
||||
it.event is TextNoteEvent ||
|
||||
it.event is PollNoteEvent ||
|
||||
it.event is ChannelMessageEvent ||
|
||||
it.event is LiveActivitiesChatMessageEvent
|
||||
) && filterParams.match(it.event) && !it.isNewThread()
|
||||
}
|
||||
) &&
|
||||
filterParams.match(it.event) &&
|
||||
!it.isNewThread()
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
+18
-22
@@ -36,24 +36,22 @@ import com.vitorpamplona.quartz.events.RepostEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.WikiNoteEvent
|
||||
|
||||
class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + account.defaultHomeFollowList.value
|
||||
}
|
||||
class HomeNewThreadFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.defaultHomeFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
override fun showHiddenKey(): Boolean =
|
||||
account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
account.defaultHomeFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
userHex = account.userProfile().pubkeyHex,
|
||||
selectedListName = account.defaultHomeFollowList.value,
|
||||
followLists = account.liveHomeFollowLists.value,
|
||||
hiddenUsers = account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val gRelays = account.activeGlobalRelays().toSet()
|
||||
@@ -73,9 +71,7 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
|
||||
return sort(notes + longFormNotes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val gRelays = account.activeGlobalRelays().toSet()
|
||||
@@ -107,13 +103,13 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
|
||||
) && filterParams.match(noteEvent, isGlobalRelay) && it.isNewThread()
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.distinctBy {
|
||||
if (it.event is RepostEvent || it.event is GenericRepostEvent) {
|
||||
it.replyTo?.lastOrNull()?.idHex ?: it.idHex // only the most recent repost per feed.
|
||||
} else {
|
||||
it.idHex
|
||||
}
|
||||
}.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> =
|
||||
collection
|
||||
.distinctBy {
|
||||
if (it.event is RepostEvent || it.event is GenericRepostEvent) {
|
||||
it.replyTo?.lastOrNull()?.idHex ?: it.idHex // only the most recent repost per feed.
|
||||
} else {
|
||||
it.idHex
|
||||
}
|
||||
}.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
+12
-18
@@ -35,18 +35,13 @@ open class NIP90ContentDiscoveryResponseFilter(
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
var latestNote: Note? = null
|
||||
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + request
|
||||
}
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + request
|
||||
|
||||
open fun followList(): String {
|
||||
return account.defaultDiscoveryFollowList.value
|
||||
}
|
||||
open fun followList(): String = account.defaultDiscoveryFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
override fun showHiddenKey(): Boolean =
|
||||
followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
followList() == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
fun acceptableEvent(note: Note): Boolean {
|
||||
val noteEvent = note.event
|
||||
@@ -71,18 +66,15 @@ open class NIP90ContentDiscoveryResponseFilter(
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
account.userProfile().pubkeyHex,
|
||||
account.defaultDiscoveryFollowList.value,
|
||||
account.liveDiscoveryFollowLists.value,
|
||||
account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
// val params = buildFilterParams(account)
|
||||
@@ -95,9 +87,11 @@ open class NIP90ContentDiscoveryResponseFilter(
|
||||
|
||||
val noteEvent = latestNote?.event as? NIP90ContentDiscoveryResponseEvent ?: return setOf()
|
||||
|
||||
return noteEvent.innerTags().mapNotNull {
|
||||
LocalCache.checkGetOrCreateNote(it)
|
||||
}.toSet()
|
||||
return noteEvent
|
||||
.innerTags()
|
||||
.mapNotNull {
|
||||
LocalCache.checkGetOrCreateNote(it)
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
|
||||
+18
-18
@@ -44,26 +44,24 @@ import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import com.vitorpamplona.quartz.events.ReactionEvent
|
||||
import com.vitorpamplona.quartz.events.RepostEvent
|
||||
|
||||
class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + account.defaultNotificationFollowList.value
|
||||
}
|
||||
class NotificationFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.defaultNotificationFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return account.defaultNotificationFollowList.value ==
|
||||
override fun showHiddenKey(): Boolean =
|
||||
account.defaultNotificationFollowList.value ==
|
||||
PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
account.defaultNotificationFollowList.value ==
|
||||
MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
userHex = account.userProfile().pubkeyHex,
|
||||
selectedListName = account.defaultNotificationFollowList.value,
|
||||
followLists = account.liveNotificationFollowLists.value,
|
||||
hiddenUsers = account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val filterParams = buildFilterParams(account)
|
||||
@@ -76,9 +74,7 @@ class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
|
||||
return sort(notifications)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val filterParams = buildFilterParams(account)
|
||||
@@ -123,9 +119,7 @@ class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
|
||||
tagsAnEventByUser(it, loggedInUserHex)
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
|
||||
fun tagsAnEventByUser(
|
||||
note: Note,
|
||||
@@ -153,11 +147,17 @@ class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
|
||||
}
|
||||
|
||||
if (event is ReactionEvent) {
|
||||
return note.replyTo?.lastOrNull()?.author?.pubkeyHex == authorHex
|
||||
return note.replyTo
|
||||
?.lastOrNull()
|
||||
?.author
|
||||
?.pubkeyHex == authorHex
|
||||
}
|
||||
|
||||
if (event is RepostEvent || event is GenericRepostEvent) {
|
||||
return note.replyTo?.lastOrNull()?.author?.pubkeyHex == authorHex
|
||||
return note.replyTo
|
||||
?.lastOrNull()
|
||||
?.author
|
||||
?.pubkeyHex == authorHex
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -29,10 +29,11 @@ import com.vitorpamplona.amethyst.model.ThreadLevelCalculator
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class ThreadFeedFilter(val account: Account, val noteId: String) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return noteId
|
||||
}
|
||||
class ThreadFeedFilter(
|
||||
val account: Account,
|
||||
val noteId: String,
|
||||
) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String = noteId
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val cachedSignatures: MutableMap<Note, LevelSignature> = mutableMapOf()
|
||||
@@ -44,14 +45,15 @@ class ThreadFeedFilter(val account: Account, val noteId: String) : FeedFilter<No
|
||||
// Currently orders by date of each event, descending, at each level of the reply stack
|
||||
val order =
|
||||
compareByDescending<Note> {
|
||||
ThreadLevelCalculator.replyLevelSignature(
|
||||
it,
|
||||
eventsInHex,
|
||||
cachedSignatures,
|
||||
account.userProfile(),
|
||||
followingKeySet,
|
||||
now,
|
||||
).signature
|
||||
ThreadLevelCalculator
|
||||
.replyLevelSignature(
|
||||
it,
|
||||
eventsInHex,
|
||||
cachedSignatures,
|
||||
account.userProfile(),
|
||||
followingKeySet,
|
||||
now,
|
||||
).signature
|
||||
}
|
||||
|
||||
return eventsToWatch.sortedWith(order)
|
||||
|
||||
+7
-13
@@ -25,10 +25,10 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.quartz.events.AppRecommendationEvent
|
||||
|
||||
class UserProfileAppRecommendationsFeedFilter(val user: User) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return user.pubkeyHex
|
||||
}
|
||||
class UserProfileAppRecommendationsFeedFilter(
|
||||
val user: User,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = user.pubkeyHex
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val recommendations =
|
||||
@@ -39,13 +39,9 @@ class UserProfileAppRecommendationsFeedFilter(val user: User) : AdditiveFeedFilt
|
||||
return sort(recommendations)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
return collection.mapNotNull { filterMap(it) }.flatten().toSet()
|
||||
}
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.mapNotNull { filterMap(it) }.flatten().toSet()
|
||||
|
||||
fun filterMap(it: Note): List<Note>? {
|
||||
val noteEvent = it.event
|
||||
@@ -58,7 +54,5 @@ class UserProfileAppRecommendationsFeedFilter(val user: User) : AdditiveFeedFilt
|
||||
return null
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
+5
-4
@@ -25,10 +25,11 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
|
||||
class UserProfileBookmarksFeedFilter(val user: User, val account: Account) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
}
|
||||
class UserProfileBookmarksFeedFilter(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : FeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val notes =
|
||||
|
||||
+13
-18
@@ -29,11 +29,11 @@ import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
|
||||
class UserProfileConversationsFeedFilter(val user: User, val account: Account) :
|
||||
AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
}
|
||||
class UserProfileConversationsFeedFilter(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val notes =
|
||||
@@ -49,27 +49,22 @@ class UserProfileConversationsFeedFilter(val user: User, val account: Account) :
|
||||
return sort(notes + longFormNotes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
return collection.filterTo(HashSet()) { acceptableEvent(it) }
|
||||
}
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet()) { acceptableEvent(it) }
|
||||
|
||||
fun acceptableEvent(it: Note): Boolean {
|
||||
return it.author == user &&
|
||||
fun acceptableEvent(it: Note): Boolean =
|
||||
it.author == user &&
|
||||
(
|
||||
it.event is TextNoteEvent ||
|
||||
it.event is PollNoteEvent ||
|
||||
it.event is ChannelMessageEvent ||
|
||||
it.event is LiveActivitiesChatMessageEvent
|
||||
) && !it.isNewThread() && account.isAcceptable(it)
|
||||
}
|
||||
) &&
|
||||
!it.isNewThread() &&
|
||||
account.isAcceptable(it)
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
|
||||
override fun limit() = 200
|
||||
}
|
||||
|
||||
+7
-7
@@ -24,16 +24,16 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
|
||||
class UserProfileFollowersFeedFilter(val user: User, val account: Account) : FeedFilter<User>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
}
|
||||
class UserProfileFollowersFeedFilter(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : FeedFilter<User>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
|
||||
override fun feed(): List<User> {
|
||||
return LocalCache.users.filter { _, it ->
|
||||
override fun feed(): List<User> =
|
||||
LocalCache.users.filter { _, it ->
|
||||
it.isFollowing(user) && !account.isHidden(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun limit() = 400
|
||||
}
|
||||
|
||||
+5
-4
@@ -25,10 +25,11 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||
|
||||
class UserProfileFollowsFeedFilter(val user: User, val account: Account) : FeedFilter<User>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
}
|
||||
class UserProfileFollowsFeedFilter(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : FeedFilter<User>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
|
||||
val cache: MutableMap<ContactListEvent, List<User>> = mutableMapOf()
|
||||
|
||||
|
||||
+10
-17
@@ -37,11 +37,11 @@ import com.vitorpamplona.quartz.events.RepostEvent
|
||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.WikiNoteEvent
|
||||
|
||||
class UserProfileNewThreadFeedFilter(val user: User, val account: Account) :
|
||||
AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
}
|
||||
class UserProfileNewThreadFeedFilter(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + user.pubkeyHex
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val notes =
|
||||
@@ -57,16 +57,12 @@ class UserProfileNewThreadFeedFilter(val user: User, val account: Account) :
|
||||
return sort(notes + longFormNotes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
return collection.filterTo(HashSet()) { acceptableEvent(it) }
|
||||
}
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> = collection.filterTo(HashSet()) { acceptableEvent(it) }
|
||||
|
||||
fun acceptableEvent(it: Note): Boolean {
|
||||
return it.author == user &&
|
||||
fun acceptableEvent(it: Note): Boolean =
|
||||
it.author == user &&
|
||||
(
|
||||
it.event is TextNoteEvent ||
|
||||
it.event is ClassifiedsEvent ||
|
||||
@@ -79,11 +75,8 @@ class UserProfileNewThreadFeedFilter(val user: User, val account: Account) :
|
||||
it.event is AudioTrackEvent ||
|
||||
it.event is AudioHeaderEvent
|
||||
) && it.isNewThread() && account.isAcceptable(it)
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
|
||||
override fun limit() = 200
|
||||
}
|
||||
|
||||
+9
-16
@@ -24,28 +24,21 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.quartz.events.ReportEvent
|
||||
|
||||
class UserProfileReportsFeedFilter(val user: User) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return user.pubkeyHex
|
||||
}
|
||||
class UserProfileReportsFeedFilter(
|
||||
val user: User,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = user.pubkeyHex
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
return sort(innerApplyFilter(user.reports.values.flatten()))
|
||||
}
|
||||
override fun feed(): List<Note> = sort(innerApplyFilter(user.reports.values.flatten()))
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
return collection
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> =
|
||||
collection
|
||||
.filter { it.event is ReportEvent && it.event?.isTaggedUser(user.pubkeyHex) == true }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
|
||||
override fun limit() = 400
|
||||
}
|
||||
|
||||
+5
-7
@@ -24,14 +24,12 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
|
||||
import com.vitorpamplona.quartz.events.zaps.UserZaps
|
||||
|
||||
class UserProfileZapsFeedFilter(val user: User) : FeedFilter<ZapReqResponse>() {
|
||||
override fun feedKey(): String {
|
||||
return user.pubkeyHex
|
||||
}
|
||||
class UserProfileZapsFeedFilter(
|
||||
val user: User,
|
||||
) : FeedFilter<ZapReqResponse>() {
|
||||
override fun feedKey(): String = user.pubkeyHex
|
||||
|
||||
override fun feed(): List<ZapReqResponse> {
|
||||
return UserZaps.forProfileFeed(user.zaps)
|
||||
}
|
||||
override fun feed(): List<ZapReqResponse> = UserZaps.forProfileFeed(user.zaps)
|
||||
|
||||
override fun limit() = 400
|
||||
}
|
||||
|
||||
@@ -31,15 +31,14 @@ import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import com.vitorpamplona.quartz.events.VideoHorizontalEvent
|
||||
import com.vitorpamplona.quartz.events.VideoVerticalEvent
|
||||
|
||||
class VideoFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String {
|
||||
return account.userProfile().pubkeyHex + "-" + account.defaultStoriesFollowList.value
|
||||
}
|
||||
class VideoFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.defaultStoriesFollowList.value
|
||||
|
||||
override fun showHiddenKey(): Boolean {
|
||||
return account.defaultStoriesFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
override fun showHiddenKey(): Boolean =
|
||||
account.defaultStoriesFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
|
||||
account.defaultStoriesFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
@@ -52,9 +51,7 @@ class VideoFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> = innerApplyFilter(collection)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
@@ -78,16 +75,13 @@ class VideoFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
|
||||
account.isAcceptable(it)
|
||||
}
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams {
|
||||
return FilterByListParams.create(
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
userHex = account.userProfile().pubkeyHex,
|
||||
selectedListName = account.defaultStoriesFollowList.value,
|
||||
followLists = account.liveStoriesFollowLists.value,
|
||||
hiddenUsers = account.flowHiddenUsers.value,
|
||||
)
|
||||
}
|
||||
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
override fun sort(collection: Set<Note>): List<Note> = collection.sortedWith(DefaultFeedOrder)
|
||||
}
|
||||
|
||||
@@ -385,7 +385,12 @@ fun AppNavigation(
|
||||
val activity = LocalContext.current.getActivity()
|
||||
|
||||
var currentIntentNextPage by remember {
|
||||
mutableStateOf(activity.intent?.data?.toString()?.ifBlank { null })
|
||||
mutableStateOf(
|
||||
activity.intent
|
||||
?.data
|
||||
?.toString()
|
||||
?.ifBlank { null },
|
||||
)
|
||||
}
|
||||
|
||||
currentIntentNextPage?.let { intentNextPage ->
|
||||
|
||||
@@ -126,18 +126,10 @@ fun routeToMessage(
|
||||
user: User,
|
||||
draftMessage: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
): String {
|
||||
return routeToMessage(user.pubkeyHex, draftMessage, accountViewModel)
|
||||
}
|
||||
): String = routeToMessage(user.pubkeyHex, draftMessage, accountViewModel)
|
||||
|
||||
fun routeFor(note: Channel): String {
|
||||
return "Channel/${note.idHex}"
|
||||
}
|
||||
fun routeFor(note: Channel): String = "Channel/${note.idHex}"
|
||||
|
||||
fun routeFor(user: User): String {
|
||||
return "User/${user.pubkeyHex}"
|
||||
}
|
||||
fun routeFor(user: User): String = "User/${user.pubkeyHex}"
|
||||
|
||||
fun authorRouteFor(note: Note): String {
|
||||
return "User/${note.author?.pubkeyHex}"
|
||||
}
|
||||
fun authorRouteFor(note: Note): String = "User/${note.author?.pubkeyHex}"
|
||||
|
||||
@@ -206,7 +206,8 @@ fun LoadCityName(
|
||||
val geoHash = runCatching { geohashStr.toGeoHash() }.getOrNull()
|
||||
if (geoHash != null) {
|
||||
val newCityName =
|
||||
CachedGeoLocations.geoLocate(geohashStr, geoHash.toLocation(), context)
|
||||
CachedGeoLocations
|
||||
.geoLocate(geohashStr, geoHash.toLocation(), context)
|
||||
?.ifBlank { null }
|
||||
if (newCityName != null && newCityName != cityName) {
|
||||
cityName = newCityName
|
||||
|
||||
@@ -86,7 +86,11 @@ class PollNoteViewModel : ViewModel() {
|
||||
valueMinimumBD = valueMinimum?.let { BigDecimal(it) }
|
||||
valueMaximumBD = valueMaximum?.let { BigDecimal(it) }
|
||||
consensusThreshold =
|
||||
pollEvent?.getTagLong(CONSENSUS_THRESHOLD)?.toFloat()?.div(100)?.toBigDecimal()
|
||||
pollEvent
|
||||
?.getTagLong(CONSENSUS_THRESHOLD)
|
||||
?.toFloat()
|
||||
?.div(100)
|
||||
?.toBigDecimal()
|
||||
closedAt = pollEvent?.getTagLong(CLOSED_AT)
|
||||
|
||||
totalZapped = BigDecimal.ZERO
|
||||
@@ -138,7 +142,8 @@ class PollNoteViewModel : ViewModel() {
|
||||
fun isVoteAmountAtomic() = valueMaximum != null && valueMinimum != null && valueMinimum == valueMaximum
|
||||
|
||||
fun isPollClosed(): Boolean =
|
||||
closedAt?.let { // allow 2 minute leeway for zap to propagate
|
||||
closedAt?.let {
|
||||
// allow 2 minute leeway for zap to propagate
|
||||
pollNote?.createdAt()?.plus(it * (86400 + 120))!! < TimeUtils.now()
|
||||
} == true
|
||||
|
||||
@@ -221,17 +226,16 @@ class PollNoteViewModel : ViewModel() {
|
||||
fun cachedIsPollOptionZappedBy(
|
||||
option: Int,
|
||||
user: User,
|
||||
): Boolean {
|
||||
return pollNote!!.zaps.any {
|
||||
): Boolean =
|
||||
pollNote!!.zaps.any {
|
||||
val zapEvent = it.value?.event as? LnZapEvent
|
||||
val privateZapAuthor = (it.key.event as? LnZapRequestEvent)?.cachedPrivateZap()
|
||||
zapEvent?.zappedPollOption() == option &&
|
||||
(it.key.author?.pubkeyHex == user.pubkeyHex || privateZapAuthor?.pubKey == user.pubkeyHex)
|
||||
}
|
||||
}
|
||||
|
||||
private fun zappedPollOptionAmount(option: Int): BigDecimal {
|
||||
return pollNote?.zaps?.values?.sumOf {
|
||||
private fun zappedPollOptionAmount(option: Int): BigDecimal =
|
||||
pollNote?.zaps?.values?.sumOf {
|
||||
val event = it?.event as? LnZapEvent
|
||||
val zapAmount = event?.amount ?: BigDecimal.ZERO
|
||||
val isValidAmount = isValidInputVoteAmount(event?.amount)
|
||||
@@ -243,10 +247,9 @@ class PollNoteViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
?: BigDecimal.ZERO
|
||||
}
|
||||
|
||||
private fun totalZapped(): BigDecimal {
|
||||
return pollNote?.zaps?.values?.sumOf {
|
||||
private fun totalZapped(): BigDecimal =
|
||||
pollNote?.zaps?.values?.sumOf {
|
||||
val zapEvent = (it?.event as? LnZapEvent)
|
||||
val zapAmount = zapEvent?.amount ?: BigDecimal.ZERO
|
||||
val isValidAmount = isValidInputVoteAmount(zapEvent?.amount)
|
||||
@@ -258,7 +261,6 @@ class PollNoteViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
?: BigDecimal.ZERO
|
||||
}
|
||||
|
||||
fun createZapOptionsThatMatchThePollingParameters(): List<Long> {
|
||||
val options =
|
||||
|
||||
@@ -23,15 +23,11 @@ package com.vitorpamplona.amethyst.ui.note
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
|
||||
fun ByteArray.toShortenHex(): String {
|
||||
return toHexKey().toShortenHex()
|
||||
}
|
||||
fun ByteArray.toShortenHex(): String = toHexKey().toShortenHex()
|
||||
|
||||
fun String.toShortenHex(): String {
|
||||
if (length <= 16) return this
|
||||
return replaceRange(8, length - 8, ":")
|
||||
}
|
||||
|
||||
fun HexKey.toDisplayHexKey(): String {
|
||||
return this.toShortenHex()
|
||||
}
|
||||
fun HexKey.toDisplayHexKey(): String = this.toShortenHex()
|
||||
|
||||
@@ -161,32 +161,31 @@ fun DrawScope.drawAllQrCodeDataBits(
|
||||
second = bytes.height,
|
||||
),
|
||||
),
|
||||
)
|
||||
.forEach { section ->
|
||||
for (y in section.first.second until section.second.second) {
|
||||
for (x in section.first.first until section.second.first) {
|
||||
if (bytes[x, y] == 1.toByte()) {
|
||||
drawPath(
|
||||
color = color,
|
||||
path =
|
||||
newPath {
|
||||
addRect(
|
||||
rect =
|
||||
Rect(
|
||||
offset =
|
||||
Offset(
|
||||
x = QR_MARGIN_PX + x * size.width,
|
||||
y = QR_MARGIN_PX + y * size.height,
|
||||
),
|
||||
size = size,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
).forEach { section ->
|
||||
for (y in section.first.second until section.second.second) {
|
||||
for (x in section.first.first until section.second.first) {
|
||||
if (bytes[x, y] == 1.toByte()) {
|
||||
drawPath(
|
||||
color = color,
|
||||
path =
|
||||
newPath {
|
||||
addRect(
|
||||
rect =
|
||||
Rect(
|
||||
offset =
|
||||
Offset(
|
||||
x = QR_MARGIN_PX + x * size.width,
|
||||
y = QR_MARGIN_PX + y * size.height,
|
||||
),
|
||||
size = size,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val FINDER_PATTERN_ROW_COUNT = 7
|
||||
@@ -216,15 +215,14 @@ internal fun DrawScope.drawQrCodeFinders(
|
||||
Offset(x = sideLength - (QR_MARGIN_PX + finderPatternSize.width), y = QR_MARGIN_PX),
|
||||
// Draw bottom finder pattern.
|
||||
Offset(x = QR_MARGIN_PX, y = sideLength - (QR_MARGIN_PX + finderPatternSize.height)),
|
||||
)
|
||||
.forEach { offset ->
|
||||
drawQrCodeFinder(
|
||||
topLeft = offset,
|
||||
finderPatternSize = finderPatternSize,
|
||||
cornerRadius = CornerRadius.Zero,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
).forEach { offset ->
|
||||
drawQrCodeFinder(
|
||||
topLeft = offset,
|
||||
finderPatternSize = finderPatternSize,
|
||||
cornerRadius = CornerRadius.Zero,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** This func is responsible for drawing a single finder pattern, for a QR code */
|
||||
|
||||
@@ -27,11 +27,15 @@ sealed class AccountState {
|
||||
|
||||
object LoggedOff : AccountState()
|
||||
|
||||
class LoggedInViewOnly(val account: Account) : AccountState() {
|
||||
class LoggedInViewOnly(
|
||||
val account: Account,
|
||||
) : AccountState() {
|
||||
val currentViewModelStore = AccountCentricViewModelStore(account)
|
||||
}
|
||||
|
||||
class LoggedIn(val account: Account) : AccountState() {
|
||||
class LoggedIn(
|
||||
val account: Account,
|
||||
) : AccountState() {
|
||||
val currentViewModelStore = AccountCentricViewModelStore(account)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,37 +32,38 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
|
||||
@Immutable
|
||||
abstract class Card() {
|
||||
abstract class Card {
|
||||
abstract fun createdAt(): Long
|
||||
|
||||
abstract fun id(): String
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class BadgeCard(val note: Note) : Card() {
|
||||
override fun createdAt(): Long {
|
||||
return note.createdAt() ?: 0
|
||||
}
|
||||
class BadgeCard(
|
||||
val note: Note,
|
||||
) : Card() {
|
||||
override fun createdAt(): Long = note.createdAt() ?: 0
|
||||
|
||||
override fun id() = note.idHex
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class NoteCard(val note: Note) : Card() {
|
||||
override fun createdAt(): Long {
|
||||
return note.createdAt() ?: 0
|
||||
}
|
||||
class NoteCard(
|
||||
val note: Note,
|
||||
) : Card() {
|
||||
override fun createdAt(): Long = note.createdAt() ?: 0
|
||||
|
||||
override fun id() = note.idHex
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class ZapUserSetCard(val user: User, val zapEvents: ImmutableList<CombinedZap>) : Card() {
|
||||
class ZapUserSetCard(
|
||||
val user: User,
|
||||
val zapEvents: ImmutableList<CombinedZap>,
|
||||
) : Card() {
|
||||
val createdAt = zapEvents.maxOf { it.createdAt() ?: 0 }
|
||||
|
||||
override fun createdAt(): Long {
|
||||
return createdAt
|
||||
}
|
||||
override fun createdAt(): Long = createdAt
|
||||
|
||||
override fun id() = user.pubkeyHex + "U" + createdAt
|
||||
}
|
||||
@@ -95,22 +96,19 @@ class MultiSetCard(
|
||||
?.content()
|
||||
?.firstFullCharOrEmoji(ImmutableListOfLists(it.event?.tags() ?: emptyArray()))
|
||||
?: "+"
|
||||
}
|
||||
.mapValues { it.value.toImmutableList() }
|
||||
}.mapValues { it.value.toImmutableList() }
|
||||
.toImmutableMap()
|
||||
|
||||
override fun createdAt(): Long {
|
||||
return maxCreatedAt
|
||||
}
|
||||
override fun createdAt(): Long = maxCreatedAt
|
||||
|
||||
override fun id() = note.idHex + "X" + maxCreatedAt + "X" + minCreatedAt
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class MessageSetCard(val note: Note) : Card() {
|
||||
override fun createdAt(): Long {
|
||||
return note.createdAt() ?: 0
|
||||
}
|
||||
class MessageSetCard(
|
||||
val note: Note,
|
||||
) : Card() {
|
||||
override fun createdAt(): Long = note.createdAt() ?: 0
|
||||
|
||||
override fun id() = note.idHex
|
||||
}
|
||||
@@ -120,10 +118,14 @@ sealed class CardFeedState {
|
||||
@Immutable object Loading : CardFeedState()
|
||||
|
||||
@Stable
|
||||
class Loaded(val feed: MutableState<ImmutableList<Card>>, val showHidden: MutableState<Boolean>) :
|
||||
CardFeedState()
|
||||
class Loaded(
|
||||
val feed: MutableState<ImmutableList<Card>>,
|
||||
val showHidden: MutableState<Boolean>,
|
||||
) : CardFeedState()
|
||||
|
||||
@Immutable object Empty : CardFeedState()
|
||||
|
||||
@Immutable class FeedError(val errorMessage: String) : CardFeedState()
|
||||
@Immutable class FeedError(
|
||||
val errorMessage: String,
|
||||
) : CardFeedState()
|
||||
}
|
||||
|
||||
@@ -60,17 +60,21 @@ import java.time.format.DateTimeFormatter
|
||||
import kotlin.time.measureTimedValue
|
||||
|
||||
@Stable
|
||||
class NotificationViewModel(val account: Account) :
|
||||
CardFeedViewModel(NotificationFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NotificationViewModel : ViewModel> create(modelClass: Class<NotificationViewModel>): NotificationViewModel {
|
||||
return NotificationViewModel(account) as NotificationViewModel
|
||||
}
|
||||
class NotificationViewModel(
|
||||
val account: Account,
|
||||
) : CardFeedViewModel(NotificationFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NotificationViewModel : ViewModel> create(modelClass: Class<NotificationViewModel>): NotificationViewModel = NotificationViewModel(account) as NotificationViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), InvalidatableViewModel {
|
||||
open class CardFeedViewModel(
|
||||
val localFilter: FeedFilter<Note>,
|
||||
) : ViewModel(),
|
||||
InvalidatableViewModel {
|
||||
private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading)
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
@@ -167,7 +171,11 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
.forEach { zapEvent ->
|
||||
val zappedPost = zapEvent.replyTo?.lastOrNull()
|
||||
if (zappedPost != null) {
|
||||
val zapRequest = zappedPost.zaps.filter { it.value == zapEvent }.keys.firstOrNull()
|
||||
val zapRequest =
|
||||
zappedPost.zaps
|
||||
.filter { it.value == zapEvent }
|
||||
.keys
|
||||
.firstOrNull()
|
||||
if (zapRequest != null) {
|
||||
// var newZapRequestEvent = LocalCache.checkPrivateZap(zapRequest.event as Event)
|
||||
// zapRequest.event = newZapRequestEvent
|
||||
@@ -182,7 +190,11 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
LocalCache.getUserIfExists(it) // don't create user if it doesn't exist
|
||||
}
|
||||
if (author != null) {
|
||||
val zapRequest = author.zaps.filter { it.value == zapEvent }.keys.firstOrNull()
|
||||
val zapRequest =
|
||||
author.zaps
|
||||
.filter { it.value == zapEvent }
|
||||
.keys
|
||||
.firstOrNull()
|
||||
if (zapRequest != null) {
|
||||
zapsPerUser
|
||||
.getOrPut(author, { mutableListOf() })
|
||||
@@ -218,7 +230,8 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
val singleList =
|
||||
(boostsInCard + zapsInCard.map { it.response } + reactionsInCard).groupBy {
|
||||
sdf.format(
|
||||
Instant.ofEpochSecond(it.createdAt() ?: 0)
|
||||
Instant
|
||||
.ofEpochSecond(it.createdAt() ?: 0)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime(),
|
||||
)
|
||||
@@ -242,10 +255,8 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
zapsInCard.filter { it.response in chunk }.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
.flatten()
|
||||
}
|
||||
.flatten()
|
||||
}.flatten()
|
||||
}.flatten()
|
||||
|
||||
val userZaps =
|
||||
zapsPerUser
|
||||
@@ -253,7 +264,8 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
val byDay =
|
||||
user.value.groupBy {
|
||||
sdf.format(
|
||||
Instant.ofEpochSecond(it.createdAt() ?: 0)
|
||||
Instant
|
||||
.ofEpochSecond(it.createdAt() ?: 0)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime(),
|
||||
)
|
||||
@@ -268,8 +280,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
.flatten()
|
||||
}.flatten()
|
||||
|
||||
val textNoteCards =
|
||||
notes
|
||||
@@ -278,8 +289,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
it.event !is RepostEvent &&
|
||||
it.event !is GenericRepostEvent &&
|
||||
it.event !is LnZapEvent
|
||||
}
|
||||
.map {
|
||||
}.map {
|
||||
if (it.event is PrivateDmEvent || it.event is ChatMessageEvent) {
|
||||
MessageSetCard(it)
|
||||
} else if (it.event is BadgeAwardEvent) {
|
||||
@@ -464,7 +474,10 @@ fun <T> equalImmutableLists(
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class CombinedZap(val request: Note, val response: Note) {
|
||||
data class CombinedZap(
|
||||
val request: Note,
|
||||
val response: Note,
|
||||
) {
|
||||
fun createdAt() = response.createdAt()
|
||||
|
||||
fun idHex() = response.idHex
|
||||
|
||||
@@ -34,5 +34,7 @@ sealed class FeedState {
|
||||
|
||||
object Empty : FeedState()
|
||||
|
||||
class FeedError(val errorMessage: String) : FeedState()
|
||||
class FeedError(
|
||||
val errorMessage: String,
|
||||
) : FeedState()
|
||||
}
|
||||
|
||||
@@ -73,248 +73,302 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NostrChannelFeedViewModel(val channel: Channel, val account: Account) :
|
||||
FeedViewModel(ChannelFeedFilter(channel, account)) {
|
||||
class Factory(val channel: Channel, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrChannelFeedViewModel : ViewModel> create(modelClass: Class<NostrChannelFeedViewModel>): NostrChannelFeedViewModel {
|
||||
return NostrChannelFeedViewModel(channel, account) as NostrChannelFeedViewModel
|
||||
}
|
||||
class NostrChannelFeedViewModel(
|
||||
val channel: Channel,
|
||||
val account: Account,
|
||||
) : FeedViewModel(ChannelFeedFilter(channel, account)) {
|
||||
class Factory(
|
||||
val channel: Channel,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrChannelFeedViewModel : ViewModel> create(modelClass: Class<NostrChannelFeedViewModel>): NostrChannelFeedViewModel = NostrChannelFeedViewModel(channel, account) as NostrChannelFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrChatroomFeedViewModel(val user: ChatroomKey, val account: Account) :
|
||||
FeedViewModel(ChatroomFeedFilter(user, account)) {
|
||||
class Factory(val user: ChatroomKey, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrChatRoomFeedViewModel : ViewModel> create(modelClass: Class<NostrChatRoomFeedViewModel>): NostrChatRoomFeedViewModel {
|
||||
return NostrChatroomFeedViewModel(user, account) as NostrChatRoomFeedViewModel
|
||||
}
|
||||
class NostrChatroomFeedViewModel(
|
||||
val user: ChatroomKey,
|
||||
val account: Account,
|
||||
) : FeedViewModel(ChatroomFeedFilter(user, account)) {
|
||||
class Factory(
|
||||
val user: ChatroomKey,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrChatRoomFeedViewModel : ViewModel> create(modelClass: Class<NostrChatRoomFeedViewModel>): NostrChatRoomFeedViewModel = NostrChatroomFeedViewModel(user, account) as NostrChatRoomFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrVideoFeedViewModel(val account: Account) : FeedViewModel(VideoFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrVideoFeedViewModel : ViewModel> create(modelClass: Class<NostrVideoFeedViewModel>): NostrVideoFeedViewModel {
|
||||
return NostrVideoFeedViewModel(account) as NostrVideoFeedViewModel
|
||||
}
|
||||
class NostrVideoFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(VideoFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrVideoFeedViewModel : ViewModel> create(modelClass: Class<NostrVideoFeedViewModel>): NostrVideoFeedViewModel = NostrVideoFeedViewModel(account) as NostrVideoFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrDiscoverMarketplaceFeedViewModel(val account: Account) :
|
||||
FeedViewModel(
|
||||
class NostrDiscoverMarketplaceFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(
|
||||
DiscoverMarketplaceFeedFilter(account),
|
||||
) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverMarketplaceFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverMarketplaceFeedViewModel>): NostrDiscoverMarketplaceFeedViewModel {
|
||||
return NostrDiscoverMarketplaceFeedViewModel(account) as NostrDiscoverMarketplaceFeedViewModel
|
||||
}
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverMarketplaceFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverMarketplaceFeedViewModel>): NostrDiscoverMarketplaceFeedViewModel = NostrDiscoverMarketplaceFeedViewModel(account) as NostrDiscoverMarketplaceFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrDiscoverNIP89FeedViewModel(val account: Account) :
|
||||
FeedViewModel(
|
||||
class NostrDiscoverNIP89FeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(
|
||||
DiscoverNIP89FeedFilter(account),
|
||||
) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverNIP89FeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverNIP89FeedViewModel>): NostrDiscoverNIP89FeedViewModel {
|
||||
return NostrDiscoverNIP89FeedViewModel(account) as NostrDiscoverNIP89FeedViewModel
|
||||
}
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverNIP89FeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverNIP89FeedViewModel>): NostrDiscoverNIP89FeedViewModel = NostrDiscoverNIP89FeedViewModel(account) as NostrDiscoverNIP89FeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrDiscoverLiveFeedViewModel(val account: Account) :
|
||||
FeedViewModel(DiscoverLiveFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverLiveFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverLiveFeedViewModel>): NostrDiscoverLiveFeedViewModel {
|
||||
return NostrDiscoverLiveFeedViewModel(account) as NostrDiscoverLiveFeedViewModel
|
||||
}
|
||||
class NostrDiscoverLiveFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(DiscoverLiveFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverLiveFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverLiveFeedViewModel>): NostrDiscoverLiveFeedViewModel = NostrDiscoverLiveFeedViewModel(account) as NostrDiscoverLiveFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrDiscoverCommunityFeedViewModel(val account: Account) :
|
||||
FeedViewModel(DiscoverCommunityFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverCommunityFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverCommunityFeedViewModel>): NostrDiscoverCommunityFeedViewModel {
|
||||
return NostrDiscoverCommunityFeedViewModel(account) as NostrDiscoverCommunityFeedViewModel
|
||||
}
|
||||
class NostrDiscoverCommunityFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(DiscoverCommunityFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverCommunityFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverCommunityFeedViewModel>): NostrDiscoverCommunityFeedViewModel = NostrDiscoverCommunityFeedViewModel(account) as NostrDiscoverCommunityFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrDiscoverChatFeedViewModel(val account: Account) :
|
||||
FeedViewModel(DiscoverChatFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverChatFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverChatFeedViewModel>): NostrDiscoverChatFeedViewModel {
|
||||
return NostrDiscoverChatFeedViewModel(account) as NostrDiscoverChatFeedViewModel
|
||||
}
|
||||
class NostrDiscoverChatFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(DiscoverChatFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrDiscoverChatFeedViewModel : ViewModel> create(modelClass: Class<NostrDiscoverChatFeedViewModel>): NostrDiscoverChatFeedViewModel = NostrDiscoverChatFeedViewModel(account) as NostrDiscoverChatFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrThreadFeedViewModel(account: Account, noteId: String) :
|
||||
FeedViewModel(ThreadFeedFilter(account, noteId)) {
|
||||
class Factory(val account: Account, val noteId: String) : ViewModelProvider.Factory {
|
||||
override fun <NostrThreadFeedViewModel : ViewModel> create(modelClass: Class<NostrThreadFeedViewModel>): NostrThreadFeedViewModel {
|
||||
return NostrThreadFeedViewModel(account, noteId) as NostrThreadFeedViewModel
|
||||
}
|
||||
class NostrThreadFeedViewModel(
|
||||
account: Account,
|
||||
noteId: String,
|
||||
) : FeedViewModel(ThreadFeedFilter(account, noteId)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
val noteId: String,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrThreadFeedViewModel : ViewModel> create(modelClass: Class<NostrThreadFeedViewModel>): NostrThreadFeedViewModel = NostrThreadFeedViewModel(account, noteId) as NostrThreadFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrUserProfileNewThreadsFeedViewModel(val user: User, val account: Account) :
|
||||
FeedViewModel(UserProfileNewThreadFeedFilter(user, account)) {
|
||||
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileNewThreadsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileNewThreadsFeedViewModel>): NostrUserProfileNewThreadsFeedViewModel {
|
||||
return NostrUserProfileNewThreadsFeedViewModel(user, account)
|
||||
class NostrUserProfileNewThreadsFeedViewModel(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : FeedViewModel(UserProfileNewThreadFeedFilter(user, account)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileNewThreadsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileNewThreadsFeedViewModel>): NostrUserProfileNewThreadsFeedViewModel =
|
||||
NostrUserProfileNewThreadsFeedViewModel(user, account)
|
||||
as NostrUserProfileNewThreadsFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NostrUserProfileConversationsFeedViewModel(val user: User, val account: Account) :
|
||||
FeedViewModel(UserProfileConversationsFeedFilter(user, account)) {
|
||||
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileConversationsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileConversationsFeedViewModel>): NostrUserProfileConversationsFeedViewModel {
|
||||
return NostrUserProfileConversationsFeedViewModel(user, account)
|
||||
class NostrUserProfileConversationsFeedViewModel(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : FeedViewModel(UserProfileConversationsFeedFilter(user, account)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileConversationsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileConversationsFeedViewModel>): NostrUserProfileConversationsFeedViewModel =
|
||||
NostrUserProfileConversationsFeedViewModel(user, account)
|
||||
as NostrUserProfileConversationsFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NostrHashtagFeedViewModel(val hashtag: String, val account: Account) :
|
||||
FeedViewModel(HashtagFeedFilter(hashtag, account)) {
|
||||
class Factory(val hashtag: String, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrHashtagFeedViewModel : ViewModel> create(modelClass: Class<NostrHashtagFeedViewModel>): NostrHashtagFeedViewModel {
|
||||
return NostrHashtagFeedViewModel(hashtag, account) as NostrHashtagFeedViewModel
|
||||
}
|
||||
class NostrHashtagFeedViewModel(
|
||||
val hashtag: String,
|
||||
val account: Account,
|
||||
) : FeedViewModel(HashtagFeedFilter(hashtag, account)) {
|
||||
class Factory(
|
||||
val hashtag: String,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrHashtagFeedViewModel : ViewModel> create(modelClass: Class<NostrHashtagFeedViewModel>): NostrHashtagFeedViewModel = NostrHashtagFeedViewModel(hashtag, account) as NostrHashtagFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrGeoHashFeedViewModel(val geohash: String, val account: Account) :
|
||||
FeedViewModel(GeoHashFeedFilter(geohash, account)) {
|
||||
class Factory(val geohash: String, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrGeoHashFeedViewModel : ViewModel> create(modelClass: Class<NostrGeoHashFeedViewModel>): NostrGeoHashFeedViewModel {
|
||||
return NostrGeoHashFeedViewModel(geohash, account) as NostrGeoHashFeedViewModel
|
||||
}
|
||||
class NostrGeoHashFeedViewModel(
|
||||
val geohash: String,
|
||||
val account: Account,
|
||||
) : FeedViewModel(GeoHashFeedFilter(geohash, account)) {
|
||||
class Factory(
|
||||
val geohash: String,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrGeoHashFeedViewModel : ViewModel> create(modelClass: Class<NostrGeoHashFeedViewModel>): NostrGeoHashFeedViewModel = NostrGeoHashFeedViewModel(geohash, account) as NostrGeoHashFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrCommunityFeedViewModel(val note: AddressableNote, val account: Account) :
|
||||
FeedViewModel(CommunityFeedFilter(note, account)) {
|
||||
class Factory(val note: AddressableNote, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrCommunityFeedViewModel : ViewModel> create(modelClass: Class<NostrCommunityFeedViewModel>): NostrCommunityFeedViewModel {
|
||||
return NostrCommunityFeedViewModel(note, account) as NostrCommunityFeedViewModel
|
||||
}
|
||||
class NostrCommunityFeedViewModel(
|
||||
val note: AddressableNote,
|
||||
val account: Account,
|
||||
) : FeedViewModel(CommunityFeedFilter(note, account)) {
|
||||
class Factory(
|
||||
val note: AddressableNote,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrCommunityFeedViewModel : ViewModel> create(modelClass: Class<NostrCommunityFeedViewModel>): NostrCommunityFeedViewModel = NostrCommunityFeedViewModel(note, account) as NostrCommunityFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrUserProfileReportFeedViewModel(val user: User) :
|
||||
FeedViewModel(UserProfileReportsFeedFilter(user)) {
|
||||
class Factory(val user: User) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileReportFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileReportFeedViewModel>): NostrUserProfileReportFeedViewModel {
|
||||
return NostrUserProfileReportFeedViewModel(user) as NostrUserProfileReportFeedViewModel
|
||||
}
|
||||
class NostrUserProfileReportFeedViewModel(
|
||||
val user: User,
|
||||
) : FeedViewModel(UserProfileReportsFeedFilter(user)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileReportFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileReportFeedViewModel>): NostrUserProfileReportFeedViewModel = NostrUserProfileReportFeedViewModel(user) as NostrUserProfileReportFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrUserProfileBookmarksFeedViewModel(val user: User, val account: Account) :
|
||||
FeedViewModel(UserProfileBookmarksFeedFilter(user, account)) {
|
||||
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileBookmarksFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileBookmarksFeedViewModel>): NostrUserProfileBookmarksFeedViewModel {
|
||||
return NostrUserProfileBookmarksFeedViewModel(user, account)
|
||||
class NostrUserProfileBookmarksFeedViewModel(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : FeedViewModel(UserProfileBookmarksFeedFilter(user, account)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileBookmarksFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileBookmarksFeedViewModel>): NostrUserProfileBookmarksFeedViewModel =
|
||||
NostrUserProfileBookmarksFeedViewModel(user, account)
|
||||
as NostrUserProfileBookmarksFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NostrChatroomListKnownFeedViewModel(val account: Account) :
|
||||
FeedViewModel(ChatroomListKnownFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrChatroomListKnownFeedViewModel : ViewModel> create(modelClass: Class<NostrChatroomListKnownFeedViewModel>): NostrChatroomListKnownFeedViewModel {
|
||||
return NostrChatroomListKnownFeedViewModel(account) as NostrChatroomListKnownFeedViewModel
|
||||
}
|
||||
class NostrChatroomListKnownFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(ChatroomListKnownFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrChatroomListKnownFeedViewModel : ViewModel> create(modelClass: Class<NostrChatroomListKnownFeedViewModel>): NostrChatroomListKnownFeedViewModel = NostrChatroomListKnownFeedViewModel(account) as NostrChatroomListKnownFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrChatroomListNewFeedViewModel(val account: Account) :
|
||||
FeedViewModel(ChatroomListNewFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrChatroomListNewFeedViewModel : ViewModel> create(modelClass: Class<NostrChatroomListNewFeedViewModel>): NostrChatroomListNewFeedViewModel {
|
||||
return NostrChatroomListNewFeedViewModel(account) as NostrChatroomListNewFeedViewModel
|
||||
}
|
||||
class NostrChatroomListNewFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(ChatroomListNewFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrChatroomListNewFeedViewModel : ViewModel> create(modelClass: Class<NostrChatroomListNewFeedViewModel>): NostrChatroomListNewFeedViewModel = NostrChatroomListNewFeedViewModel(account) as NostrChatroomListNewFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrHomeFeedViewModel(val account: Account) :
|
||||
FeedViewModel(HomeNewThreadFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrHomeFeedViewModel : ViewModel> create(modelClass: Class<NostrHomeFeedViewModel>): NostrHomeFeedViewModel {
|
||||
return NostrHomeFeedViewModel(account) as NostrHomeFeedViewModel
|
||||
}
|
||||
class NostrHomeFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(HomeNewThreadFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrHomeFeedViewModel : ViewModel> create(modelClass: Class<NostrHomeFeedViewModel>): NostrHomeFeedViewModel = NostrHomeFeedViewModel(account) as NostrHomeFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrHomeRepliesFeedViewModel(val account: Account) :
|
||||
FeedViewModel(HomeConversationsFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrHomeRepliesFeedViewModel : ViewModel> create(modelClass: Class<NostrHomeRepliesFeedViewModel>): NostrHomeRepliesFeedViewModel {
|
||||
return NostrHomeRepliesFeedViewModel(account) as NostrHomeRepliesFeedViewModel
|
||||
}
|
||||
class NostrHomeRepliesFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(HomeConversationsFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrHomeRepliesFeedViewModel : ViewModel> create(modelClass: Class<NostrHomeRepliesFeedViewModel>): NostrHomeRepliesFeedViewModel = NostrHomeRepliesFeedViewModel(account) as NostrHomeRepliesFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrBookmarkPublicFeedViewModel(val account: Account) :
|
||||
FeedViewModel(BookmarkPublicFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrBookmarkPublicFeedViewModel : ViewModel> create(modelClass: Class<NostrBookmarkPublicFeedViewModel>): NostrBookmarkPublicFeedViewModel {
|
||||
return NostrBookmarkPublicFeedViewModel(account) as NostrBookmarkPublicFeedViewModel
|
||||
}
|
||||
class NostrBookmarkPublicFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(BookmarkPublicFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrBookmarkPublicFeedViewModel : ViewModel> create(modelClass: Class<NostrBookmarkPublicFeedViewModel>): NostrBookmarkPublicFeedViewModel = NostrBookmarkPublicFeedViewModel(account) as NostrBookmarkPublicFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrBookmarkPrivateFeedViewModel(val account: Account) :
|
||||
FeedViewModel(BookmarkPrivateFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrBookmarkPrivateFeedViewModel : ViewModel> create(modelClass: Class<NostrBookmarkPrivateFeedViewModel>): NostrBookmarkPrivateFeedViewModel {
|
||||
return NostrBookmarkPrivateFeedViewModel(account) as NostrBookmarkPrivateFeedViewModel
|
||||
}
|
||||
class NostrBookmarkPrivateFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(BookmarkPrivateFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrBookmarkPrivateFeedViewModel : ViewModel> create(modelClass: Class<NostrBookmarkPrivateFeedViewModel>): NostrBookmarkPrivateFeedViewModel = NostrBookmarkPrivateFeedViewModel(account) as NostrBookmarkPrivateFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrNIP90ContentDiscoveryFeedViewModel(val account: Account, val dvmkey: String, val requestid: String) :
|
||||
FeedViewModel(NIP90ContentDiscoveryResponseFilter(account, dvmkey, requestid)) {
|
||||
class Factory(val account: Account, val dvmkey: String, val requestid: String) : ViewModelProvider.Factory {
|
||||
override fun <NostrNIP90ContentDiscoveryFeedViewModel : ViewModel> create(modelClass: Class<NostrNIP90ContentDiscoveryFeedViewModel>): NostrNIP90ContentDiscoveryFeedViewModel {
|
||||
return NostrNIP90ContentDiscoveryFeedViewModel(account, dvmkey, requestid) as NostrNIP90ContentDiscoveryFeedViewModel
|
||||
}
|
||||
class NostrNIP90ContentDiscoveryFeedViewModel(
|
||||
val account: Account,
|
||||
val dvmkey: String,
|
||||
val requestid: String,
|
||||
) : FeedViewModel(NIP90ContentDiscoveryResponseFilter(account, dvmkey, requestid)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
val dvmkey: String,
|
||||
val requestid: String,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrNIP90ContentDiscoveryFeedViewModel : ViewModel> create(modelClass: Class<NostrNIP90ContentDiscoveryFeedViewModel>): NostrNIP90ContentDiscoveryFeedViewModel = NostrNIP90ContentDiscoveryFeedViewModel(account, dvmkey, requestid) as NostrNIP90ContentDiscoveryFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class NostrDraftEventsFeedViewModel(val account: Account) :
|
||||
FeedViewModel(DraftEventsFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrDraftEventsFeedViewModel : ViewModel> create(modelClass: Class<NostrDraftEventsFeedViewModel>): NostrDraftEventsFeedViewModel {
|
||||
return NostrDraftEventsFeedViewModel(account) as NostrDraftEventsFeedViewModel
|
||||
}
|
||||
class NostrDraftEventsFeedViewModel(
|
||||
val account: Account,
|
||||
) : FeedViewModel(DraftEventsFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrDraftEventsFeedViewModel : ViewModel> create(modelClass: Class<NostrDraftEventsFeedViewModel>): NostrDraftEventsFeedViewModel = NostrDraftEventsFeedViewModel(account) as NostrDraftEventsFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrUserAppRecommendationsFeedViewModel(val user: User) :
|
||||
FeedViewModel(UserProfileAppRecommendationsFeedFilter(user)) {
|
||||
class Factory(val user: User) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserAppRecommendationsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserAppRecommendationsFeedViewModel>): NostrUserAppRecommendationsFeedViewModel {
|
||||
return NostrUserAppRecommendationsFeedViewModel(user)
|
||||
class NostrUserAppRecommendationsFeedViewModel(
|
||||
val user: User,
|
||||
) : FeedViewModel(UserProfileAppRecommendationsFeedFilter(user)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserAppRecommendationsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserAppRecommendationsFeedViewModel>): NostrUserAppRecommendationsFeedViewModel =
|
||||
NostrUserAppRecommendationsFeedViewModel(user)
|
||||
as NostrUserAppRecommendationsFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
abstract class FeedViewModel(val localFilter: FeedFilter<Note>) :
|
||||
ViewModel(), InvalidatableViewModel {
|
||||
abstract class FeedViewModel(
|
||||
val localFilter: FeedFilter<Note>,
|
||||
) : ViewModel(),
|
||||
InvalidatableViewModel {
|
||||
private val _feedContent = MutableStateFlow<FeedState>(FeedState.Loading)
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
@@ -391,7 +445,9 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) :
|
||||
} else {
|
||||
val deletedEventIds = deletionEvents.flatMapTo(HashSet()) { it.deleteEvents() }
|
||||
val deletedEventAddresses = deletionEvents.flatMapTo(HashSet()) { it.deleteAddresses() }
|
||||
oldNotesState.feed.value.filter { !it.wasOrShouldBeDeletedBy(deletedEventIds, deletedEventAddresses) }.toImmutableList()
|
||||
oldNotesState.feed.value
|
||||
.filter { !it.wasOrShouldBeDeletedBy(deletedEventIds, deletedEventAddresses) }
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
val newList =
|
||||
|
||||
@@ -26,15 +26,22 @@ import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable data class ZapReqResponse(val zapRequest: Note, val zapEvent: Note)
|
||||
@Immutable data class ZapReqResponse(
|
||||
val zapRequest: Note,
|
||||
val zapEvent: Note,
|
||||
)
|
||||
|
||||
@Stable
|
||||
sealed class LnZapFeedState {
|
||||
object Loading : LnZapFeedState()
|
||||
|
||||
class Loaded(val feed: MutableState<ImmutableList<ZapReqResponse>>) : LnZapFeedState()
|
||||
class Loaded(
|
||||
val feed: MutableState<ImmutableList<ZapReqResponse>>,
|
||||
) : LnZapFeedState()
|
||||
|
||||
object Empty : LnZapFeedState()
|
||||
|
||||
class FeedError(val errorMessage: String) : LnZapFeedState()
|
||||
class FeedError(
|
||||
val errorMessage: String,
|
||||
) : LnZapFeedState()
|
||||
}
|
||||
|
||||
@@ -41,17 +41,20 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NostrUserProfileZapsFeedViewModel(user: User) :
|
||||
LnZapFeedViewModel(UserProfileZapsFeedFilter(user)) {
|
||||
class Factory(val user: User) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileZapsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileZapsFeedViewModel>): NostrUserProfileZapsFeedViewModel {
|
||||
return NostrUserProfileZapsFeedViewModel(user) as NostrUserProfileZapsFeedViewModel
|
||||
}
|
||||
class NostrUserProfileZapsFeedViewModel(
|
||||
user: User,
|
||||
) : LnZapFeedViewModel(UserProfileZapsFeedFilter(user)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileZapsFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileZapsFeedViewModel>): NostrUserProfileZapsFeedViewModel = NostrUserProfileZapsFeedViewModel(user) as NostrUserProfileZapsFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
open class LnZapFeedViewModel(val dataSource: FeedFilter<ZapReqResponse>) : ViewModel() {
|
||||
open class LnZapFeedViewModel(
|
||||
val dataSource: FeedFilter<ZapReqResponse>,
|
||||
) : ViewModel() {
|
||||
private val _feedContent = MutableStateFlow<LnZapFeedState>(LnZapFeedState.Loading)
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
|
||||
@@ -50,7 +50,9 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Stable
|
||||
class RelayFeedViewModel : ViewModel(), InvalidatableViewModel {
|
||||
class RelayFeedViewModel :
|
||||
ViewModel(),
|
||||
InvalidatableViewModel {
|
||||
val order =
|
||||
compareByDescending<RelayInfo> { it.lastEvent }
|
||||
.thenByDescending { it.counter }
|
||||
|
||||
@@ -26,9 +26,13 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
sealed class StringFeedState {
|
||||
object Loading : StringFeedState()
|
||||
|
||||
class Loaded(val feed: MutableState<ImmutableList<String>>) : StringFeedState()
|
||||
class Loaded(
|
||||
val feed: MutableState<ImmutableList<String>>,
|
||||
) : StringFeedState()
|
||||
|
||||
object Empty : StringFeedState()
|
||||
|
||||
class FeedError(val errorMessage: String) : StringFeedState()
|
||||
class FeedError(
|
||||
val errorMessage: String,
|
||||
) : StringFeedState()
|
||||
}
|
||||
|
||||
@@ -41,20 +41,23 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NostrHiddenWordsFeedViewModel(val account: Account) :
|
||||
StringFeedViewModel(
|
||||
class NostrHiddenWordsFeedViewModel(
|
||||
val account: Account,
|
||||
) : StringFeedViewModel(
|
||||
HiddenWordsFeedFilter(account),
|
||||
) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrHiddenWordsFeedViewModel : ViewModel> create(modelClass: Class<NostrHiddenWordsFeedViewModel>): NostrHiddenWordsFeedViewModel {
|
||||
return NostrHiddenWordsFeedViewModel(account) as NostrHiddenWordsFeedViewModel
|
||||
}
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrHiddenWordsFeedViewModel : ViewModel> create(modelClass: Class<NostrHiddenWordsFeedViewModel>): NostrHiddenWordsFeedViewModel = NostrHiddenWordsFeedViewModel(account) as NostrHiddenWordsFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
open class StringFeedViewModel(val dataSource: FeedFilter<String>) :
|
||||
ViewModel(), InvalidatableViewModel {
|
||||
open class StringFeedViewModel(
|
||||
val dataSource: FeedFilter<String>,
|
||||
) : ViewModel(),
|
||||
InvalidatableViewModel {
|
||||
private val _feedContent = MutableStateFlow<StringFeedState>(StringFeedState.Loading)
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
|
||||
@@ -27,9 +27,13 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
sealed class UserFeedState {
|
||||
object Loading : UserFeedState()
|
||||
|
||||
class Loaded(val feed: MutableState<ImmutableList<User>>) : UserFeedState()
|
||||
class Loaded(
|
||||
val feed: MutableState<ImmutableList<User>>,
|
||||
) : UserFeedState()
|
||||
|
||||
object Empty : UserFeedState()
|
||||
|
||||
class FeedError(val errorMessage: String) : UserFeedState()
|
||||
class FeedError(
|
||||
val errorMessage: String,
|
||||
) : UserFeedState()
|
||||
}
|
||||
|
||||
@@ -45,47 +45,59 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NostrUserProfileFollowsUserFeedViewModel(val user: User, val account: Account) :
|
||||
UserFeedViewModel(UserProfileFollowsFeedFilter(user, account)) {
|
||||
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileFollowsUserFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileFollowsUserFeedViewModel>): NostrUserProfileFollowsUserFeedViewModel {
|
||||
return NostrUserProfileFollowsUserFeedViewModel(user, account)
|
||||
class NostrUserProfileFollowsUserFeedViewModel(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : UserFeedViewModel(UserProfileFollowsFeedFilter(user, account)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileFollowsUserFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileFollowsUserFeedViewModel>): NostrUserProfileFollowsUserFeedViewModel =
|
||||
NostrUserProfileFollowsUserFeedViewModel(user, account)
|
||||
as NostrUserProfileFollowsUserFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NostrUserProfileFollowersUserFeedViewModel(val user: User, val account: Account) :
|
||||
UserFeedViewModel(UserProfileFollowersFeedFilter(user, account)) {
|
||||
class Factory(val user: User, val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileFollowersUserFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileFollowersUserFeedViewModel>): NostrUserProfileFollowersUserFeedViewModel {
|
||||
return NostrUserProfileFollowersUserFeedViewModel(user, account)
|
||||
class NostrUserProfileFollowersUserFeedViewModel(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : UserFeedViewModel(UserProfileFollowersFeedFilter(user, account)) {
|
||||
class Factory(
|
||||
val user: User,
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrUserProfileFollowersUserFeedViewModel : ViewModel> create(modelClass: Class<NostrUserProfileFollowersUserFeedViewModel>): NostrUserProfileFollowersUserFeedViewModel =
|
||||
NostrUserProfileFollowersUserFeedViewModel(user, account)
|
||||
as NostrUserProfileFollowersUserFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NostrHiddenAccountsFeedViewModel(val account: Account) :
|
||||
UserFeedViewModel(HiddenAccountsFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrHiddenAccountsFeedViewModel : ViewModel> create(modelClass: Class<NostrHiddenAccountsFeedViewModel>): NostrHiddenAccountsFeedViewModel {
|
||||
return NostrHiddenAccountsFeedViewModel(account) as NostrHiddenAccountsFeedViewModel
|
||||
}
|
||||
class NostrHiddenAccountsFeedViewModel(
|
||||
val account: Account,
|
||||
) : UserFeedViewModel(HiddenAccountsFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrHiddenAccountsFeedViewModel : ViewModel> create(modelClass: Class<NostrHiddenAccountsFeedViewModel>): NostrHiddenAccountsFeedViewModel = NostrHiddenAccountsFeedViewModel(account) as NostrHiddenAccountsFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
class NostrSpammerAccountsFeedViewModel(val account: Account) :
|
||||
UserFeedViewModel(SpammerAccountsFeedFilter(account)) {
|
||||
class Factory(val account: Account) : ViewModelProvider.Factory {
|
||||
override fun <NostrSpammerAccountsFeedViewModel : ViewModel> create(modelClass: Class<NostrSpammerAccountsFeedViewModel>): NostrSpammerAccountsFeedViewModel {
|
||||
return NostrSpammerAccountsFeedViewModel(account) as NostrSpammerAccountsFeedViewModel
|
||||
}
|
||||
class NostrSpammerAccountsFeedViewModel(
|
||||
val account: Account,
|
||||
) : UserFeedViewModel(SpammerAccountsFeedFilter(account)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <NostrSpammerAccountsFeedViewModel : ViewModel> create(modelClass: Class<NostrSpammerAccountsFeedViewModel>): NostrSpammerAccountsFeedViewModel = NostrSpammerAccountsFeedViewModel(account) as NostrSpammerAccountsFeedViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
open class UserFeedViewModel(val dataSource: FeedFilter<User>) :
|
||||
ViewModel(), InvalidatableViewModel {
|
||||
open class UserFeedViewModel(
|
||||
val dataSource: FeedFilter<User>,
|
||||
) : ViewModel(),
|
||||
InvalidatableViewModel {
|
||||
private val _feedContent = MutableStateFlow<UserFeedState>(UserFeedState.Loading)
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user