Porównaj commity

...

44 Commity

Autor SHA1 Wiadomość Data
Tony Giorgio 261481ecba
Merge 08f1b43908 into 9be4895080 2024-04-05 12:18:07 -03:00
Vitor Pamplona 9be4895080
Merge pull request #828 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2024-04-05 08:19:17 -04:00
Crowdin Bot 6250db01d1 New Crowdin translations by GitHub Action 2024-04-05 12:05:47 +00:00
Vitor Pamplona 48f9045f1b
Merge pull request #825 from greenart7c3/onion_url
add ws:// if not present in .onion urls
2024-04-05 08:03:52 -04:00
Vitor Pamplona 818ca7e39e
Merge pull request #826 from greenart7c3/draft_decryption_error
fix draft decryption error
2024-04-05 08:03:39 -04:00
Vitor Pamplona e8675b8e45
Merge pull request #827 from davotoula/update_translations
added  translations for CS, DE, SV
2024-04-05 08:03:11 -04:00
David Kaspar cef7e17447 added translations for CS, DE, SV 2024-04-05 11:38:51 +01:00
greenart7c3 6b15a0db8e fix draft decryption error 2024-04-05 05:41:03 -03:00
greenart7c3 50c5845a11 add ws:// if not present in .onion urls 2024-04-05 05:30:51 -03:00
Vitor Pamplona 1b7ba3de01 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2024-04-04 23:06:08 -04:00
Vitor Pamplona 712063f5d2 Avoids double notifications. 2024-04-04 23:05:58 -04:00
Vitor Pamplona d92f23e274 LargeCache inner map should be private 2024-04-04 23:05:35 -04:00
Vitor Pamplona 3b7f530c0b
Merge pull request #824 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2024-04-04 18:15:14 -04:00
Crowdin Bot 623a8d377c New Crowdin translations by GitHub Action 2024-04-04 22:08:10 +00:00
Vitor Pamplona 79489d0b07 v0.86.1 2024-04-04 18:06:36 -04:00
Vitor Pamplona 827512b225 Avoids circular rendering of Drafts. 2024-04-04 18:06:27 -04:00
Vitor Pamplona 6acfadeb9b Reduces cache for expandable texts. 2024-04-04 18:06:09 -04:00
Vitor Pamplona e159af2cd7 v0.86.0 2024-04-04 17:21:03 -04:00
Vitor Pamplona 89c2e9d2e0 Changes precision of Zap Splits to 1% steps 2024-04-04 17:19:03 -04:00
Vitor Pamplona 06f6ab6719 Adds button to load Zap Splits from the cited users in the text 2024-04-04 17:18:42 -04:00
Vitor Pamplona 98c48e8b6b Fixes contract violation when sorting users. 2024-04-04 16:54:14 -04:00
Vitor Pamplona 25cde455d8 removes chat bubble animation when size changes 2024-04-04 15:58:19 -04:00
Vitor Pamplona ef0fdf553c Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2024-04-04 15:24:59 -04:00
Vitor Pamplona 719b950272 Fixing the use of filters that didn't discriminate the relay type setup 2024-04-04 15:24:04 -04:00
Vitor Pamplona 2d02fad6b9
Merge pull request #823 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2024-04-04 15:16:49 -04:00
Crowdin Bot a39db5bf7b New Crowdin translations by GitHub Action 2024-04-04 19:01:46 +00:00
Vitor Pamplona 7fd37367fc refactoring of cache methods in GiftWraps 2024-04-04 14:59:49 -04:00
Vitor Pamplona e1c134830e Avoiding feed jitter when pressing the notification button twice. 2024-04-04 10:03:28 -04:00
Vitor Pamplona 621d1c7731 Migrating Refreshable feeds to the reusable box 2024-04-04 09:41:51 -04:00
Vitor Pamplona 7475143506 Continues the migration from LiveData to Flow 2024-04-04 08:56:15 -04:00
Vitor Pamplona 2509d639bd
Merge pull request #822 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2024-04-03 13:07:08 -04:00
Crowdin Bot 85dd5cf698 New Crowdin translations by GitHub Action 2024-04-03 17:02:53 +00:00
Vitor Pamplona 274ce6ad77 Fixing the spacing for channels 2024-04-03 12:26:57 -04:00
Vitor Pamplona 0e8d2fc33a adds save when closing the screen on new posts. 2024-04-03 10:20:19 -04:00
Vitor Pamplona b88723b68b Fixes wrong refactoring 2024-04-03 10:20:05 -04:00
Vitor Pamplona 638dba770d Moves the creation of Draft Notes to the IO Thread 2024-04-02 17:49:52 -04:00
Vitor Pamplona 4d7de6bc19 No need to switch to IO this early in the process 2024-04-02 16:08:59 -04:00
Vitor Pamplona fbf676bdb2 Reduces recomposition of the hash verification 2024-04-02 16:08:38 -04:00
Vitor Pamplona 793780f02c
Merge pull request #821 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2024-04-02 10:02:21 -04:00
Crowdin Bot adf31ed115 New Crowdin translations by GitHub Action 2024-04-02 13:54:47 +00:00
Vitor Pamplona 96e434bfcf Fixes the lack of following mark on chats 2024-04-02 09:52:52 -04:00
Vitor Pamplona c7563c938d Minor refactoring 2024-04-02 09:42:01 -04:00
Vitor Pamplona 4380393c5b No need to update UUID anymore. After deletion the draft can just be updated. 2024-04-02 09:23:08 -04:00
Tony Giorgio 08f1b43908
Change release to use personal github token 2023-06-08 11:07:08 -05:00
41 zmienionych plików z 474 dodań i 389 usunięć

Wyświetl plik

@ -84,7 +84,7 @@ jobs:
id: create_release id: create_release
uses: actions/create-release@v1 uses: actions/create-release@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }} release_name: Release ${{ github.ref }}
@ -96,7 +96,7 @@ jobs:
id: upload-release-asset-play-universal-apk id: upload-release-asset-play-universal-apk
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/apk/play/release/app-play-universal-release-unsigned-signed.apk asset_path: app/build/outputs/apk/play/release/app-play-universal-release-unsigned-signed.apk
@ -107,7 +107,7 @@ jobs:
id: upload-release-asset-play-x86-apk id: upload-release-asset-play-x86-apk
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/apk/play/release/app-play-x86-release-unsigned-signed.apk asset_path: app/build/outputs/apk/play/release/app-play-x86-release-unsigned-signed.apk
@ -118,7 +118,7 @@ jobs:
id: upload-release-asset-play-x86-64-apk id: upload-release-asset-play-x86-64-apk
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/apk/play/release/app-play-x86_64-release-unsigned-signed.apk asset_path: app/build/outputs/apk/play/release/app-play-x86_64-release-unsigned-signed.apk
@ -152,7 +152,7 @@ jobs:
id: upload-release-asset-fdroid-universal-apk id: upload-release-asset-fdroid-universal-apk
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-universal-release-unsigned-signed.apk asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-universal-release-unsigned-signed.apk
@ -163,7 +163,7 @@ jobs:
id: upload-release-asset-fdroid-x86-apk id: upload-release-asset-fdroid-x86-apk
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86-release-unsigned-signed.apk asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86-release-unsigned-signed.apk
@ -174,7 +174,7 @@ jobs:
id: upload-release-asset-fdroid-x86-64-apk id: upload-release-asset-fdroid-x86-64-apk
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86_64-release-unsigned-signed.apk asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86_64-release-unsigned-signed.apk
@ -210,7 +210,7 @@ jobs:
id: upload-release-asset-play-aab id: upload-release-asset-play-aab
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/bundle/playRelease/app-play-release.aab asset_path: app/build/outputs/bundle/playRelease/app-play-release.aab
@ -222,7 +222,7 @@ jobs:
id: upload-release-asset-fdroid-aab id: upload-release-asset-fdroid-aab
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: app/build/outputs/bundle/fdroidRelease/app-fdroid-release.aab asset_path: app/build/outputs/bundle/fdroidRelease/app-fdroid-release.aab

Wyświetl plik

@ -12,9 +12,9 @@ android {
applicationId "com.vitorpamplona.amethyst" applicationId "com.vitorpamplona.amethyst"
minSdk 26 minSdk 26
targetSdk 34 targetSdk 34
versionCode 362 versionCode 364
versionName "0.85.3" versionName "0.86.1"
buildConfigField "String", "RELEASE_NOTES_ID", "\"d8da33fd13d129d86c53564aedefafbe3716f007c520431be4a8e488d3925afb\"" buildConfigField "String", "RELEASE_NOTES_ID", "\"a704a11334ed4fe6fc6ee6f8856f6f005da33644770616f1437f8b2b488b52b1\""
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

Wyświetl plik

@ -108,11 +108,15 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flattenMerge import kotlinx.coroutines.flow.flattenMerge
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -216,179 +220,114 @@ class Account(
val communities: ImmutableSet<String> = persistentSetOf(), val communities: ImmutableSet<String> = persistentSetOf(),
) )
class ListNameNotePair(val listName: String, val event: GeneralListEvent?)
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val liveKind3Follows: StateFlow<LiveFollowLists> by lazy { val liveKind3FollowsFlow: Flow<LiveFollowLists> =
userProfile() userProfile().flow().follows.stateFlow.transformLatest {
.live() emit(
.follows LiveFollowLists(
.asFlow() it.user.cachedFollowingKeySet().toImmutableSet(),
.transformLatest { it.user.cachedFollowingTagSet().toImmutableSet(),
emit( it.user.cachedFollowingGeohashSet().toImmutableSet(),
LiveFollowLists( it.user.cachedFollowingCommunitiesSet().toImmutableSet(),
userProfile().cachedFollowingKeySet().toImmutableSet(), ),
userProfile().cachedFollowingTagSet().toImmutableSet(), )
userProfile().cachedFollowingGeohashSet().toImmutableSet(), }
userProfile().cachedFollowingCommunitiesSet().toImmutableSet(),
), val liveKind3Follows = liveKind3FollowsFlow.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists())
)
} @OptIn(ExperimentalCoroutinesApi::class)
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) private val liveHomeList: Flow<ListNameNotePair> by lazy {
defaultHomeFollowList.flatMapLatest { listName ->
loadPeopleListFlowFromListName(listName)
}
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private val liveHomeList: StateFlow<NoteState?> by lazy { fun loadPeopleListFlowFromListName(listName: String): Flow<ListNameNotePair> {
defaultHomeFollowList return if (listName != GLOBAL_FOLLOWS && listName != KIND3_FOLLOWS) {
.transformLatest { val note = LocalCache.checkGetOrCreateAddressableNote(listName)
if (it != GLOBAL_FOLLOWS && it != KIND3_FOLLOWS) { note?.flow()?.metadata?.stateFlow?.mapLatest {
LocalCache.checkGetOrCreateAddressableNote(it)?.flow()?.metadata?.stateFlow?.let { val noteEvent = it.note.event as? GeneralListEvent
emit(it) ListNameNotePair(listName, noteEvent)
} } ?: MutableStateFlow(ListNameNotePair(listName, null))
} else {
MutableStateFlow(ListNameNotePair(listName, null))
}
}
fun combinePeopleListFlows(
kind3FollowsSource: Flow<LiveFollowLists>,
peopleListFollowsSource: Flow<ListNameNotePair>,
): Flow<LiveFollowLists?> {
return combineTransform(kind3FollowsSource, peopleListFollowsSource) { kind3Follows, peopleListFollows ->
if (peopleListFollows.listName == GLOBAL_FOLLOWS) {
emit(null)
} else if (peopleListFollows.listName == KIND3_FOLLOWS) {
emit(kind3Follows)
} else if (peopleListFollows.event == null) {
emit(LiveFollowLists())
} else {
val result = waitToDecrypt(peopleListFollows.event)
if (result == null) {
emit(LiveFollowLists())
} else {
emit(result)
} }
} }
.flattenMerge() }
.stateIn(scope, SharingStarted.Eagerly, null)
} }
val liveHomeFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveHomeFollowLists: StateFlow<LiveFollowLists?> by lazy {
combineTransform(defaultHomeFollowList, liveKind3Follows, liveHomeList) { combinePeopleListFlows(liveKind3FollowsFlow, liveHomeList)
listName,
kind3Follows,
peopleListFollows,
->
if (listName == GLOBAL_FOLLOWS) {
emit(null)
} else if (listName == KIND3_FOLLOWS) {
emit(kind3Follows)
} else {
val result =
withTimeoutOrNull(1000) {
suspendCancellableCoroutine { continuation ->
decryptLiveFollows(peopleListFollows) { continuation.resume(it) }
}
}
result?.let { emit(it) } ?: run { emit(LiveFollowLists()) }
}
}
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists())
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private val liveNotificationList: StateFlow<NoteState?> by lazy { private val liveNotificationList: Flow<ListNameNotePair> by lazy {
defaultNotificationFollowList defaultNotificationFollowList
.transformLatest { .transformLatest { listName ->
if (it != GLOBAL_FOLLOWS && it != KIND3_FOLLOWS) { emit(loadPeopleListFlowFromListName(listName))
LocalCache.checkGetOrCreateAddressableNote(it)?.flow()?.metadata?.stateFlow?.let { }.flattenMerge()
emit(it)
}
}
}
.flattenMerge()
.stateIn(scope, SharingStarted.Eagerly, null)
} }
val liveNotificationFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveNotificationFollowLists: StateFlow<LiveFollowLists?> by lazy {
combineTransform(defaultNotificationFollowList, liveKind3Follows, liveNotificationList) { combinePeopleListFlows(liveKind3FollowsFlow, liveNotificationList)
listName,
kind3Follows,
peopleListFollows,
->
if (listName == GLOBAL_FOLLOWS) {
emit(null)
} else if (listName == KIND3_FOLLOWS) {
emit(kind3Follows)
} else {
val result =
withTimeoutOrNull(1000) {
suspendCancellableCoroutine { continuation ->
decryptLiveFollows(peopleListFollows) { continuation.resume(it) }
}
}
result?.let { emit(it) } ?: run { emit(LiveFollowLists()) }
}
}
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists())
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private val liveStoriesList: StateFlow<NoteState?> by lazy { private val liveStoriesList: Flow<ListNameNotePair> by lazy {
defaultStoriesFollowList defaultStoriesFollowList
.transformLatest { .transformLatest { listName ->
if (it != GLOBAL_FOLLOWS && it != KIND3_FOLLOWS) { emit(loadPeopleListFlowFromListName(listName))
LocalCache.checkGetOrCreateAddressableNote(it)?.flow()?.metadata?.stateFlow?.let { }.flattenMerge()
emit(it)
}
}
}
.flattenMerge()
.stateIn(scope, SharingStarted.Eagerly, null)
} }
val liveStoriesFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveStoriesFollowLists: StateFlow<LiveFollowLists?> by lazy {
combineTransform(defaultStoriesFollowList, liveKind3Follows, liveStoriesList) { combinePeopleListFlows(liveKind3FollowsFlow, liveStoriesList)
listName,
kind3Follows,
peopleListFollows,
->
if (listName == GLOBAL_FOLLOWS) {
emit(null)
} else if (listName == KIND3_FOLLOWS) {
emit(kind3Follows)
} else {
val result =
withTimeoutOrNull(1000) {
suspendCancellableCoroutine { continuation ->
decryptLiveFollows(peopleListFollows) { continuation.resume(it) }
}
}
result?.let { emit(it) } ?: run { emit(LiveFollowLists()) }
}
}
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists())
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private val liveDiscoveryList: StateFlow<NoteState?> by lazy { private val liveDiscoveryList: Flow<ListNameNotePair> by lazy {
defaultDiscoveryFollowList defaultDiscoveryFollowList
.transformLatest { .transformLatest { listName ->
if (it != GLOBAL_FOLLOWS && it != KIND3_FOLLOWS) { emit(loadPeopleListFlowFromListName(listName))
LocalCache.checkGetOrCreateAddressableNote(it)?.flow()?.metadata?.stateFlow?.let { }.flattenMerge()
emit(it)
}
}
}
.flattenMerge()
.stateIn(scope, SharingStarted.Eagerly, null)
} }
val liveDiscoveryFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveDiscoveryFollowLists: StateFlow<LiveFollowLists?> by lazy {
combineTransform(defaultDiscoveryFollowList, liveKind3Follows, liveDiscoveryList) { combinePeopleListFlows(liveKind3FollowsFlow, liveDiscoveryList)
listName,
kind3Follows,
peopleListFollows,
->
if (listName == GLOBAL_FOLLOWS) {
emit(null)
} else if (listName == KIND3_FOLLOWS) {
emit(kind3Follows)
} else {
val result =
withTimeoutOrNull(1000) {
suspendCancellableCoroutine { continuation ->
decryptLiveFollows(peopleListFollows) { continuation.resume(it) }
}
}
result?.let { emit(it) } ?: run { emit(LiveFollowLists()) }
}
}
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists())
} }
private fun decryptLiveFollows( private fun decryptLiveFollows(
peopleListFollows: NoteState?, listEvent: GeneralListEvent,
onReady: (LiveFollowLists) -> Unit, onReady: (LiveFollowLists) -> Unit,
) { ) {
val listEvent = (peopleListFollows?.note?.event as? GeneralListEvent) listEvent.privateTags(signer) { privateTagList ->
listEvent?.privateTags(signer) { privateTagList ->
onReady( onReady(
LiveFollowLists( LiveFollowLists(
users = users =
@ -406,6 +345,16 @@ class Account(
} }
} }
suspend fun waitToDecrypt(peopleListFollows: GeneralListEvent): LiveFollowLists? {
return withTimeoutOrNull(1000) {
suspendCancellableCoroutine { continuation ->
decryptLiveFollows(peopleListFollows) {
continuation.resume(it)
}
}
}
}
@Immutable @Immutable
data class LiveHiddenUsers( data class LiveHiddenUsers(
val hiddenUsers: ImmutableSet<String>, val hiddenUsers: ImmutableSet<String>,
@ -2353,7 +2302,7 @@ class Account(
} else if (event is LnZapRequestEvent && event.isPrivateZap() && isWriteable()) { } else if (event is LnZapRequestEvent && event.isPrivateZap() && isWriteable()) {
event.cachedPrivateZap()?.content event.cachedPrivateZap()?.content
} else { } else {
event?.content() event.content()
} }
} }

Wyświetl plik

@ -2306,6 +2306,10 @@ object LocalCache {
e.printStackTrace() e.printStackTrace()
} }
} }
fun hasConsumed(notificationEvent: Event): Boolean {
return notes.containsKey(notificationEvent.id)
}
} }
@Stable @Stable

Wyświetl plik

@ -26,8 +26,6 @@ import com.vitorpamplona.amethyst.service.previews.BahaUrlPreview
import com.vitorpamplona.amethyst.service.previews.IUrlPreviewCallback import com.vitorpamplona.amethyst.service.previews.IUrlPreviewCallback
import com.vitorpamplona.amethyst.service.previews.UrlInfoItem import com.vitorpamplona.amethyst.service.previews.UrlInfoItem
import com.vitorpamplona.amethyst.ui.components.UrlPreviewState import com.vitorpamplona.amethyst.ui.components.UrlPreviewState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Stable @Stable
object UrlCachedPreviewer { object UrlCachedPreviewer {
@ -37,46 +35,44 @@ object UrlCachedPreviewer {
suspend fun previewInfo( suspend fun previewInfo(
url: String, url: String,
onReady: suspend (UrlPreviewState) -> Unit, onReady: suspend (UrlPreviewState) -> Unit,
) = withContext(Dispatchers.IO) { ) {
cache[url]?.let { cache[url]?.let {
onReady(it) onReady(it)
return@withContext return
} }
BahaUrlPreview( BahaUrlPreview(
url, url,
object : IUrlPreviewCallback { object : IUrlPreviewCallback {
override suspend fun onComplete(urlInfo: UrlInfoItem) = override suspend fun onComplete(urlInfo: UrlInfoItem) {
withContext(Dispatchers.IO) { cache[url]?.let {
cache[url]?.let { if (it is UrlPreviewState.Loaded || it is UrlPreviewState.Empty) {
if (it is UrlPreviewState.Loaded || it is UrlPreviewState.Empty) {
onReady(it)
return@withContext
}
}
val state =
if (urlInfo.fetchComplete() && urlInfo.url == url) {
UrlPreviewState.Loaded(urlInfo)
} else {
UrlPreviewState.Empty
}
cache.put(url, state)
onReady(state)
}
override suspend fun onFailed(throwable: Throwable) =
withContext(Dispatchers.IO) {
cache[url]?.let {
onReady(it) onReady(it)
return@withContext return
}
}
val state =
if (urlInfo.fetchComplete() && urlInfo.url == url) {
UrlPreviewState.Loaded(urlInfo)
} else {
UrlPreviewState.Empty
} }
val state = UrlPreviewState.Error(throwable.message ?: "Error Loading url preview") cache.put(url, state)
cache.put(url, state) onReady(state)
onReady(state) }
override suspend fun onFailed(throwable: Throwable) {
cache[url]?.let {
onReady(it)
return
} }
val state = UrlPreviewState.Error(throwable.message ?: "Error Loading url preview")
cache.put(url, state)
onReady(state)
}
}, },
) )
.fetchUrlPreview() .fetchUrlPreview()

Wyświetl plik

@ -127,6 +127,7 @@ class User(val pubkeyHex: String) {
// Update following of the current user // Update following of the current user
liveSet?.innerFollows?.invalidateData() liveSet?.innerFollows?.invalidateData()
flowSet?.follows?.invalidateData()
// Update Followers of the past user list // Update Followers of the past user list
// Update Followers of the new contact list // Update Followers of the new contact list
@ -474,14 +475,16 @@ class User(val pubkeyHex: String) {
@Stable @Stable
class UserFlowSet(u: User) { class UserFlowSet(u: User) {
// Observers line up here. // Observers line up here.
val follows = UserBundledRefresherFlow(u)
val relays = UserBundledRefresherFlow(u) val relays = UserBundledRefresherFlow(u)
fun isInUse(): Boolean { fun isInUse(): Boolean {
return relays.stateFlow.subscriptionCount.value > 0 return relays.stateFlow.subscriptionCount.value > 0 || follows.stateFlow.subscriptionCount.value > 0
} }
fun destroy() { fun destroy() {
relays.destroy() relays.destroy()
follows.destroy()
} }
} }

Wyświetl plik

@ -64,15 +64,17 @@ class EventNotificationConsumer(private val applicationContext: Context) {
account: Account, account: Account,
) { ) {
pushWrappedEvent.cachedGift(account.signer) { notificationEvent -> pushWrappedEvent.cachedGift(account.signer) { notificationEvent ->
LocalCache.justConsume(notificationEvent, null) if (!LocalCache.hasConsumed(notificationEvent) && LocalCache.justVerify(notificationEvent)) {
unwrapAndConsume(notificationEvent, account) { innerEvent ->
unwrapAndConsume(notificationEvent, account) { innerEvent -> if (!LocalCache.hasConsumed(innerEvent)) {
if (innerEvent is PrivateDmEvent) { if (innerEvent is PrivateDmEvent) {
notify(innerEvent, account) notify(innerEvent, account)
} else if (innerEvent is LnZapEvent) { } else if (innerEvent is LnZapEvent) {
notify(innerEvent, account) notify(innerEvent, account)
} else if (innerEvent is ChatMessageEvent) { } else if (innerEvent is ChatMessageEvent) {
notify(innerEvent, account) notify(innerEvent, account)
}
}
} }
} }
} }

Wyświetl plik

@ -21,18 +21,14 @@
package com.vitorpamplona.amethyst.service.previews package com.vitorpamplona.amethyst.service.previews
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) { class BahaUrlPreview(val url: String, var callback: IUrlPreviewCallback?) {
suspend fun fetchUrlPreview(timeOut: Int = 30000) = suspend fun fetchUrlPreview(timeOut: Int = 30000) =
withContext(Dispatchers.IO) { try {
try { fetch(timeOut)
fetch(timeOut) } catch (t: Throwable) {
} catch (t: Throwable) { if (t is CancellationException) throw t
if (t is CancellationException) throw t callback?.onFailed(t)
callback?.onFailed(t)
}
} }
private suspend fun fetch(timeOut: Int = 30000) { private suspend fun fetch(timeOut: Int = 30000) {

Wyświetl plik

@ -353,9 +353,14 @@ class Relay(
if (read) { if (read) {
if (isConnected()) { if (isConnected()) {
if (isReady) { if (isReady) {
if (filters.isNotEmpty()) { val relayFilters =
filters.filter { filter ->
activeTypes.any { it in filter.types }
}
if (relayFilters.isNotEmpty()) {
val request = val request =
filters.joinToStringLimited( relayFilters.joinToStringLimited(
separator = ",", separator = ",",
limit = 20, limit = 20,
prefix = """["REQ","$requestId",""", prefix = """["REQ","$requestId",""",
@ -423,12 +428,7 @@ class Relay(
fun renewFilters() { fun renewFilters() {
// Force update all filters after AUTH. // Force update all filters after AUTH.
Client.allSubscriptions().forEach { Client.allSubscriptions().forEach {
val filters = sendFilter(requestId = it.key, it.value)
it.value.filter { filter ->
activeTypes.any { it in filter.types }
}
sendFilter(requestId = it.key, filters)
} }
} }

Wyświetl plik

@ -81,7 +81,9 @@ object RelayPool : Relay.Listener {
subscriptionId: String, subscriptionId: String,
filters: List<TypedFilter>, filters: List<TypedFilter>,
) { ) {
relays.forEach { it.sendFilter(subscriptionId, filters) } relays.forEach { relay ->
relay.sendFilter(subscriptionId, filters)
}
} }
fun connectAndSendFiltersIfDisconnected() { fun connectAndSendFiltersIfDisconnected() {

Wyświetl plik

@ -235,7 +235,12 @@ fun NewPostView(
} }
Dialog( Dialog(
onDismissRequest = { onClose() }, onDismissRequest = {
scope.launch {
postViewModel.sendDraftSync(relayList = relayList)
onClose()
}
},
properties = properties =
DialogProperties( DialogProperties(
usePlatformDefaultWidth = false, usePlatformDefaultWidth = false,
@ -294,8 +299,9 @@ fun NewPostView(
Spacer(modifier = StdHorzSpacer) Spacer(modifier = StdHorzSpacer)
CloseButton( CloseButton(
onPress = { onPress = {
postViewModel.cancel()
scope.launch { scope.launch {
postViewModel.sendDraftSync(relayList = relayList)
postViewModel.cancel()
delay(100) delay(100)
onClose() onClose()
} }
@ -1096,8 +1102,12 @@ fun FowardZapTo(
text = stringResource(R.string.zap_split_title), text = stringResource(R.string.zap_split_title),
fontSize = 20.sp, fontSize = 20.sp,
fontWeight = FontWeight.W500, fontWeight = FontWeight.W500,
modifier = Modifier.padding(start = 10.dp), modifier = Modifier.padding(horizontal = 10.dp).weight(1f),
) )
OutlinedButton(onClick = { postViewModel.updateZapFromText() }) {
Text(text = stringResource(R.string.load_from_text))
}
} }
HorizontalDivider(thickness = DividerThickness) HorizontalDivider(thickness = DividerThickness)
@ -1133,7 +1143,7 @@ fun FowardZapTo(
Slider( Slider(
value = splitItem.percentage, value = splitItem.percentage,
onValueChange = { sliderValue -> onValueChange = { sliderValue ->
val rounded = (round(sliderValue * 20)) / 20.0f val rounded = (round(sliderValue * 100)) / 100.0f
postViewModel.updateZapPercentage(index, rounded) postViewModel.updateZapPercentage(index, rounded)
}, },
modifier = Modifier.weight(1.5f), modifier = Modifier.weight(1.5f),

Wyświetl plik

@ -77,6 +77,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.UUID import java.util.UUID
enum class UserSuggestionAnchor { enum class UserSuggestionAnchor {
@ -200,12 +201,16 @@ open class NewPostViewModel() : ViewModel() {
val noteAuthor = draft?.author val noteAuthor = draft?.author
if (draft != null && noteEvent is DraftEvent && noteAuthor != null) { if (draft != null && noteEvent is DraftEvent && noteAuthor != null) {
accountViewModel.createTempDraftNote(noteEvent, noteAuthor) { innerNote -> viewModelScope.launch(Dispatchers.IO) {
val oldTag = (draft.event as? AddressableEvent)?.dTag() accountViewModel.createTempDraftNote(noteEvent) { innerNote ->
if (oldTag != null) { if (innerNote != null) {
draftTag = oldTag val oldTag = (draft.event as? AddressableEvent)?.dTag()
if (oldTag != null) {
draftTag = oldTag
}
loadFromDraft(innerNote, accountViewModel)
}
} }
loadFromDraft(innerNote, accountViewModel)
} }
} else { } else {
originalNote = replyingTo originalNote = replyingTo
@ -442,18 +447,22 @@ open class NewPostViewModel() : ViewModel() {
} }
fun sendDraft(relayList: List<Relay>? = null) { fun sendDraft(relayList: List<Relay>? = null) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch {
innerSendPost(relayList, draftTag) sendDraftSync(relayList)
} }
} }
suspend fun sendDraftSync(relayList: List<Relay>? = null) {
innerSendPost(relayList, draftTag)
}
private suspend fun innerSendPost( private suspend fun innerSendPost(
relayList: List<Relay>? = null, relayList: List<Relay>? = null,
localDraft: String?, localDraft: String?,
) { ) = withContext(Dispatchers.IO) {
if (accountViewModel == null) { if (accountViewModel == null) {
cancel() cancel()
return return@withContext
} }
val tagger = NewMessageTagger(message.text, pTags, eTags, originalNote?.channelHex(), accountViewModel!!) val tagger = NewMessageTagger(message.text, pTags, eTags, originalNote?.channelHex(), accountViewModel!!)
@ -919,6 +928,7 @@ open class NewPostViewModel() : ViewModel() {
compareBy( compareBy(
{ account?.isFollowing(it) }, { account?.isFollowing(it) },
{ it.toBestDisplayName() }, { it.toBestDisplayName() },
{ it.pubkeyHex },
), ),
) )
.reversed() .reversed()
@ -928,7 +938,6 @@ open class NewPostViewModel() : ViewModel() {
userSuggestions = emptyList() userSuggestions = emptyList()
} }
} }
saveDraft()
} }
open fun autocompleteWithUser(item: User) { open fun autocompleteWithUser(item: User) {
@ -947,16 +956,6 @@ open class NewPostViewModel() : ViewModel() {
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.FORWARD_ZAPS) { } else if (userSuggestionsMainMessage == UserSuggestionAnchor.FORWARD_ZAPS) {
forwardZapTo.addItem(item) forwardZapTo.addItem(item)
forwardZapToEditting = TextFieldValue("") forwardZapToEditting = TextFieldValue("")
/*
val lastWord = forwardZapToEditting.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ")
val lastWordStart = it.end - lastWord.length
val wordToInsert = "@${item.pubkeyNpub()}"
forwardZapTo = item
forwardZapToEditting = TextFieldValue(
forwardZapToEditting.text.replaceRange(lastWordStart, it.end, wordToInsert),
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length)
)*/
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.TO_USERS) { } else if (userSuggestionsMainMessage == UserSuggestionAnchor.TO_USERS) {
val lastWord = val lastWord =
toUsers.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ") toUsers.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ")
@ -1199,6 +1198,18 @@ open class NewPostViewModel() : ViewModel() {
forwardZapTo.updatePercentage(index, sliderValue) forwardZapTo.updatePercentage(index, sliderValue)
} }
fun updateZapFromText() {
viewModelScope.launch(Dispatchers.Default) {
val tagger = NewMessageTagger(message.text, emptyList(), emptyList(), null, accountViewModel!!)
tagger.run()
tagger.pTags?.forEach { taggedUser ->
if (!forwardZapTo.items.any { it.key == taggedUser }) {
forwardZapTo.addItem(taggedUser)
}
}
}
}
fun updateZapRaiserAmount(newAmount: Long?) { fun updateZapRaiserAmount(newAmount: Long?) {
zapRaiserAmount = newAmount zapRaiserAmount = newAmount
saveDraft() saveDraft()

Wyświetl plik

@ -900,7 +900,15 @@ fun EditableServerConfig(
onClick = { onClick = {
if (url.isNotBlank() && url != "/") { if (url.isNotBlank() && url != "/") {
var addedWSS = var addedWSS =
if (!url.startsWith("wss://") && !url.startsWith("ws://")) "wss://$url" else url if (!url.startsWith("wss://") && !url.startsWith("ws://")) {
if (url.endsWith(".onion") || url.endsWith(".onion/")) {
"ws://$url"
} else {
"wss://$url"
}
} else {
url
}
if (url.endsWith("/")) addedWSS = addedWSS.dropLast(1) if (url.endsWith("/")) addedWSS = addedWSS.dropLast(1)
onNewRelay(RelaySetupInfo(addedWSS, read, write, feedTypes = FeedType.values().toSet())) onNewRelay(RelaySetupInfo(addedWSS, read, write, feedTypes = FeedType.values().toSet()))
url = "" url = ""

Wyświetl plik

@ -53,7 +53,7 @@ import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground
import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.ImmutableListOfLists
object ShowFullTextCache { object ShowFullTextCache {
val cache = LruCache<String, Boolean>(20) val cache = LruCache<String, Boolean>(10)
} }
@Composable @Composable

Wyświetl plik

@ -501,8 +501,6 @@ private fun AddedImageFeatures(
ImageUrlWithDownloadButton(content.url, showImage) ImageUrlWithDownloadButton(content.url, showImage)
} }
} else { } else {
var verifiedHash by remember(content.url) { mutableStateOf<Boolean?>(null) }
when (painter.value) { when (painter.value) {
null, null,
is AsyncImagePainter.State.Loading, is AsyncImagePainter.State.Loading,
@ -528,24 +526,32 @@ private fun AddedImageFeatures(
} }
} }
is AsyncImagePainter.State.Success -> { is AsyncImagePainter.State.Success -> {
if (content.hash != null) { ShowHash(content, verifiedModifier)
LaunchedEffect(key1 = content.url) {
launch(Dispatchers.IO) {
val newVerifiedHash = verifyHash(content)
if (newVerifiedHash != verifiedHash) {
verifiedHash = newVerifiedHash
}
}
}
}
verifiedHash?.let { HashVerificationSymbol(it, verifiedModifier) }
} }
else -> {} else -> {}
} }
} }
} }
@Composable
fun ShowHash(
content: MediaUrlContent,
verifiedModifier: Modifier,
) {
var verifiedHash by remember(content.url) { mutableStateOf<Boolean?>(null) }
if (content.hash != null) {
LaunchedEffect(key1 = content.url) {
val newVerifiedHash = verifyHash(content)
if (newVerifiedHash != verifiedHash) {
verifiedHash = newVerifiedHash
}
}
}
verifiedHash?.let { HashVerificationSymbol(it, verifiedModifier) }
}
fun aspectRatio(dim: String?): Float? { fun aspectRatio(dim: String?): Float? {
if (dim == null) return null if (dim == null) return null
if (dim == "0x0") return null if (dim == "0x0") return null

Wyświetl plik

@ -20,11 +20,11 @@
*/ */
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -83,6 +83,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
import com.vitorpamplona.amethyst.ui.theme.Size20dp import com.vitorpamplona.amethyst.ui.theme.Size20dp
import com.vitorpamplona.amethyst.ui.theme.Size5dp import com.vitorpamplona.amethyst.ui.theme.Size5dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.chatAuthorBox
import com.vitorpamplona.amethyst.ui.theme.chatAuthorImage import com.vitorpamplona.amethyst.ui.theme.chatAuthorImage
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
@ -303,7 +304,6 @@ private fun RenderBubble(
bubbleSize.intValue = it.width bubbleSize.intValue = it.width
} }
} }
.animateContentSize()
} }
Column(modifier = bubbleModifier) { Column(modifier = bubbleModifier) {
@ -346,6 +346,7 @@ private fun MessageBubbleLines(
baseNote, baseNote,
alignment, alignment,
accountViewModel.settings.showProfilePictures.value, accountViewModel.settings.showProfilePictures.value,
accountViewModel,
nav, nav,
) )
} }
@ -466,13 +467,9 @@ private fun NoteRow(
) { ) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
when (note.event) { when (note.event) {
is ChannelCreateEvent -> { is ChannelCreateEvent -> RenderCreateChannelNote(note)
RenderCreateChannelNote(note) is ChannelMetadataEvent -> RenderChangeChannelMetadataNote(note)
} is DraftEvent ->
is ChannelMetadataEvent -> {
RenderChangeChannelMetadataNote(note)
}
is DraftEvent -> {
RenderDraftEvent( RenderDraftEvent(
note, note,
canPreview, canPreview,
@ -483,8 +480,7 @@ private fun NoteRow(
accountViewModel, accountViewModel,
nav, nav,
) )
} else ->
else -> {
RenderRegularTextNote( RenderRegularTextNote(
note, note,
canPreview, canPreview,
@ -493,7 +489,6 @@ private fun NoteRow(
accountViewModel, accountViewModel,
nav, nav,
) )
}
} }
} }
} }
@ -710,6 +705,7 @@ private fun DrawAuthorInfo(
baseNote: Note, baseNote: Note,
alignment: Arrangement.Horizontal, alignment: Arrangement.Horizontal,
loadProfilePicture: Boolean, loadProfilePicture: Boolean,
accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
baseNote.author?.let { baseNote.author?.let {
@ -723,7 +719,7 @@ private fun DrawAuthorInfo(
nav("User/${baseNote.author?.pubkeyHex}") nav("User/${baseNote.author?.pubkeyHex}")
}, },
) { ) {
WatchAndDisplayUser(it, loadProfilePicture, nav) WatchAndDisplayUser(it, loadProfilePicture, accountViewModel, nav)
} }
} }
} }
@ -732,11 +728,23 @@ private fun DrawAuthorInfo(
private fun WatchAndDisplayUser( private fun WatchAndDisplayUser(
author: User, author: User,
loadProfilePicture: Boolean, loadProfilePicture: Boolean,
accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
val userState by author.live().userMetadataInfo.observeAsState() val userState by author.live().userMetadataInfo.observeAsState()
UserIcon(author.pubkeyHex, userState?.picture, loadProfilePicture) Box(chatAuthorBox, contentAlignment = Alignment.TopEnd) {
InnerUserPicture(
userHex = author.pubkeyHex,
userPicture = userState?.picture,
userName = userState?.bestName(),
size = Size20dp,
modifier = Modifier,
accountViewModel = accountViewModel,
)
ObserveAndDisplayFollowingMark(author.pubkeyHex, Size5dp, accountViewModel)
}
if (userState != null) { if (userState != null) {
DisplayMessageUsername(userState?.bestName() ?: author.pubkeyDisplayHex(), userState?.tags ?: EmptyTagList) DisplayMessageUsername(userState?.bestName() ?: author.pubkeyDisplayHex(), userState?.tags ?: EmptyTagList)

Wyświetl plik

@ -25,6 +25,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -70,19 +71,11 @@ fun LoadDecryptedContentOrNull(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
inner: @Composable (String?) -> Unit, inner: @Composable (String?) -> Unit,
) { ) {
var decryptedContent by val decryptedContent by
remember(note.event) { produceState(initialValue = accountViewModel.cachedDecrypt(note), key1 = note.event?.id()) {
mutableStateOf( accountViewModel.decrypt(note) { value = it }
accountViewModel.cachedDecrypt(note),
)
} }
if (decryptedContent == null) {
LaunchedEffect(key1 = decryptedContent) {
accountViewModel.decrypt(note) { decryptedContent = it }
}
}
inner(decryptedContent) inner(decryptedContent)
} }

Wyświetl plik

@ -39,7 +39,6 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -54,6 +53,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map import androidx.lifecycle.map
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.produceCachedStateAsync
import com.vitorpamplona.amethyst.model.Channel import com.vitorpamplona.amethyst.model.Channel
import com.vitorpamplona.amethyst.model.FeatureSetType import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
@ -562,7 +562,7 @@ private fun RenderNoteRow(
is AppDefinitionEvent -> RenderAppDefinition(baseNote, accountViewModel, nav) is AppDefinitionEvent -> RenderAppDefinition(baseNote, accountViewModel, nav)
is AudioTrackEvent -> RenderAudioTrack(baseNote, accountViewModel, nav) is AudioTrackEvent -> RenderAudioTrack(baseNote, accountViewModel, nav)
is AudioHeaderEvent -> RenderAudioHeader(baseNote, accountViewModel, nav) is AudioHeaderEvent -> RenderAudioHeader(baseNote, accountViewModel, nav)
is DraftEvent -> RenderDraft(baseNote, backgroundColor, accountViewModel, nav) is DraftEvent -> RenderDraft(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
is ReactionEvent -> RenderReaction(baseNote, quotesLeft, backgroundColor, accountViewModel, nav) is ReactionEvent -> RenderReaction(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
is RepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav) is RepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
is GenericRepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav) is GenericRepostEvent -> RenderRepost(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
@ -724,16 +724,8 @@ fun ObserveDraftEvent(
val noteState by note.live().metadata.observeAsState() val noteState by note.live().metadata.observeAsState()
val noteEvent = noteState?.note?.event as? DraftEvent ?: return val noteEvent = noteState?.note?.event as? DraftEvent ?: return
val noteAuthor = noteState?.note?.author ?: return
val innerNote = val innerNote = produceCachedStateAsync(cache = accountViewModel.draftNoteCache, key = noteEvent)
produceState(initialValue = accountViewModel.createTempCachedDraftNote(noteEvent, noteAuthor), noteEvent.id) {
if (value == null || value?.event?.id() != noteEvent.id) {
accountViewModel.createTempDraftNote(noteEvent, noteAuthor) {
value = it
}
}
}
innerNote.value?.let { innerNote.value?.let {
render(it) render(it)
@ -743,6 +735,7 @@ fun ObserveDraftEvent(
@Composable @Composable
fun RenderDraft( fun RenderDraft(
note: Note, note: Note,
quotesLeft: Int,
backgroundColor: MutableState<Color>, backgroundColor: MutableState<Color>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
@ -756,7 +749,7 @@ fun RenderDraft(
makeItShort = false, makeItShort = false,
canPreview = true, canPreview = true,
editState = edits, editState = edits,
quotesLeft = 3, quotesLeft = quotesLeft,
unPackReply = true, unPackReply = true,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,

Wyświetl plik

@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.screen
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -32,18 +31,13 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.pullrefresh.PullRefreshIndicator
import androidx.compose.material3.pullrefresh.pullRefresh
import androidx.compose.material3.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -68,30 +62,8 @@ fun RefreshableCardView(
scrollStateKey: String? = null, scrollStateKey: String? = null,
enablePullRefresh: Boolean = true, enablePullRefresh: Boolean = true,
) { ) {
var refreshing by remember { mutableStateOf(false) } RefresheableBox(viewModel, enablePullRefresh) {
val pullRefreshState =
rememberPullRefreshState(
refreshing,
onRefresh = {
refreshing = true
viewModel.invalidateData()
refreshing = false
},
)
val modifier =
if (enablePullRefresh) {
Modifier.fillMaxSize().pullRefresh(pullRefreshState)
} else {
Modifier.fillMaxSize()
}
Box(modifier) {
SaveableCardFeedState(viewModel, accountViewModel, nav, routeForLastRead, scrollStateKey) SaveableCardFeedState(viewModel, accountViewModel, nav, routeForLastRead, scrollStateKey)
if (enablePullRefresh) {
PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter))
}
} }
} }

Wyświetl plik

@ -70,7 +70,7 @@ class NotificationViewModel(val account: Account) :
} }
@Stable @Stable
open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() { open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), InvalidatableViewModel {
private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading) private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading)
val feedContent = _feedContent.asStateFlow() val feedContent = _feedContent.asStateFlow()
@ -358,7 +358,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
private val bundler = BundledUpdate(1000, Dispatchers.IO) private val bundler = BundledUpdate(1000, Dispatchers.IO)
private val bundlerInsert = BundledInsert<Set<Note>>(1000, Dispatchers.IO) private val bundlerInsert = BundledInsert<Set<Note>>(1000, Dispatchers.IO)
fun invalidateData(ignoreIfDoing: Boolean = false) { override fun invalidateData(ignoreIfDoing: Boolean) {
bundler.invalidate(ignoreIfDoing) { bundler.invalidate(ignoreIfDoing) {
// adds the time to perform the refresh into this delay // adds the time to perform the refresh into this delay
// holding off new updates in case of heavy refresh routines. // holding off new updates in case of heavy refresh routines.
@ -367,9 +367,9 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
} }
} }
fun invalidateDataAndSendToTop() { fun invalidateDataAndSendToTop(ignoreIfDoing: Boolean) {
clear() clear()
bundler.invalidate(false) { bundler.invalidate(ignoreIfDoing) {
// adds the time to perform the refresh into this delay // adds the time to perform the refresh into this delay
// holding off new updates in case of heavy refresh routines. // holding off new updates in case of heavy refresh routines.
val (value, elapsed) = val (value, elapsed) =

Wyświetl plik

@ -21,23 +21,16 @@
package com.vitorpamplona.amethyst.ui.screen package com.vitorpamplona.amethyst.ui.screen
import android.util.Log import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.pullrefresh.PullRefreshIndicator
import androidx.compose.material3.pullrefresh.pullRefresh
import androidx.compose.material3.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -57,7 +50,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Stable @Stable
class RelayFeedViewModel : ViewModel() { class RelayFeedViewModel : ViewModel(), InvalidatableViewModel {
val order = val order =
compareByDescending<RelayInfo> { it.lastEvent } compareByDescending<RelayInfo> { it.lastEvent }
.thenByDescending { it.counter } .thenByDescending { it.counter }
@ -112,8 +105,8 @@ class RelayFeedViewModel : ViewModel() {
private val bundler = BundledUpdate(250, Dispatchers.IO) private val bundler = BundledUpdate(250, Dispatchers.IO)
fun invalidateData() { override fun invalidateData(ignoreIfDoing: Boolean) {
bundler.invalidate { bundler.invalidate(ignoreIfDoing) {
// adds the time to perform the refresh into this delay // adds the time to perform the refresh into this delay
// holding off new updates in case of heavy refresh routines. // holding off new updates in case of heavy refresh routines.
refreshSuspended() refreshSuspended()
@ -142,22 +135,7 @@ fun RelayFeedView(
NewRelayListView({ wantsToAddRelay = "" }, accountViewModel, wantsToAddRelay, nav = nav) NewRelayListView({ wantsToAddRelay = "" }, accountViewModel, wantsToAddRelay, nav = nav)
} }
var refreshing by remember { mutableStateOf(false) } RefresheableBox(viewModel, enablePullRefresh) {
val refresh = {
refreshing = true
viewModel.refresh()
refreshing = false
}
val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh = refresh)
val modifier =
if (enablePullRefresh) {
Modifier.fillMaxSize().pullRefresh(pullRefreshState)
} else {
Modifier.fillMaxSize()
}
Box(modifier) {
val listState = rememberLazyListState() val listState = rememberLazyListState()
LazyColumn( LazyColumn(
@ -176,9 +154,5 @@ fun RelayFeedView(
) )
} }
} }
if (enablePullRefresh) {
PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter))
}
} }
} }

Wyświetl plik

@ -554,7 +554,7 @@ fun NoteMaster(
} else if (noteEvent is AppDefinitionEvent) { } else if (noteEvent is AppDefinitionEvent) {
RenderAppDefinition(baseNote, accountViewModel, nav) RenderAppDefinition(baseNote, accountViewModel, nav)
} else if (noteEvent is DraftEvent) { } else if (noteEvent is DraftEvent) {
RenderDraft(baseNote, backgroundColor, accountViewModel, nav) RenderDraft(baseNote, 3, backgroundColor, accountViewModel, nav)
} else if (noteEvent is HighlightEvent) { } else if (noteEvent is HighlightEvent) {
DisplayHighlight( DisplayHighlight(
noteEvent.quote(), noteEvent.quote(),

Wyświetl plik

@ -38,6 +38,7 @@ import coil.request.ImageRequest
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.commons.compose.GenericBaseCache import com.vitorpamplona.amethyst.commons.compose.GenericBaseCache
import com.vitorpamplona.amethyst.commons.compose.GenericBaseCacheAsync
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AccountState import com.vitorpamplona.amethyst.model.AccountState
import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.AddressableNote
@ -1322,23 +1323,11 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
account.deleteDraft(draftTag) account.deleteDraft(draftTag)
} }
fun createTempCachedDraftNote( suspend fun createTempDraftNote(
noteEvent: DraftEvent, noteEvent: DraftEvent,
author: User, onReady: (Note?) -> Unit,
): Note? {
return noteEvent.preCachedDraft(account.signer)?.let { createTempDraftNote(it, author) }
}
fun createTempDraftNote(
noteEvent: DraftEvent,
author: User,
onReady: (Note) -> Unit,
) { ) {
viewModelScope.launch(Dispatchers.IO) { draftNoteCache.update(noteEvent, onReady)
noteEvent.cachedDraft(account.signer) {
onReady(createTempDraftNote(it, author))
}
}
} }
fun createTempDraftNote( fun createTempDraftNote(
@ -1355,6 +1344,21 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
return note return note
} }
val draftNoteCache = CachedDraftNotes(this)
class CachedDraftNotes(val accountViewModel: AccountViewModel) : GenericBaseCacheAsync<DraftEvent, Note>(20) {
override suspend fun compute(
key: DraftEvent,
onReady: (Note?) -> Unit,
) = withContext(Dispatchers.IO) {
key.cachedDraft(accountViewModel.account.signer) {
val author = LocalCache.getOrCreateUser(key.pubKey)
val note = accountViewModel.createTempDraftNote(it, author)
onReady(note)
}
}
}
val bechLinkCache = CachedLoadedBechLink(this) val bechLinkCache = CachedLoadedBechLink(this)
class CachedLoadedBechLink(val accountViewModel: AccountViewModel) : GenericBaseCache<String, LoadedBechLink>(20) { class CachedLoadedBechLink(val accountViewModel: AccountViewModel) : GenericBaseCache<String, LoadedBechLink>(20) {

Wyświetl plik

@ -146,6 +146,7 @@ import com.vitorpamplona.amethyst.ui.theme.EditFieldLeadingIconModifier
import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier
import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier
import com.vitorpamplona.amethyst.ui.theme.HeaderPictureModifier import com.vitorpamplona.amethyst.ui.theme.HeaderPictureModifier
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
import com.vitorpamplona.amethyst.ui.theme.Size25dp import com.vitorpamplona.amethyst.ui.theme.Size25dp
import com.vitorpamplona.amethyst.ui.theme.Size34dp import com.vitorpamplona.amethyst.ui.theme.Size34dp
import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size35dp
@ -174,7 +175,6 @@ import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
import java.util.UUID
@Composable @Composable
fun ChannelScreen( fun ChannelScreen(
@ -328,7 +328,6 @@ fun ChannelScreen(
newPostModel.message = TextFieldValue("") newPostModel.message = TextFieldValue("")
replyTo.value = null replyTo.value = null
accountViewModel.deleteDraft(newPostModel.draftTag) accountViewModel.deleteDraft(newPostModel.draftTag)
newPostModel.draftTag = UUID.randomUUID().toString()
feedViewModel.sendToTop() feedViewModel.sendToTop()
} }
} }
@ -976,21 +975,21 @@ private fun ShortChannelActionOptions(
) { ) {
LoadNote(baseNoteHex = channel.idHex, accountViewModel) { LoadNote(baseNoteHex = channel.idHex, accountViewModel) {
it?.let { it?.let {
Spacer(modifier = StdHorzSpacer) Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = RowColSpacing) {
LikeReaction( LikeReaction(
baseNote = it, baseNote = it,
grayTint = MaterialTheme.colorScheme.onSurface, grayTint = MaterialTheme.colorScheme.onSurface,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav, nav,
) )
Spacer(modifier = StdHorzSpacer) ZapReaction(
ZapReaction( baseNote = it,
baseNote = it, grayTint = MaterialTheme.colorScheme.onSurface,
grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel,
accountViewModel = accountViewModel, nav = nav,
nav = nav, )
) Spacer(modifier = StdHorzSpacer)
Spacer(modifier = StdHorzSpacer) }
} }
} }

Wyświetl plik

@ -128,7 +128,6 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.UUID
@Composable @Composable
fun ChatroomScreen( fun ChatroomScreen(
@ -356,7 +355,6 @@ fun ChatroomScreen(
accountViewModel.deleteDraft(newPostModel.draftTag) accountViewModel.deleteDraft(newPostModel.draftTag)
newPostModel.message = TextFieldValue("") newPostModel.message = TextFieldValue("")
newPostModel.draftTag = UUID.randomUUID().toString()
replyTo.value = null replyTo.value = null
feedViewModel.sendToTop() feedViewModel.sendToTop()

Wyświetl plik

@ -276,7 +276,7 @@ fun MainScreen(
discoveryChatFeedViewModel.sendToTop() discoveryChatFeedViewModel.sendToTop()
} }
Route.Notification.base -> { Route.Notification.base -> {
notifFeedViewModel.invalidateDataAndSendToTop() notifFeedViewModel.invalidateDataAndSendToTop(true)
} }
} }

Wyświetl plik

@ -228,5 +228,6 @@ val liveStreamTag =
.background(Color.Black) .background(Color.Black)
.padding(horizontal = Size5dp) .padding(horizontal = Size5dp)
val chatAuthorBox = Modifier.size(20.dp)
val chatAuthorImage = Modifier.size(20.dp).clip(shape = CircleShape) val chatAuthorImage = Modifier.size(20.dp).clip(shape = CircleShape)
val AuthorInfoVideoFeed = Modifier.width(75.dp).padding(end = 15.dp) val AuthorInfoVideoFeed = Modifier.width(75.dp).padding(end = 15.dp)

Wyświetl plik

@ -274,6 +274,7 @@
<string name="report_dialog_title">Blokovat a nahlásit</string> <string name="report_dialog_title">Blokovat a nahlásit</string>
<string name="block_only">Blokovat</string> <string name="block_only">Blokovat</string>
<string name="bookmarks">Záložky</string> <string name="bookmarks">Záložky</string>
<string name="drafts">Koncepty</string>
<string name="private_bookmarks">Soukromé záložky</string> <string name="private_bookmarks">Soukromé záložky</string>
<string name="public_bookmarks">Veřejné záložky</string> <string name="public_bookmarks">Veřejné záložky</string>
<string name="add_to_private_bookmarks">Přidat do soukromých záložek</string> <string name="add_to_private_bookmarks">Přidat do soukromých záložek</string>
@ -620,6 +621,7 @@
<string name="server_did_not_provide_a_url_after_uploading">Server po nahrání neposkytl URL</string> <string name="server_did_not_provide_a_url_after_uploading">Server po nahrání neposkytl URL</string>
<string name="could_not_download_from_the_server">Nepodařilo se stáhnout nahraná média ze serveru</string> <string name="could_not_download_from_the_server">Nepodařilo se stáhnout nahraná média ze serveru</string>
<string name="could_not_prepare_local_file_to_upload">Nelze připravit místní soubor k nahrání: %1$s</string> <string name="could_not_prepare_local_file_to_upload">Nelze připravit místní soubor k nahrání: %1$s</string>
<string name="edit_draft">Upravit koncept</string>
<string name="login_with_qr_code">Přihlášení pomocí QR kódu</string> <string name="login_with_qr_code">Přihlášení pomocí QR kódu</string>
<string name="route">Trasa</string> <string name="route">Trasa</string>
<string name="route_home">Domů</string> <string name="route_home">Domů</string>
@ -691,4 +693,10 @@
<string name="accessibility_play_username">Přehrát uživatelské jméno jako audio</string> <string name="accessibility_play_username">Přehrát uživatelské jméno jako audio</string>
<string name="accessibility_scan_qr_code">Skenovat QR kód</string> <string name="accessibility_scan_qr_code">Skenovat QR kód</string>
<string name="accessibility_navigate_to_alby">Přejít na poskytovatele peněženky třetí strany Alby</string> <string name="accessibility_navigate_to_alby">Přejít na poskytovatele peněženky třetí strany Alby</string>
<string name="it_s_not_possible_to_reply_to_a_draft_note">Není možné odpovědět na koncept</string>
<string name="it_s_not_possible_to_quote_to_a_draft_note">Není možné citovat koncept</string>
<string name="it_s_not_possible_to_react_to_a_draft_note">Není možné reagovat na koncept</string>
<string name="it_s_not_possible_to_zap_to_a_draft_note">Není možné poslat zap konceptu</string>
<string name="draft_note">Koncept</string>
<string name="load_from_text">Ze zprávy</string>
</resources> </resources>

Wyświetl plik

@ -280,6 +280,7 @@ anz der Bedingungen ist erforderlich</string>
<string name="report_dialog_title">Blockieren und melden</string> <string name="report_dialog_title">Blockieren und melden</string>
<string name="block_only">Blockieren</string> <string name="block_only">Blockieren</string>
<string name="bookmarks">Lesezeichen</string> <string name="bookmarks">Lesezeichen</string>
<string name="drafts">Entwürfe</string>
<string name="private_bookmarks">Private Lesezeichen</string> <string name="private_bookmarks">Private Lesezeichen</string>
<string name="public_bookmarks">Öffentliche Lesezeichen</string> <string name="public_bookmarks">Öffentliche Lesezeichen</string>
<string name="add_to_private_bookmarks">Zu den privaten Lesezeichen hinzufügen</string> <string name="add_to_private_bookmarks">Zu den privaten Lesezeichen hinzufügen</string>
@ -625,6 +626,7 @@ anz der Bedingungen ist erforderlich</string>
<string name="server_did_not_provide_a_url_after_uploading">Der Server hat nach dem Hochladen keine URL angegeben</string> <string name="server_did_not_provide_a_url_after_uploading">Der Server hat nach dem Hochladen keine URL angegeben</string>
<string name="could_not_download_from_the_server">Hochgeladene Medien konnten nicht vom Server heruntergeladen werden</string> <string name="could_not_download_from_the_server">Hochgeladene Medien konnten nicht vom Server heruntergeladen werden</string>
<string name="could_not_prepare_local_file_to_upload">Lokale Datei konnte nicht zum Hochladen vorbereitet werden: %1$s</string> <string name="could_not_prepare_local_file_to_upload">Lokale Datei konnte nicht zum Hochladen vorbereitet werden: %1$s</string>
<string name="edit_draft">Entwurf bearbeiten</string>
<string name="login_with_qr_code">Einloggen mit QR-Code</string> <string name="login_with_qr_code">Einloggen mit QR-Code</string>
<string name="route">Route</string> <string name="route">Route</string>
<string name="route_home">Startseite</string> <string name="route_home">Startseite</string>
@ -696,4 +698,10 @@ anz der Bedingungen ist erforderlich</string>
<string name="accessibility_play_username">Benutzernamen als Audio abspielen</string> <string name="accessibility_play_username">Benutzernamen als Audio abspielen</string>
<string name="accessibility_scan_qr_code">QR-Code scannen</string> <string name="accessibility_scan_qr_code">QR-Code scannen</string>
<string name="accessibility_navigate_to_alby">Navigieren Sie zum Drittanbieter-Wallet-Anbieter Alby</string> <string name="accessibility_navigate_to_alby">Navigieren Sie zum Drittanbieter-Wallet-Anbieter Alby</string>
<string name="it_s_not_possible_to_reply_to_a_draft_note">Es ist nicht möglich, auf einen Entwurf zu antworten</string>
<string name="it_s_not_possible_to_quote_to_a_draft_note">Es ist nicht möglich, einen Entwurf zu zitieren</string>
<string name="it_s_not_possible_to_react_to_a_draft_note">Es ist nicht möglich, auf einen Entwurf zu reagieren</string>
<string name="it_s_not_possible_to_zap_to_a_draft_note">Es ist nicht möglich, zap Zahlung an einen Entwurf senden</string>
<string name="draft_note">Entwurf</string>
<string name="load_from_text">Aus Nachricht laden</string>
</resources> </resources>

Wyświetl plik

@ -276,6 +276,7 @@
<string name="report_dialog_title">Bloquear y reportar</string> <string name="report_dialog_title">Bloquear y reportar</string>
<string name="block_only">Bloquear</string> <string name="block_only">Bloquear</string>
<string name="bookmarks">Marcadores</string> <string name="bookmarks">Marcadores</string>
<string name="drafts">Borradores</string>
<string name="private_bookmarks">Marcadores privados</string> <string name="private_bookmarks">Marcadores privados</string>
<string name="public_bookmarks">Marcadores públicos</string> <string name="public_bookmarks">Marcadores públicos</string>
<string name="add_to_private_bookmarks">Agregar a marcadores privados</string> <string name="add_to_private_bookmarks">Agregar a marcadores privados</string>

Wyświetl plik

@ -276,6 +276,7 @@
<string name="report_dialog_title">Bloquear y reportar</string> <string name="report_dialog_title">Bloquear y reportar</string>
<string name="block_only">Bloquear</string> <string name="block_only">Bloquear</string>
<string name="bookmarks">Marcadores</string> <string name="bookmarks">Marcadores</string>
<string name="drafts">Borradores</string>
<string name="private_bookmarks">Marcadores privados</string> <string name="private_bookmarks">Marcadores privados</string>
<string name="public_bookmarks">Marcadores públicos</string> <string name="public_bookmarks">Marcadores públicos</string>
<string name="add_to_private_bookmarks">Agregar a marcadores privados</string> <string name="add_to_private_bookmarks">Agregar a marcadores privados</string>

Wyświetl plik

@ -276,6 +276,7 @@
<string name="report_dialog_title">Bloquer et Signaler</string> <string name="report_dialog_title">Bloquer et Signaler</string>
<string name="block_only">Bloquer</string> <string name="block_only">Bloquer</string>
<string name="bookmarks">Favoris</string> <string name="bookmarks">Favoris</string>
<string name="drafts">Brouillons</string>
<string name="private_bookmarks">Favoris Privés</string> <string name="private_bookmarks">Favoris Privés</string>
<string name="public_bookmarks">Favoris Publics</string> <string name="public_bookmarks">Favoris Publics</string>
<string name="add_to_private_bookmarks">Ajouter aux Favoris Privés</string> <string name="add_to_private_bookmarks">Ajouter aux Favoris Privés</string>

Wyświetl plik

@ -276,6 +276,7 @@
<string name="report_dialog_title">Blokkeer en rapporteer</string> <string name="report_dialog_title">Blokkeer en rapporteer</string>
<string name="block_only">Blokkeren</string> <string name="block_only">Blokkeren</string>
<string name="bookmarks">Bladwijzers</string> <string name="bookmarks">Bladwijzers</string>
<string name="drafts">Concepten</string>
<string name="private_bookmarks">Privé bladwijzers</string> <string name="private_bookmarks">Privé bladwijzers</string>
<string name="public_bookmarks">Publieke Bladwijzers</string> <string name="public_bookmarks">Publieke Bladwijzers</string>
<string name="add_to_private_bookmarks">Toevoegen aan privé bladwijzers</string> <string name="add_to_private_bookmarks">Toevoegen aan privé bladwijzers</string>
@ -441,6 +442,8 @@
<string name="connectivity_type_always">Altijd</string> <string name="connectivity_type_always">Altijd</string>
<string name="connectivity_type_wifi_only">Alleen wifi</string> <string name="connectivity_type_wifi_only">Alleen wifi</string>
<string name="connectivity_type_never">Nooit</string> <string name="connectivity_type_never">Nooit</string>
<string name="ui_feature_set_type_complete">Voltooid</string>
<string name="ui_feature_set_type_simplified">Vereenvoudigd</string>
<string name="system">Systeem</string> <string name="system">Systeem</string>
<string name="light">Licht</string> <string name="light">Licht</string>
<string name="dark">Donker</string> <string name="dark">Donker</string>
@ -452,6 +455,8 @@
<string name="automatically_show_url_preview">Automatisch URL preview tonen</string> <string name="automatically_show_url_preview">Automatisch URL preview tonen</string>
<string name="automatically_hide_nav_bars">Volledig scrollen</string> <string name="automatically_hide_nav_bars">Volledig scrollen</string>
<string name="automatically_hide_nav_bars_description">Navigatie verbergen bij scrollen</string> <string name="automatically_hide_nav_bars_description">Navigatie verbergen bij scrollen</string>
<string name="ui_style">UI modus</string>
<string name="ui_style_description">Kies de stijl van het bericht</string>
<string name="load_image">Afbeelding laden</string> <string name="load_image">Afbeelding laden</string>
<string name="spamming_users">Spammers</string> <string name="spamming_users">Spammers</string>
<string name="muted_button">Geen geluid. Klik om voor geluid</string> <string name="muted_button">Geen geluid. Klik om voor geluid</string>
@ -618,6 +623,7 @@
<string name="server_did_not_provide_a_url_after_uploading">Server heeft geen URL opgegeven na uploaden</string> <string name="server_did_not_provide_a_url_after_uploading">Server heeft geen URL opgegeven na uploaden</string>
<string name="could_not_download_from_the_server">Kan de geüploade media niet downloaden van de server</string> <string name="could_not_download_from_the_server">Kan de geüploade media niet downloaden van de server</string>
<string name="could_not_prepare_local_file_to_upload">Kon lokaal bestand niet voorbereiden voor upload: %1$s</string> <string name="could_not_prepare_local_file_to_upload">Kon lokaal bestand niet voorbereiden voor upload: %1$s</string>
<string name="edit_draft">Concept bewerken</string>
<string name="login_with_qr_code">Inloggen met QR-Code</string> <string name="login_with_qr_code">Inloggen met QR-Code</string>
<string name="route">Route</string> <string name="route">Route</string>
<string name="route_home">Beginscherm</string> <string name="route_home">Beginscherm</string>
@ -689,4 +695,9 @@
<string name="accessibility_play_username">Speel gebruikersnaam af als audio</string> <string name="accessibility_play_username">Speel gebruikersnaam af als audio</string>
<string name="accessibility_scan_qr_code">Scan de QR-code</string> <string name="accessibility_scan_qr_code">Scan de QR-code</string>
<string name="accessibility_navigate_to_alby">Navigeer naar de externe wallet provider Alby</string> <string name="accessibility_navigate_to_alby">Navigeer naar de externe wallet provider Alby</string>
<string name="it_s_not_possible_to_reply_to_a_draft_note">Het is niet mogelijk om een concept te beantwoorden</string>
<string name="it_s_not_possible_to_quote_to_a_draft_note">Het is niet mogelijk om een concept te citeren</string>
<string name="it_s_not_possible_to_react_to_a_draft_note">Het is niet mogelijk om op een concept te reageren</string>
<string name="it_s_not_possible_to_zap_to_a_draft_note">Het is niet mogelijk om een concept op te zappen</string>
<string name="draft_note">Concept notitie</string>
</resources> </resources>

Wyświetl plik

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="point_to_the_qr_code">Aponte para o código QR</string> <string name="point_to_the_qr_code">Aponte para o QR code</string>
<string name="show_qr">Mostrar QR</string> <string name="show_qr">Mostrar QR</string>
<string name="profile_image">Imagem de perfil</string> <string name="profile_image">Imagem de perfil</string>
<string name="your_profile_image">Sua Foto de Perfil</string> <string name="your_profile_image">Sua Foto de Perfil</string>
@ -274,6 +274,7 @@
<string name="report_dialog_title">Bloquear e Denunciar</string> <string name="report_dialog_title">Bloquear e Denunciar</string>
<string name="block_only">Bloquear</string> <string name="block_only">Bloquear</string>
<string name="bookmarks">Itens Salvos</string> <string name="bookmarks">Itens Salvos</string>
<string name="drafts">Rascunhos</string>
<string name="private_bookmarks">Itens Salvos Privados</string> <string name="private_bookmarks">Itens Salvos Privados</string>
<string name="public_bookmarks">Itens Salvos Públicos</string> <string name="public_bookmarks">Itens Salvos Públicos</string>
<string name="add_to_private_bookmarks">Adicionar aos Itens Salvos Privados</string> <string name="add_to_private_bookmarks">Adicionar aos Itens Salvos Privados</string>
@ -620,6 +621,7 @@
<string name="server_did_not_provide_a_url_after_uploading">O servidor não forneceu uma URL após o upload</string> <string name="server_did_not_provide_a_url_after_uploading">O servidor não forneceu uma URL após o upload</string>
<string name="could_not_download_from_the_server">Não foi possível baixar o arquivo de mídia carregado do servidor</string> <string name="could_not_download_from_the_server">Não foi possível baixar o arquivo de mídia carregado do servidor</string>
<string name="could_not_prepare_local_file_to_upload">Não foi possível preparar o arquivo local para enviar: %1$s</string> <string name="could_not_prepare_local_file_to_upload">Não foi possível preparar o arquivo local para enviar: %1$s</string>
<string name="edit_draft">Editar rascunho</string>
<string name="login_with_qr_code">Entrar com Código QR</string> <string name="login_with_qr_code">Entrar com Código QR</string>
<string name="route">Rota</string> <string name="route">Rota</string>
<string name="route_home">Início</string> <string name="route_home">Início</string>
@ -691,4 +693,10 @@
<string name="accessibility_play_username">Reproduzir nome de usuário como áudio</string> <string name="accessibility_play_username">Reproduzir nome de usuário como áudio</string>
<string name="accessibility_scan_qr_code">Escanear código QR</string> <string name="accessibility_scan_qr_code">Escanear código QR</string>
<string name="accessibility_navigate_to_alby">Navegar para o provedor de carteira de terceiros Alby</string> <string name="accessibility_navigate_to_alby">Navegar para o provedor de carteira de terceiros Alby</string>
<string name="it_s_not_possible_to_reply_to_a_draft_note">Não é possível responder uma nota em rascunho</string>
<string name="it_s_not_possible_to_quote_to_a_draft_note">Não é possível citar uma nota em rascunho</string>
<string name="it_s_not_possible_to_react_to_a_draft_note">Não é possível reagir uma nota em rascunho</string>
<string name="it_s_not_possible_to_zap_to_a_draft_note">Não é possível fazer um zap em uma nota em rascunho</string>
<string name="draft_note">Nota de rascunho</string>
<string name="load_from_text">Carregar do texto</string>
</resources> </resources>

Wyświetl plik

@ -274,6 +274,7 @@
<string name="report_dialog_title">Blockera och rapportera</string> <string name="report_dialog_title">Blockera och rapportera</string>
<string name="block_only">Blockera</string> <string name="block_only">Blockera</string>
<string name="bookmarks">Bokmärken</string> <string name="bookmarks">Bokmärken</string>
<string name="drafts">Utkast</string>
<string name="private_bookmarks">Privata Bokmärken</string> <string name="private_bookmarks">Privata Bokmärken</string>
<string name="public_bookmarks">Publika Bokmärken</string> <string name="public_bookmarks">Publika Bokmärken</string>
<string name="add_to_private_bookmarks">Lägg till i Privata Bokmärken</string> <string name="add_to_private_bookmarks">Lägg till i Privata Bokmärken</string>
@ -619,6 +620,7 @@
<string name="server_did_not_provide_a_url_after_uploading">Servern gav inte en URL efter uppladdning</string> <string name="server_did_not_provide_a_url_after_uploading">Servern gav inte en URL efter uppladdning</string>
<string name="could_not_download_from_the_server">Kunde inte ladda ner uppladdade medier från servern</string> <string name="could_not_download_from_the_server">Kunde inte ladda ner uppladdade medier från servern</string>
<string name="could_not_prepare_local_file_to_upload">Kunde inte förbereda lokal fil att ladda upp: %1$s</string> <string name="could_not_prepare_local_file_to_upload">Kunde inte förbereda lokal fil att ladda upp: %1$s</string>
<string name="edit_draft">Redigera utkast</string>
<string name="login_with_qr_code">Logga in med QR-kod</string> <string name="login_with_qr_code">Logga in med QR-kod</string>
<string name="route">Rutt</string> <string name="route">Rutt</string>
<string name="route_home">Hem</string> <string name="route_home">Hem</string>
@ -690,4 +692,10 @@
<string name="accessibility_play_username">Spela upp användarnamn som ljud</string> <string name="accessibility_play_username">Spela upp användarnamn som ljud</string>
<string name="accessibility_scan_qr_code">Skanna QR-kod</string> <string name="accessibility_scan_qr_code">Skanna QR-kod</string>
<string name="accessibility_navigate_to_alby">Navigera till tredjeparts plånboksleverantören Alby</string> <string name="accessibility_navigate_to_alby">Navigera till tredjeparts plånboksleverantören Alby</string>
<string name="it_s_not_possible_to_reply_to_a_draft_note">Det går inte att svara på ett utkast</string>
<string name="it_s_not_possible_to_quote_to_a_draft_note">Det går inte att citera ett utkast</string>
<string name="it_s_not_possible_to_react_to_a_draft_note">Det går inte att reagera på ett utkast</string>
<string name="it_s_not_possible_to_zap_to_a_draft_note">Det går inte att zappa ett utkast</string>
<string name="draft_note">Utkast</string>
<string name="load_from_text">Från meddelande</string>
</resources> </resources>

Wyświetl plik

@ -827,4 +827,6 @@
<string name="it_s_not_possible_to_react_to_a_draft_note">It\'s not possible to react a draft note</string> <string name="it_s_not_possible_to_react_to_a_draft_note">It\'s not possible to react a draft note</string>
<string name="it_s_not_possible_to_zap_to_a_draft_note">It\'s not possible to zap a draft note</string> <string name="it_s_not_possible_to_zap_to_a_draft_note">It\'s not possible to zap a draft note</string>
<string name="draft_note">Draft Note</string> <string name="draft_note">Draft Note</string>
<string name="load_from_text">From Msg</string>
</resources> </resources>

Wyświetl plik

@ -0,0 +1,88 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.commons.compose
import android.util.LruCache
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
@Composable
fun <K, V> produceCachedStateAsync(
cache: AsyncCachedState<K, V>,
key: K,
): State<V?> {
return produceState(initialValue = cache.cached(key), key1 = key) {
cache.update(key) {
value = it
}
}
}
@Composable
fun <K, V> produceCachedStateAsync(
cache: AsyncCachedState<K, V>,
key: String,
updateValue: K,
): State<V?> {
return produceState(initialValue = cache.cached(updateValue), key1 = key) {
cache.update(updateValue) {
value = it
}
}
}
interface AsyncCachedState<K, V> {
fun cached(k: K): V?
suspend fun update(
k: K,
onReady: (V?) -> Unit,
)
}
abstract class GenericBaseCacheAsync<K, V>(capacity: Int) : AsyncCachedState<K, V> {
private val cache = LruCache<K, V>(capacity)
override fun cached(k: K): V? {
return cache[k]
}
override suspend fun update(
k: K,
onReady: (V?) -> Unit,
) {
cache[k]?.let { onReady(it) }
compute(k) {
if (it != null) {
cache.put(k, it)
}
onReady(it)
}
}
abstract suspend fun compute(
key: K,
onReady: (V?) -> Unit,
)
}

Wyświetl plik

@ -24,7 +24,7 @@ import java.util.concurrent.ConcurrentSkipListMap
import java.util.function.BiConsumer import java.util.function.BiConsumer
class LargeCache<K, V> { class LargeCache<K, V> {
val cache = ConcurrentSkipListMap<K, V>() private val cache = ConcurrentSkipListMap<K, V>()
fun get(key: K) = cache.get(key) fun get(key: K) = cache.get(key)

Wyświetl plik

@ -78,7 +78,13 @@ class DraftEvent(
onReady: (Event) -> Unit, onReady: (Event) -> Unit,
) { ) {
try { try {
plainContent(signer) { onReady(fromJson(it)) } plainContent(signer) {
try {
onReady(fromJson(it))
} catch (e: Exception) {
// Log.e("UnwrapError", "Couldn't Decrypt the content", e)
}
}
} catch (e: Exception) { } catch (e: Exception) {
// Log.e("UnwrapError", "Couldn't Decrypt the content", e) // Log.e("UnwrapError", "Couldn't Decrypt the content", e)
} }

Wyświetl plik

@ -44,6 +44,13 @@ class GiftWrapEvent(
return cachedInnerEvent[signer.pubKey] return cachedInnerEvent[signer.pubKey]
} }
fun addToCache(
pubKey: HexKey,
gift: Event,
) {
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
}
fun cachedGift( fun cachedGift(
signer: NostrSigner, signer: NostrSigner,
onReady: (Event) -> Unit, onReady: (Event) -> Unit,
@ -56,7 +63,7 @@ class GiftWrapEvent(
if (gift is WrappedEvent) { if (gift is WrappedEvent) {
gift.host = this gift.host = this
} }
cachedInnerEvent = cachedInnerEvent + Pair(signer.pubKey, gift) addToCache(signer.pubKey, gift)
onReady(gift) onReady(gift)
} }
@ -98,8 +105,8 @@ class GiftWrapEvent(
val serializedContent = toJson(event) val serializedContent = toJson(event)
val tags = arrayOf(arrayOf("p", recipientPubKey)) val tags = arrayOf(arrayOf("p", recipientPubKey))
signer.nip44Encrypt(serializedContent, recipientPubKey) { signer.nip44Encrypt(serializedContent, recipientPubKey) { content ->
signer.sign(createdAt, KIND, tags, it, onReady) signer.sign(createdAt, KIND, tags, content, onReady)
} }
} }
} }

Wyświetl plik

@ -45,6 +45,13 @@ class SealedGossipEvent(
return cachedInnerEvent[signer.pubKey] return cachedInnerEvent[signer.pubKey]
} }
fun addToCache(
pubKey: HexKey,
gift: Event,
) {
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
}
fun cachedGossip( fun cachedGossip(
signer: NostrSigner, signer: NostrSigner,
onReady: (Event) -> Unit, onReady: (Event) -> Unit,
@ -59,8 +66,8 @@ class SealedGossipEvent(
if (event is WrappedEvent) { if (event is WrappedEvent) {
event.host = host ?: this event.host = host ?: this
} }
addToCache(signer.pubKey, event)
cachedInnerEvent = cachedInnerEvent + Pair(signer.pubKey, event)
onReady(event) onReady(event)
} }
} }