Merge branch 'main' into adding_translations

This commit is contained in:
Vitor Pamplona
2024-03-01 12:17:12 -05:00
committed by GitHub
25 changed files with 454 additions and 200 deletions
+65 -64
View File
@@ -1,7 +1,7 @@
plugins { plugins {
id 'com.android.application' alias(libs.plugins.androidApplication)
id 'org.jetbrains.kotlin.android' alias(libs.plugins.jetbrainsKotlinAndroid)
id 'com.google.gms.google-services' alias(libs.plugins.googleServices)
} }
android { android {
@@ -145,14 +145,13 @@ android {
// Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin // Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin
kotlinCompilerExtensionVersion "1.5.8" kotlinCompilerExtensionVersion "1.5.8"
} }
packagingOptions { packagingOptions {
resources { resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}' excludes += ['/META-INF/{AL2.0,LGPL2.1}', '**/libscrypt.dylib']
} }
exclude '**/libscrypt.dylib'
} }
lint { lint {
disable 'MissingTranslation' disable 'MissingTranslation'
} }
@@ -165,75 +164,78 @@ android {
dependencies { dependencies {
implementation project(path: ':quartz') implementation project(path: ':quartz')
implementation project(path: ':commons') implementation project(path: ':commons')
implementation "androidx.core:core-ktx:$core_ktx_version" implementation libs.androidx.core.ktx
implementation 'androidx.activity:activity-compose:1.8.2' implementation libs.androidx.activity.compose
implementation "androidx.compose.ui:ui:$compose_ui_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" implementation platform(libs.androidx.compose.bom)
implementation libs.androidx.ui
implementation libs.androidx.ui.graphics
implementation libs.androidx.ui.tooling.preview
// Needs this to open gallery / image upload // Needs this to open gallery / image upload
implementation "androidx.fragment:fragment-ktx:$fragment_version" implementation libs.androidx.fragment.ktx
// Navigation // Navigation
implementation "androidx.navigation:navigation-compose:$nav_version" implementation libs.androidx.navigation.compose
// Observe Live data as State // Observe Live data as State
implementation "androidx.compose.runtime:runtime-livedata:$compose_ui_version" implementation libs.androidx.runtime.livedata
// Material 3 Design // Material 3 Design
implementation "androidx.compose.material3:material3:${material3_version}" implementation libs.androidx.material3
implementation "androidx.compose.material:material-icons-extended:$compose_ui_version" implementation libs.androidx.material.icons
// Adaptive Layout / Two Pane // Adaptive Layout / Two Pane
implementation "androidx.compose.material3:material3-window-size-class:${material3_version}" implementation libs.androidx.material3.windowSize
implementation 'com.google.accompanist:accompanist-adaptive:0.34.0' implementation libs.accompanist.adaptive
// Lifecycle // Lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version" implementation libs.androidx.lifecycle.runtime.ktx
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" implementation libs.androidx.lifecycle.runtime.compose
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" implementation libs.androidx.lifecycle.viewmodel.compose
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation libs.androidx.lifecycle.livedata.ktx
// Zoomable images // Zoomable images
implementation 'net.engawapg.lib:zoomable:1.6.0' implementation libs.zoomable
// Biometrics // Biometrics
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05" implementation libs.androidx.biometric.ktx
// Websockets API // Websockets API
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.12' implementation libs.okhttp
// HTML Parsing for Link Preview // HTML Parsing for Link Preview
implementation 'org.jsoup:jsoup:1.17.2' implementation libs.jsoup
// Encrypted Key Storage // Encrypted Key Storage
implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha06' implementation libs.androidx.security.crypto.ktx
// view videos // view videos
implementation "androidx.media3:media3-exoplayer:$media3_version" implementation libs.androidx.media3.exoplayer
implementation "androidx.media3:media3-exoplayer-hls:$media3_version" implementation libs.androidx.media3.exoplayer.hls
implementation "androidx.media3:media3-ui:$media3_version" implementation libs.androidx.media3.ui
implementation "androidx.media3:media3-session:$media3_version" implementation libs.androidx.media3.session
// important for proxy / tor // important for proxy / tor
implementation "androidx.media3:media3-datasource-okhttp:$media3_version" implementation libs.androidx.media3.datasource.okhttp
// Load images from the web. // Load images from the web.
implementation "io.coil-kt:coil-compose:$coil_version" implementation libs.coil.compose
// view gifs // view gifs
implementation "io.coil-kt:coil-gif:$coil_version" implementation libs.coil.gif
// view svgs // view svgs
implementation "io.coil-kt:coil-svg:$coil_version" implementation libs.coil.svg
// create blurhash // create blurhash
implementation group: 'io.trbl', name: 'blurhash', version: '1.0.0' implementation libs.trbl.blurhash
// Permission to upload pictures: // Permission to upload pictures:
implementation "com.google.accompanist:accompanist-permissions:$accompanist_version" implementation libs.accompanist.permissions
// For QR generation // For QR generation
implementation 'com.google.zxing:core:3.5.3' implementation libs.zxing
implementation 'com.journeyapps:zxing-android-embedded:4.3.0' implementation libs.zxing.embedded
// Markdown // Markdown
//implementation "com.halilibo.compose-richtext:richtext-ui:0.16.0" //implementation "com.halilibo.compose-richtext:richtext-ui:0.16.0"
@@ -241,51 +243,50 @@ dependencies {
//implementation "com.halilibo.compose-richtext:richtext-commonmark:0.16.0" //implementation "com.halilibo.compose-richtext:richtext-commonmark:0.16.0"
// Markdown (With fix for full-image bleeds) // Markdown (With fix for full-image bleeds)
implementation('com.github.vitorpamplona.compose-richtext:richtext-ui:48702a8ced') implementation libs.markdown.ui
implementation('com.github.vitorpamplona.compose-richtext:richtext-ui-material3:48702a8ced') implementation libs.markdown.ui.material3
implementation('com.github.vitorpamplona.compose-richtext:richtext-commonmark:48702a8ced') implementation libs.markdown.commonmark
// Language picker and Theme chooser // Language picker and Theme chooser
implementation 'androidx.appcompat:appcompat:1.6.1' implementation libs.androidx.appcompat
// Local model for language identification // Local model for language identification
playImplementation 'com.google.mlkit:language-id:17.0.5' playImplementation libs.google.mlkit.language.id
// Google services model the translate text // Google services model the translate text
playImplementation 'com.google.mlkit:translate:17.0.2' playImplementation libs.google.mlkit.translate
// PushNotifications // PushNotifications
playImplementation platform('com.google.firebase:firebase-bom:32.7.2') playImplementation platform(libs.firebase.bom)
playImplementation 'com.google.firebase:firebase-messaging-ktx' playImplementation libs.firebase.messaging
//PushNotifications(FDroid) //PushNotifications(FDroid)
fdroidImplementation 'com.github.UnifiedPush:android-connector:2.2.0' fdroidImplementation libs.unifiedpush
// Charts // Charts
implementation "com.patrykandpatrick.vico:core:${vico_version}" implementation libs.vico.charts.core
implementation "com.patrykandpatrick.vico:compose:${vico_version}" implementation libs.vico.charts.compose
implementation "com.patrykandpatrick.vico:views:${vico_version}" implementation libs.vico.charts.views
implementation "com.patrykandpatrick.vico:compose-m2:${vico_version}" implementation libs.vico.charts.m3
// GeoHash // GeoHash
implementation 'com.github.drfonfon:android-kotlin-geohash:1.0' implementation libs.drfonfon.geohash
// Waveform visualizer // Waveform visualizer
implementation 'com.github.lincollincol:compose-audiowaveform:1.1.1' implementation libs.audiowaveform
// Video compression lib // Video compression lib
implementation 'com.github.AbedElazizShe:LightCompressor:1.3.2' implementation libs.abedElazizShe.image.compressor
// Image compression lib // Image compression lib
implementation 'id.zelory:compressor:3.0.1' implementation libs.zelory.video.compressor
testImplementation 'junit:junit:4.13.2' testImplementation libs.junit
testImplementation 'io.mockk:mockk:1.13.9' testImplementation libs.mockk
androidTestImplementation 'androidx.test.ext:junit:1.2.0-alpha03' androidTestImplementation libs.androidx.junit
androidTestImplementation 'androidx.test.ext:junit-ktx:1.2.0-alpha03' androidTestImplementation libs.androidx.junit.ktx
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation libs.androidx.espresso.core
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" debugImplementation libs.androidx.ui.tooling
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" debugImplementation libs.androidx.ui.test.manifest
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
} }
// https://gitlab.com/fdroid/wiki/-/wikis/HOWTO:-diff-&-fix-APKs-for-Reproducible-Builds#differing-assetsdexoptbaselineprofm-easy-to-fix // https://gitlab.com/fdroid/wiki/-/wikis/HOWTO:-diff-&-fix-APKs-for-Reproducible-Builds#differing-assetsdexoptbaselineprofm-easy-to-fix
@@ -21,6 +21,7 @@
package com.vitorpamplona.amethyst.model package com.vitorpamplona.amethyst.model
import android.util.Log import android.util.Log
import android.util.LruCache
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.service.checkNotInMainThread
@@ -1402,6 +1403,13 @@ object LocalCache {
note.loadEvent(event, author, emptyList()) note.loadEvent(event, author, emptyList())
event.editedNote()?.let {
getNoteIfExists(it)?.let { editedNote ->
modificationCache.remove(editedNote.idHex)
editedNote.liveSet?.innerModifications?.invalidateData()
}
}
refreshObservers(note) refreshObservers(note)
} }
@@ -1716,15 +1724,31 @@ object LocalCache {
return minTime return minTime
} }
val modificationCache = LruCache<HexKey, List<Note>>(20)
fun cachedModificationEventsForNote(note: Note): List<Note>? {
return modificationCache[note.idHex]
}
suspend fun findLatestModificationForNote(note: Note): List<Note> { suspend fun findLatestModificationForNote(note: Note): List<Note> {
checkNotInMainThread() checkNotInMainThread()
modificationCache[note.idHex]?.let {
return it
}
val time = TimeUtils.now() val time = TimeUtils.now()
return noteListCache.filter { item -> val newNotes =
val noteEvent = item.event noteListCache.filter { item ->
val noteEvent = item.event
noteEvent is TextNoteModificationEvent && noteEvent.isTaggedEvent(note.idHex) && !noteEvent.isExpirationBefore(time) noteEvent is TextNoteModificationEvent && noteEvent.isTaggedEvent(note.idHex) && !noteEvent.isExpirationBefore(time)
} }.sortedWith(compareBy({ it.createdAt() }, { it.idHex }))
modificationCache.put(note.idHex, newNotes)
return newNotes
} }
fun cleanObservers() { fun cleanObservers() {
@@ -37,26 +37,26 @@ class JsonFilter(
val filter = val filter =
factory.objectNode().apply { factory.objectNode().apply {
ids?.run { ids?.run {
put( replace(
"ids", "ids",
factory.arrayNode(ids.size).apply { ids.forEach { add(it) } }, factory.arrayNode(ids.size).apply { ids.forEach { add(it) } },
) )
} }
authors?.run { authors?.run {
put( replace(
"authors", "authors",
factory.arrayNode(authors.size).apply { authors.forEach { add(it) } }, factory.arrayNode(authors.size).apply { authors.forEach { add(it) } },
) )
} }
kinds?.run { kinds?.run {
put( replace(
"kinds", "kinds",
factory.arrayNode(kinds.size).apply { kinds.forEach { add(it) } }, factory.arrayNode(kinds.size).apply { kinds.forEach { add(it) } },
) )
} }
tags?.run { tags?.run {
entries.forEach { kv -> entries.forEach { kv ->
put( replace(
"#${kv.key}", "#${kv.key}",
factory.arrayNode(kv.value.size).apply { kv.value.forEach { add(it) } }, factory.arrayNode(kv.value.size).apply { kv.value.forEach { add(it) } },
) )
@@ -47,7 +47,7 @@ data class Subscription(
return factory.objectNode().apply { return factory.objectNode().apply {
put("id", id) put("id", id)
typedFilters?.also { filters -> typedFilters?.also { filters ->
put( replace(
"typedFilters", "typedFilters",
factory.arrayNode(filters.size).apply { factory.arrayNode(filters.size).apply {
filters.forEach { filter -> add(filter.toJsonObject()) } filters.forEach { filter -> add(filter.toJsonObject()) }
@@ -36,8 +36,8 @@ class TypedFilter(
val factory = Event.mapper.nodeFactory val factory = Event.mapper.nodeFactory
return factory.objectNode().apply { return factory.objectNode().apply {
put("types", typesToJson(types)) replace("types", typesToJson(types))
put("filter", filterToJson(filter)) replace("filter", filterToJson(filter))
} }
} }
@@ -50,26 +50,26 @@ class TypedFilter(
val factory = Event.mapper.nodeFactory val factory = Event.mapper.nodeFactory
return factory.objectNode().apply { return factory.objectNode().apply {
filter.ids?.run { filter.ids?.run {
put( replace(
"ids", "ids",
factory.arrayNode(filter.ids.size).apply { filter.ids.forEach { add(it) } }, factory.arrayNode(filter.ids.size).apply { filter.ids.forEach { add(it) } },
) )
} }
filter.authors?.run { filter.authors?.run {
put( replace(
"authors", "authors",
factory.arrayNode(filter.authors.size).apply { filter.authors.forEach { add(it) } }, factory.arrayNode(filter.authors.size).apply { filter.authors.forEach { add(it) } },
) )
} }
filter.kinds?.run { filter.kinds?.run {
put( replace(
"kinds", "kinds",
factory.arrayNode(filter.kinds.size).apply { filter.kinds.forEach { add(it) } }, factory.arrayNode(filter.kinds.size).apply { filter.kinds.forEach { add(it) } },
) )
} }
filter.tags?.run { filter.tags?.run {
entries.forEach { kv -> entries.forEach { kv ->
put( replace(
"#${kv.key}", "#${kv.key}",
factory.arrayNode(kv.value.size).apply { kv.value.forEach { add(it) } }, factory.arrayNode(kv.value.size).apply { kv.value.forEach { add(it) } },
) )
@@ -61,6 +61,7 @@ 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.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf 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
@@ -1088,10 +1089,58 @@ fun InnerNoteWithReactions(
} }
@Stable @Stable
class EditState( class EditState() {
val showOriginal: MutableState<Boolean> = mutableStateOf(false), private var modificationsList: List<Note> = persistentListOf()
val modificationsInOrder: MutableState<List<Note>> = mutableStateOf(emptyList()), private var modificationToShowIndex: Int = -1
)
val modificationToShow: MutableState<Note?> = mutableStateOf(null)
val showingVersion: MutableState<Int> = mutableStateOf(0)
fun hasModificationsToShow(): Boolean = modificationsList.isNotEmpty()
fun isOriginal(): Boolean = modificationToShowIndex < 0
fun isLatest(): Boolean = modificationToShowIndex == modificationsList.lastIndex
fun originalVersionId() = 0
fun lastVersionId() = modificationsList.size
fun versionId() = modificationToShowIndex + 1
fun nextModification() {
if (modificationToShowIndex < 0) {
modificationToShowIndex = 0
modificationToShow.value = modificationsList.getOrNull(0)
} else {
modificationToShowIndex++
if (modificationToShowIndex >= modificationsList.size) {
modificationToShowIndex = -1
modificationToShow.value = null
} else {
modificationToShow.value = modificationsList.getOrNull(modificationToShowIndex)
}
}
showingVersion.value = versionId()
}
fun updateModifications(newModifications: List<Note>) {
if (modificationsList != newModifications) {
modificationsList = newModifications
if (newModifications.isEmpty()) {
modificationToShow.value = null
modificationToShowIndex = -1
} else {
modificationToShowIndex = newModifications.lastIndex
modificationToShow.value = newModifications.last()
}
}
showingVersion.value = versionId()
}
}
@Composable @Composable
private fun NoteBody( private fun NoteBody(
@@ -1105,14 +1154,7 @@ private fun NoteBody(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
val editState by val editState = observeEdits(baseNote = baseNote, accountViewModel = accountViewModel)
produceState(initialValue = EditState(), key1 = baseNote) {
accountViewModel.findModificationEventsForNote(baseNote) { newModifications ->
if (value.modificationsInOrder.value != newModifications) {
value.modificationsInOrder.value = newModifications
}
}
}
FirstUserInfoRow( FirstUserInfoRow(
baseNote = baseNote, baseNote = baseNote,
@@ -1168,7 +1210,7 @@ private fun RenderNoteRow(
backgroundColor: MutableState<Color>, backgroundColor: MutableState<Color>,
makeItShort: Boolean, makeItShort: Boolean,
canPreview: Boolean, canPreview: Boolean,
editState: EditState, editState: State<GenericLoadable<EditState>>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
@@ -1358,7 +1400,7 @@ fun RenderTextEvent(
makeItShort: Boolean, makeItShort: Boolean,
canPreview: Boolean, canPreview: Boolean,
backgroundColor: MutableState<Color>, backgroundColor: MutableState<Color>,
editState: EditState, editState: State<GenericLoadable<EditState>>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
@@ -1371,10 +1413,10 @@ fun RenderTextEvent(
derivedStateOf { derivedStateOf {
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null } val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
val newBody = val newBody =
if (editState.showOriginal.value || editState.modificationsInOrder.value.isEmpty()) { if (editState.value is GenericLoadable.Loaded) {
body (editState.value as? GenericLoadable.Loaded)?.loaded?.modificationToShow?.value?.event?.content() ?: body
} else { } else {
editState.modificationsInOrder.value.firstOrNull()?.event?.content() ?: body body
} }
if (!subject.isNullOrBlank() && !newBody.split("\n")[0].contains(subject)) { if (!subject.isNullOrBlank() && !newBody.split("\n")[0].contains(subject)) {
@@ -2822,7 +2864,7 @@ fun DisplayLocation(
fun FirstUserInfoRow( fun FirstUserInfoRow(
baseNote: Note, baseNote: Note,
showAuthorPicture: Boolean, showAuthorPicture: Boolean,
editState: EditState, editState: State<GenericLoadable<EditState>>,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
@@ -2857,8 +2899,10 @@ fun FirstUserInfoRow(
DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav) DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav)
} }
if (!editState.modificationsInOrder.value.isEmpty()) { if (editState.value is GenericLoadable.Loaded) {
DisplayEditStatus(editState.showOriginal) (editState.value as? GenericLoadable.Loaded<EditState>)?.loaded?.let {
DisplayEditStatus(it)
}
} }
TimeAgo(baseNote) TimeAgo(baseNote)
@@ -2868,15 +2912,69 @@ fun FirstUserInfoRow(
} }
@Composable @Composable
fun DisplayEditStatus(showOriginal: MutableState<Boolean>) { fun observeEdits(
baseNote: Note,
accountViewModel: AccountViewModel,
): State<GenericLoadable<EditState>> {
val editState =
remember(baseNote.idHex) {
val cached = accountViewModel.cachedModificationEventsForNote(baseNote)
mutableStateOf(
if (cached != null) {
if (cached.isEmpty()) {
GenericLoadable.Empty<EditState>()
} else {
val state = EditState()
state.updateModifications(cached)
GenericLoadable.Loaded<EditState>(state)
}
} else {
GenericLoadable.Loading<EditState>()
},
)
}
val updatedNote = baseNote.live().innerModifications.observeAsState()
LaunchedEffect(key1 = updatedNote) {
updatedNote.value?.note?.let {
accountViewModel.findModificationEventsForNote(it) { newModifications ->
if (newModifications.isEmpty()) {
if (editState.value !is GenericLoadable.Empty) {
editState.value = GenericLoadable.Empty<EditState>()
}
} else {
if (editState.value is GenericLoadable.Loaded) {
(editState.value as? GenericLoadable.Loaded<EditState>)?.loaded?.updateModifications(newModifications)
} else {
val state = EditState()
state.updateModifications(newModifications)
editState.value = GenericLoadable.Loaded(state)
}
}
}
}
}
return editState
}
@Composable
fun DisplayEditStatus(editState: EditState) {
ClickableText( ClickableText(
text = text =
if (showOriginal.value) { buildAnnotatedString {
buildAnnotatedString { append(stringResource(id = R.string.original)) } if (editState.showingVersion.value == editState.originalVersionId()) {
} else { append(stringResource(id = R.string.original))
buildAnnotatedString { append(stringResource(id = R.string.edited)) } } else if (editState.showingVersion.value == editState.lastVersionId()) {
append(stringResource(id = R.string.edited))
} else {
append(stringResource(id = R.string.edited_number, editState.versionId()))
}
}, },
onClick = { showOriginal.value = !showOriginal.value }, onClick = {
editState.nextModification()
},
style = style =
LocalTextStyle.current.copy( LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.placeholderText, color = MaterialTheme.colorScheme.placeholderText,
@@ -111,14 +111,17 @@ class PollNoteViewModel : ViewModel() {
} }
tallies.forEach { tallies.forEach {
it.zappedValue.value = zappedPollOptionAmount(it.option) val zappedValue = zappedPollOptionAmount(it.option)
it.tally.value = val tallyValue =
if (totalZapped.compareTo(BigDecimal.ZERO) > 0) { if (totalZapped > BigDecimal.ZERO) {
it.zappedValue.value.divide(totalZapped, 2, RoundingMode.HALF_UP) zappedValue.divide(totalZapped, 2, RoundingMode.HALF_UP)
} else { } else {
BigDecimal.ZERO BigDecimal.ZERO
} }
it.consensusThreadhold.value = consensusThreshold != null && it.tally.value >= consensusThreshold!!
it.zappedValue.value = zappedValue
it.tally.value = tallyValue
it.consensusThreadhold.value = consensusThreshold != null && tallyValue >= consensusThreshold!!
it.zappedByLoggedIn.value = account?.userProfile()?.let { it1 -> cachedIsPollOptionZappedBy(it.option, it1) } ?: false it.zappedByLoggedIn.value = account?.userProfile()?.let { it1 -> cachedIsPollOptionZappedBy(it.option, it1) } ?: false
} }
} }
@@ -483,7 +483,10 @@ fun NoteDropDownMenu(
if (wantsToEditPost.value) { if (wantsToEditPost.value) {
EditPostView( EditPostView(
onClose = { wantsToEditPost.value = false }, onClose = {
popupExpanded.value = false
wantsToEditPost.value = false
},
edit = note, edit = note,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
@@ -58,7 +58,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.runtime.setValue import androidx.compose.runtime.setValue
@@ -86,6 +85,7 @@ import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.ui.components.InlineCarrousel import com.vitorpamplona.amethyst.ui.components.InlineCarrousel
import com.vitorpamplona.amethyst.ui.components.LoadNote import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
@@ -132,6 +132,7 @@ import com.vitorpamplona.amethyst.ui.note.RenderPostApproval
import com.vitorpamplona.amethyst.ui.note.RenderRepost import com.vitorpamplona.amethyst.ui.note.RenderRepost
import com.vitorpamplona.amethyst.ui.note.RenderTextEvent import com.vitorpamplona.amethyst.ui.note.RenderTextEvent
import com.vitorpamplona.amethyst.ui.note.VideoDisplay import com.vitorpamplona.amethyst.ui.note.VideoDisplay
import com.vitorpamplona.amethyst.ui.note.observeEdits
import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.note.timeAgo import com.vitorpamplona.amethyst.ui.note.timeAgo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -377,20 +378,13 @@ fun NoteMaster(
onClick = { showHiddenNote = true }, onClick = { showHiddenNote = true },
) )
} else { } else {
val editState by
produceState(initialValue = EditState(), key1 = baseNote) {
accountViewModel.findModificationEventsForNote(baseNote) { newModifications ->
if (value.modificationsInOrder.value != newModifications) {
value.modificationsInOrder.value = newModifications
}
}
}
Column( Column(
modifier modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 10.dp), .padding(top = 10.dp),
) { ) {
val editState = observeEdits(baseNote = baseNote, accountViewModel = accountViewModel)
Row( Row(
modifier = modifier =
Modifier Modifier
@@ -421,8 +415,10 @@ fun NoteMaster(
DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav) DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav)
} }
if (!editState.modificationsInOrder.value.isEmpty()) { if (editState.value is GenericLoadable.Loaded) {
DisplayEditStatus(editState.showOriginal) (editState.value as? GenericLoadable.Loaded<EditState>)?.loaded?.let {
DisplayEditStatus(it)
}
} }
Text( Text(
@@ -850,9 +846,9 @@ private fun RenderWikiHeaderForThreadPreview() {
val accountViewModel = mockAccountViewModel() val accountViewModel = mockAccountViewModel()
val nav: (String) -> Unit = {} val nav: (String) -> Unit = {}
val editState by val editState =
remember { remember {
mutableStateOf(EditState()) mutableStateOf(GenericLoadable.Empty<EditState>())
} }
runBlocking { runBlocking {
@@ -932,6 +932,8 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
} }
} }
fun cachedModificationEventsForNote(note: Note) = LocalCache.cachedModificationEventsForNote(note)
suspend fun findModificationEventsForNote( suspend fun findModificationEventsForNote(
note: Note, note: Note,
onResult: (List<Note>) -> Unit, onResult: (List<Note>) -> Unit,
+1
View File
@@ -51,6 +51,7 @@
<string name="boost">Boost</string> <string name="boost">Boost</string>
<string name="boosted">boosted</string> <string name="boosted">boosted</string>
<string name="edited">edited</string> <string name="edited">edited</string>
<string name="edited_number">edit #%1$s</string>
<string name="original">original</string> <string name="original">original</string>
<string name="quote">Quote</string> <string name="quote">Quote</string>
<string name="fork">Fork</string> <string name="fork">Fork</string>
+7 -7
View File
@@ -1,7 +1,7 @@
plugins { plugins {
id 'com.android.library' alias(libs.plugins.androidLibrary)
id 'androidx.benchmark' alias(libs.plugins.jetbrainsKotlinAndroid)
id 'org.jetbrains.kotlin.android' alias(libs.plugins.androidBenchmark)
} }
android { android {
@@ -49,10 +49,10 @@ android {
} }
dependencies { dependencies {
androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation libs.androidx.runner
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation libs.androidx.junit
androidTestImplementation 'junit:junit:4.13.2' androidTestImplementation libs.junit
androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.2.3' androidTestImplementation libs.androidx.benchmark.junit4
androidTestImplementation project(path: ':quartz') androidTestImplementation project(path: ':quartz')
androidTestImplementation project(path: ':commons') androidTestImplementation project(path: ':commons')
+7 -25
View File
@@ -1,31 +1,13 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
buildscript {
ext {
fragment_version = "1.6.2"
lifecycle_version = '2.7.0'
compose_ui_version = '1.6.2'
nav_version = '2.7.7'
room_version = "2.4.3"
accompanist_version = '0.34.0'
coil_version = '2.6.0'
vico_version = '1.14.0'
media3_version = '1.2.1'
core_ktx_version = '1.12.0'
material3_version = '1.2.0'
}
dependencies {
classpath 'com.google.gms:google-services:4.4.1'
}
}
plugins { plugins {
id 'com.android.application' version '8.2.2' apply false alias(libs.plugins.androidApplication) apply false
id 'com.android.library' version '8.2.2' apply false alias(libs.plugins.jetbrainsKotlinAndroid) apply false
id 'org.jetbrains.kotlin.android' version '1.9.22' apply false alias(libs.plugins.androidLibrary) apply false
id 'org.jetbrains.kotlin.jvm' version '1.9.22' apply false alias(libs.plugins.jetbrainsKotlinJvm) apply false
id 'androidx.benchmark' version '1.2.3' apply false alias(libs.plugins.androidBenchmark) apply false
id 'com.diffplug.spotless' version '6.25.0' apply false alias(libs.plugins.diffplugSpotless) apply false
alias(libs.plugins.googleServices) apply false
} }
subprojects { subprojects {
+8 -7
View File
@@ -1,6 +1,6 @@
plugins { plugins {
id 'com.android.library' alias(libs.plugins.androidLibrary)
id 'org.jetbrains.kotlin.android' alias(libs.plugins.jetbrainsKotlinAndroid)
} }
android { android {
@@ -38,12 +38,13 @@ dependencies {
implementation project(path: ':quartz') implementation project(path: ':quartz')
// Import @Immutable and @Stable // Import @Immutable and @Stable
implementation "androidx.compose.ui:ui:$compose_ui_version" implementation platform(libs.androidx.compose.bom)
implementation libs.androidx.ui
// immutable collections to avoid recomposition // immutable collections to avoid recomposition
api('org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7') api libs.kotlinx.collections.immutable
testImplementation 'junit:junit:4.13.2' testImplementation libs.junit
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation libs.androidx.junit
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation libs.androidx.espresso.core
} }
+124
View File
@@ -0,0 +1,124 @@
[versions]
accompanistAdaptive = "0.34.0"
activityCompose = "1.8.2"
agp = "8.3.0"
androidKotlinGeohash = "1.0"
androidLifecycle = "2.7.0"
androidxJunit = "1.2.0-alpha03"
appcompat = "1.6.1"
audiowaveform = "1.1.1"
benchmark = "1.2.3"
benchmarkJunit4 = "1.2.3"
biometricKtx = "1.2.0-alpha05"
blurhash = "1.0.0"
coil = "2.6.0"
composeBom = "2024.02.01"
coreKtx = "1.12.0"
espressoCore = "3.5.1"
firebaseBom = "32.7.3"
fragmentKtx = "1.6.2"
gms = "4.4.1"
jacksonModuleKotlin = "2.16.1"
jna = "5.14.0"
jsoup = "1.17.2"
junit = "4.13.2"
kotlin = "1.9.22"
kotlinxCollectionsImmutable = "0.3.7"
languageId = "17.0.5"
lazysodiumAndroid = "5.1.0"
lightcompressor = "1.3.2"
markdown = "48702a8ced"
media3 = "1.2.1"
mockk = "1.13.9"
navigationCompose = "2.7.7"
okhttp = "5.0.0-alpha.12"
runner = "1.5.2"
secp256k1KmpJniAndroid = "0.14.0"
securityCryptoKtx = "1.1.0-alpha06"
spotless = "6.25.0"
translate = "17.0.2"
unifiedpush = "2.2.0"
urlDetector = "0.1.23"
vico-charts = "1.14.0"
zelory = "3.0.1"
zoomable = "1.6.0"
zxing = "3.5.3"
zxingAndroidEmbedded = "4.3.0"
[libraries]
abedElazizShe-image-compressor = { group = "com.github.AbedElazizShe", name = "LightCompressor", version.ref = "lightcompressor" }
accompanist-adaptive = { group = "com.google.accompanist", name = "accompanist-adaptive", version.ref = "accompanistAdaptive" }
accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanistAdaptive" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-benchmark-junit4 = { group = "androidx.benchmark", name = "benchmark-junit4", version.ref = "benchmarkJunit4" }
androidx-biometric-ktx = { group = "androidx.biometric", name = "biometric-ktx", version.ref = "biometricKtx" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" }
androidx-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxJunit" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidLifecycle" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidLifecycle" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidLifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidLifecycle" }
androidx-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-material3-windowSize = { group = "androidx.compose.material3", name = "material3-window-size-class" }
androidx-media3-datasource-okhttp = { group = "androidx.media3", name = "media3-datasource-okhttp", version.ref = "media3" }
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
androidx-media3-exoplayer-hls = { group = "androidx.media3", name = "media3-exoplayer-hls", version.ref = "media3" }
androidx-media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3" }
androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
androidx-runner = { group = "androidx.test", name = "runner", version.ref = "runner" }
androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" }
androidx-runtime-runtime = { group = "androidx.compose.runtime", name = "runtime" }
androidx-security-crypto-ktx = { group = "androidx.security", name = "security-crypto-ktx", version.ref = "securityCryptoKtx" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
audiowaveform = { group = "com.github.lincollincol", name = "compose-audiowaveform", version.ref = "audiowaveform" }
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
coil-gif = { group = "io.coil-kt", name = "coil-gif", version.ref = "coil" }
coil-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil" }
drfonfon-geohash = { group = "com.github.drfonfon", name = "android-kotlin-geohash", version.ref = "androidKotlinGeohash" }
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" }
firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging-ktx" }
google-mlkit-language-id = { group = "com.google.mlkit", name = "language-id", version.ref = "languageId" }
google-mlkit-translate = { group = "com.google.mlkit", name = "translate", version.ref = "translate" }
jackson-module-kotlin = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin", version.ref = "jacksonModuleKotlin" }
jna = { group = "net.java.dev.jna", name = "jna", version.ref = "jna" }
jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinxCollectionsImmutable" }
lazysodium-android = { group = "com.goterl", name = "lazysodium-android", version.ref = "lazysodiumAndroid" }
markdown-commonmark = { group = "com.github.vitorpamplona.compose-richtext", name = "richtext-commonmark", version.ref = "markdown" }
markdown-ui = { group = "com.github.vitorpamplona.compose-richtext", name = "richtext-ui", version.ref = "markdown" }
markdown-ui-material3 = { group = "com.github.vitorpamplona.compose-richtext", name = "richtext-ui-material3", version.ref = "markdown" }
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
secp256k1-kmp-jni-android = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-android", version.ref = "secp256k1KmpJniAndroid" }
trbl-blurhash = { group = "io.trbl", name = "blurhash", version.ref = "blurhash" }
unifiedpush = { group = "com.github.UnifiedPush", name = "android-connector", version.ref = "unifiedpush" }
url-detector = { group = "io.github.url-detector", name = "url-detector", version.ref = "urlDetector" }
vico-charts-compose = { group = "com.patrykandpatrick.vico", name = "compose", version.ref = "vico-charts" }
vico-charts-core = { group = "com.patrykandpatrick.vico", name = "core", version.ref = "vico-charts" }
vico-charts-m3 = { group = "com.patrykandpatrick.vico", name = "compose-m3", version.ref = "vico-charts" }
vico-charts-views = { group = "com.patrykandpatrick.vico", name = "views", version.ref = "vico-charts" }
zelory-video-compressor = { group = "id.zelory", name = "compressor", version.ref = "zelory" }
zoomable = { group = "net.engawapg.lib", name = "zoomable", version.ref = "zoomable" }
zxing = { group = "com.google.zxing", name = "core", version.ref = "zxing" }
zxing-embedded = { group = "com.journeyapps", name = "zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidBenchmark = { id = "androidx.benchmark", version.ref = "benchmark" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
diffplugSpotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
googleServices = { id = "com.google.gms.google-services", version.ref = "gms" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+1 -1
View File
@@ -1,6 +1,6 @@
#Wed Jan 04 09:23:50 EST 2023 #Wed Jan 04 09:23:50 EST 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+17 -14
View File
@@ -1,6 +1,6 @@
plugins { plugins {
id 'com.android.library' alias(libs.plugins.androidLibrary)
id 'org.jetbrains.kotlin.android' alias(libs.plugins.jetbrainsKotlinAndroid)
} }
android { android {
@@ -33,36 +33,39 @@ android {
jvmTarget = '17' jvmTarget = '17'
freeCompilerArgs += "-Xstring-concat=inline" freeCompilerArgs += "-Xstring-concat=inline"
} }
packagingOptions { packagingOptions {
// delete native scrypt lib because we cannot use dylibs in android resources {
exclude '**/libscrypt.dylib' excludes += ['**/libscrypt.dylib']
}
} }
} }
dependencies { dependencies {
implementation "androidx.core:core-ktx:$core_ktx_version" implementation libs.androidx.core.ktx
implementation platform(libs.androidx.compose.bom)
// @Immutable and @Stable // @Immutable and @Stable
implementation "androidx.compose.runtime:runtime:$compose_ui_version" implementation libs.androidx.runtime.runtime
// Bitcoin secp256k1 bindings to Android // Bitcoin secp256k1 bindings to Android
api 'fr.acinq.secp256k1:secp256k1-kmp-jni-android:0.14.0' api libs.secp256k1.kmp.jni.android
// LibSodium for ChaCha encryption (NIP-44) // LibSodium for ChaCha encryption (NIP-44)
// Wait for @aar support in version catalogs
implementation "com.goterl:lazysodium-android:5.1.0@aar" implementation "com.goterl:lazysodium-android:5.1.0@aar"
implementation 'net.java.dev.jna:jna:5.14.0@aar' implementation 'net.java.dev.jna:jna:5.14.0@aar'
// Performant Parser of JSONs into Events // Performant Parser of JSONs into Events
api 'com.fasterxml.jackson.module:jackson-module-kotlin:2.16.1' api libs.jackson.module.kotlin
// immutable collections to avoid recomposition // immutable collections to avoid recomposition
api('org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7') api libs.kotlinx.collections.immutable
// Parses URLs from Text: // Parses URLs from Text:
api "io.github.url-detector:url-detector:0.1.23" api libs.url.detector
testImplementation 'junit:junit:4.13.2' testImplementation libs.junit
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation libs.androidx.junit
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation libs.androidx.espresso.core
} }
@@ -30,7 +30,7 @@ class Lud06 {
fun toLud16(str: String): String? { fun toLud16(str: String): String? {
return try { return try {
val url = toLnUrlp(str) val url = toLnUrlp(str) ?: return null
val matcher = LNURLP_PATTERN.matcher(url) val matcher = LNURLP_PATTERN.matcher(url)
if (matcher.find()) { if (matcher.find()) {
@@ -94,7 +94,9 @@ object Nip19Bech32 {
val key = matcher.group(3) // bech32 val key = matcher.group(3) // bech32
val additionalChars = matcher.group(4) // additional chars val additionalChars = matcher.group(4) // additional chars
return parseComponents(type!!, key, additionalChars) if (type == null) return null
return parseComponents(type, key, additionalChars)
} catch (e: Throwable) { } catch (e: Throwable) {
Log.e("NIP19 Parser", "Issue trying to Decode NIP19 $uri: ${e.message}", e) Log.e("NIP19 Parser", "Issue trying to Decode NIP19 $uri: ${e.message}", e)
} }
@@ -105,14 +105,16 @@ open class BaseTextNoteEvent(
val additionalChars = matcher2.group(4) // additional chars val additionalChars = matcher2.group(4) // additional chars
try { try {
val parsed = Nip19Bech32.parseComponents(type, key, additionalChars)?.entity if (type != null) {
val parsed = Nip19Bech32.parseComponents(type, key, additionalChars)?.entity
if (parsed != null) { if (parsed != null) {
if (parsed is Nip19Bech32.NProfile) { if (parsed is Nip19Bech32.NProfile) {
returningList.add(parsed.hex) returningList.add(parsed.hex)
} }
if (parsed is Nip19Bech32.NPub) { if (parsed is Nip19Bech32.NPub) {
returningList.add(parsed.hex) returningList.add(parsed.hex)
}
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@@ -151,14 +153,16 @@ open class BaseTextNoteEvent(
val key = matcher2.group(3) // bech32 val key = matcher2.group(3) // bech32
val additionalChars = matcher2.group(4) // additional chars val additionalChars = matcher2.group(4) // additional chars
val parsed = Nip19Bech32.parseComponents(type, key, additionalChars)?.entity if (type != null) {
val parsed = Nip19Bech32.parseComponents(type, key, additionalChars)?.entity
if (parsed != null) { if (parsed != null) {
when (parsed) { when (parsed) {
is Nip19Bech32.NEvent -> citations.add(parsed.hex) is Nip19Bech32.NEvent -> citations.add(parsed.hex)
is Nip19Bech32.NAddress -> citations.add(parsed.atag) is Nip19Bech32.NAddress -> citations.add(parsed.atag)
is Nip19Bech32.Note -> citations.add(parsed.hex) is Nip19Bech32.Note -> citations.add(parsed.hex)
is Nip19Bech32.NEmbed -> citations.add(parsed.event.id) is Nip19Bech32.NEmbed -> citations.add(parsed.event.id)
}
} }
} }
} }
@@ -386,7 +386,7 @@ open class Event(
put("pubkey", pubKey) put("pubkey", pubKey)
put("created_at", createdAt) put("created_at", createdAt)
put("kind", kind) put("kind", kind)
put( replace(
"tags", "tags",
factory.arrayNode(tags.size).apply { factory.arrayNode(tags.size).apply {
tags.forEach { tag -> tags.forEach { tag ->
@@ -63,7 +63,7 @@ interface EventInterface {
fun isTaggedUser(idHex: String): Boolean fun isTaggedUser(idHex: String): Boolean
fun isTaggedUsers(idHex: Set<String>): Boolean fun isTaggedUsers(idHexes: Set<String>): Boolean
fun isTaggedEvent(idHex: String): Boolean fun isTaggedEvent(idHex: String): Boolean
@@ -60,7 +60,7 @@ class GitPatchEvent(
fun commitPGPSig() = tags.firstOrNull { it.size > 1 && it[0] == "commit-pgp-sig" }?.get(1) fun commitPGPSig() = tags.firstOrNull { it.size > 1 && it[0] == "commit-pgp-sig" }?.get(1)
fun committer() = fun committer() =
tags.filter { it.size > 1 && it[0] == "committer" }?.mapNotNull { tags.filter { it.size > 1 && it[0] == "committer" }.mapNotNull {
Committer(it.getOrNull(1), it.getOrNull(2), it.getOrNull(3), it.getOrNull(4)) Committer(it.getOrNull(1), it.getOrNull(2), it.getOrNull(3), it.getOrNull(4))
} }
@@ -34,6 +34,8 @@ class TextNoteModificationEvent(
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
fun editedNote() = firstTaggedEvent()
companion object { companion object {
const val KIND = 1010 const val KIND = 1010
const val ALT = "Content Change Event" const val ALT = "Content Change Event"
+10 -2
View File
@@ -1,8 +1,14 @@
pluginManagement { pluginManagement {
repositories { repositories {
gradlePluginPortal() google {
google() content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral() mavenCentral()
gradlePluginPortal()
maven { maven {
url "https://jitpack.io" url "https://jitpack.io"
content { content {
@@ -11,6 +17,7 @@ pluginManagement {
} }
} }
} }
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories { repositories {
@@ -19,6 +26,7 @@ dependencyResolutionManagement {
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
} }
} }
rootProject.name = "Amethyst" rootProject.name = "Amethyst"
include ':app' include ':app'
include ':benchmark' include ':benchmark'