Merge branch 'main' into adding_translations
This commit is contained in:
+65
-64
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'com.google.gms.google-services'
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.jetbrainsKotlinAndroid)
|
||||
alias(libs.plugins.googleServices)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -145,14 +145,13 @@ android {
|
||||
// Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin
|
||||
kotlinCompilerExtensionVersion "1.5.8"
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||
excludes += ['/META-INF/{AL2.0,LGPL2.1}', '**/libscrypt.dylib']
|
||||
}
|
||||
exclude '**/libscrypt.dylib'
|
||||
}
|
||||
|
||||
|
||||
lint {
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
@@ -165,75 +164,78 @@ android {
|
||||
dependencies {
|
||||
implementation project(path: ':quartz')
|
||||
implementation project(path: ':commons')
|
||||
implementation "androidx.core:core-ktx:$core_ktx_version"
|
||||
implementation 'androidx.activity:activity-compose:1.8.2'
|
||||
implementation "androidx.compose.ui:ui:$compose_ui_version"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
|
||||
implementation libs.androidx.core.ktx
|
||||
implementation libs.androidx.activity.compose
|
||||
|
||||
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
|
||||
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
||||
implementation libs.androidx.fragment.ktx
|
||||
|
||||
// Navigation
|
||||
implementation "androidx.navigation:navigation-compose:$nav_version"
|
||||
implementation libs.androidx.navigation.compose
|
||||
|
||||
// Observe Live data as State
|
||||
implementation "androidx.compose.runtime:runtime-livedata:$compose_ui_version"
|
||||
implementation libs.androidx.runtime.livedata
|
||||
|
||||
// Material 3 Design
|
||||
implementation "androidx.compose.material3:material3:${material3_version}"
|
||||
implementation "androidx.compose.material:material-icons-extended:$compose_ui_version"
|
||||
implementation libs.androidx.material3
|
||||
implementation libs.androidx.material.icons
|
||||
|
||||
// Adaptive Layout / Two Pane
|
||||
implementation "androidx.compose.material3:material3-window-size-class:${material3_version}"
|
||||
implementation 'com.google.accompanist:accompanist-adaptive:0.34.0'
|
||||
|
||||
implementation libs.androidx.material3.windowSize
|
||||
implementation libs.accompanist.adaptive
|
||||
|
||||
// Lifecycle
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||
implementation libs.androidx.lifecycle.runtime.ktx
|
||||
implementation libs.androidx.lifecycle.runtime.compose
|
||||
implementation libs.androidx.lifecycle.viewmodel.compose
|
||||
implementation libs.androidx.lifecycle.livedata.ktx
|
||||
|
||||
// Zoomable images
|
||||
implementation 'net.engawapg.lib:zoomable:1.6.0'
|
||||
implementation libs.zoomable
|
||||
|
||||
// Biometrics
|
||||
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
||||
implementation libs.androidx.biometric.ktx
|
||||
|
||||
// Websockets API
|
||||
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.12'
|
||||
implementation libs.okhttp
|
||||
|
||||
// HTML Parsing for Link Preview
|
||||
implementation 'org.jsoup:jsoup:1.17.2'
|
||||
implementation libs.jsoup
|
||||
|
||||
// Encrypted Key Storage
|
||||
implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha06'
|
||||
implementation libs.androidx.security.crypto.ktx
|
||||
|
||||
// view videos
|
||||
implementation "androidx.media3:media3-exoplayer:$media3_version"
|
||||
implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
|
||||
implementation "androidx.media3:media3-ui:$media3_version"
|
||||
implementation "androidx.media3:media3-session:$media3_version"
|
||||
implementation libs.androidx.media3.exoplayer
|
||||
implementation libs.androidx.media3.exoplayer.hls
|
||||
implementation libs.androidx.media3.ui
|
||||
implementation libs.androidx.media3.session
|
||||
|
||||
// important for proxy / tor
|
||||
implementation "androidx.media3:media3-datasource-okhttp:$media3_version"
|
||||
implementation libs.androidx.media3.datasource.okhttp
|
||||
|
||||
// Load images from the web.
|
||||
implementation "io.coil-kt:coil-compose:$coil_version"
|
||||
implementation libs.coil.compose
|
||||
// view gifs
|
||||
implementation "io.coil-kt:coil-gif:$coil_version"
|
||||
implementation libs.coil.gif
|
||||
// view svgs
|
||||
implementation "io.coil-kt:coil-svg:$coil_version"
|
||||
implementation libs.coil.svg
|
||||
|
||||
// create blurhash
|
||||
implementation group: 'io.trbl', name: 'blurhash', version: '1.0.0'
|
||||
implementation libs.trbl.blurhash
|
||||
|
||||
// Permission to upload pictures:
|
||||
implementation "com.google.accompanist:accompanist-permissions:$accompanist_version"
|
||||
implementation libs.accompanist.permissions
|
||||
|
||||
// For QR generation
|
||||
implementation 'com.google.zxing:core:3.5.3'
|
||||
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
|
||||
implementation libs.zxing
|
||||
implementation libs.zxing.embedded
|
||||
|
||||
// Markdown
|
||||
//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"
|
||||
|
||||
// Markdown (With fix for full-image bleeds)
|
||||
implementation('com.github.vitorpamplona.compose-richtext:richtext-ui:48702a8ced')
|
||||
implementation('com.github.vitorpamplona.compose-richtext:richtext-ui-material3:48702a8ced')
|
||||
implementation('com.github.vitorpamplona.compose-richtext:richtext-commonmark:48702a8ced')
|
||||
implementation libs.markdown.ui
|
||||
implementation libs.markdown.ui.material3
|
||||
implementation libs.markdown.commonmark
|
||||
|
||||
// Language picker and Theme chooser
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation libs.androidx.appcompat
|
||||
|
||||
// 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
|
||||
playImplementation 'com.google.mlkit:translate:17.0.2'
|
||||
playImplementation libs.google.mlkit.translate
|
||||
|
||||
// PushNotifications
|
||||
playImplementation platform('com.google.firebase:firebase-bom:32.7.2')
|
||||
playImplementation 'com.google.firebase:firebase-messaging-ktx'
|
||||
playImplementation platform(libs.firebase.bom)
|
||||
playImplementation libs.firebase.messaging
|
||||
|
||||
//PushNotifications(FDroid)
|
||||
fdroidImplementation 'com.github.UnifiedPush:android-connector:2.2.0'
|
||||
fdroidImplementation libs.unifiedpush
|
||||
|
||||
// Charts
|
||||
implementation "com.patrykandpatrick.vico:core:${vico_version}"
|
||||
implementation "com.patrykandpatrick.vico:compose:${vico_version}"
|
||||
implementation "com.patrykandpatrick.vico:views:${vico_version}"
|
||||
implementation "com.patrykandpatrick.vico:compose-m2:${vico_version}"
|
||||
implementation libs.vico.charts.core
|
||||
implementation libs.vico.charts.compose
|
||||
implementation libs.vico.charts.views
|
||||
implementation libs.vico.charts.m3
|
||||
|
||||
// GeoHash
|
||||
implementation 'com.github.drfonfon:android-kotlin-geohash:1.0'
|
||||
implementation libs.drfonfon.geohash
|
||||
|
||||
// Waveform visualizer
|
||||
implementation 'com.github.lincollincol:compose-audiowaveform:1.1.1'
|
||||
implementation libs.audiowaveform
|
||||
|
||||
// Video compression lib
|
||||
implementation 'com.github.AbedElazizShe:LightCompressor:1.3.2'
|
||||
implementation libs.abedElazizShe.image.compressor
|
||||
// Image compression lib
|
||||
implementation 'id.zelory:compressor:3.0.1'
|
||||
implementation libs.zelory.video.compressor
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'io.mockk:mockk:1.13.9'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.0-alpha03'
|
||||
androidTestImplementation 'androidx.test.ext:junit-ktx:1.2.0-alpha03'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
|
||||
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
|
||||
testImplementation libs.junit
|
||||
testImplementation libs.mockk
|
||||
androidTestImplementation libs.androidx.junit
|
||||
androidTestImplementation libs.androidx.junit.ktx
|
||||
androidTestImplementation libs.androidx.espresso.core
|
||||
debugImplementation libs.androidx.ui.tooling
|
||||
debugImplementation libs.androidx.ui.test.manifest
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
import android.util.Log
|
||||
import android.util.LruCache
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
@@ -1402,6 +1403,13 @@ object LocalCache {
|
||||
|
||||
note.loadEvent(event, author, emptyList())
|
||||
|
||||
event.editedNote()?.let {
|
||||
getNoteIfExists(it)?.let { editedNote ->
|
||||
modificationCache.remove(editedNote.idHex)
|
||||
editedNote.liveSet?.innerModifications?.invalidateData()
|
||||
}
|
||||
}
|
||||
|
||||
refreshObservers(note)
|
||||
}
|
||||
|
||||
@@ -1716,15 +1724,31 @@ object LocalCache {
|
||||
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> {
|
||||
checkNotInMainThread()
|
||||
|
||||
modificationCache[note.idHex]?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val time = TimeUtils.now()
|
||||
|
||||
return noteListCache.filter { item ->
|
||||
val noteEvent = item.event
|
||||
val newNotes =
|
||||
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() {
|
||||
|
||||
@@ -37,26 +37,26 @@ class JsonFilter(
|
||||
val filter =
|
||||
factory.objectNode().apply {
|
||||
ids?.run {
|
||||
put(
|
||||
replace(
|
||||
"ids",
|
||||
factory.arrayNode(ids.size).apply { ids.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
authors?.run {
|
||||
put(
|
||||
replace(
|
||||
"authors",
|
||||
factory.arrayNode(authors.size).apply { authors.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
kinds?.run {
|
||||
put(
|
||||
replace(
|
||||
"kinds",
|
||||
factory.arrayNode(kinds.size).apply { kinds.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
tags?.run {
|
||||
entries.forEach { kv ->
|
||||
put(
|
||||
replace(
|
||||
"#${kv.key}",
|
||||
factory.arrayNode(kv.value.size).apply { kv.value.forEach { add(it) } },
|
||||
)
|
||||
|
||||
@@ -47,7 +47,7 @@ data class Subscription(
|
||||
return factory.objectNode().apply {
|
||||
put("id", id)
|
||||
typedFilters?.also { filters ->
|
||||
put(
|
||||
replace(
|
||||
"typedFilters",
|
||||
factory.arrayNode(filters.size).apply {
|
||||
filters.forEach { filter -> add(filter.toJsonObject()) }
|
||||
|
||||
@@ -36,8 +36,8 @@ class TypedFilter(
|
||||
val factory = Event.mapper.nodeFactory
|
||||
|
||||
return factory.objectNode().apply {
|
||||
put("types", typesToJson(types))
|
||||
put("filter", filterToJson(filter))
|
||||
replace("types", typesToJson(types))
|
||||
replace("filter", filterToJson(filter))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,26 +50,26 @@ class TypedFilter(
|
||||
val factory = Event.mapper.nodeFactory
|
||||
return factory.objectNode().apply {
|
||||
filter.ids?.run {
|
||||
put(
|
||||
replace(
|
||||
"ids",
|
||||
factory.arrayNode(filter.ids.size).apply { filter.ids.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
filter.authors?.run {
|
||||
put(
|
||||
replace(
|
||||
"authors",
|
||||
factory.arrayNode(filter.authors.size).apply { filter.authors.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
filter.kinds?.run {
|
||||
put(
|
||||
replace(
|
||||
"kinds",
|
||||
factory.arrayNode(filter.kinds.size).apply { filter.kinds.forEach { add(it) } },
|
||||
)
|
||||
}
|
||||
filter.tags?.run {
|
||||
entries.forEach { kv ->
|
||||
put(
|
||||
replace(
|
||||
"#${kv.key}",
|
||||
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.MutableState
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
@@ -1088,10 +1089,58 @@ fun InnerNoteWithReactions(
|
||||
}
|
||||
|
||||
@Stable
|
||||
class EditState(
|
||||
val showOriginal: MutableState<Boolean> = mutableStateOf(false),
|
||||
val modificationsInOrder: MutableState<List<Note>> = mutableStateOf(emptyList()),
|
||||
)
|
||||
class EditState() {
|
||||
private var modificationsList: List<Note> = persistentListOf()
|
||||
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
|
||||
private fun NoteBody(
|
||||
@@ -1105,14 +1154,7 @@ private fun NoteBody(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
val editState by
|
||||
produceState(initialValue = EditState(), key1 = baseNote) {
|
||||
accountViewModel.findModificationEventsForNote(baseNote) { newModifications ->
|
||||
if (value.modificationsInOrder.value != newModifications) {
|
||||
value.modificationsInOrder.value = newModifications
|
||||
}
|
||||
}
|
||||
}
|
||||
val editState = observeEdits(baseNote = baseNote, accountViewModel = accountViewModel)
|
||||
|
||||
FirstUserInfoRow(
|
||||
baseNote = baseNote,
|
||||
@@ -1168,7 +1210,7 @@ private fun RenderNoteRow(
|
||||
backgroundColor: MutableState<Color>,
|
||||
makeItShort: Boolean,
|
||||
canPreview: Boolean,
|
||||
editState: EditState,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
@@ -1358,7 +1400,7 @@ fun RenderTextEvent(
|
||||
makeItShort: Boolean,
|
||||
canPreview: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
editState: EditState,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
@@ -1371,10 +1413,10 @@ fun RenderTextEvent(
|
||||
derivedStateOf {
|
||||
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
|
||||
val newBody =
|
||||
if (editState.showOriginal.value || editState.modificationsInOrder.value.isEmpty()) {
|
||||
body
|
||||
if (editState.value is GenericLoadable.Loaded) {
|
||||
(editState.value as? GenericLoadable.Loaded)?.loaded?.modificationToShow?.value?.event?.content() ?: body
|
||||
} else {
|
||||
editState.modificationsInOrder.value.firstOrNull()?.event?.content() ?: body
|
||||
body
|
||||
}
|
||||
|
||||
if (!subject.isNullOrBlank() && !newBody.split("\n")[0].contains(subject)) {
|
||||
@@ -2822,7 +2864,7 @@ fun DisplayLocation(
|
||||
fun FirstUserInfoRow(
|
||||
baseNote: Note,
|
||||
showAuthorPicture: Boolean,
|
||||
editState: EditState,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
@@ -2857,8 +2899,10 @@ fun FirstUserInfoRow(
|
||||
DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
if (!editState.modificationsInOrder.value.isEmpty()) {
|
||||
DisplayEditStatus(editState.showOriginal)
|
||||
if (editState.value is GenericLoadable.Loaded) {
|
||||
(editState.value as? GenericLoadable.Loaded<EditState>)?.loaded?.let {
|
||||
DisplayEditStatus(it)
|
||||
}
|
||||
}
|
||||
|
||||
TimeAgo(baseNote)
|
||||
@@ -2868,15 +2912,69 @@ fun FirstUserInfoRow(
|
||||
}
|
||||
|
||||
@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(
|
||||
text =
|
||||
if (showOriginal.value) {
|
||||
buildAnnotatedString { append(stringResource(id = R.string.original)) }
|
||||
} else {
|
||||
buildAnnotatedString { append(stringResource(id = R.string.edited)) }
|
||||
buildAnnotatedString {
|
||||
if (editState.showingVersion.value == editState.originalVersionId()) {
|
||||
append(stringResource(id = R.string.original))
|
||||
} 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 =
|
||||
LocalTextStyle.current.copy(
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
|
||||
@@ -111,14 +111,17 @@ class PollNoteViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
tallies.forEach {
|
||||
it.zappedValue.value = zappedPollOptionAmount(it.option)
|
||||
it.tally.value =
|
||||
if (totalZapped.compareTo(BigDecimal.ZERO) > 0) {
|
||||
it.zappedValue.value.divide(totalZapped, 2, RoundingMode.HALF_UP)
|
||||
val zappedValue = zappedPollOptionAmount(it.option)
|
||||
val tallyValue =
|
||||
if (totalZapped > BigDecimal.ZERO) {
|
||||
zappedValue.divide(totalZapped, 2, RoundingMode.HALF_UP)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +483,10 @@ fun NoteDropDownMenu(
|
||||
|
||||
if (wantsToEditPost.value) {
|
||||
EditPostView(
|
||||
onClose = { wantsToEditPost.value = false },
|
||||
onClose = {
|
||||
popupExpanded.value = false
|
||||
wantsToEditPost.value = false
|
||||
},
|
||||
edit = note,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
|
||||
@@ -58,7 +58,6 @@ import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -86,6 +85,7 @@ import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
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.LoadNote
|
||||
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.RenderTextEvent
|
||||
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.timeAgo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -377,20 +378,13 @@ fun NoteMaster(
|
||||
onClick = { showHiddenNote = true },
|
||||
)
|
||||
} else {
|
||||
val editState by
|
||||
produceState(initialValue = EditState(), key1 = baseNote) {
|
||||
accountViewModel.findModificationEventsForNote(baseNote) { newModifications ->
|
||||
if (value.modificationsInOrder.value != newModifications) {
|
||||
value.modificationsInOrder.value = newModifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 10.dp),
|
||||
) {
|
||||
val editState = observeEdits(baseNote = baseNote, accountViewModel = accountViewModel)
|
||||
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
@@ -421,8 +415,10 @@ fun NoteMaster(
|
||||
DisplayFollowingHashtagsInPost(baseNote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
if (!editState.modificationsInOrder.value.isEmpty()) {
|
||||
DisplayEditStatus(editState.showOriginal)
|
||||
if (editState.value is GenericLoadable.Loaded) {
|
||||
(editState.value as? GenericLoadable.Loaded<EditState>)?.loaded?.let {
|
||||
DisplayEditStatus(it)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
@@ -850,9 +846,9 @@ private fun RenderWikiHeaderForThreadPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
val nav: (String) -> Unit = {}
|
||||
|
||||
val editState by
|
||||
val editState =
|
||||
remember {
|
||||
mutableStateOf(EditState())
|
||||
mutableStateOf(GenericLoadable.Empty<EditState>())
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
|
||||
@@ -932,6 +932,8 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
|
||||
}
|
||||
}
|
||||
|
||||
fun cachedModificationEventsForNote(note: Note) = LocalCache.cachedModificationEventsForNote(note)
|
||||
|
||||
suspend fun findModificationEventsForNote(
|
||||
note: Note,
|
||||
onResult: (List<Note>) -> Unit,
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<string name="boost">Boost</string>
|
||||
<string name="boosted">boosted</string>
|
||||
<string name="edited">edited</string>
|
||||
<string name="edited_number">edit #%1$s</string>
|
||||
<string name="original">original</string>
|
||||
<string name="quote">Quote</string>
|
||||
<string name="fork">Fork</string>
|
||||
|
||||
Reference in New Issue
Block a user