feat: spoilered images (send and recv)
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
bdd706fda5
commit
a7c1a20fbc
|
|
@ -22,13 +22,13 @@ plugins {
|
||||||
val composeBomVersion = "2025.03.00"
|
val composeBomVersion = "2025.03.00"
|
||||||
val accompanistVersion = "0.34.0"
|
val accompanistVersion = "0.34.0"
|
||||||
val okhttpVersion = "4.12.0"
|
val okhttpVersion = "4.12.0"
|
||||||
val navVersion = "2.8.7"
|
val navVersion = "2.9.0"
|
||||||
val hiltVersion = "2.52"
|
val hiltVersion = "2.52"
|
||||||
val glideVersion = "4.16.0"
|
val glideVersion = "4.16.0"
|
||||||
val ktorVersion = "3.0.0-beta-2"
|
val ktorVersion = "3.0.0-beta-2"
|
||||||
val media3Version = "1.5.0"
|
val media3Version = "1.7.1"
|
||||||
val livekitVersion = "2.2.0"
|
val livekitVersion = "2.2.0"
|
||||||
val material3Version = "1.4.0-alpha10"
|
val material3Version = "1.4.0-alpha15"
|
||||||
val androidXTestVersion = "1.6.1"
|
val androidXTestVersion = "1.6.1"
|
||||||
|
|
||||||
fun property(fileName: String, propertyName: String, fallbackEnv: String? = null): String? {
|
fun property(fileName: String, propertyName: String, fallbackEnv: String? = null): String? {
|
||||||
|
|
@ -184,14 +184,14 @@ sentry {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Android/Kotlin Core
|
// Android/Kotlin Core
|
||||||
implementation("androidx.core:core-ktx:1.15.0")
|
implementation("androidx.core:core-ktx:1.16.0")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.10")
|
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.20")
|
||||||
|
|
||||||
// Kotlinx - various first-party extensions for Kotlin
|
// Kotlinx - various first-party extensions for Kotlin
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.6.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.6.1")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
||||||
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
|
implementation("androidx.profileinstaller:profileinstaller:1.4.1")
|
||||||
|
|
||||||
// Compose BOM
|
// Compose BOM
|
||||||
val composeBom = platform("androidx.compose:compose-bom:$composeBomVersion")
|
val composeBom = platform("androidx.compose:compose-bom:$composeBomVersion")
|
||||||
|
|
@ -200,16 +200,16 @@ dependencies {
|
||||||
androidTestImplementation(composeBom)
|
androidTestImplementation(composeBom)
|
||||||
|
|
||||||
// Jetpack Compose
|
// Jetpack Compose
|
||||||
implementation("androidx.compose.ui:ui:1.8.0-rc01")
|
implementation("androidx.compose.ui:ui:1.8.2")
|
||||||
implementation("androidx.compose.ui:ui-util")
|
implementation("androidx.compose.ui:ui-util")
|
||||||
implementation("androidx.compose.material3:material3:$material3Version")
|
implementation("androidx.compose.material3:material3:$material3Version")
|
||||||
implementation("androidx.compose.material3:material3-window-size-class:$material3Version")
|
implementation("androidx.compose.material3:material3-window-size-class:$material3Version")
|
||||||
implementation("androidx.compose.material:material-icons-core:1.7.8")
|
implementation("androidx.compose.material:material-icons-core:1.7.8")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.runtime:runtime-livedata")
|
implementation("androidx.compose.runtime:runtime-livedata")
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.1")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.1")
|
||||||
implementation("androidx.activity:activity-compose:1.10.0")
|
implementation("androidx.activity:activity-compose:1.10.1")
|
||||||
|
|
||||||
// Accompanist - Jetpack Compose Extensions
|
// Accompanist - Jetpack Compose Extensions
|
||||||
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
|
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
|
||||||
|
|
@ -243,26 +243,26 @@ dependencies {
|
||||||
implementation("com.mikepenz:aboutlibraries-core:11.3.0-rc02")
|
implementation("com.mikepenz:aboutlibraries-core:11.3.0-rc02")
|
||||||
|
|
||||||
// Sentry - crash reporting
|
// Sentry - crash reporting
|
||||||
implementation("io.sentry:sentry-android:7.16.0")
|
implementation("io.sentry:sentry-android:8.13.2")
|
||||||
implementation("io.sentry:sentry-compose-android:7.16.0")
|
implementation("io.sentry:sentry-compose-android:8.13.2")
|
||||||
|
|
||||||
// Other AndroidX libraries - used for various things and never seem to have a consistent version
|
// Other AndroidX libraries
|
||||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
implementation("androidx.documentfile:documentfile:1.1.0")
|
||||||
implementation("androidx.browser:browser:1.8.0")
|
implementation("androidx.browser:browser:1.8.0")
|
||||||
implementation("androidx.webkit:webkit:1.12.1")
|
implementation("androidx.webkit:webkit:1.14.0")
|
||||||
implementation("androidx.core:core-splashscreen:1.2.0-beta01")
|
implementation("androidx.core:core-splashscreen:1.2.0-beta02")
|
||||||
implementation("androidx.palette:palette-ktx:1.0.0")
|
implementation("androidx.palette:palette-ktx:1.0.0")
|
||||||
|
|
||||||
// Libraries used for legacy View-based UI
|
// Libraries used for legacy View-based UI
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
|
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
||||||
implementation("androidx.appcompat:appcompat:1.7.0")
|
implementation("androidx.appcompat:appcompat:1.7.1")
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
|
|
||||||
// hCaptcha - captcha provider
|
// hCaptcha - captcha provider
|
||||||
implementation("com.github.hcaptcha:hcaptcha-android-sdk:3.8.1")
|
implementation("com.github.hcaptcha:hcaptcha-android-sdk:3.8.1")
|
||||||
|
|
||||||
// JDK Desugaring - polyfill for new Java APIs
|
// JDK Desugaring - polyfill for new Java APIs
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||||
|
|
||||||
// AndroidX Media3 w/ ExoPlayer
|
// AndroidX Media3 w/ ExoPlayer
|
||||||
implementation("androidx.media3:media3-exoplayer:$media3Version")
|
implementation("androidx.media3:media3-exoplayer:$media3Version")
|
||||||
|
|
@ -273,15 +273,17 @@ dependencies {
|
||||||
// Compose libraries
|
// Compose libraries
|
||||||
implementation("me.saket.telephoto:zoomable-image:1.0.0-alpha02")
|
implementation("me.saket.telephoto:zoomable-image:1.0.0-alpha02")
|
||||||
implementation("me.saket.telephoto:zoomable-image-glide:1.0.0-alpha02")
|
implementation("me.saket.telephoto:zoomable-image-glide:1.0.0-alpha02")
|
||||||
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0")
|
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.1")
|
||||||
|
implementation("dev.chrisbanes.haze:haze:1.6.4")
|
||||||
|
implementation("dev.chrisbanes.haze:haze-materials:1.6.4")
|
||||||
|
|
||||||
// ZXing - QR Code generation
|
// ZXing - QR Code generation
|
||||||
implementation("com.google.zxing:core:3.5.3")
|
implementation("com.google.zxing:core:3.5.3")
|
||||||
|
|
||||||
// Persistence
|
// Persistence
|
||||||
implementation("app.cash.sqldelight:android-driver:2.0.1")
|
implementation("app.cash.sqldelight:android-driver:2.0.1")
|
||||||
implementation("androidx.datastore:datastore:1.1.2")
|
implementation("androidx.datastore:datastore:1.1.7")
|
||||||
implementation("androidx.datastore:datastore-preferences:1.1.2")
|
implementation("androidx.datastore:datastore-preferences:1.1.7")
|
||||||
|
|
||||||
// Markup
|
// Markup
|
||||||
implementation("org.jetbrains:markdown:0.7.3")
|
implementation("org.jetbrains:markdown:0.7.3")
|
||||||
|
|
@ -292,7 +294,7 @@ dependencies {
|
||||||
// implementation "io.livekit:livekit-android:$livekit_version"
|
// implementation "io.livekit:livekit-android:$livekit_version"
|
||||||
|
|
||||||
// Firebase - Cloud Messaging
|
// Firebase - Cloud Messaging
|
||||||
implementation(platform("com.google.firebase:firebase-bom:33.9.0"))
|
implementation(platform("com.google.firebase:firebase-bom:33.15.0"))
|
||||||
implementation("com.google.firebase:firebase-messaging")
|
implementation("com.google.firebase:firebase-messaging")
|
||||||
|
|
||||||
// Shimmer - loading animations
|
// Shimmer - loading animations
|
||||||
|
|
|
||||||
|
|
@ -377,6 +377,16 @@ fun ShareTargetScreen(
|
||||||
attachments = viewModel.attachments,
|
attachments = viewModel.attachments,
|
||||||
uploading = viewModel.attachmentsUploading,
|
uploading = viewModel.attachmentsUploading,
|
||||||
uploadProgress = viewModel.attachmentProgress,
|
uploadProgress = viewModel.attachmentProgress,
|
||||||
|
onToggleSpoiler = {
|
||||||
|
val index = viewModel.attachments
|
||||||
|
.indexOfFirst { a -> a.pickerIdentifier == it.pickerIdentifier }
|
||||||
|
if (index != -1) {
|
||||||
|
val attachment = viewModel.attachments[index]
|
||||||
|
viewModel.attachments[index] = attachment.copy(
|
||||||
|
spoiler = !attachment.spoiler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
onRemove = {},
|
onRemove = {},
|
||||||
canRemove = false
|
canRemove = false
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ data class FileArgs(
|
||||||
val file: File,
|
val file: File,
|
||||||
val filename: String,
|
val filename: String,
|
||||||
val contentType: String,
|
val contentType: String,
|
||||||
val pickerIdentifier: String? = null
|
val spoiler: Boolean = false,
|
||||||
|
val pickerIdentifier: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun uploadToAutumn(
|
suspend fun uploadToAutumn(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package chat.revolt.composables.chat
|
package chat.revolt.composables.chat
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.text.format.Formatter
|
import android.text.format.Formatter
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
|
@ -20,6 +21,10 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
|
@ -34,6 +39,11 @@ import chat.revolt.api.REVOLT_FILES
|
||||||
import chat.revolt.api.schemas.AutumnResource
|
import chat.revolt.api.schemas.AutumnResource
|
||||||
import chat.revolt.composables.generic.RemoteImage
|
import chat.revolt.composables.generic.RemoteImage
|
||||||
import chat.revolt.composables.media.AudioPlayer
|
import chat.revolt.composables.media.AudioPlayer
|
||||||
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
|
import dev.chrisbanes.haze.hazeSource
|
||||||
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
|
import dev.chrisbanes.haze.rememberHazeState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FileAttachment(attachment: AutumnResource) {
|
fun FileAttachment(attachment: AutumnResource) {
|
||||||
|
|
@ -71,9 +81,14 @@ fun FileAttachment(attachment: AutumnResource) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalHazeMaterialsApi::class)
|
||||||
|
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||||
@Composable
|
@Composable
|
||||||
fun ImageAttachment(attachment: AutumnResource) {
|
fun ImageAttachment(attachment: AutumnResource) {
|
||||||
val url = "$REVOLT_FILES/attachments/${attachment.id}/${attachment.filename}"
|
val url = "$REVOLT_FILES/attachments/${attachment.id}/${attachment.filename}"
|
||||||
|
var spoilerShown by remember { mutableStateOf(false) }
|
||||||
|
val hazeState =
|
||||||
|
if (attachment.filename?.startsWith("SPOILER_") == true) rememberHazeState() else null
|
||||||
|
|
||||||
BoxWithConstraints {
|
BoxWithConstraints {
|
||||||
RemoteImage(
|
RemoteImage(
|
||||||
|
|
@ -83,9 +98,34 @@ fun ImageAttachment(attachment: AutumnResource) {
|
||||||
.width(attachment.metadata?.width?.toInt()?.dp ?: maxWidth)
|
.width(attachment.metadata?.width?.toInt()?.dp ?: maxWidth)
|
||||||
.aspectRatio(
|
.aspectRatio(
|
||||||
attachment.metadata!!.width!!.toFloat() / attachment.metadata.height!!.toFloat()
|
attachment.metadata!!.width!!.toFloat() / attachment.metadata.height!!.toFloat()
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
if (hazeState != null) Modifier.hazeSource(state = hazeState)
|
||||||
|
else Modifier
|
||||||
),
|
),
|
||||||
description = attachment.filename ?: "Image"
|
description = attachment.filename ?: "Image"
|
||||||
)
|
)
|
||||||
|
if (attachment.filename?.startsWith("SPOILER_") == true && !spoilerShown) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.hazeEffect(state = hazeState, style = HazeMaterials.ultraThin())
|
||||||
|
.width(attachment.metadata?.width?.toInt()?.dp ?: maxWidth)
|
||||||
|
.aspectRatio(
|
||||||
|
attachment.metadata!!.width!!.toFloat() / attachment.metadata.height!!.toFloat()
|
||||||
|
)
|
||||||
|
.clickable { spoilerShown = true },
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(MaterialTheme.shapes.medium)
|
||||||
|
.hazeEffect(state = hazeState, style = HazeMaterials.regular())
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.attachment_spoiler))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,6 +148,7 @@ fun VideoPlayButton() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||||
@Composable
|
@Composable
|
||||||
fun VideoAttachment(attachment: AutumnResource) {
|
fun VideoAttachment(attachment: AutumnResource) {
|
||||||
val url = "$REVOLT_FILES/attachments/${attachment.id}/${attachment.filename}"
|
val url = "$REVOLT_FILES/attachments/${attachment.id}/${attachment.filename}"
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
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
|
||||||
|
|
@ -21,10 +22,13 @@ import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.ListItemDefaults
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
|
@ -37,6 +41,7 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
|
@ -53,10 +58,16 @@ import java.io.File
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FilePreviewSheet(
|
fun FilePreviewSheet(
|
||||||
args: FileArgs, canRemove: Boolean, onRemove: () -> Unit, onDismiss: () -> Unit
|
args: FileArgs,
|
||||||
|
canRemove: Boolean,
|
||||||
|
onRemove: () -> Unit,
|
||||||
|
onToggleSpoiler: () -> Unit,
|
||||||
|
onDismiss: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
var localIsSpoiler by remember { mutableStateOf(args.spoiler) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
|
@ -80,6 +91,39 @@ fun FilePreviewSheet(
|
||||||
color = LocalContentColor.current.copy(alpha = 0.6f),
|
color = LocalContentColor.current.copy(alpha = 0.6f),
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(MaterialTheme.shapes.medium)
|
||||||
|
.clickable {
|
||||||
|
onToggleSpoiler()
|
||||||
|
localIsSpoiler = !localIsSpoiler
|
||||||
|
}
|
||||||
|
.padding(top = 8.dp)
|
||||||
|
) {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.attachment_preview_spoiler)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.attachment_preview_spoiler_description)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
trailingContent = {
|
||||||
|
Switch(
|
||||||
|
checked = localIsSpoiler,
|
||||||
|
onCheckedChange = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = ListItemDefaults.colors().copy(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
|
|
@ -111,6 +155,7 @@ fun AttachmentManager(
|
||||||
uploading: Boolean,
|
uploading: Boolean,
|
||||||
uploadProgress: Float = 0f,
|
uploadProgress: Float = 0f,
|
||||||
onRemove: (FileArgs) -> Unit,
|
onRemove: (FileArgs) -> Unit,
|
||||||
|
onToggleSpoiler: (FileArgs) -> Unit,
|
||||||
canRemove: Boolean = true,
|
canRemove: Boolean = true,
|
||||||
canPreview: Boolean = true
|
canPreview: Boolean = true
|
||||||
) {
|
) {
|
||||||
|
|
@ -126,18 +171,26 @@ fun AttachmentManager(
|
||||||
}, sheetState = sheetState
|
}, sheetState = sheetState
|
||||||
) {
|
) {
|
||||||
previewingAttachment?.let {
|
previewingAttachment?.let {
|
||||||
FilePreviewSheet(args = it, canRemove = canRemove, onRemove = {
|
FilePreviewSheet(
|
||||||
onRemove(it)
|
args = it,
|
||||||
scope.launch {
|
canRemove = canRemove,
|
||||||
sheetState.hide()
|
onRemove = {
|
||||||
showPreviewSheet = false
|
onRemove(it)
|
||||||
|
scope.launch {
|
||||||
|
sheetState.hide()
|
||||||
|
showPreviewSheet = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onToggleSpoiler = {
|
||||||
|
onToggleSpoiler(it)
|
||||||
|
},
|
||||||
|
onDismiss = {
|
||||||
|
scope.launch {
|
||||||
|
sheetState.hide()
|
||||||
|
showPreviewSheet = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, onDismiss = {
|
)
|
||||||
scope.launch {
|
|
||||||
sheetState.hide()
|
|
||||||
showPreviewSheet = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,20 +212,21 @@ fun AttachmentManager(
|
||||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
) {
|
) {
|
||||||
attachments.forEach { attachment ->
|
attachments.forEach { attachment ->
|
||||||
Row(modifier = Modifier
|
Row(
|
||||||
.padding(4.dp)
|
modifier = Modifier
|
||||||
.clip(MaterialTheme.shapes.small)
|
.padding(4.dp)
|
||||||
.clickable {
|
.clip(MaterialTheme.shapes.small)
|
||||||
if (canPreview) {
|
.clickable {
|
||||||
previewingAttachment = attachment
|
if (canPreview) {
|
||||||
showPreviewSheet = true
|
previewingAttachment = attachment
|
||||||
|
showPreviewSheet = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.background(
|
||||||
.background(
|
color = MaterialTheme.colorScheme.background,
|
||||||
color = MaterialTheme.colorScheme.background,
|
shape = MaterialTheme.shapes.small
|
||||||
shape = MaterialTheme.shapes.small
|
)
|
||||||
)
|
.padding(8.dp)) {
|
||||||
.padding(8.dp)) {
|
|
||||||
Text(attachment.filename, maxLines = 1)
|
Text(attachment.filename, maxLines = 1)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
@ -190,13 +244,14 @@ fun AttachmentManager(
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun AttachmentManagerPreview() {
|
fun AttachmentManagerPreview() {
|
||||||
AttachmentManager(attachments = listOf(
|
AttachmentManager(
|
||||||
FileArgs(
|
attachments = listOf(
|
||||||
filename = "file1.png", contentType = "image/png", file = File("file1.png")
|
FileArgs(
|
||||||
), FileArgs(
|
filename = "file1.png", contentType = "image/png", file = File("file1.png")
|
||||||
filename = "file2.png", contentType = "image/png", file = File("file2.png")
|
), FileArgs(
|
||||||
), FileArgs(
|
filename = "file2.png", contentType = "image/png", file = File("file2.png")
|
||||||
filename = "file3.png", contentType = "image/png", file = File("file3.png")
|
), FileArgs(
|
||||||
)
|
filename = "file3.png", contentType = "image/png", file = File("file3.png")
|
||||||
), uploading = false, onRemove = {})
|
)
|
||||||
|
), uploading = false, onToggleSpoiler = {}, onRemove = {})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,6 @@ import androidx.compose.material3.AssistChip
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DrawerState
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
|
@ -95,7 +94,6 @@ import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.Placeholder
|
import androidx.compose.ui.text.Placeholder
|
||||||
|
|
@ -155,6 +153,7 @@ import com.valentinilk.shimmer.shimmer
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
|
import logcat.logcat
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
|
@ -957,6 +956,21 @@ fun ChannelScreen(
|
||||||
canPreview = true,
|
canPreview = true,
|
||||||
onRemove = {
|
onRemove = {
|
||||||
viewModel.draftAttachments.remove(it)
|
viewModel.draftAttachments.remove(it)
|
||||||
|
},
|
||||||
|
onToggleSpoiler = {
|
||||||
|
val index = viewModel.draftAttachments
|
||||||
|
.indexOfFirst { a -> a.pickerIdentifier == it.pickerIdentifier }
|
||||||
|
logcat {
|
||||||
|
"Toggling spoiler for attachment at index $index"
|
||||||
|
}
|
||||||
|
if (index != -1) {
|
||||||
|
val attachment =
|
||||||
|
viewModel.draftAttachments[index]
|
||||||
|
viewModel.draftAttachments[index] =
|
||||||
|
attachment.copy(
|
||||||
|
spoiler = !attachment.spoiler
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,7 @@ class ChannelScreenViewModel @Inject constructor(
|
||||||
try {
|
try {
|
||||||
val id = uploadToAutumn(
|
val id = uploadToAutumn(
|
||||||
it.file,
|
it.file,
|
||||||
it.filename,
|
if (it.spoiler) "SPOILER_${it.filename}" else it.filename,
|
||||||
"attachments",
|
"attachments",
|
||||||
ContentType.parse(it.contentType),
|
ContentType.parse(it.contentType),
|
||||||
onProgress = { current, total ->
|
onProgress = { current, total ->
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,12 @@
|
||||||
<string name="reply_mention_off">\@ off</string>
|
<string name="reply_mention_off">\@ off</string>
|
||||||
<string name="too_many_replies">You can only reply to %1$d messages at a time.</string>
|
<string name="too_many_replies">You can only reply to %1$d messages at a time.</string>
|
||||||
|
|
||||||
|
<string name="attachment_spoiler">Spoiler</string>
|
||||||
|
|
||||||
<string name="attachment_preview_remove">Remove</string>
|
<string name="attachment_preview_remove">Remove</string>
|
||||||
<string name="attachment_preview_close">Close</string>
|
<string name="attachment_preview_close">Close</string>
|
||||||
|
<string name="attachment_preview_spoiler">Mark as spoiler</string>
|
||||||
|
<string name="attachment_preview_spoiler_description">This attachment will only be revealed when tapped.</string>
|
||||||
|
|
||||||
<string name="emoji_category_smileys">Smileys & Emotions</string>
|
<string name="emoji_category_smileys">Smileys & Emotions</string>
|
||||||
<string name="emoji_category_people">People & Body</string>
|
<string name="emoji_category_people">People & Body</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue