mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-07-22 15:52:22 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dab02f2cbd | ||
|
|
978f13fbf3 | ||
|
|
831386a656 | ||
|
|
8b7406e25a | ||
|
|
9e7fed6646 | ||
|
|
89d88326e8 | ||
|
|
f18dd02161 | ||
|
|
e00db19485 | ||
|
|
a706b71d7c | ||
|
|
a83b26cc6f | ||
|
|
dc73843447 | ||
|
|
3e6aa2b943 | ||
|
|
909d3f4fbe | ||
|
|
fb0748f4a7 | ||
|
|
1ec4823122 | ||
|
|
cce5e0bf21 | ||
|
|
0006595786 | ||
|
|
6b884f645e | ||
|
|
946723a80e |
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "com.vitorpamplona.amethyst"
|
||||
minSdk 26
|
||||
targetSdk 34
|
||||
versionCode 298
|
||||
versionName "0.77.1"
|
||||
versionCode 301
|
||||
versionName "0.77.4"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
@@ -352,7 +352,11 @@ object LocalPreferences {
|
||||
val latestContactList = try {
|
||||
getString(PrefKeys.LATEST_CONTACT_LIST, null)?.let {
|
||||
println("Decoding Contact List: " + it)
|
||||
Event.fromJson(it) as ContactListEvent?
|
||||
if (it != null) {
|
||||
Event.fromJson(it) as ContactListEvent?
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.w("LocalPreferences", "Error Decoding Contact List ${getString(PrefKeys.LATEST_CONTACT_LIST, null)}", e)
|
||||
|
||||
@@ -1202,7 +1202,7 @@ class Account(
|
||||
size = headerInfo.size.toString(),
|
||||
dimensions = headerInfo.dim,
|
||||
blurhash = headerInfo.blurHash,
|
||||
description = headerInfo.description,
|
||||
alt = headerInfo.alt,
|
||||
sensitiveContent = headerInfo.sensitiveContent,
|
||||
pubKey = keyPair.pubKey.toHexKey()
|
||||
)
|
||||
@@ -1234,7 +1234,7 @@ class Account(
|
||||
size = headerInfo.size.toString(),
|
||||
dimensions = headerInfo.dim,
|
||||
blurhash = headerInfo.blurHash,
|
||||
description = headerInfo.description,
|
||||
alt = headerInfo.alt,
|
||||
sensitiveContent = headerInfo.sensitiveContent,
|
||||
privateKey = keyPair.privKey!!
|
||||
)
|
||||
@@ -1273,7 +1273,7 @@ class Account(
|
||||
size = headerInfo.size.toString(),
|
||||
dimensions = headerInfo.dim,
|
||||
blurhash = headerInfo.blurHash,
|
||||
description = headerInfo.description,
|
||||
alt = headerInfo.alt,
|
||||
sensitiveContent = headerInfo.sensitiveContent,
|
||||
keyPair = keyPair
|
||||
)
|
||||
@@ -1298,7 +1298,7 @@ class Account(
|
||||
size = headerInfo.size.toString(),
|
||||
dimensions = headerInfo.dim,
|
||||
blurhash = headerInfo.blurHash,
|
||||
description = headerInfo.description,
|
||||
alt = headerInfo.alt,
|
||||
sensitiveContent = headerInfo.sensitiveContent,
|
||||
keyPair = keyPair
|
||||
)
|
||||
|
||||
@@ -16,16 +16,16 @@ class FileHeader(
|
||||
val size: Int,
|
||||
val dim: String?,
|
||||
val blurHash: String?,
|
||||
val description: String? = null,
|
||||
val alt: String? = null,
|
||||
val sensitiveContent: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
suspend fun prepare(fileUrl: String, mimeType: String?, description: String?, sensitiveContent: Boolean, onReady: (FileHeader) -> Unit, onError: () -> Unit) {
|
||||
suspend fun prepare(fileUrl: String, mimeType: String?, alt: String?, sensitiveContent: Boolean, onReady: (FileHeader) -> Unit, onError: () -> Unit) {
|
||||
try {
|
||||
val imageData: ByteArray? = ImageDownloader().waitAndGetImage(fileUrl)
|
||||
|
||||
if (imageData != null) {
|
||||
prepare(imageData, fileUrl, mimeType, description, sensitiveContent, onReady, onError)
|
||||
prepare(imageData, fileUrl, mimeType, alt, sensitiveContent, onReady, onError)
|
||||
} else {
|
||||
onError()
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class FileHeader(
|
||||
data: ByteArray,
|
||||
fileUrl: String,
|
||||
mimeType: String?,
|
||||
description: String?,
|
||||
alt: String?,
|
||||
sensitiveContent: Boolean,
|
||||
onReady: (FileHeader) -> Unit,
|
||||
onError: () -> Unit
|
||||
@@ -79,7 +79,7 @@ class FileHeader(
|
||||
Pair(null, null)
|
||||
}
|
||||
|
||||
onReady(FileHeader(fileUrl, mimeType, hash, size, dim, blurHash, description, sensitiveContent))
|
||||
onReady(FileHeader(fileUrl, mimeType, hash, size, dim, blurHash, alt, sensitiveContent))
|
||||
} catch (e: Exception) {
|
||||
Log.e("ImageDownload", "Couldn't convert image in to File Header: ${e.message}")
|
||||
onError()
|
||||
|
||||
@@ -26,7 +26,7 @@ open class NewMediaModel : ViewModel() {
|
||||
var mediaType by mutableStateOf<String?>(null)
|
||||
|
||||
var selectedServer by mutableStateOf<ServersAvailable?>(null)
|
||||
var description by mutableStateOf("")
|
||||
var alt by mutableStateOf("")
|
||||
var sensitiveContent by mutableStateOf(false)
|
||||
|
||||
// Images and Videos
|
||||
@@ -79,7 +79,7 @@ open class NewMediaModel : ViewModel() {
|
||||
uploadingPercentage.value = 0.2f
|
||||
uploadingDescription.value = "Loading"
|
||||
contentResolver.openInputStream(fileUri)?.use {
|
||||
createNIP95Record(it.readBytes(), contentType, description, sensitiveContent, relayList = relayList)
|
||||
createNIP95Record(it.readBytes(), contentType, alt, sensitiveContent, relayList = relayList)
|
||||
}
|
||||
?: run {
|
||||
viewModelScope.launch {
|
||||
@@ -103,7 +103,7 @@ open class NewMediaModel : ViewModel() {
|
||||
createNIP94Record(
|
||||
imageUrl,
|
||||
mimeType,
|
||||
description,
|
||||
alt,
|
||||
sensitiveContent,
|
||||
relayList = relayList
|
||||
)
|
||||
@@ -139,7 +139,7 @@ open class NewMediaModel : ViewModel() {
|
||||
uploadingDescription.value = null
|
||||
uploadingPercentage.value = 0.0f
|
||||
|
||||
description = ""
|
||||
alt = ""
|
||||
selectedServer = account?.defaultFileServer
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ open class NewMediaModel : ViewModel() {
|
||||
return !isUploadingImage && galleryUri != null && selectedServer != null
|
||||
}
|
||||
|
||||
fun createNIP94Record(imageUrl: String, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
fun createNIP94Record(imageUrl: String, mimeType: String?, alt: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
uploadingPercentage.value = 0.40f
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
uploadingDescription.value = "Server Processing"
|
||||
@@ -166,7 +166,7 @@ open class NewMediaModel : ViewModel() {
|
||||
imageData,
|
||||
imageUrl,
|
||||
mimeType,
|
||||
description,
|
||||
alt,
|
||||
sensitiveContent,
|
||||
onReady = {
|
||||
uploadingPercentage.value = 0.90f
|
||||
@@ -200,7 +200,7 @@ open class NewMediaModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun createNIP95Record(bytes: ByteArray, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
fun createNIP95Record(bytes: ByteArray, mimeType: String?, alt: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
uploadingPercentage.value = 0.30f
|
||||
uploadingDescription.value = "Hashing"
|
||||
|
||||
@@ -209,7 +209,7 @@ open class NewMediaModel : ViewModel() {
|
||||
bytes,
|
||||
"",
|
||||
mimeType,
|
||||
description,
|
||||
alt,
|
||||
sensitiveContent,
|
||||
onReady = {
|
||||
uploadingDescription.value = "Signing"
|
||||
|
||||
@@ -287,8 +287,8 @@ fun ImageVideoPost(postViewModel: NewMediaModel, accountViewModel: AccountViewMo
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
value = postViewModel.description,
|
||||
onValueChange = { postViewModel.description = it },
|
||||
value = postViewModel.alt,
|
||||
onValueChange = { postViewModel.alt = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.content_description_example),
|
||||
|
||||
@@ -338,8 +338,8 @@ fun NewPostView(
|
||||
ImageVideoDescription(
|
||||
url,
|
||||
account.defaultFileServer,
|
||||
onAdd = { description, server, sensitiveContent ->
|
||||
postViewModel.upload(url, description, sensitiveContent, server, context, relayList)
|
||||
onAdd = { alt, server, sensitiveContent ->
|
||||
postViewModel.upload(url, alt, sensitiveContent, server, context, relayList)
|
||||
account.changeDefaultFileServer(server)
|
||||
},
|
||||
onCancel = {
|
||||
|
||||
@@ -299,7 +299,7 @@ open class NewPostViewModel() : ViewModel() {
|
||||
cancel()
|
||||
}
|
||||
|
||||
fun upload(galleryUri: Uri, description: String, sensitiveContent: Boolean, server: ServersAvailable, context: Context, relayList: List<Relay>? = null) {
|
||||
fun upload(galleryUri: Uri, alt: String, sensitiveContent: Boolean, server: ServersAvailable, context: Context, relayList: List<Relay>? = null) {
|
||||
isUploadingImage = true
|
||||
contentToAddUrl = null
|
||||
|
||||
@@ -314,7 +314,7 @@ open class NewPostViewModel() : ViewModel() {
|
||||
onReady = { fileUri, contentType, size ->
|
||||
if (server == ServersAvailable.NIP95) {
|
||||
contentResolver.openInputStream(fileUri)?.use {
|
||||
createNIP95Record(it.readBytes(), contentType, description, sensitiveContent, relayList = relayList)
|
||||
createNIP95Record(it.readBytes(), contentType, alt, sensitiveContent, relayList = relayList)
|
||||
}
|
||||
} else {
|
||||
ImageUploader.uploadImage(
|
||||
@@ -325,7 +325,7 @@ open class NewPostViewModel() : ViewModel() {
|
||||
contentResolver = contentResolver,
|
||||
onSuccess = { imageUrl, mimeType ->
|
||||
if (isNIP94Server(server)) {
|
||||
createNIP94Record(imageUrl, mimeType, description, sensitiveContent)
|
||||
createNIP94Record(imageUrl, mimeType, alt, sensitiveContent)
|
||||
} else {
|
||||
isUploadingImage = false
|
||||
message = TextFieldValue(message.text + "\n\n" + imageUrl)
|
||||
@@ -537,13 +537,13 @@ open class NewPostViewModel() : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun createNIP94Record(imageUrl: String, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
fun createNIP94Record(imageUrl: String, mimeType: String?, alt: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
// Images don't seem to be ready immediately after upload
|
||||
FileHeader.prepare(
|
||||
imageUrl,
|
||||
mimeType,
|
||||
description,
|
||||
alt,
|
||||
sensitiveContent,
|
||||
onReady = {
|
||||
val note = account?.sendHeader(it, relayList = relayList)
|
||||
@@ -568,13 +568,13 @@ open class NewPostViewModel() : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun createNIP95Record(bytes: ByteArray, mimeType: String?, description: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
fun createNIP95Record(bytes: ByteArray, mimeType: String?, alt: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
FileHeader.prepare(
|
||||
bytes,
|
||||
"",
|
||||
mimeType,
|
||||
description,
|
||||
alt,
|
||||
sensitiveContent,
|
||||
onReady = {
|
||||
val nip95 = account?.createNip95(bytes, headerInfo = it)
|
||||
|
||||
@@ -179,7 +179,7 @@ private fun Galeries(
|
||||
val hasLikeEvents by remember { derivedStateOf { multiSetCard.likeEvents.isNotEmpty() } }
|
||||
|
||||
if (hasZapEvents) {
|
||||
var zapEvents by remember(multiSetCard) {
|
||||
var zapEvents by remember(multiSetCard.zapEvents) {
|
||||
mutableStateOf<ImmutableList<ZapAmountCommentNotification>>(persistentListOf())
|
||||
}
|
||||
|
||||
|
||||
@@ -3343,7 +3343,7 @@ fun FileHeaderDisplay(note: Note, roundedCorner: Boolean, accountViewModel: Acco
|
||||
val blurHash = event.blurhash()
|
||||
val hash = event.hash()
|
||||
val dimensions = event.dimensions()
|
||||
val description = event.content
|
||||
val description = event.alt() ?: event.content
|
||||
val isImage = imageExtensions.any { fullUrl.split("?")[0].lowercase().endsWith(it) }
|
||||
val uri = note.toNostrUri()
|
||||
|
||||
@@ -3407,7 +3407,7 @@ private fun ObserverAndRenderNIP95(
|
||||
val localDir = note?.idHex?.let { File(File(appContext.cacheDir, "NIP95"), it) }
|
||||
val blurHash = eventHeader.blurhash()
|
||||
val dimensions = eventHeader.dimensions()
|
||||
val description = eventHeader.content
|
||||
val description = eventHeader.alt() ?: eventHeader.content
|
||||
val mimeType = eventHeader.mimeType()
|
||||
|
||||
val newContent = if (mimeType?.startsWith("image") == true) {
|
||||
|
||||
@@ -1366,18 +1366,14 @@ private fun ActionableReactionButton(
|
||||
onChangeAmount: () -> Unit,
|
||||
toRemove: Set<String>
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Button(
|
||||
modifier = Modifier.padding(horizontal = 3.dp),
|
||||
onClick = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.reactToOrDelete(
|
||||
baseNote,
|
||||
reactionType
|
||||
)
|
||||
onDismiss()
|
||||
}
|
||||
accountViewModel.reactToOrDelete(
|
||||
baseNote,
|
||||
reactionType
|
||||
)
|
||||
onDismiss()
|
||||
},
|
||||
shape = ButtonBorder,
|
||||
colors = ButtonDefaults
|
||||
@@ -1388,13 +1384,11 @@ private fun ActionableReactionButton(
|
||||
val thisModifier = remember(reactionType) {
|
||||
Modifier.combinedClickable(
|
||||
onClick = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountViewModel.reactToOrDelete(
|
||||
baseNote,
|
||||
reactionType
|
||||
)
|
||||
onDismiss()
|
||||
}
|
||||
accountViewModel.reactToOrDelete(
|
||||
baseNote,
|
||||
reactionType
|
||||
)
|
||||
onDismiss()
|
||||
},
|
||||
onLongClick = {
|
||||
onChangeAmount()
|
||||
|
||||
@@ -117,11 +117,13 @@ class AccountViewModel(val account: Account) : ViewModel(), Dao {
|
||||
}
|
||||
|
||||
fun reactToOrDelete(note: Note, reaction: String) {
|
||||
val currentReactions = account.reactionTo(note, reaction)
|
||||
if (currentReactions.isNotEmpty()) {
|
||||
account.delete(currentReactions)
|
||||
} else {
|
||||
account.reactTo(note, reaction)
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val currentReactions = account.reactionTo(note, reaction)
|
||||
if (currentReactions.isNotEmpty()) {
|
||||
account.delete(currentReactions)
|
||||
} else {
|
||||
account.reactTo(note, reaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,14 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.animation.with
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -15,11 +21,18 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
@@ -44,7 +57,7 @@ import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.ThemeViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun MainScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
@@ -180,6 +193,23 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val bottomBarHeight = 50.dp
|
||||
val bottomBarHeightPx = with(LocalDensity.current) { bottomBarHeight.roundToPx().toFloat() }
|
||||
val bottomBarOffsetHeightPx = remember { mutableStateOf(0f) }
|
||||
|
||||
val nestedScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||
val delta = available.y
|
||||
val newOffset = bottomBarOffsetHeightPx.value + delta
|
||||
bottomBarOffsetHeightPx.value = newOffset.coerceIn(-bottomBarHeightPx, 0f)
|
||||
|
||||
return Offset.Zero
|
||||
}
|
||||
}
|
||||
}
|
||||
val shouldShow = bottomBarOffsetHeightPx.value == 0f
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
sheetState = sheetState,
|
||||
sheetContent = {
|
||||
@@ -189,12 +219,40 @@ fun MainScreen(
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colors.primaryVariant)
|
||||
.statusBarsPadding(),
|
||||
.statusBarsPadding()
|
||||
.nestedScroll(nestedScrollConnection),
|
||||
bottomBar = {
|
||||
AppBottomBar(accountViewModel, navState, navBottomRow)
|
||||
AnimatedContent(
|
||||
targetState = shouldShow,
|
||||
transitionSpec = {
|
||||
slideInVertically { height -> height } togetherWith
|
||||
slideOutVertically { height -> height }
|
||||
}
|
||||
) { isVisible ->
|
||||
if (isVisible) {
|
||||
AppBottomBar(accountViewModel, navState, navBottomRow)
|
||||
}
|
||||
}
|
||||
},
|
||||
topBar = {
|
||||
AppTopBar(followLists, navState, scaffoldState, accountViewModel, nav = nav, navPopBack)
|
||||
AnimatedContent(
|
||||
targetState = shouldShow,
|
||||
transitionSpec = {
|
||||
slideInVertically { height -> 0 } togetherWith
|
||||
slideOutVertically { height -> 0 }
|
||||
}
|
||||
) { isVisible ->
|
||||
if (isVisible) {
|
||||
AppTopBar(
|
||||
followLists,
|
||||
navState,
|
||||
scaffoldState,
|
||||
accountViewModel,
|
||||
nav = nav,
|
||||
navPopBack
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
drawerContent = {
|
||||
DrawerContent(nav, scaffoldState, sheetState, accountViewModel)
|
||||
@@ -203,11 +261,27 @@ fun MainScreen(
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingButtons(navState, accountViewModel, accountStateViewModel, nav, navBottomRow)
|
||||
Crossfade(
|
||||
targetState = shouldShow,
|
||||
animationSpec = tween(durationMillis = 100)
|
||||
) { state ->
|
||||
if (state) {
|
||||
FloatingButtons(
|
||||
navState,
|
||||
accountViewModel,
|
||||
accountStateViewModel,
|
||||
nav,
|
||||
navBottomRow
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
scaffoldState = scaffoldState
|
||||
) {
|
||||
Column(modifier = Modifier.padding(bottom = it.calculateBottomPadding())) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(bottom = if (bottomBarOffsetHeightPx.value == 0f || navState.value?.destination?.route != Route.Home.base) it.calculateBottomPadding() else 0.dp)
|
||||
) {
|
||||
AppNavigation(
|
||||
homeFeedViewModel = homeFeedViewModel,
|
||||
repliesFeedViewModel = repliesFeedViewModel,
|
||||
|
||||
@@ -26,6 +26,7 @@ import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.halilibo.richtext.ui.RichTextStyle
|
||||
import com.halilibo.richtext.ui.resolveDefaults
|
||||
import com.patrykandpatrick.vico.compose.style.ChartStyle
|
||||
@@ -347,11 +348,14 @@ fun AmethystTheme(themeViewModel: ThemeViewModel, content: @Composable () -> Uni
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
val insetsController = WindowCompat.getInsetsController(window, view)
|
||||
if (darkTheme) {
|
||||
window.statusBarColor = colors.background.toArgb()
|
||||
} else {
|
||||
window.statusBarColor = colors.primary.toArgb()
|
||||
}
|
||||
window.navigationBarColor = colors.background.toArgb()
|
||||
insetsController.isAppearanceLightNavigationBars = !darkTheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,7 +473,6 @@
|
||||
<string name="poll_zap_value_min_max_explainer">I voti sono ponderati con l\'importo dello zap. È possibile impostare una quantità minima per evitare gli spammer e una quantità massima per evitare che un grande zapper predomini il sondaggio. Usa lo stesso importo in entrambi i campi per assicurarti che ogni voto valga lo stesso importo. Lascialo vuoto per accettare qualsiasi importo.</string>
|
||||
<string name="error_dialog_zap_error">Impossibile inviare lo zap</string>
|
||||
<string name="error_dialog_talk_to_user">Invia un messaggio all\'utente</string>
|
||||
<string name="error_dialog_button_ok">OK</string>
|
||||
<string name="relay_information_document_error_assemble_url">Impossibile raggiungere %1$s: %2$s</string>
|
||||
<string name="relay_information_document_error_reach_server">Impossibile raggiungere %1$s: %2$s</string>
|
||||
<string name="relay_information_document_error_parse_result">Impossibile analizzare il risultato da %1$s: %2$s</string>
|
||||
|
||||
@@ -561,7 +561,7 @@
|
||||
|
||||
<string name="error_dialog_zap_error">Unable to send zap</string>
|
||||
<string name="error_dialog_talk_to_user">Message the User</string>
|
||||
<string name="error_dialog_button_ok">Ok</string>
|
||||
<string name="error_dialog_button_ok">OK</string>
|
||||
|
||||
<string name="relay_information_document_error_assemble_url">Failed to reach %1$s: %2$s</string>
|
||||
<string name="relay_information_document_error_reach_server">Failed to reach %1$s: %2$s</string>
|
||||
|
||||
@@ -22,6 +22,7 @@ class FileHeaderEvent(
|
||||
fun mimeType() = tags.firstOrNull { it.size > 1 && it[0] == MIME_TYPE }?.get(1)
|
||||
fun hash() = tags.firstOrNull { it.size > 1 && it[0] == HASH }?.get(1)
|
||||
fun size() = tags.firstOrNull { it.size > 1 && it[0] == FILE_SIZE }?.get(1)
|
||||
fun alt() = tags.firstOrNull { it.size > 1 && it[0] == ALT }?.get(1)
|
||||
fun dimensions() = tags.firstOrNull { it.size > 1 && it[0] == DIMENSION }?.get(1)
|
||||
fun magnetURI() = tags.firstOrNull { it.size > 1 && it[0] == MAGNET_URI }?.get(1)
|
||||
fun torrentInfoHash() = tags.firstOrNull { it.size > 1 && it[0] == TORRENT_INFOHASH }?.get(1)
|
||||
@@ -41,11 +42,12 @@ class FileHeaderEvent(
|
||||
private const val MAGNET_URI = "magnet"
|
||||
private const val TORRENT_INFOHASH = "i"
|
||||
private const val BLUR_HASH = "blurhash"
|
||||
private const val ALT = "alt"
|
||||
|
||||
fun create(
|
||||
url: String,
|
||||
mimeType: String? = null,
|
||||
description: String? = null,
|
||||
alt: String? = null,
|
||||
hash: String? = null,
|
||||
size: String? = null,
|
||||
dimensions: String? = null,
|
||||
@@ -60,11 +62,13 @@ class FileHeaderEvent(
|
||||
val tags = listOfNotNull(
|
||||
listOf(URL, url),
|
||||
mimeType?.let { listOf(MIME_TYPE, mimeType) },
|
||||
alt?.let { listOf(ALT, it) },
|
||||
hash?.let { listOf(HASH, it) },
|
||||
size?.let { listOf(FILE_SIZE, it) },
|
||||
dimensions?.let { listOf(DIMENSION, it) },
|
||||
blurhash?.let { listOf(BLUR_HASH, it) },
|
||||
magnetURI?.let { listOf(MAGNET_URI, it) },
|
||||
|
||||
torrentInfoHash?.let { listOf(TORRENT_INFOHASH, it) },
|
||||
encryptionKey?.let { listOf(ENCRYPTION_KEY, it.key, it.nonce) },
|
||||
sensitiveContent?.let {
|
||||
@@ -76,7 +80,7 @@ class FileHeaderEvent(
|
||||
}
|
||||
)
|
||||
|
||||
val content = description ?: ""
|
||||
val content = alt ?: ""
|
||||
val pubKey = keyPair.pubKey.toHexKey()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
val sig = if (keyPair.privKey == null) null else CryptoUtils.sign(id, keyPair.privKey)
|
||||
|
||||
@@ -22,6 +22,7 @@ class FileStorageHeaderEvent(
|
||||
fun mimeType() = tags.firstOrNull { it.size > 1 && it[0] == MIME_TYPE }?.get(1)
|
||||
fun hash() = tags.firstOrNull { it.size > 1 && it[0] == HASH }?.get(1)
|
||||
fun size() = tags.firstOrNull { it.size > 1 && it[0] == FILE_SIZE }?.get(1)
|
||||
fun alt() = tags.firstOrNull { it.size > 1 && it[0] == ALT }?.get(1)
|
||||
fun dimensions() = tags.firstOrNull { it.size > 1 && it[0] == DIMENSION }?.get(1)
|
||||
fun magnetURI() = tags.firstOrNull { it.size > 1 && it[0] == MAGNET_URI }?.get(1)
|
||||
fun torrentInfoHash() = tags.firstOrNull { it.size > 1 && it[0] == TORRENT_INFOHASH }?.get(1)
|
||||
@@ -38,11 +39,12 @@ class FileStorageHeaderEvent(
|
||||
private const val MAGNET_URI = "magnet"
|
||||
private const val TORRENT_INFOHASH = "i"
|
||||
private const val BLUR_HASH = "blurhash"
|
||||
private const val ALT = "alt"
|
||||
|
||||
fun create(
|
||||
storageEvent: FileStorageEvent,
|
||||
mimeType: String? = null,
|
||||
description: String? = null,
|
||||
alt: String? = null,
|
||||
hash: String? = null,
|
||||
size: String? = null,
|
||||
dimensions: String? = null,
|
||||
@@ -57,6 +59,7 @@ class FileStorageHeaderEvent(
|
||||
val tags = listOfNotNull(
|
||||
listOf("e", storageEvent.id),
|
||||
mimeType?.let { listOf(MIME_TYPE, mimeType) },
|
||||
alt?.let { listOf(ALT, it) },
|
||||
hash?.let { listOf(HASH, it) },
|
||||
size?.let { listOf(FILE_SIZE, it) },
|
||||
dimensions?.let { listOf(DIMENSION, it) },
|
||||
@@ -73,7 +76,7 @@ class FileStorageHeaderEvent(
|
||||
}
|
||||
)
|
||||
|
||||
val content = description ?: ""
|
||||
val content = alt ?: ""
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
return FileStorageHeaderEvent(id.toHexKey(), pubKey, createdAt, tags, content, "")
|
||||
}
|
||||
@@ -81,7 +84,7 @@ class FileStorageHeaderEvent(
|
||||
fun create(
|
||||
storageEvent: FileStorageEvent,
|
||||
mimeType: String? = null,
|
||||
description: String? = null,
|
||||
alt: String? = null,
|
||||
hash: String? = null,
|
||||
size: String? = null,
|
||||
dimensions: String? = null,
|
||||
@@ -97,6 +100,7 @@ class FileStorageHeaderEvent(
|
||||
listOf("e", storageEvent.id),
|
||||
mimeType?.let { listOf(MIME_TYPE, mimeType) },
|
||||
hash?.let { listOf(HASH, it) },
|
||||
alt?.let { listOf(ALT, it) },
|
||||
size?.let { listOf(FILE_SIZE, it) },
|
||||
dimensions?.let { listOf(DIMENSION, it) },
|
||||
blurhash?.let { listOf(BLUR_HASH, it) },
|
||||
@@ -112,7 +116,7 @@ class FileStorageHeaderEvent(
|
||||
}
|
||||
)
|
||||
|
||||
val content = description ?: ""
|
||||
val content = alt ?: ""
|
||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
val sig = CryptoUtils.sign(id, privateKey)
|
||||
|
||||
Reference in New Issue
Block a user