mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
Merge branch 'main' into redesign_emoji_selector
This commit is contained in:
+3
-3
@@ -12,9 +12,9 @@ android {
|
||||
applicationId "com.vitorpamplona.amethyst"
|
||||
minSdk 26
|
||||
targetSdk 34
|
||||
versionCode 376
|
||||
versionName "0.87.7"
|
||||
buildConfigField "String", "RELEASE_NOTES_ID", "\"863124b6592359494396c22b917f9d251ad12ac31b09ea29e0265e3c088ce9f8\""
|
||||
versionCode 379
|
||||
versionName "0.88.2"
|
||||
buildConfigField "String", "RELEASE_NOTES_ID", "\"2a34cbadd03212c8162e1ff896ba12641821088a2ec8d5e40d54aa80c0510800\""
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
+13
-14
@@ -43,7 +43,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
@@ -60,6 +59,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.CheckifItNeedsToRequestNoti
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SettingsRow
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@@ -79,7 +79,7 @@ fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesView
|
||||
LoadDistributors { currentDistributor, list, readableListWithExplainer ->
|
||||
if (readableListWithExplainer.size > 1) {
|
||||
SpinnerSelectionDialog(
|
||||
title = stringResource(id = R.string.select_push_server),
|
||||
title = stringRes(id = R.string.select_push_server),
|
||||
options = readableListWithExplainer,
|
||||
onSelect = { index ->
|
||||
if (list[index] == "None") {
|
||||
@@ -101,9 +101,9 @@ fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesView
|
||||
} else {
|
||||
AlertDialog(
|
||||
onDismissRequest = { distributorPresent = true },
|
||||
title = { Text(stringResource(R.string.push_server_install_app)) },
|
||||
title = { Text(stringRes(R.string.push_server_install_app)) },
|
||||
text = {
|
||||
val content = stringResource(R.string.push_server_install_app_description)
|
||||
val content = stringRes(R.string.push_server_install_app_description)
|
||||
|
||||
val astNode =
|
||||
remember {
|
||||
@@ -128,7 +128,7 @@ fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesView
|
||||
sharedPreferencesViewModel.dontShowPushNotificationSelector()
|
||||
},
|
||||
) {
|
||||
Text(stringResource(R.string.quick_action_dont_show_again_button))
|
||||
Text(stringRes(R.string.quick_action_dont_show_again_button))
|
||||
}
|
||||
Button(
|
||||
onClick = { distributorPresent = true },
|
||||
@@ -142,7 +142,7 @@ fun SelectNotificationProvider(sharedPreferencesViewModel: SharedPreferencesView
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(stringResource(R.string.error_dialog_button_ok))
|
||||
Text(stringRes(R.string.error_dialog_button_ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,20 +168,19 @@ fun LoadDistributors(onInner: @Composable (String, ImmutableList<String>, Immuta
|
||||
}
|
||||
|
||||
val readableListWithExplainer =
|
||||
PushDistributorHandler.formattedDistributorNames()
|
||||
PushDistributorHandler
|
||||
.formattedDistributorNames()
|
||||
.mapIndexed { index, name ->
|
||||
TitleExplainer(
|
||||
name,
|
||||
stringResource(id = R.string.push_server_uses_app_explainer, list[index]),
|
||||
stringRes(id = R.string.push_server_uses_app_explainer, list[index]),
|
||||
)
|
||||
}
|
||||
.plus(
|
||||
}.plus(
|
||||
TitleExplainer(
|
||||
stringResource(id = R.string.push_server_none),
|
||||
stringResource(id = R.string.push_server_none_explainer),
|
||||
stringRes(id = R.string.push_server_none),
|
||||
stringRes(id = R.string.push_server_none_explainer),
|
||||
),
|
||||
)
|
||||
.toImmutableList()
|
||||
).toImmutableList()
|
||||
|
||||
onInner(
|
||||
currentDistributor,
|
||||
|
||||
@@ -99,14 +99,7 @@ private object PrefKeys {
|
||||
const val WARN_ABOUT_REPORTS = "warn_about_reports"
|
||||
const val FILTER_SPAM_FROM_STRANGERS = "filter_spam_from_strangers"
|
||||
const val LAST_READ_PER_ROUTE = "last_read_route_per_route"
|
||||
const val AUTOMATICALLY_SHOW_IMAGES = "automatically_show_images"
|
||||
const val AUTOMATICALLY_START_PLAYBACK = "automatically_start_playback"
|
||||
const val THEME = "theme"
|
||||
const val PREFERRED_LANGUAGE = "preferred_Language"
|
||||
const val AUTOMATICALLY_LOAD_URL_PREVIEW = "automatically_load_url_preview"
|
||||
const val AUTOMATICALLY_HIDE_NAV_BARS = "automatically_hide_nav_bars"
|
||||
const val LOGIN_WITH_EXTERNAL_SIGNER = "login_with_external_signer"
|
||||
const val AUTOMATICALLY_SHOW_PROFILE_PICTURE = "automatically_show_profile_picture"
|
||||
const val SIGNER_PACKAGE_NAME = "signer_package_name"
|
||||
const val HAS_DONATED_IN_VERSION = "has_donated_in_version"
|
||||
const val PENDING_ATTESTATIONS = "pending_attestations"
|
||||
@@ -148,7 +141,7 @@ object LocalPreferences {
|
||||
Event.mapper.readValue<List<AccountInfo>>(it)
|
||||
}
|
||||
|
||||
if (newSystemOfAccounts != null && newSystemOfAccounts.isNotEmpty()) {
|
||||
if (!newSystemOfAccounts.isNullOrEmpty()) {
|
||||
savedAccounts = newSystemOfAccounts
|
||||
} else {
|
||||
val oldAccounts = getString(PrefKeys.SAVED_ACCOUNTS, null)?.split(COMMA) ?: listOf()
|
||||
@@ -164,6 +157,8 @@ object LocalPreferences {
|
||||
}
|
||||
|
||||
savedAccounts = migrated
|
||||
|
||||
edit().apply { putString(PrefKeys.ALL_ACCOUNT_INFO, Event.mapper.writeValueAsString(savedAccounts)) }.apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,9 +260,7 @@ object LocalPreferences {
|
||||
saveToEncryptedStorage(account)
|
||||
}
|
||||
|
||||
fun allSavedAccounts(): List<AccountInfo> {
|
||||
return savedAccounts()
|
||||
}
|
||||
fun allSavedAccounts(): List<AccountInfo> = savedAccounts()
|
||||
|
||||
suspend fun saveToEncryptedStorage(account: Account) {
|
||||
Log.d("LocalPreferences", "Saving to encrypted storage")
|
||||
@@ -345,14 +338,11 @@ object LocalPreferences {
|
||||
PrefKeys.PENDING_ATTESTATIONS,
|
||||
Event.mapper.writeValueAsString(account.pendingAttestations.value),
|
||||
)
|
||||
}
|
||||
.apply()
|
||||
}.apply()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadCurrentAccountFromEncryptedStorage(): Account? {
|
||||
return currentAccount()?.let { loadCurrentAccountFromEncryptedStorage(it) }
|
||||
}
|
||||
suspend fun loadCurrentAccountFromEncryptedStorage(): Account? = currentAccount()?.let { loadCurrentAccountFromEncryptedStorage(it) }
|
||||
|
||||
suspend fun saveSharedSettings(
|
||||
sharedSettings: Settings,
|
||||
|
||||
@@ -34,17 +34,20 @@ data class Settings(
|
||||
val automaticallyShowProfilePictures: ConnectivityType = ConnectivityType.ALWAYS,
|
||||
val dontShowPushNotificationSelector: Boolean = false,
|
||||
val dontAskForNotificationPermissions: Boolean = false,
|
||||
val featureSet: FeatureSetType = FeatureSetType.COMPLETE,
|
||||
val featureSet: FeatureSetType = FeatureSetType.SIMPLIFIED,
|
||||
)
|
||||
|
||||
enum class ThemeType(val screenCode: Int, val resourceId: Int) {
|
||||
enum class ThemeType(
|
||||
val screenCode: Int,
|
||||
val resourceId: Int,
|
||||
) {
|
||||
SYSTEM(0, R.string.system),
|
||||
LIGHT(1, R.string.light),
|
||||
DARK(2, R.string.dark),
|
||||
}
|
||||
|
||||
fun parseThemeType(code: Int?): ThemeType {
|
||||
return when (code) {
|
||||
fun parseThemeType(code: Int?): ThemeType =
|
||||
when (code) {
|
||||
ThemeType.SYSTEM.screenCode -> ThemeType.SYSTEM
|
||||
ThemeType.LIGHT.screenCode -> ThemeType.LIGHT
|
||||
ThemeType.DARK.screenCode -> ThemeType.DARK
|
||||
@@ -52,21 +55,28 @@ fun parseThemeType(code: Int?): ThemeType {
|
||||
ThemeType.SYSTEM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ConnectivityType(val prefCode: Boolean?, val screenCode: Int, val resourceId: Int) {
|
||||
enum class ConnectivityType(
|
||||
val prefCode: Boolean?,
|
||||
val screenCode: Int,
|
||||
val resourceId: Int,
|
||||
) {
|
||||
ALWAYS(null, 0, R.string.connectivity_type_always),
|
||||
WIFI_ONLY(true, 1, R.string.connectivity_type_wifi_only),
|
||||
NEVER(false, 2, R.string.connectivity_type_never),
|
||||
}
|
||||
|
||||
enum class FeatureSetType(val screenCode: Int, val resourceId: Int) {
|
||||
enum class FeatureSetType(
|
||||
val screenCode: Int,
|
||||
val resourceId: Int,
|
||||
) {
|
||||
COMPLETE(0, R.string.ui_feature_set_type_complete),
|
||||
SIMPLIFIED(1, R.string.ui_feature_set_type_simplified),
|
||||
PERFORMANCE(2, R.string.ui_feature_set_type_performance),
|
||||
}
|
||||
|
||||
fun parseConnectivityType(code: Boolean?): ConnectivityType {
|
||||
return when (code) {
|
||||
fun parseConnectivityType(code: Boolean?): ConnectivityType =
|
||||
when (code) {
|
||||
ConnectivityType.ALWAYS.prefCode -> ConnectivityType.ALWAYS
|
||||
ConnectivityType.WIFI_ONLY.prefCode -> ConnectivityType.WIFI_ONLY
|
||||
ConnectivityType.NEVER.prefCode -> ConnectivityType.NEVER
|
||||
@@ -74,10 +84,9 @@ fun parseConnectivityType(code: Boolean?): ConnectivityType {
|
||||
ConnectivityType.ALWAYS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseConnectivityType(screenCode: Int): ConnectivityType {
|
||||
return when (screenCode) {
|
||||
fun parseConnectivityType(screenCode: Int): ConnectivityType =
|
||||
when (screenCode) {
|
||||
ConnectivityType.ALWAYS.screenCode -> ConnectivityType.ALWAYS
|
||||
ConnectivityType.WIFI_ONLY.screenCode -> ConnectivityType.WIFI_ONLY
|
||||
ConnectivityType.NEVER.screenCode -> ConnectivityType.NEVER
|
||||
@@ -85,39 +94,40 @@ fun parseConnectivityType(screenCode: Int): ConnectivityType {
|
||||
ConnectivityType.ALWAYS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseFeatureSetType(screenCode: Int): FeatureSetType {
|
||||
return when (screenCode) {
|
||||
fun parseFeatureSetType(screenCode: Int): FeatureSetType =
|
||||
when (screenCode) {
|
||||
FeatureSetType.COMPLETE.screenCode -> FeatureSetType.COMPLETE
|
||||
FeatureSetType.SIMPLIFIED.screenCode -> FeatureSetType.SIMPLIFIED
|
||||
FeatureSetType.PERFORMANCE.screenCode -> FeatureSetType.PERFORMANCE
|
||||
else -> {
|
||||
FeatureSetType.COMPLETE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class BooleanType(val prefCode: Boolean?, val screenCode: Int, val reourceId: Int) {
|
||||
enum class BooleanType(
|
||||
val prefCode: Boolean?,
|
||||
val screenCode: Int,
|
||||
val reourceId: Int,
|
||||
) {
|
||||
ALWAYS(null, 0, R.string.connectivity_type_always),
|
||||
NEVER(false, 1, R.string.connectivity_type_never),
|
||||
}
|
||||
|
||||
fun parseBooleanType(code: Boolean?): BooleanType {
|
||||
return when (code) {
|
||||
fun parseBooleanType(code: Boolean?): BooleanType =
|
||||
when (code) {
|
||||
BooleanType.ALWAYS.prefCode -> BooleanType.ALWAYS
|
||||
BooleanType.NEVER.prefCode -> BooleanType.NEVER
|
||||
else -> {
|
||||
BooleanType.ALWAYS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseBooleanType(screenCode: Int): BooleanType {
|
||||
return when (screenCode) {
|
||||
fun parseBooleanType(screenCode: Int): BooleanType =
|
||||
when (screenCode) {
|
||||
BooleanType.ALWAYS.screenCode -> BooleanType.ALWAYS
|
||||
BooleanType.NEVER.screenCode -> BooleanType.NEVER
|
||||
else -> {
|
||||
BooleanType.ALWAYS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,16 @@ import com.vitorpamplona.quartz.events.ImmutableListOfLists
|
||||
object CachedRichTextParser {
|
||||
val richTextCache = LruCache<String, RichTextViewerState>(50)
|
||||
|
||||
fun getCached(content: String): RichTextViewerState? = richTextCache[content]
|
||||
|
||||
fun parseText(
|
||||
content: String,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
callbackUri: String? = null,
|
||||
): RichTextViewerState {
|
||||
return if (richTextCache[content] != null) {
|
||||
richTextCache[content]
|
||||
val cached = richTextCache[content]
|
||||
return if (cached != null) {
|
||||
cached
|
||||
} else {
|
||||
val newUrls = RichTextParser().parseText(content, tags, callbackUri)
|
||||
richTextCache.put(content, newUrls)
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Request
|
||||
@@ -46,9 +47,7 @@ data class CashuToken(
|
||||
object CachedCashuProcessor {
|
||||
val cashuCache = LruCache<String, GenericLoadable<CashuToken>>(20)
|
||||
|
||||
fun cached(token: String): GenericLoadable<CashuToken> {
|
||||
return cashuCache[token] ?: GenericLoadable.Loading()
|
||||
}
|
||||
fun cached(token: String): GenericLoadable<CashuToken> = cashuCache[token] ?: GenericLoadable.Loading()
|
||||
|
||||
fun parse(token: String): GenericLoadable<CashuToken> {
|
||||
if (cashuCache[token] !is GenericLoadable.Loaded) {
|
||||
@@ -150,7 +149,12 @@ class CashuProcessor {
|
||||
|
||||
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||
val requestBody = jsonObject.toString().toRequestBody(mediaType)
|
||||
val request = Request.Builder().url(url).post(requestBody).build()
|
||||
val request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.post(requestBody)
|
||||
.build()
|
||||
|
||||
client.newCall(request).execute().use {
|
||||
val body = it.body.string()
|
||||
@@ -163,13 +167,19 @@ class CashuProcessor {
|
||||
feeCost,
|
||||
)
|
||||
} else {
|
||||
val msg = tree?.get("detail")?.asText()?.split('.')?.getOrNull(0)?.ifBlank { null }
|
||||
val msg =
|
||||
tree
|
||||
?.get("detail")
|
||||
?.asText()
|
||||
?.split('.')
|
||||
?.getOrNull(0)
|
||||
?.ifBlank { null }
|
||||
onError(
|
||||
context.getString(R.string.cashu_failed_redemption),
|
||||
stringRes(context, R.string.cashu_failed_redemption),
|
||||
if (msg != null) {
|
||||
context.getString(R.string.cashu_failed_redemption_explainer_error_msg, msg)
|
||||
stringRes(context, R.string.cashu_failed_redemption_explainer_error_msg, msg)
|
||||
} else {
|
||||
context.getString(R.string.cashu_failed_redemption_explainer_error_msg)
|
||||
stringRes(context, R.string.cashu_failed_redemption_explainer_error_msg)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -177,8 +187,8 @@ class CashuProcessor {
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
onError(
|
||||
context.getString(R.string.cashu_successful_redemption),
|
||||
context.getString(R.string.cashu_failed_redemption_explainer_error_msg, e.message),
|
||||
stringRes(context, R.string.cashu_successful_redemption),
|
||||
stringRes(context, R.string.cashu_failed_redemption_explainer_error_msg, e.message),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -203,7 +213,12 @@ class CashuProcessor {
|
||||
|
||||
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||
val requestBody = jsonObject.toString().toRequestBody(mediaType)
|
||||
val request = Request.Builder().url(url).post(requestBody).build()
|
||||
val request =
|
||||
Request
|
||||
.Builder()
|
||||
.url(url)
|
||||
.post(requestBody)
|
||||
.build()
|
||||
|
||||
client.newCall(request).execute().use {
|
||||
val body = it.body.string()
|
||||
@@ -213,21 +228,28 @@ class CashuProcessor {
|
||||
|
||||
if (successful) {
|
||||
onSuccess(
|
||||
context.getString(R.string.cashu_successful_redemption),
|
||||
context.getString(
|
||||
stringRes(context, R.string.cashu_successful_redemption),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.cashu_successful_redemption_explainer,
|
||||
token.totalAmount.toString(),
|
||||
fees.toString(),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
val msg = tree?.get("detail")?.asText()?.split('.')?.getOrNull(0)?.ifBlank { null }
|
||||
val msg =
|
||||
tree
|
||||
?.get("detail")
|
||||
?.asText()
|
||||
?.split('.')
|
||||
?.getOrNull(0)
|
||||
?.ifBlank { null }
|
||||
onError(
|
||||
context.getString(R.string.cashu_failed_redemption),
|
||||
stringRes(context, R.string.cashu_failed_redemption),
|
||||
if (msg != null) {
|
||||
context.getString(R.string.cashu_failed_redemption_explainer_error_msg, msg)
|
||||
stringRes(context, R.string.cashu_failed_redemption_explainer_error_msg, msg)
|
||||
} else {
|
||||
context.getString(R.string.cashu_failed_redemption_explainer_error_msg)
|
||||
stringRes(context, R.string.cashu_failed_redemption_explainer_error_msg)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -235,8 +257,8 @@ class CashuProcessor {
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
onError(
|
||||
context.getString(R.string.cashu_successful_redemption),
|
||||
context.getString(R.string.cashu_failed_redemption_explainer_error_msg, e.message),
|
||||
stringRes(context, R.string.cashu_successful_redemption),
|
||||
stringRes(context, R.string.cashu_failed_redemption_explainer_error_msg, e.message),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,9 @@ import com.vitorpamplona.amethyst.BuildConfig
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
class Nip05NostrAddressVerifier() {
|
||||
class Nip05NostrAddressVerifier {
|
||||
fun assembleUrl(nip05address: String): String? {
|
||||
val parts = nip05address.trim().split("@")
|
||||
|
||||
@@ -46,7 +43,7 @@ class Nip05NostrAddressVerifier() {
|
||||
|
||||
suspend fun fetchNip05Json(
|
||||
nip05: String,
|
||||
onSuccess: (String) -> Unit,
|
||||
onSuccess: suspend (String) -> Unit,
|
||||
onError: (String) -> Unit,
|
||||
) = withContext(Dispatchers.IO) {
|
||||
checkNotInMainThread()
|
||||
@@ -60,52 +57,39 @@ class Nip05NostrAddressVerifier() {
|
||||
|
||||
try {
|
||||
val request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.url(url)
|
||||
.build()
|
||||
|
||||
HttpClientManager.getHttpClient()
|
||||
// Fetchers MUST ignore any HTTP redirects given by the /.well-known/nostr.json endpoint.
|
||||
HttpClientManager
|
||||
.getHttpClient()
|
||||
.newBuilder()
|
||||
.followRedirects(false)
|
||||
.build()
|
||||
.newCall(request)
|
||||
.enqueue(
|
||||
object : Callback {
|
||||
override fun onResponse(
|
||||
call: Call,
|
||||
response: Response,
|
||||
) {
|
||||
checkNotInMainThread()
|
||||
.execute()
|
||||
.use {
|
||||
checkNotInMainThread()
|
||||
|
||||
response.use {
|
||||
if (it.isSuccessful) {
|
||||
onSuccess(it.body.string())
|
||||
} else {
|
||||
onError(
|
||||
"Could not resolve $nip05. Error: ${it.code}. Check if the server is up and if the address $nip05 is correct",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call,
|
||||
e: java.io.IOException,
|
||||
) {
|
||||
onError(
|
||||
"Could not resolve $url. Check if the server is up and if the address $nip05 is correct",
|
||||
)
|
||||
e.printStackTrace()
|
||||
}
|
||||
},
|
||||
)
|
||||
if (it.isSuccessful) {
|
||||
onSuccess(it.body.string())
|
||||
} else {
|
||||
onError(
|
||||
"Could not resolve $nip05. Error: ${it.code}. Check if the server is up and if the address $nip05 is correct",
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
onError("Could not resolve '$url': ${e.message}")
|
||||
onError("Could not resolve NIP-05 $nip05 as URL $url: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun verifyNip05(
|
||||
nip05: String,
|
||||
onSuccess: (String) -> Unit,
|
||||
onSuccess: suspend (String) -> Unit,
|
||||
onError: (String) -> Unit,
|
||||
) {
|
||||
// check fails on tests
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.vitorpamplona.amethyst.service.relays.COMMON_FEED_TYPES
|
||||
import com.vitorpamplona.amethyst.service.relays.Client
|
||||
import com.vitorpamplona.amethyst.service.relays.EOSEAccount
|
||||
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
||||
import com.vitorpamplona.amethyst.service.relays.EVENT_FINDER_TYPES
|
||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||
@@ -76,8 +77,8 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
val latestEOSEs = EOSEAccount()
|
||||
val hasLoadedTheBasics = mutableMapOf<User, Boolean>()
|
||||
|
||||
fun createAccountContactListFilter(): TypedFilter {
|
||||
return TypedFilter(
|
||||
fun createAccountContactListFilter(): TypedFilter =
|
||||
TypedFilter(
|
||||
types = COMMON_FEED_TYPES,
|
||||
filter =
|
||||
JsonFilter(
|
||||
@@ -86,10 +87,9 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
limit = 1,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createAccountMetadataFilter(): TypedFilter {
|
||||
return TypedFilter(
|
||||
fun createAccountMetadataFilter(): TypedFilter =
|
||||
TypedFilter(
|
||||
types = COMMON_FEED_TYPES,
|
||||
filter =
|
||||
JsonFilter(
|
||||
@@ -98,10 +98,9 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
limit = 1,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createAccountRelayListFilter(): TypedFilter {
|
||||
return TypedFilter(
|
||||
fun createAccountRelayListFilter(): TypedFilter =
|
||||
TypedFilter(
|
||||
types = COMMON_FEED_TYPES,
|
||||
filter =
|
||||
JsonFilter(
|
||||
@@ -110,12 +109,11 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
limit = 10,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createOtherAccountsBaseFilter(): TypedFilter? {
|
||||
if (otherAccounts.isEmpty()) return null
|
||||
return TypedFilter(
|
||||
types = COMMON_FEED_TYPES,
|
||||
types = EVENT_FINDER_TYPES,
|
||||
filter =
|
||||
JsonFilter(
|
||||
kinds =
|
||||
@@ -136,8 +134,8 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
)
|
||||
}
|
||||
|
||||
fun createAccountSettingsFilter(): TypedFilter {
|
||||
return TypedFilter(
|
||||
fun createAccountSettingsFilter(): TypedFilter =
|
||||
TypedFilter(
|
||||
types = COMMON_FEED_TYPES,
|
||||
filter =
|
||||
JsonFilter(
|
||||
@@ -146,10 +144,9 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
limit = 100,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createAccountReportsFilter(): TypedFilter {
|
||||
return TypedFilter(
|
||||
fun createAccountReportsFilter(): TypedFilter =
|
||||
TypedFilter(
|
||||
types = COMMON_FEED_TYPES,
|
||||
filter =
|
||||
JsonFilter(
|
||||
@@ -162,10 +159,9 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
?.relayList,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createAccountLastPostsListFilter(): TypedFilter {
|
||||
return TypedFilter(
|
||||
fun createAccountLastPostsListFilter(): TypedFilter =
|
||||
TypedFilter(
|
||||
types = COMMON_FEED_TYPES,
|
||||
filter =
|
||||
JsonFilter(
|
||||
@@ -173,7 +169,6 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
limit = 400,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createNotificationFilter(): TypedFilter {
|
||||
val since =
|
||||
@@ -404,8 +399,8 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateChannelFilters() {
|
||||
return if (hasLoadedTheBasics[account.userProfile()] != null) {
|
||||
override fun updateChannelFilters() =
|
||||
if (hasLoadedTheBasics[account.userProfile()] != null) {
|
||||
// gets everything about the user logged in
|
||||
accountChannel.typedFilters =
|
||||
listOfNotNull(
|
||||
@@ -419,8 +414,7 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
createAccountSettingsFilter(),
|
||||
createAccountLastPostsListFilter(),
|
||||
createOtherAccountsBaseFilter(),
|
||||
)
|
||||
.ifEmpty { null }
|
||||
).ifEmpty { null }
|
||||
} else {
|
||||
// just the basics.
|
||||
accountChannel.typedFilters =
|
||||
@@ -429,10 +423,8 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||
createAccountContactListFilter(),
|
||||
createAccountRelayListFilter(),
|
||||
createAccountSettingsFilter(),
|
||||
)
|
||||
.ifEmpty { null }
|
||||
).ifEmpty { null }
|
||||
}
|
||||
}
|
||||
|
||||
override fun auth(
|
||||
relay: Relay,
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.vitorpamplona.quartz.events.ReportEvent
|
||||
import com.vitorpamplona.quartz.events.StatusEvent
|
||||
|
||||
object NostrSingleUserDataSource : NostrDataSource("SingleUserFeed") {
|
||||
var usersToWatch = setOf<User>()
|
||||
private var usersToWatch = setOf<User>()
|
||||
|
||||
fun createUserMetadataFilter(): List<TypedFilter>? {
|
||||
if (usersToWatch.isEmpty()) return null
|
||||
@@ -83,8 +83,7 @@ object NostrSingleUserDataSource : NostrDataSource("SingleUserFeed") {
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
.flatten()
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
val userChannel =
|
||||
@@ -110,8 +109,7 @@ object NostrSingleUserDataSource : NostrDataSource("SingleUserFeed") {
|
||||
listOfNotNull(
|
||||
createUserMetadataFilter(),
|
||||
createUserMetadataStatusReportFilter(),
|
||||
)
|
||||
.flatten()
|
||||
).flatten()
|
||||
.ifEmpty { null }
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ object NostrUserProfileDataSource : NostrDataSource("UserProfileFeed") {
|
||||
JsonFilter(
|
||||
kinds = listOf(LnZapEvent.KIND),
|
||||
tags = mapOf("p" to listOf(it.pubkeyHex)),
|
||||
limit = 200,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -178,7 +179,6 @@ object NostrUserProfileDataSource : NostrDataSource("UserProfileFeed") {
|
||||
createAcceptedAwardsFilter(),
|
||||
createReceivedAwardsFilter(),
|
||||
createBookmarksFilter(),
|
||||
)
|
||||
.ifEmpty { null }
|
||||
).ifEmpty { null }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.collectSuccessfulSigningOperations
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.events.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.events.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesEvent
|
||||
@@ -41,7 +42,9 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.math.round
|
||||
|
||||
class ZapPaymentHandler(val account: Account) {
|
||||
class ZapPaymentHandler(
|
||||
val account: Account,
|
||||
) {
|
||||
@Immutable
|
||||
data class Payable(
|
||||
val info: ZapSplitSetup,
|
||||
@@ -79,8 +82,9 @@ class ZapPaymentHandler(val account: Account) {
|
||||
|
||||
if (lud16.isNullOrBlank()) {
|
||||
onError(
|
||||
context.getString(R.string.missing_lud16),
|
||||
context.getString(
|
||||
stringRes(context, R.string.missing_lud16),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.user_does_not_have_a_lightning_address_setup_to_receive_sats,
|
||||
),
|
||||
)
|
||||
@@ -94,8 +98,9 @@ class ZapPaymentHandler(val account: Account) {
|
||||
|
||||
if (lud16.isNullOrBlank()) {
|
||||
onError(
|
||||
context.getString(R.string.missing_lud16),
|
||||
context.getString(
|
||||
stringRes(context, R.string.missing_lud16),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.user_does_not_have_a_lightning_address_setup_to_receive_sats,
|
||||
),
|
||||
)
|
||||
@@ -132,14 +137,15 @@ class ZapPaymentHandler(val account: Account) {
|
||||
}
|
||||
} else {
|
||||
onPayViaIntent(
|
||||
it.map {
|
||||
Payable(
|
||||
info = it.key.first,
|
||||
user = it.key.second.user,
|
||||
amountMilliSats = it.value.zapValue,
|
||||
invoice = it.value.invoice,
|
||||
)
|
||||
}.toImmutableList(),
|
||||
it
|
||||
.map {
|
||||
Payable(
|
||||
info = it.key.first,
|
||||
user = it.key.second.user,
|
||||
amountMilliSats = it.value.zapValue,
|
||||
invoice = it.value.invoice,
|
||||
)
|
||||
}.toImmutableList(),
|
||||
)
|
||||
|
||||
onProgress(0f)
|
||||
@@ -172,13 +178,16 @@ class ZapPaymentHandler(val account: Account) {
|
||||
onAllDone: suspend (MutableMap<ZapSplitSetup, SignAllZapRequestsReturn>) -> Unit,
|
||||
) {
|
||||
val authorRelayList =
|
||||
note.author?.pubkeyHex?.let {
|
||||
(
|
||||
LocalCache.getAddressableNoteIfExists(
|
||||
AdvertisedRelayListEvent.createAddressTag(it),
|
||||
)?.event as? AdvertisedRelayListEvent?
|
||||
)?.readRelays()
|
||||
}?.toSet()
|
||||
note.author
|
||||
?.pubkeyHex
|
||||
?.let {
|
||||
(
|
||||
LocalCache
|
||||
.getAddressableNoteIfExists(
|
||||
AdvertisedRelayListEvent.createAddressTag(it),
|
||||
)?.event as? AdvertisedRelayListEvent?
|
||||
)?.readRelays()
|
||||
}?.toSet()
|
||||
|
||||
collectSuccessfulSigningOperations<ZapSplitSetup, SignAllZapRequestsReturn>(
|
||||
operationsInput = zapsToSend,
|
||||
@@ -194,9 +203,10 @@ class ZapPaymentHandler(val account: Account) {
|
||||
val userRelayList =
|
||||
(
|
||||
(
|
||||
LocalCache.getAddressableNoteIfExists(
|
||||
AdvertisedRelayListEvent.createAddressTag(next.lnAddressOrPubKeyHex),
|
||||
)?.event as? AdvertisedRelayListEvent?
|
||||
LocalCache
|
||||
.getAddressableNoteIfExists(
|
||||
AdvertisedRelayListEvent.createAddressTag(next.lnAddressOrPubKeyHex),
|
||||
)?.event as? AdvertisedRelayListEvent?
|
||||
)?.readRelays()?.toSet() ?: emptySet()
|
||||
) + (authorRelayList ?: emptySet())
|
||||
|
||||
@@ -272,8 +282,9 @@ class ZapPaymentHandler(val account: Account) {
|
||||
progressAllPayments += 0.5f / invoices.size
|
||||
onProgress(progressAllPayments)
|
||||
onError(
|
||||
context.getString(R.string.error_dialog_pay_invoice_error),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_dialog_pay_invoice_error),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.wallet_connect_pay_invoice_error_error,
|
||||
response.error?.message
|
||||
?: response.error?.code?.toString() ?: "Error parsing error message",
|
||||
@@ -339,10 +350,12 @@ class ZapPaymentHandler(val account: Account) {
|
||||
} else {
|
||||
if (showErrorIfNoLnAddress) {
|
||||
onError(
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.missing_lud16,
|
||||
),
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.user_x_does_not_have_a_lightning_address_setup_to_receive_sats,
|
||||
user?.toBestDisplayName() ?: splitSetup.lnAddressOrPubKeyHex,
|
||||
),
|
||||
|
||||
+35
-23
@@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.BuildConfig
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.HttpClientManager
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
|
||||
import com.vitorpamplona.quartz.encoders.Lud06
|
||||
import okhttp3.Request
|
||||
@@ -34,7 +35,7 @@ import java.math.RoundingMode
|
||||
import java.net.URLEncoder
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
class LightningAddressResolver() {
|
||||
class LightningAddressResolver {
|
||||
val client = HttpClientManager.getHttpClient()
|
||||
|
||||
fun assembleUrl(lnaddress: String): String? {
|
||||
@@ -63,8 +64,9 @@ class LightningAddressResolver() {
|
||||
|
||||
if (url == null) {
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.could_not_assemble_lnurl_from_lightning_address_check_the_user_s_setup,
|
||||
lnaddress,
|
||||
),
|
||||
@@ -74,7 +76,8 @@ class LightningAddressResolver() {
|
||||
|
||||
try {
|
||||
val request: Request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.url(url)
|
||||
.build()
|
||||
@@ -84,8 +87,9 @@ class LightningAddressResolver() {
|
||||
onSuccess(it.body.string())
|
||||
} else {
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string
|
||||
.the_receiver_s_lightning_service_at_is_not_available_it_was_calculated_from_the_lightning_address_error_check_if_the_server_is_up_and_if_the_lightning_address_is_correct,
|
||||
url,
|
||||
@@ -99,8 +103,9 @@ class LightningAddressResolver() {
|
||||
if (e is CancellationException) throw e
|
||||
e.printStackTrace()
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string
|
||||
.could_not_resolve_check_if_you_are_connected_if_the_server_is_up_and_if_the_lightning_address_is_correct_exception,
|
||||
url,
|
||||
@@ -133,7 +138,8 @@ class LightningAddressResolver() {
|
||||
}
|
||||
|
||||
val request: Request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.url(url)
|
||||
.build()
|
||||
@@ -143,8 +149,8 @@ class LightningAddressResolver() {
|
||||
onSuccess(it.body.string())
|
||||
} else {
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(R.string.could_not_fetch_invoice_from, lnCallback),
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(context, R.string.could_not_fetch_invoice_from, lnCallback),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -173,8 +179,9 @@ class LightningAddressResolver() {
|
||||
} catch (t: Throwable) {
|
||||
if (t is CancellationException) throw t
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.error_parsing_json_from_lightning_address_check_the_user_s_lightning_setup_with_user,
|
||||
lnaddress,
|
||||
),
|
||||
@@ -186,8 +193,9 @@ class LightningAddressResolver() {
|
||||
|
||||
if (callback == null) {
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.callback_url_not_found_in_the_user_s_lightning_address_server_configuration_with_user,
|
||||
lnaddress,
|
||||
),
|
||||
@@ -211,8 +219,9 @@ class LightningAddressResolver() {
|
||||
} catch (t: Throwable) {
|
||||
if (t is CancellationException) throw t
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string
|
||||
.error_parsing_json_from_lightning_address_s_invoice_fetch_check_the_user_s_lightning_setup_with_user,
|
||||
lnaddress,
|
||||
@@ -236,8 +245,9 @@ class LightningAddressResolver() {
|
||||
} else {
|
||||
onProgress(0.0f)
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string.incorrect_invoice_amount_sats_from_it_should_have_been,
|
||||
invoiceAmount.toLong().toString(),
|
||||
lnaddress,
|
||||
@@ -253,8 +263,9 @@ class LightningAddressResolver() {
|
||||
?.let { reason ->
|
||||
onProgress(0.0f)
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string
|
||||
.unable_to_create_a_lightning_invoice_before_sending_the_zap_the_receiver_s_lightning_wallet_sent_the_following_error_with_user,
|
||||
lnaddress,
|
||||
@@ -265,8 +276,9 @@ class LightningAddressResolver() {
|
||||
?: run {
|
||||
onProgress(0.0f)
|
||||
onError(
|
||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||
context.getString(
|
||||
stringRes(context, R.string.error_unable_to_fetch_invoice),
|
||||
stringRes(
|
||||
context,
|
||||
R.string
|
||||
.unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json_with_user,
|
||||
lnaddress,
|
||||
|
||||
+18
-12
@@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendDMNotification
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendZapNotification
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
@@ -44,7 +45,9 @@ import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
class EventNotificationConsumer(
|
||||
private val applicationContext: Context,
|
||||
) {
|
||||
suspend fun consume(event: GiftWrapEvent) {
|
||||
Log.d("EventNotificationConsumer", "New Notification Arrived")
|
||||
if (!LocalCache.justVerify(event)) return
|
||||
@@ -124,7 +127,8 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
acc: Account,
|
||||
) {
|
||||
if (
|
||||
event.createdAt > TimeUtils.fifteenMinutesAgo() && // old event being re-broadcasted
|
||||
event.createdAt > TimeUtils.fifteenMinutesAgo() &&
|
||||
// old event being re-broadcasted
|
||||
event.pubKey != acc.userProfile().pubkeyHex
|
||||
) { // from the user
|
||||
|
||||
@@ -137,7 +141,8 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
(
|
||||
acc.userProfile().privateChatrooms[chatRoom]?.senderIntersects(followingKeySet) == true ||
|
||||
acc.userProfile().hasSentMessagesTo(chatRoom)
|
||||
) && !acc.isAllHidden(chatRoom.users)
|
||||
) &&
|
||||
!acc.isAllHidden(chatRoom.users)
|
||||
|
||||
if (isKnownRoom) {
|
||||
val content = chatNote.event?.content() ?: ""
|
||||
@@ -178,9 +183,9 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
(
|
||||
acc.userProfile().privateChatrooms[it]?.senderIntersects(followingKeySet) == true ||
|
||||
acc.userProfile().hasSentMessagesTo(it)
|
||||
) && !acc.isAllHidden(it.users)
|
||||
}
|
||||
.toSet()
|
||||
) &&
|
||||
!acc.isAllHidden(it.users)
|
||||
}.toSet()
|
||||
|
||||
note.author?.let {
|
||||
if (ChatroomKey(persistentSetOf(it.pubkeyHex)) in knownChatrooms) {
|
||||
@@ -223,17 +228,19 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
|
||||
val user = senderInfo.first.toBestDisplayName()
|
||||
var title =
|
||||
applicationContext.getString(R.string.app_notification_zaps_channel_message, amount)
|
||||
stringRes(applicationContext, R.string.app_notification_zaps_channel_message, amount)
|
||||
senderInfo.second?.ifBlank { null }?.let { title += " ($it)" }
|
||||
var content =
|
||||
applicationContext.getString(
|
||||
stringRes(
|
||||
applicationContext,
|
||||
R.string.app_notification_zaps_channel_message_from,
|
||||
user,
|
||||
)
|
||||
zappedContent?.let {
|
||||
content +=
|
||||
" " +
|
||||
applicationContext.getString(
|
||||
stringRes(
|
||||
applicationContext,
|
||||
R.string.app_notification_zaps_channel_message_for,
|
||||
zappedContent,
|
||||
)
|
||||
@@ -255,8 +262,7 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun notificationManager(): NotificationManager {
|
||||
return ContextCompat.getSystemService(applicationContext, NotificationManager::class.java)
|
||||
fun notificationManager(): NotificationManager =
|
||||
ContextCompat.getSystemService(applicationContext, NotificationManager::class.java)
|
||||
as NotificationManager
|
||||
}
|
||||
}
|
||||
|
||||
+26
-27
@@ -34,6 +34,7 @@ import coil.executeBlocking
|
||||
import coil.request.ImageRequest
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.MainActivity
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
object NotificationUtils {
|
||||
private var dmChannel: NotificationChannel? = null
|
||||
@@ -46,14 +47,13 @@ object NotificationUtils {
|
||||
|
||||
dmChannel =
|
||||
NotificationChannel(
|
||||
applicationContext.getString(R.string.app_notification_dms_channel_id),
|
||||
applicationContext.getString(R.string.app_notification_dms_channel_name),
|
||||
stringRes(applicationContext, R.string.app_notification_dms_channel_id),
|
||||
stringRes(applicationContext, R.string.app_notification_dms_channel_name),
|
||||
NotificationManager.IMPORTANCE_DEFAULT,
|
||||
)
|
||||
.apply {
|
||||
description =
|
||||
applicationContext.getString(R.string.app_notification_dms_channel_description)
|
||||
}
|
||||
).apply {
|
||||
description =
|
||||
stringRes(applicationContext, R.string.app_notification_dms_channel_description)
|
||||
}
|
||||
|
||||
// Register the channel with the system
|
||||
val notificationManager: NotificationManager =
|
||||
@@ -69,14 +69,13 @@ object NotificationUtils {
|
||||
|
||||
zapChannel =
|
||||
NotificationChannel(
|
||||
applicationContext.getString(R.string.app_notification_zaps_channel_id),
|
||||
applicationContext.getString(R.string.app_notification_zaps_channel_name),
|
||||
stringRes(applicationContext, R.string.app_notification_zaps_channel_id),
|
||||
stringRes(applicationContext, R.string.app_notification_zaps_channel_name),
|
||||
NotificationManager.IMPORTANCE_DEFAULT,
|
||||
)
|
||||
.apply {
|
||||
description =
|
||||
applicationContext.getString(R.string.app_notification_zaps_channel_description)
|
||||
}
|
||||
).apply {
|
||||
description =
|
||||
stringRes(applicationContext, R.string.app_notification_zaps_channel_description)
|
||||
}
|
||||
|
||||
// Register the channel with the system
|
||||
val notificationManager: NotificationManager =
|
||||
@@ -96,7 +95,7 @@ object NotificationUtils {
|
||||
applicationContext: Context,
|
||||
) {
|
||||
val zapChannel = getOrCreateZapChannel(applicationContext)
|
||||
val channelId = applicationContext.getString(R.string.app_notification_zaps_channel_id)
|
||||
val channelId = stringRes(applicationContext, R.string.app_notification_zaps_channel_id)
|
||||
|
||||
sendNotification(
|
||||
id,
|
||||
@@ -119,7 +118,7 @@ object NotificationUtils {
|
||||
applicationContext: Context,
|
||||
) {
|
||||
val dmChannel = getOrCreateDMChannel(applicationContext)
|
||||
val channelId = applicationContext.getString(R.string.app_notification_dms_channel_id)
|
||||
val channelId = stringRes(applicationContext, R.string.app_notification_dms_channel_id)
|
||||
|
||||
sendNotification(
|
||||
id,
|
||||
@@ -205,13 +204,13 @@ object NotificationUtils {
|
||||
|
||||
// Build the notification
|
||||
val builderPublic =
|
||||
NotificationCompat.Builder(
|
||||
applicationContext,
|
||||
channelId,
|
||||
)
|
||||
.setSmallIcon(R.drawable.amethyst)
|
||||
NotificationCompat
|
||||
.Builder(
|
||||
applicationContext,
|
||||
channelId,
|
||||
).setSmallIcon(R.drawable.amethyst)
|
||||
.setContentTitle(messageTitle)
|
||||
.setContentText(applicationContext.getString(R.string.app_notification_private_message))
|
||||
.setContentText(stringRes(applicationContext, R.string.app_notification_private_message))
|
||||
.setLargeIcon(picture?.bitmap)
|
||||
// .setGroup(messageTitle)
|
||||
// .setGroup(notificationGroupKey) //-> Might need a Group summary as well before we
|
||||
@@ -222,11 +221,11 @@ object NotificationUtils {
|
||||
|
||||
// Build the notification
|
||||
val builder =
|
||||
NotificationCompat.Builder(
|
||||
applicationContext,
|
||||
channelId,
|
||||
)
|
||||
.setSmallIcon(R.drawable.amethyst)
|
||||
NotificationCompat
|
||||
.Builder(
|
||||
applicationContext,
|
||||
channelId,
|
||||
).setSmallIcon(R.drawable.amethyst)
|
||||
.setContentTitle(messageTitle)
|
||||
.setContentText(messageBody)
|
||||
.setLargeIcon(picture?.bitmap)
|
||||
|
||||
@@ -30,14 +30,15 @@ import com.vitorpamplona.quartz.ots.exceptions.CommitmentNotFoundException
|
||||
import com.vitorpamplona.quartz.ots.exceptions.DeserializationException
|
||||
import com.vitorpamplona.quartz.ots.exceptions.ExceededSizeException
|
||||
import com.vitorpamplona.quartz.ots.exceptions.UrlException
|
||||
import com.vitorpamplona.quartz.ots.http.Request
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
|
||||
/**
|
||||
* Class representing remote calendar server interface.
|
||||
*/
|
||||
class OkHttpCalendar(val url: String) : ICalendar {
|
||||
class OkHttpCalendar(
|
||||
val url: String,
|
||||
) : ICalendar {
|
||||
/**
|
||||
* Submitting a digest to remote calendar. Returns a com.eternitywall.ots.Timestamp committing to that digest.
|
||||
*
|
||||
@@ -57,7 +58,8 @@ class OkHttpCalendar(val url: String) : ICalendar {
|
||||
val requestBody = digest.toRequestBody(mediaType)
|
||||
|
||||
val request =
|
||||
okhttp3.Request.Builder()
|
||||
okhttp3.Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.header("Accept", "application/vnd.opentimestamps.v1")
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
@@ -104,7 +106,8 @@ class OkHttpCalendar(val url: String) : ICalendar {
|
||||
val url = url + "/timestamp/" + Hex.encode(commitment)
|
||||
|
||||
val request =
|
||||
okhttp3.Request.Builder()
|
||||
okhttp3.Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.header("Accept", "application/vnd.opentimestamps.v1")
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
@@ -87,9 +87,7 @@ class Relay(
|
||||
listeners = listeners.minus(listener)
|
||||
}
|
||||
|
||||
fun isConnected(): Boolean {
|
||||
return socket != null
|
||||
}
|
||||
fun isConnected(): Boolean = socket != null
|
||||
|
||||
fun connect() {
|
||||
connectAndRun {
|
||||
@@ -123,7 +121,8 @@ class Relay(
|
||||
lastConnectTentative = TimeUtils.now()
|
||||
|
||||
val request =
|
||||
Request.Builder()
|
||||
Request
|
||||
.Builder()
|
||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||
.url(url.trim())
|
||||
.build()
|
||||
@@ -141,7 +140,9 @@ class Relay(
|
||||
}
|
||||
}
|
||||
|
||||
inner class RelayListener(val onConnected: (Relay) -> Unit) : WebSocketListener() {
|
||||
inner class RelayListener(
|
||||
val onConnected: (Relay) -> Unit,
|
||||
) : WebSocketListener() {
|
||||
override fun onOpen(
|
||||
webSocket: WebSocket,
|
||||
response: Response,
|
||||
@@ -524,12 +525,11 @@ class Relay(
|
||||
writeToSocket("""["CLOSE","$subscriptionId"]""")
|
||||
}
|
||||
|
||||
fun isSameRelayConfig(other: RelaySetupInfo): Boolean {
|
||||
return url == other.url &&
|
||||
fun isSameRelayConfig(other: RelaySetupInfo): Boolean =
|
||||
url == other.url &&
|
||||
write == other.write &&
|
||||
read == other.read &&
|
||||
activeTypes == other.feedTypes
|
||||
}
|
||||
|
||||
enum class StateType {
|
||||
// Websocket connected
|
||||
|
||||
@@ -90,6 +90,11 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
val locales = this.applicationContext.resources.configuration.locales
|
||||
if (!locales.isEmpty) {
|
||||
checkLanguage(locales.get(0).language)
|
||||
}
|
||||
|
||||
Log.d("Lifetime Event", "MainActivity.onResume")
|
||||
|
||||
// starts muted every time
|
||||
@@ -263,8 +268,8 @@ class GetMediaActivityResultContract : ActivityResultContracts.GetContent() {
|
||||
}
|
||||
}
|
||||
|
||||
fun uriToRoute(uri: String?): String? {
|
||||
return if (uri.equals("nostr:Notifications", true)) {
|
||||
fun uriToRoute(uri: String?): String? =
|
||||
if (uri.equals("nostr:Notifications", true)) {
|
||||
Route.Notification.route.replace("{scrollToTop}", "true")
|
||||
} else {
|
||||
if (uri?.startsWith("nostr:Hashtag?id=") == true) {
|
||||
@@ -317,4 +322,3 @@ fun uriToRoute(uri: String?): String? {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.util.LruCache
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
||||
/**
|
||||
* Cache for stringResource because it seems to be > 1ms function in some phones
|
||||
*/
|
||||
private val resourceCache = LruCache<Int, String>(300)
|
||||
private var resourceCacheLanguage: String? = null
|
||||
|
||||
fun checkLanguage(currentLanguage: String) {
|
||||
if (resourceCacheLanguage == null) {
|
||||
resourceCacheLanguage = currentLanguage
|
||||
} else {
|
||||
if (resourceCacheLanguage != currentLanguage) {
|
||||
resourceCacheLanguage = currentLanguage
|
||||
resourceCache.evictAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun stringRes(id: Int): String = resourceCache.get(id) ?: stringResource(id).also { resourceCache.put(id, it) }
|
||||
|
||||
@Composable
|
||||
fun stringRes(
|
||||
id: Int,
|
||||
vararg args: Any,
|
||||
): String =
|
||||
String
|
||||
.format(
|
||||
LocalConfiguration.current.locales.get(0),
|
||||
resourceCache.get(id) ?: stringResource(id),
|
||||
*args,
|
||||
).also { resourceCache.put(id, it) }
|
||||
|
||||
fun stringRes(
|
||||
ctx: Context,
|
||||
id: Int,
|
||||
): String = resourceCache.get(id) ?: ctx.getString(id).also { resourceCache.put(id, it) }
|
||||
|
||||
fun stringRes(
|
||||
ctx: Context,
|
||||
id: Int,
|
||||
vararg args: Any?,
|
||||
): String {
|
||||
val res = ctx.resources
|
||||
|
||||
return String
|
||||
.format(
|
||||
res.configuration.locales.get(0),
|
||||
resourceCache.get(id) ?: res.getString(id),
|
||||
*args,
|
||||
).also { resourceCache.put(id, it) }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.actions
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Composable
|
||||
fun <T> CrossfadeIfEnabled(
|
||||
targetState: T,
|
||||
modifier: Modifier = Modifier,
|
||||
animationSpec: FiniteAnimationSpec<Float> = tween(),
|
||||
label: String = "Crossfade",
|
||||
accountViewModel: AccountViewModel,
|
||||
content: @Composable (T) -> Unit,
|
||||
) {
|
||||
if (accountViewModel.settings.featureSet == FeatureSetType.PERFORMANCE) {
|
||||
Box(modifier) {
|
||||
content(targetState)
|
||||
}
|
||||
} else {
|
||||
Crossfade(targetState, modifier, animationSpec, label, content)
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,6 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
@@ -98,6 +97,7 @@ import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.UserLine
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||
@@ -189,7 +189,7 @@ fun EditPostView(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.relays),
|
||||
contentDescription = stringResource(id = R.string.relay_list_selector),
|
||||
contentDescription = stringRes(id = R.string.relay_list_selector),
|
||||
modifier = Modifier.height(25.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@@ -236,8 +236,7 @@ fun EditPostView(
|
||||
top = pad.calculateTopPadding(),
|
||||
end = Size10dp,
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
)
|
||||
.fillMaxSize(),
|
||||
).fillMaxSize(),
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
@@ -365,8 +364,8 @@ fun EditPostView(
|
||||
lud16,
|
||||
user.pubkeyHex,
|
||||
accountViewModel.account,
|
||||
stringResource(id = R.string.lightning_invoice),
|
||||
stringResource(id = R.string.lightning_create_and_add_invoice),
|
||||
stringRes(id = R.string.lightning_invoice),
|
||||
stringRes(id = R.string.lightning_create_and_add_invoice),
|
||||
onSuccess = {
|
||||
postViewModel.message =
|
||||
TextFieldValue(postViewModel.message.text + "\n\n" + it)
|
||||
@@ -386,7 +385,7 @@ fun EditPostView(
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.message_to_author),
|
||||
text = stringRes(R.string.message_to_author),
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -399,7 +398,7 @@ fun EditPostView(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.message_to_author_placeholder),
|
||||
text = stringRes(R.string.message_to_author_placeholder),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -514,8 +513,7 @@ private fun MessageField(postViewModel: EditPostViewModel) {
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
)
|
||||
.focusRequester(focusRequester)
|
||||
).focusRequester(focusRequester)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
keyboardController?.show()
|
||||
@@ -523,7 +521,7 @@ private fun MessageField(postViewModel: EditPostViewModel) {
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.what_s_on_your_mind),
|
||||
text = stringRes(R.string.what_s_on_your_mind),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -548,14 +546,14 @@ private fun AddLnInvoiceButton(
|
||||
if (!isLnInvoiceActive) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CurrencyBitcoin,
|
||||
contentDescription = stringResource(id = R.string.add_bitcoin_invoice),
|
||||
contentDescription = stringRes(id = R.string.add_bitcoin_invoice),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CurrencyBitcoin,
|
||||
contentDescription = stringResource(id = R.string.cancel_bitcoin_invoice),
|
||||
contentDescription = stringRes(id = R.string.cancel_bitcoin_invoice),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = BitcoinOrange,
|
||||
)
|
||||
|
||||
@@ -34,8 +34,8 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size16dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
|
||||
@@ -64,7 +64,7 @@ fun InformationDialog(
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(StdHorzSpacer)
|
||||
Text(stringResource(R.string.error_dialog_button_ok))
|
||||
Text(stringRes(R.string.error_dialog_button_ok))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -59,10 +59,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -73,6 +71,7 @@ import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
||||
@@ -83,6 +82,7 @@ import com.vitorpamplona.amethyst.ui.note.SearchIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchBarViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
@@ -161,7 +161,7 @@ fun JoinUserOrChannelView(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.channel_list_join_conversation),
|
||||
text = stringRes(R.string.channel_list_join_conversation),
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
|
||||
@@ -278,13 +278,13 @@ private fun SearchEditTextForJoin(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.channel_list_user_or_group_id)) },
|
||||
label = { Text(text = stringRes(R.string.channel_list_user_or_group_id)) },
|
||||
value = searchBarViewModel.searchValue,
|
||||
onValueChange = {
|
||||
searchBarViewModel.updateSearchValue(it)
|
||||
scope.launch(Dispatchers.IO) { searchTextChanges.trySend(it) }
|
||||
},
|
||||
leadingIcon = { SearchIcon(modifier = Size20Modifier, Color.Unspecified) },
|
||||
leadingIcon = { SearchIcon(modifier = Size20Modifier, MaterialTheme.colorScheme.placeholderText) },
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f, true)
|
||||
@@ -297,7 +297,7 @@ private fun SearchEditTextForJoin(
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.channel_list_user_or_group_id_demo),
|
||||
text = stringRes(R.string.channel_list_user_or_group_id_demo),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -311,7 +311,7 @@ private fun SearchEditTextForJoin(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Clear,
|
||||
contentDescription = stringResource(R.string.clear),
|
||||
contentDescription = stringRes(R.string.clear),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -331,11 +331,6 @@ private fun RenderSearchResults(
|
||||
val users by searchBarViewModel.searchResultsUsers.collectAsStateWithLifecycle()
|
||||
val channels by searchBarViewModel.searchResultsChannels.collectAsStateWithLifecycle()
|
||||
|
||||
val automaticallyShowProfilePicture =
|
||||
remember {
|
||||
accountViewModel.settings.showProfilePictures.value
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
@@ -367,7 +362,11 @@ private fun RenderSearchResults(
|
||||
channels,
|
||||
key = { _, item -> "c" + item.idHex },
|
||||
) { _, item ->
|
||||
RenderChannel(item, automaticallyShowProfilePicture) {
|
||||
RenderChannel(
|
||||
item,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
) {
|
||||
nav("Channel/${item.idHex}")
|
||||
searchBarViewModel.clear()
|
||||
}
|
||||
@@ -385,6 +384,7 @@ private fun RenderSearchResults(
|
||||
private fun RenderChannel(
|
||||
item: com.vitorpamplona.amethyst.model.Channel,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val hasNewMessages = remember { mutableStateOf(false) }
|
||||
@@ -403,6 +403,7 @@ private fun RenderChannel(
|
||||
hasNewMessages,
|
||||
onClick = onClick,
|
||||
loadProfilePicture = loadProfilePicture,
|
||||
loadRobohash = loadRobohash,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -414,14 +415,15 @@ fun UserComposeForChat(
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.clickable(
|
||||
onClick = onClick,
|
||||
).padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = 10.dp,
|
||||
bottom = 10.dp,
|
||||
),
|
||||
Modifier
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
).padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = 10.dp,
|
||||
bottom = 10.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ClickableUserPicture(baseUser, Size55dp, accountViewModel)
|
||||
@@ -432,7 +434,7 @@ fun UserComposeForChat(
|
||||
.padding(start = 10.dp)
|
||||
.weight(1f),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) { UsernameDisplay(baseUser) }
|
||||
Row(verticalAlignment = Alignment.CenterVertically) { UsernameDisplay(baseUser, accountViewModel = accountViewModel) }
|
||||
|
||||
DisplayUserAboutInfo(baseUser)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -48,6 +47,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
@Composable
|
||||
@@ -87,20 +87,21 @@ fun NewChannelView(
|
||||
postViewModel.create()
|
||||
onClose()
|
||||
},
|
||||
postViewModel.channelName.value.text.isNotBlank(),
|
||||
postViewModel.channelName.value.text
|
||||
.isNotBlank(),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.channel_name)) },
|
||||
label = { Text(text = stringRes(R.string.channel_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.channelName.value,
|
||||
onValueChange = { postViewModel.channelName.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.my_awesome_group),
|
||||
text = stringRes(R.string.my_awesome_group),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -114,7 +115,7 @@ fun NewChannelView(
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.picture_url)) },
|
||||
label = { Text(text = stringRes(R.string.picture_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.channelPicture.value,
|
||||
onValueChange = { postViewModel.channelPicture.value = it },
|
||||
@@ -129,13 +130,13 @@ fun NewChannelView(
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.description)) },
|
||||
label = { Text(text = stringRes(R.string.description)) },
|
||||
modifier = Modifier.fillMaxWidth().height(100.dp),
|
||||
value = postViewModel.channelDescription.value,
|
||||
onValueChange = { postViewModel.channelDescription.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.about_us),
|
||||
text = stringRes(R.string.about_us),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.vitorpamplona.amethyst.service.FileHeader
|
||||
import com.vitorpamplona.amethyst.service.Nip96MediaServers
|
||||
import com.vitorpamplona.amethyst.service.Nip96Uploader
|
||||
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -114,7 +115,7 @@ open class NewMediaModel : ViewModel() {
|
||||
}
|
||||
?: run {
|
||||
viewModelScope.launch {
|
||||
onError(context.getString(R.string.could_not_open_the_compressed_file))
|
||||
onError(stringRes(context, R.string.could_not_open_the_compressed_file))
|
||||
isUploadingImage = false
|
||||
uploadingPercentage.value = 0.00f
|
||||
uploadingDescription.value = null
|
||||
@@ -153,7 +154,7 @@ open class NewMediaModel : ViewModel() {
|
||||
isUploadingImage = false
|
||||
uploadingPercentage.value = 0.00f
|
||||
uploadingDescription.value = null
|
||||
onError(context.getString(R.string.failed_to_upload_media, e.message))
|
||||
onError(stringRes(context, R.string.failed_to_upload_media, e.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +163,7 @@ open class NewMediaModel : ViewModel() {
|
||||
isUploadingImage = false
|
||||
uploadingPercentage.value = 0.00f
|
||||
uploadingDescription.value = null
|
||||
onError(context.getString(R.string.error_when_compressing_media, it))
|
||||
onError(stringRes(context, R.string.error_when_compressing_media, it))
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -179,9 +180,7 @@ open class NewMediaModel : ViewModel() {
|
||||
selectedServer = ServerOption(defaultServer(), false)
|
||||
}
|
||||
|
||||
fun canPost(): Boolean {
|
||||
return !isUploadingImage && galleryUri != null && selectedServer != null
|
||||
}
|
||||
fun canPost(): Boolean = !isUploadingImage && galleryUri != null && selectedServer != null
|
||||
|
||||
suspend fun createNIP94Record(
|
||||
uploadingResult: Nip96Uploader.PartialEvent,
|
||||
@@ -197,11 +196,20 @@ open class NewMediaModel : ViewModel() {
|
||||
|
||||
val imageUrl = uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "url" }?.get(1)
|
||||
val remoteMimeType =
|
||||
uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "m" }?.get(1)?.ifBlank { null }
|
||||
uploadingResult.tags
|
||||
?.firstOrNull { it.size > 1 && it[0] == "m" }
|
||||
?.get(1)
|
||||
?.ifBlank { null }
|
||||
val originalHash =
|
||||
uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "ox" }?.get(1)?.ifBlank { null }
|
||||
uploadingResult.tags
|
||||
?.firstOrNull { it.size > 1 && it[0] == "ox" }
|
||||
?.get(1)
|
||||
?.ifBlank { null }
|
||||
val dim =
|
||||
uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "dim" }?.get(1)?.ifBlank { null }
|
||||
uploadingResult.tags
|
||||
?.firstOrNull { it.size > 1 && it[0] == "dim" }
|
||||
?.get(1)
|
||||
?.ifBlank { null }
|
||||
val magnet =
|
||||
uploadingResult.tags
|
||||
?.firstOrNull { it.size > 1 && it[0] == "magnet" }
|
||||
@@ -214,7 +222,7 @@ open class NewMediaModel : ViewModel() {
|
||||
uploadingPercentage.value = 0.00f
|
||||
uploadingDescription.value = null
|
||||
isUploadingImage = false
|
||||
onError(context.getString(R.string.server_did_not_provide_a_url_after_uploading))
|
||||
onError(stringRes(context, R.string.server_did_not_provide_a_url_after_uploading))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -254,7 +262,7 @@ open class NewMediaModel : ViewModel() {
|
||||
uploadingPercentage.value = 0.00f
|
||||
uploadingDescription.value = null
|
||||
isUploadingImage = false
|
||||
onError(context.getString(R.string.could_not_prepare_local_file_to_upload, it))
|
||||
onError(stringRes(context, R.string.could_not_prepare_local_file_to_upload, it))
|
||||
},
|
||||
)
|
||||
} else {
|
||||
@@ -263,7 +271,7 @@ open class NewMediaModel : ViewModel() {
|
||||
uploadingPercentage.value = 0.00f
|
||||
uploadingDescription.value = null
|
||||
isUploadingImage = false
|
||||
onError(context.getString(R.string.could_not_download_from_the_server))
|
||||
onError(stringRes(context, R.string.could_not_download_from_the_server))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +320,7 @@ open class NewMediaModel : ViewModel() {
|
||||
uploadingPercentage.value = 0.00f
|
||||
isUploadingImage = false
|
||||
cancel()
|
||||
onError(context.getString(R.string.could_not_prepare_local_file_to_upload, it))
|
||||
onError(stringRes(context, R.string.could_not_prepare_local_file_to_upload, it))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
@@ -70,6 +69,7 @@ import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -93,7 +93,7 @@ fun NewMediaView(
|
||||
LaunchedEffect(uri) {
|
||||
val mediaType = resolver.getType(uri) ?: ""
|
||||
postViewModel.load(account, uri, mediaType) {
|
||||
accountViewModel.toast(context.getString(R.string.failed_to_upload_media_no_details), it)
|
||||
accountViewModel.toast(stringRes(context, R.string.failed_to_upload_media_no_details), it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,8 @@ fun NewMediaView(
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp)
|
||||
Modifier
|
||||
.padding(start = 10.dp, end = 10.dp, top = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.imePadding(),
|
||||
@@ -194,7 +195,7 @@ fun ImageVideoPost(
|
||||
ServerOption(
|
||||
Nip96MediaServers.ServerName(
|
||||
"NIP95",
|
||||
stringResource(id = R.string.upload_server_relays_nip95),
|
||||
stringRes(id = R.string.upload_server_relays_nip95),
|
||||
),
|
||||
true,
|
||||
),
|
||||
@@ -209,7 +210,8 @@ fun ImageVideoPost(
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 10.dp)
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
) {
|
||||
@@ -219,7 +221,8 @@ fun ImageVideoPost(
|
||||
contentDescription = postViewModel.galleryUri.toString(),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier =
|
||||
Modifier.padding(top = 4.dp)
|
||||
Modifier
|
||||
.padding(top = 4.dp)
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
)
|
||||
@@ -265,7 +268,7 @@ fun ImageVideoPost(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
TextSpinner(
|
||||
label = stringResource(id = R.string.file_server),
|
||||
label = stringRes(id = R.string.file_server),
|
||||
placeholder =
|
||||
fileServers
|
||||
.firstOrNull { it.server == accountViewModel.account.defaultFileServer }
|
||||
@@ -296,13 +299,13 @@ fun ImageVideoPost(
|
||||
modifier = Modifier.fillMaxWidth().windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.content_description)) },
|
||||
label = { Text(text = stringRes(R.string.content_description)) },
|
||||
modifier = Modifier.fillMaxWidth().windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
value = postViewModel.alt,
|
||||
onValueChange = { postViewModel.alt = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.content_description_example),
|
||||
text = stringRes(R.string.content_description_example),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -36,12 +36,12 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
@@ -87,13 +87,13 @@ fun NewPollClosing(pollViewModel: NewPostViewModel) {
|
||||
colors = if (pollViewModel.isValidClosedAt.value) colorValid else colorInValid,
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_closing_time),
|
||||
text = stringRes(R.string.poll_closing_time),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_closing_time_days),
|
||||
text = stringRes(R.string.poll_closing_time_days),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
+3
-3
@@ -36,12 +36,12 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
@@ -87,13 +87,13 @@ fun NewPollConsensusThreshold(pollViewModel: NewPostViewModel) {
|
||||
colors = if (pollViewModel.isValidConsensusThreshold.value) colorValid else colorInValid,
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_consensus_threshold),
|
||||
text = stringRes(R.string.poll_consensus_threshold),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_consensus_threshold_percent),
|
||||
text = stringRes(R.string.poll_consensus_threshold_percent),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -31,10 +31,10 @@ import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
@Composable
|
||||
@@ -51,7 +51,7 @@ fun NewPollOption(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.clear),
|
||||
contentDescription = stringRes(R.string.clear),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -64,13 +64,13 @@ fun NewPollOption(
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_option_index).format(optionIndex + 1),
|
||||
text = stringRes(R.string.poll_option_index).format(optionIndex + 1),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_option_description),
|
||||
text = stringRes(R.string.poll_option_description),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
+3
-3
@@ -37,13 +37,13 @@ import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
||||
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@@ -74,7 +74,7 @@ fun NewPollPrimaryDescription(pollViewModel: NewPostViewModel) {
|
||||
onValueChange = { pollViewModel.updateMessage(it) },
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_primary_description),
|
||||
text = stringRes(R.string.poll_primary_description),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -90,7 +90,7 @@ fun NewPollPrimaryDescription(pollViewModel: NewPostViewModel) {
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_primary_description),
|
||||
text = stringRes(R.string.poll_primary_description),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -26,10 +26,10 @@ import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
@Composable
|
||||
@@ -51,13 +51,13 @@ fun NewPollRecipientsField(
|
||||
enabled = false,
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_zap_recipients),
|
||||
text = stringRes(R.string.poll_zap_recipients),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_zap_recipients),
|
||||
text = stringRes(R.string.poll_zap_recipients),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -34,11 +34,11 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
|
||||
@@ -67,13 +67,13 @@ fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) {
|
||||
colors = if (pollViewModel.isValidvalueMinimum.value) colorValid else colorInValid,
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_zap_value_min),
|
||||
text = stringRes(R.string.poll_zap_value_min),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.sats),
|
||||
text = stringRes(R.string.sats),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -89,13 +89,13 @@ fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) {
|
||||
colors = if (pollViewModel.isValidvalueMaximum.value) colorValid else colorInValid,
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_zap_value_max),
|
||||
text = stringRes(R.string.poll_zap_value_max),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.sats),
|
||||
text = stringRes(R.string.sats),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -107,7 +107,7 @@ fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) {
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.poll_zap_value_min_max_explainer),
|
||||
text = stringRes(R.string.poll_zap_value_min_max_explainer),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
)
|
||||
|
||||
@@ -105,7 +105,6 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
@@ -150,6 +149,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.MyTextField
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShowUserSuggestionList
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
@@ -276,7 +276,7 @@ fun NewPostView(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.relays),
|
||||
contentDescription = stringResource(id = R.string.relay_list_selector),
|
||||
contentDescription = stringRes(id = R.string.relay_list_selector),
|
||||
modifier = Modifier.height(25.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@@ -324,8 +324,7 @@ fun NewPostView(
|
||||
top = pad.calculateTopPadding(),
|
||||
end = Size10dp,
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
)
|
||||
.fillMaxSize(),
|
||||
).fillMaxSize(),
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
@@ -511,8 +510,8 @@ fun NewPostView(
|
||||
lud16,
|
||||
accountViewModel.account.userProfile().pubkeyHex,
|
||||
accountViewModel.account,
|
||||
stringResource(id = R.string.lightning_invoice),
|
||||
stringResource(id = R.string.lightning_create_and_add_invoice),
|
||||
stringRes(id = R.string.lightning_invoice),
|
||||
stringRes(id = R.string.lightning_create_and_add_invoice),
|
||||
onSuccess = {
|
||||
postViewModel.insertAtCursor(it)
|
||||
postViewModel.wantsInvoice = false
|
||||
@@ -531,7 +530,7 @@ fun NewPostView(
|
||||
modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp),
|
||||
) {
|
||||
ZapRaiserRequest(
|
||||
stringResource(id = R.string.zapraiser),
|
||||
stringRes(id = R.string.zapraiser),
|
||||
postViewModel,
|
||||
)
|
||||
}
|
||||
@@ -575,7 +574,7 @@ private fun BottomRowActions(postViewModel: NewPostViewModel) {
|
||||
|
||||
if (postViewModel.canUsePoll) {
|
||||
// These should be hashtag recommendations the user selects in the future.
|
||||
// val hashtag = stringResource(R.string.poll_hashtag)
|
||||
// val hashtag = stringRes(R.string.poll_hashtag)
|
||||
// postViewModel.includePollHashtagInMessage(postViewModel.wantsPoll, hashtag)
|
||||
AddPollButton(postViewModel.wantsPoll) {
|
||||
postViewModel.wantsPoll = !postViewModel.wantsPoll
|
||||
@@ -676,8 +675,7 @@ private fun MessageField(postViewModel: NewPostViewModel) {
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
)
|
||||
.focusRequester(focusRequester)
|
||||
).focusRequester(focusRequester)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
keyboardController?.show()
|
||||
@@ -687,9 +685,9 @@ private fun MessageField(postViewModel: NewPostViewModel) {
|
||||
Text(
|
||||
text =
|
||||
if (postViewModel.wantsProduct) {
|
||||
stringResource(R.string.description)
|
||||
stringRes(R.string.description)
|
||||
} else {
|
||||
stringResource(R.string.what_s_on_your_mind)
|
||||
stringRes(R.string.what_s_on_your_mind)
|
||||
},
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
@@ -723,7 +721,7 @@ fun ContentSensitivityExplainer(postViewModel: NewPostViewModel) {
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.VisibilityOff,
|
||||
contentDescription = stringResource(id = R.string.content_warning),
|
||||
contentDescription = stringRes(id = R.string.content_warning),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(18.dp)
|
||||
@@ -732,7 +730,7 @@ fun ContentSensitivityExplainer(postViewModel: NewPostViewModel) {
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Warning,
|
||||
contentDescription = stringResource(id = R.string.content_warning),
|
||||
contentDescription = stringRes(id = R.string.content_warning),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(10.dp)
|
||||
@@ -742,7 +740,7 @@ fun ContentSensitivityExplainer(postViewModel: NewPostViewModel) {
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.add_sensitive_content_label),
|
||||
text = stringRes(R.string.add_sensitive_content_label),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
@@ -752,7 +750,7 @@ fun ContentSensitivityExplainer(postViewModel: NewPostViewModel) {
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.add_sensitive_content_explainer),
|
||||
text = stringRes(R.string.add_sensitive_content_explainer),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
)
|
||||
@@ -769,7 +767,7 @@ fun SendDirectMessageTo(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.messages_new_message_to),
|
||||
text = stringRes(R.string.messages_new_message_to),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -780,7 +778,7 @@ fun SendDirectMessageTo(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.messages_new_message_to_caption),
|
||||
text = stringRes(R.string.messages_new_message_to_caption),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -803,7 +801,7 @@ fun SendDirectMessageTo(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.messages_new_message_subject),
|
||||
text = stringRes(R.string.messages_new_message_subject),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -814,7 +812,7 @@ fun SendDirectMessageTo(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.messages_new_message_subject_caption),
|
||||
text = stringRes(R.string.messages_new_message_subject_caption),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -844,7 +842,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.classifieds_title),
|
||||
text = stringRes(R.string.classifieds_title),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -857,7 +855,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.classifieds_title_placeholder),
|
||||
text = stringRes(R.string.classifieds_title_placeholder),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -880,7 +878,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.classifieds_price),
|
||||
text = stringRes(R.string.classifieds_price),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -917,7 +915,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.classifieds_condition),
|
||||
text = stringRes(R.string.classifieds_condition),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -926,23 +924,23 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
listOf(
|
||||
Triple(
|
||||
ClassifiedsEvent.CONDITION.NEW,
|
||||
stringResource(id = R.string.classifieds_condition_new),
|
||||
stringResource(id = R.string.classifieds_condition_new_explainer),
|
||||
stringRes(id = R.string.classifieds_condition_new),
|
||||
stringRes(id = R.string.classifieds_condition_new_explainer),
|
||||
),
|
||||
Triple(
|
||||
ClassifiedsEvent.CONDITION.USED_LIKE_NEW,
|
||||
stringResource(id = R.string.classifieds_condition_like_new),
|
||||
stringResource(id = R.string.classifieds_condition_like_new_explainer),
|
||||
stringRes(id = R.string.classifieds_condition_like_new),
|
||||
stringRes(id = R.string.classifieds_condition_like_new_explainer),
|
||||
),
|
||||
Triple(
|
||||
ClassifiedsEvent.CONDITION.USED_GOOD,
|
||||
stringResource(id = R.string.classifieds_condition_good),
|
||||
stringResource(id = R.string.classifieds_condition_good_explainer),
|
||||
stringRes(id = R.string.classifieds_condition_good),
|
||||
stringRes(id = R.string.classifieds_condition_good_explainer),
|
||||
),
|
||||
Triple(
|
||||
ClassifiedsEvent.CONDITION.USED_FAIR,
|
||||
stringResource(id = R.string.classifieds_condition_fair),
|
||||
stringResource(id = R.string.classifieds_condition_fair_explainer),
|
||||
stringRes(id = R.string.classifieds_condition_fair),
|
||||
stringRes(id = R.string.classifieds_condition_fair_explainer),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -984,7 +982,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.classifieds_category),
|
||||
text = stringRes(R.string.classifieds_category),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -1009,7 +1007,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
R.string.classifieds_category_other,
|
||||
)
|
||||
|
||||
val categoryTypes = categoryList.map { Triple(it, stringResource(id = it), null) }
|
||||
val categoryTypes = categoryList.map { Triple(it, stringRes(id = it), null) }
|
||||
|
||||
val categoryOptions =
|
||||
remember {
|
||||
@@ -1050,7 +1048,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.classifieds_location),
|
||||
text = stringRes(R.string.classifieds_location),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.W500,
|
||||
)
|
||||
@@ -1063,7 +1061,7 @@ fun SellProduct(postViewModel: NewPostViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.classifieds_location_placeholder),
|
||||
text = stringRes(R.string.classifieds_location_placeholder),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -1101,21 +1099,21 @@ fun FowardZapTo(
|
||||
ZapSplitIcon()
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.zap_split_title),
|
||||
text = stringRes(R.string.zap_split_title),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(horizontal = 10.dp).weight(1f),
|
||||
)
|
||||
|
||||
OutlinedButton(onClick = { postViewModel.updateZapFromText() }) {
|
||||
Text(text = stringResource(R.string.load_from_text))
|
||||
Text(text = stringRes(R.string.load_from_text))
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.zap_split_explainer),
|
||||
text = stringRes(R.string.zap_split_explainer),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
)
|
||||
@@ -1130,7 +1128,7 @@ fun FowardZapTo(
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
UsernameDisplay(splitItem.key)
|
||||
UsernameDisplay(splitItem.key, accountViewModel = accountViewModel)
|
||||
Text(
|
||||
text = String.format("%.0f%%", splitItem.percentage * 100),
|
||||
maxLines = 1,
|
||||
@@ -1156,11 +1154,11 @@ fun FowardZapTo(
|
||||
OutlinedTextField(
|
||||
value = postViewModel.forwardZapToEditting,
|
||||
onValueChange = { postViewModel.updateZapForwardTo(it) },
|
||||
label = { Text(text = stringResource(R.string.zap_split_search_and_add_user)) },
|
||||
label = { Text(text = stringRes(R.string.zap_split_search_and_add_user)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.zap_split_search_and_add_user_placeholder),
|
||||
text = stringRes(R.string.zap_split_search_and_add_user_placeholder),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -1207,7 +1205,7 @@ fun LocationAsHash(postViewModel: NewPostViewModel) {
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.geohash_title),
|
||||
text = stringRes(R.string.geohash_title),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
@@ -1219,7 +1217,7 @@ fun LocationAsHash(postViewModel: NewPostViewModel) {
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.geohash_explainer),
|
||||
text = stringRes(R.string.geohash_explainer),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
)
|
||||
@@ -1277,7 +1275,7 @@ fun Notifying(
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(5.dp)) {
|
||||
if (!mentions.isNullOrEmpty()) {
|
||||
Text(
|
||||
stringResource(R.string.reply_notify),
|
||||
stringRes(R.string.reply_notify),
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.align(CenterVertically),
|
||||
@@ -1350,7 +1348,7 @@ private fun AddZapraiserButton(
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Bolt,
|
||||
contentDescription = stringResource(R.string.add_zapraiser),
|
||||
contentDescription = stringRes(R.string.add_zapraiser),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(13.dp)
|
||||
@@ -1369,7 +1367,7 @@ private fun AddZapraiserButton(
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Bolt,
|
||||
contentDescription = stringResource(R.string.cancel_zapraiser),
|
||||
contentDescription = stringRes(R.string.cancel_zapraiser),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(13.dp)
|
||||
@@ -1392,14 +1390,14 @@ fun AddGeoHash(
|
||||
if (!postViewModel.wantsToAddGeoHash) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.LocationOff,
|
||||
contentDescription = stringResource(id = R.string.add_location),
|
||||
contentDescription = stringRes(id = R.string.add_location),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Default.LocationOn,
|
||||
contentDescription = stringResource(id = R.string.remove_location),
|
||||
contentDescription = stringRes(id = R.string.remove_location),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
@@ -1418,14 +1416,14 @@ private fun AddLnInvoiceButton(
|
||||
if (!isLnInvoiceActive) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CurrencyBitcoin,
|
||||
contentDescription = stringResource(id = R.string.add_bitcoin_invoice),
|
||||
contentDescription = stringRes(id = R.string.add_bitcoin_invoice),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CurrencyBitcoin,
|
||||
contentDescription = stringResource(id = R.string.cancel_bitcoin_invoice),
|
||||
contentDescription = stringRes(id = R.string.cancel_bitcoin_invoice),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = BitcoinOrange,
|
||||
)
|
||||
@@ -1460,14 +1458,14 @@ private fun AddClassifiedsButton(
|
||||
if (!postViewModel.wantsProduct) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Sell,
|
||||
contentDescription = stringResource(R.string.classifieds),
|
||||
contentDescription = stringRes(R.string.classifieds),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Sell,
|
||||
contentDescription = stringResource(id = R.string.cancel_classifieds),
|
||||
contentDescription = stringRes(id = R.string.cancel_classifieds),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = BitcoinOrange,
|
||||
)
|
||||
@@ -1491,7 +1489,7 @@ private fun MarkAsSensitive(
|
||||
if (!postViewModel.wantsToMarkAsSensitive) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Visibility,
|
||||
contentDescription = stringResource(R.string.add_content_warning),
|
||||
contentDescription = stringRes(R.string.add_content_warning),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(18.dp)
|
||||
@@ -1500,7 +1498,7 @@ private fun MarkAsSensitive(
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Warning,
|
||||
contentDescription = stringResource(R.string.add_content_warning),
|
||||
contentDescription = stringRes(R.string.add_content_warning),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(10.dp)
|
||||
@@ -1510,7 +1508,7 @@ private fun MarkAsSensitive(
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Default.VisibilityOff,
|
||||
contentDescription = stringResource(id = R.string.remove_content_warning),
|
||||
contentDescription = stringRes(id = R.string.remove_content_warning),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(18.dp)
|
||||
@@ -1519,7 +1517,7 @@ private fun MarkAsSensitive(
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Warning,
|
||||
contentDescription = stringResource(id = R.string.remove_content_warning),
|
||||
contentDescription = stringRes(id = R.string.remove_content_warning),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(10.dp)
|
||||
@@ -1552,7 +1550,7 @@ fun PostButton(
|
||||
enabled = isActive,
|
||||
onClick = onPost,
|
||||
) {
|
||||
Text(text = stringResource(R.string.post))
|
||||
Text(text = stringRes(R.string.post))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1567,7 +1565,7 @@ fun SaveButton(
|
||||
modifier = modifier,
|
||||
onClick = onPost,
|
||||
) {
|
||||
Text(text = stringResource(R.string.save))
|
||||
Text(text = stringRes(R.string.save))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,7 +1588,7 @@ fun CreateButton(
|
||||
containerColor = if (isActive) MaterialTheme.colorScheme.primary else Color.Gray,
|
||||
),
|
||||
) {
|
||||
Text(text = stringResource(R.string.create), color = Color.White)
|
||||
Text(text = stringRes(R.string.create), color = Color.White)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1615,7 +1613,7 @@ fun ImageVideoDescription(
|
||||
ServerOption(
|
||||
Nip96MediaServers.ServerName(
|
||||
"NIP95",
|
||||
stringResource(id = R.string.upload_server_relays_nip95),
|
||||
stringRes(id = R.string.upload_server_relays_nip95),
|
||||
),
|
||||
true,
|
||||
),
|
||||
@@ -1657,7 +1655,7 @@ fun ImageVideoDescription(
|
||||
) {
|
||||
Text(
|
||||
text =
|
||||
stringResource(
|
||||
stringRes(
|
||||
if (isImage) {
|
||||
R.string.content_description_add_image
|
||||
} else {
|
||||
@@ -1747,7 +1745,7 @@ fun ImageVideoDescription(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
TextSpinner(
|
||||
label = stringResource(id = R.string.file_server),
|
||||
label = stringRes(id = R.string.file_server),
|
||||
placeholder =
|
||||
fileServers
|
||||
.firstOrNull { it.server == accountViewModel.account.defaultFileServer }
|
||||
@@ -1787,7 +1785,7 @@ fun ImageVideoDescription(
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.content_description)) },
|
||||
label = { Text(text = stringRes(R.string.content_description)) },
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -1796,7 +1794,7 @@ fun ImageVideoDescription(
|
||||
onValueChange = { message = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.content_description_example),
|
||||
text = stringRes(R.string.content_description_example),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -1819,7 +1817,7 @@ fun ImageVideoDescription(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
) {
|
||||
Text(text = stringResource(R.string.add_content), color = Color.White, fontSize = 20.sp)
|
||||
Text(text = stringRes(R.string.add_content), color = Color.White, fontSize = 20.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1853,12 +1851,12 @@ fun SettingSwitchItem(
|
||||
verticalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
text = stringRes(id = title),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = description),
|
||||
text = stringRes(id = description),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.Gray,
|
||||
maxLines = 2,
|
||||
|
||||
@@ -40,7 +40,6 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
@@ -48,6 +47,7 @@ import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -108,13 +108,13 @@ fun NewUserMetadataView(
|
||||
modifier = Modifier.padding(10.dp).verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.display_name)) },
|
||||
label = { Text(text = stringRes(R.string.display_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.displayName.value,
|
||||
onValueChange = { postViewModel.displayName.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.my_display_name),
|
||||
text = stringRes(R.string.my_display_name),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -128,13 +128,13 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.about_me)) },
|
||||
label = { Text(text = stringRes(R.string.about_me)) },
|
||||
modifier = Modifier.fillMaxWidth().height(100.dp),
|
||||
value = postViewModel.about.value,
|
||||
onValueChange = { postViewModel.about.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.about_me),
|
||||
text = stringRes(id = R.string.about_me),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -148,7 +148,7 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.avatar_url)) },
|
||||
label = { Text(text = stringRes(R.string.avatar_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.picture.value,
|
||||
onValueChange = { postViewModel.picture.value = it },
|
||||
@@ -173,7 +173,7 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.banner_url)) },
|
||||
label = { Text(text = stringRes(R.string.banner_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.banner.value,
|
||||
onValueChange = { postViewModel.banner.value = it },
|
||||
@@ -198,7 +198,7 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.website_url)) },
|
||||
label = { Text(text = stringRes(R.string.website_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.website.value,
|
||||
onValueChange = { postViewModel.website.value = it },
|
||||
@@ -214,7 +214,7 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.nip_05)) },
|
||||
label = { Text(text = stringRes(R.string.nip_05)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.nip05.value,
|
||||
onValueChange = { postViewModel.nip05.value = it },
|
||||
@@ -229,7 +229,7 @@ fun NewUserMetadataView(
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.ln_address)) },
|
||||
label = { Text(text = stringRes(R.string.ln_address)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.lnAddress.value,
|
||||
onValueChange = { postViewModel.lnAddress.value = it },
|
||||
@@ -245,13 +245,13 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.ln_url_outdated)) },
|
||||
label = { Text(text = stringRes(R.string.ln_url_outdated)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.lnURL.value,
|
||||
onValueChange = { postViewModel.lnURL.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.lnurl),
|
||||
text = stringRes(R.string.lnurl),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -260,13 +260,13 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.twitter)) },
|
||||
label = { Text(text = stringRes(R.string.twitter)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.twitter.value,
|
||||
onValueChange = { postViewModel.twitter.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.twitter_proof_url_template),
|
||||
text = stringRes(R.string.twitter_proof_url_template),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -275,13 +275,13 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.mastodon)) },
|
||||
label = { Text(text = stringRes(R.string.mastodon)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.mastodon.value,
|
||||
onValueChange = { postViewModel.mastodon.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.mastodon_proof_url_template),
|
||||
text = stringRes(R.string.mastodon_proof_url_template),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -290,13 +290,13 @@ fun NewUserMetadataView(
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.github)) },
|
||||
label = { Text(text = stringRes(R.string.github)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.github.value,
|
||||
onValueChange = { postViewModel.github.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.github_proof_url_template),
|
||||
text = stringRes(R.string.github_proof_url_template),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -38,10 +38,10 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size16dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.quartz.events.EmptyTagList
|
||||
@@ -89,7 +89,7 @@ fun NotifyRequestDialog(
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(StdHorzSpacer)
|
||||
Text(stringResource(R.string.error_dialog_button_ok))
|
||||
Text(stringRes(R.string.error_dialog_button_ok))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.RelayInformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.quartz.encoders.Nip11RelayInformation
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -141,7 +142,7 @@ fun RelaySelectionDialog(
|
||||
}
|
||||
|
||||
RelaySwitch(
|
||||
text = context.getString(R.string.select_deselect_all),
|
||||
text = stringRes(context, R.string.select_deselect_all),
|
||||
checked = selected,
|
||||
onClick = {
|
||||
selected = !selected
|
||||
@@ -185,25 +186,29 @@ fun RelaySelectionDialog(
|
||||
val msg =
|
||||
when (errorCode) {
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
@@ -211,7 +216,7 @@ fun RelaySelectionDialog(
|
||||
}
|
||||
|
||||
accountViewModel.toast(
|
||||
context.getString(R.string.unable_to_download_relay_document),
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -28,11 +28,11 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
@@ -54,22 +54,22 @@ fun SaveToGallery(url: String) {
|
||||
url = url,
|
||||
onSuccess = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
localContext,
|
||||
localContext.getString(R.string.image_saved_to_the_gallery),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
localContext,
|
||||
stringRes(localContext, R.string.image_saved_to_the_gallery),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
localContext,
|
||||
localContext.getString(R.string.failed_to_save_the_image),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
localContext,
|
||||
stringRes(localContext, R.string.failed_to_save_the_image),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -96,7 +96,7 @@ fun SaveToGallery(url: String) {
|
||||
}
|
||||
},
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.save))
|
||||
Text(text = stringRes(id = R.string.save))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,22 +116,22 @@ fun SaveToGallery(
|
||||
mimeType = mimeType,
|
||||
onSuccess = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
localContext,
|
||||
localContext.getString(R.string.image_saved_to_the_gallery),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
localContext,
|
||||
stringRes(localContext, R.string.image_saved_to_the_gallery),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
localContext,
|
||||
localContext.getString(R.string.failed_to_save_the_image),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
localContext,
|
||||
stringRes(localContext, R.string.failed_to_save_the_image),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -159,6 +159,6 @@ fun SaveToGallery(
|
||||
},
|
||||
shape = ButtonBorder,
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.save))
|
||||
Text(text = stringRes(id = R.string.save))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
@@ -58,6 +57,7 @@ import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.GetMediaActivityResultContract
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@@ -114,7 +114,7 @@ private fun UploadBoxButton(
|
||||
if (!isUploading) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.AddPhotoAlternate,
|
||||
contentDescription = stringResource(id = R.string.upload_image),
|
||||
contentDescription = stringRes(id = R.string.upload_image),
|
||||
modifier = Modifier.height(25.dp),
|
||||
tint = tint,
|
||||
)
|
||||
@@ -137,8 +137,7 @@ val DefaultAnimationColors =
|
||||
Color(0xFFFCAF45),
|
||||
Color(0xFFFFDC80),
|
||||
Color(0xFF5851D8),
|
||||
)
|
||||
.toImmutableList()
|
||||
).toImmutableList()
|
||||
|
||||
@Composable
|
||||
fun LoadingAnimation(
|
||||
@@ -164,7 +163,8 @@ fun LoadingAnimation(
|
||||
|
||||
CircularProgressIndicator(
|
||||
modifier =
|
||||
Modifier.size(size = indicatorSize)
|
||||
Modifier
|
||||
.size(size = indicatorSize)
|
||||
.rotate(degrees = rotateAnimation)
|
||||
.border(
|
||||
width = 4.dp,
|
||||
|
||||
+4
-4
@@ -37,7 +37,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
@@ -46,6 +45,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
@@ -76,7 +76,7 @@ fun AddDMRelayListDialog(
|
||||
) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
|
||||
Text(stringResource(R.string.dm_relays_title))
|
||||
Text(stringRes(R.string.dm_relays_title))
|
||||
|
||||
SaveButton(
|
||||
onPost = {
|
||||
@@ -126,13 +126,13 @@ private fun Explanation() {
|
||||
Card(modifier = MaterialTheme.colorScheme.imageModifier) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.dm_relays_not_found_editing),
|
||||
text = stringRes(id = R.string.dm_relays_not_found_editing),
|
||||
)
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.dm_relays_not_found_examples),
|
||||
text = stringRes(id = R.string.dm_relays_not_found_examples),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -37,7 +37,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
@@ -46,6 +45,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
@@ -76,7 +76,7 @@ fun AddSearchRelayListDialog(
|
||||
) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
|
||||
Text(stringResource(R.string.search_relays_title))
|
||||
Text(stringRes(R.string.search_relays_title))
|
||||
|
||||
SaveButton(
|
||||
onPost = {
|
||||
@@ -126,13 +126,13 @@ private fun Explanation() {
|
||||
Card(modifier = MaterialTheme.colorScheme.imageModifier) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_relays_not_found_editing),
|
||||
text = stringRes(id = R.string.search_relays_not_found_editing),
|
||||
)
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_relays_not_found_examples),
|
||||
text = stringRes(id = R.string.search_relays_not_found_examples),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -41,7 +41,6 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
@@ -53,6 +52,7 @@ import com.vitorpamplona.amethyst.service.relays.RelayStat
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.MinHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
|
||||
@@ -111,7 +111,7 @@ fun AllRelayListView(
|
||||
Spacer(modifier = MinHorzSpacer)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.relay_settings),
|
||||
text = stringRes(R.string.relay_settings),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.widthIn(max = 200.dp),
|
||||
)
|
||||
@@ -168,8 +168,8 @@ fun AllRelayListView(
|
||||
LazyColumn(contentPadding = FeedPadding) {
|
||||
item {
|
||||
SettingsCategory(
|
||||
stringResource(R.string.public_home_section),
|
||||
stringResource(R.string.public_home_section_explainer),
|
||||
stringRes(R.string.public_home_section),
|
||||
stringRes(R.string.public_home_section_explainer),
|
||||
Modifier.padding(bottom = 8.dp),
|
||||
)
|
||||
}
|
||||
@@ -177,32 +177,32 @@ fun AllRelayListView(
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
stringResource(R.string.public_notif_section),
|
||||
stringResource(R.string.public_notif_section_explainer),
|
||||
stringRes(R.string.public_notif_section),
|
||||
stringRes(R.string.public_notif_section_explainer),
|
||||
)
|
||||
}
|
||||
renderNip65NotifItems(notifFeedState, nip65ViewModel, accountViewModel, onClose, nav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
stringResource(R.string.private_inbox_section),
|
||||
stringResource(R.string.private_inbox_section_explainer),
|
||||
stringRes(R.string.private_inbox_section),
|
||||
stringRes(R.string.private_inbox_section_explainer),
|
||||
)
|
||||
}
|
||||
renderDMItems(dmFeedState, dmViewModel, accountViewModel, onClose, nav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
stringResource(R.string.private_outbox_section),
|
||||
stringResource(R.string.private_outbox_section_explainer),
|
||||
stringRes(R.string.private_outbox_section),
|
||||
stringRes(R.string.private_outbox_section_explainer),
|
||||
)
|
||||
}
|
||||
renderPrivateOutboxItems(privateOutboxFeedState, privateOutboxViewModel, accountViewModel, onClose, nav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
stringResource(R.string.search_section),
|
||||
stringResource(R.string.search_section_explainer),
|
||||
stringRes(R.string.search_section),
|
||||
stringRes(R.string.search_section_explainer),
|
||||
action = {
|
||||
ResetSearchRelays(searchViewModel)
|
||||
},
|
||||
@@ -212,16 +212,16 @@ fun AllRelayListView(
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
stringResource(R.string.local_section),
|
||||
stringResource(R.string.local_section_explainer),
|
||||
stringRes(R.string.local_section),
|
||||
stringRes(R.string.local_section_explainer),
|
||||
)
|
||||
}
|
||||
renderLocalItems(localFeedState, localViewModel, accountViewModel, onClose, nav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
stringResource(R.string.kind_3_section),
|
||||
stringResource(R.string.kind_3_section_description),
|
||||
stringRes(R.string.kind_3_section),
|
||||
stringRes(R.string.kind_3_section_description),
|
||||
action = {
|
||||
ResetKind3Relays(kind3ViewModel)
|
||||
},
|
||||
@@ -243,7 +243,7 @@ fun ResetKind3Relays(postViewModel: Kind3RelayListViewModel) {
|
||||
postViewModel.loadRelayDocuments()
|
||||
},
|
||||
) {
|
||||
Text(stringResource(R.string.default_relays))
|
||||
Text(stringRes(R.string.default_relays))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ fun ResetSearchRelays(postViewModel: SearchRelayListViewModel) {
|
||||
postViewModel.loadRelayDocuments()
|
||||
},
|
||||
) {
|
||||
Text(stringResource(R.string.default_relays))
|
||||
Text(stringRes(R.string.default_relays))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.ui.theme.largeRelayIconModifier
|
||||
fun BasicRelaySetupInfoClickableRow(
|
||||
item: BasicRelaySetupInfo,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
onDelete: (BasicRelaySetupInfo) -> Unit,
|
||||
onClick: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
@@ -65,6 +66,7 @@ fun BasicRelaySetupInfoClickableRow(
|
||||
item.briefInfo.displayUrl,
|
||||
iconUrlFromRelayInfoDoc ?: item.briefInfo.favIcon,
|
||||
loadProfilePicture,
|
||||
loadRobohash,
|
||||
MaterialTheme.colorScheme.largeRelayIconModifier,
|
||||
)
|
||||
}
|
||||
|
||||
+13
-11
@@ -27,10 +27,12 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
@Composable
|
||||
fun BasicRelaySetupInfoDialog(
|
||||
@@ -52,14 +54,10 @@ fun BasicRelaySetupInfoDialog(
|
||||
)
|
||||
}
|
||||
|
||||
val automaticallyShowProfilePicture =
|
||||
remember {
|
||||
accountViewModel.settings.showProfilePictures.value
|
||||
}
|
||||
|
||||
BasicRelaySetupInfoClickableRow(
|
||||
item = item,
|
||||
loadProfilePicture = automaticallyShowProfilePicture,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
onDelete = onDelete,
|
||||
accountViewModel = accountViewModel,
|
||||
onClick = {
|
||||
@@ -72,28 +70,32 @@ fun BasicRelaySetupInfoDialog(
|
||||
val msg =
|
||||
when (errorCode) {
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
@@ -101,7 +103,7 @@ fun BasicRelaySetupInfoDialog(
|
||||
}
|
||||
|
||||
accountViewModel.toast(
|
||||
context.getString(R.string.unable_to_download_relay_document),
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
+94
-82
@@ -61,12 +61,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
|
||||
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
@@ -76,6 +76,7 @@ import com.vitorpamplona.amethyst.service.relays.RelayStat
|
||||
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
@@ -146,6 +147,7 @@ fun LazyListScope.renderKind3Items(
|
||||
fun ServerConfigPreview() {
|
||||
ClickableRelayItem(
|
||||
loadProfilePicture = true,
|
||||
loadRobohash = false,
|
||||
item =
|
||||
Kind3BasicRelaySetupInfo(
|
||||
url = "nostr.mom",
|
||||
@@ -200,14 +202,10 @@ fun LoadRelayInfo(
|
||||
)
|
||||
}
|
||||
|
||||
val automaticallyShowProfilePicture =
|
||||
remember {
|
||||
accountViewModel.settings.showProfilePictures.value
|
||||
}
|
||||
|
||||
ClickableRelayItem(
|
||||
item = item,
|
||||
loadProfilePicture = automaticallyShowProfilePicture,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
onToggleDownload = onToggleDownload,
|
||||
onToggleUpload = onToggleUpload,
|
||||
onToggleFollows = onToggleFollows,
|
||||
@@ -226,28 +224,32 @@ fun LoadRelayInfo(
|
||||
val msg =
|
||||
when (errorCode) {
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
)
|
||||
|
||||
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS ->
|
||||
context.getString(
|
||||
stringRes(
|
||||
context,
|
||||
R.string.relay_information_document_error_assemble_url,
|
||||
url,
|
||||
exceptionMessage,
|
||||
@@ -255,7 +257,7 @@ fun LoadRelayInfo(
|
||||
}
|
||||
|
||||
accountViewModel.toast(
|
||||
context.getString(R.string.unable_to_download_relay_document),
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
},
|
||||
@@ -268,6 +270,7 @@ fun LoadRelayInfo(
|
||||
fun ClickableRelayItem(
|
||||
item: Kind3BasicRelaySetupInfo,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
onToggleDownload: (Kind3BasicRelaySetupInfo) -> Unit,
|
||||
onToggleUpload: (Kind3BasicRelaySetupInfo) -> Unit,
|
||||
onToggleFollows: (Kind3BasicRelaySetupInfo) -> Unit,
|
||||
@@ -293,6 +296,7 @@ fun ClickableRelayItem(
|
||||
item.briefInfo.displayUrl,
|
||||
iconUrlFromRelayInfoDoc ?: item.briefInfo.favIcon,
|
||||
loadProfilePicture,
|
||||
loadRobohash,
|
||||
MaterialTheme.colorScheme.largeRelayIconModifier,
|
||||
)
|
||||
}
|
||||
@@ -346,19 +350,20 @@ private fun StatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
contentDescription = stringResource(R.string.read_from_relay),
|
||||
contentDescription = stringRes(R.string.read_from_relay),
|
||||
modifier =
|
||||
Modifier.size(15.dp)
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = { onToggleDownload(item) },
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.read_from_relay),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.read_from_relay),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -382,19 +387,20 @@ private fun StatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Upload,
|
||||
stringResource(R.string.write_to_relay),
|
||||
stringRes(R.string.write_to_relay),
|
||||
modifier =
|
||||
Modifier.size(15.dp)
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = { onToggleUpload(item) },
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.write_to_relay),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.write_to_relay),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -418,19 +424,20 @@ private fun StatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.SyncProblem,
|
||||
stringResource(R.string.errors),
|
||||
stringRes(R.string.errors),
|
||||
modifier =
|
||||
Modifier.size(15.dp)
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = {},
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.errors),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.errors),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -452,19 +459,20 @@ private fun StatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.DeleteSweep,
|
||||
stringResource(R.string.spam),
|
||||
stringRes(R.string.spam),
|
||||
modifier =
|
||||
Modifier.size(15.dp)
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = {},
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.spam),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.spam),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -498,7 +506,7 @@ private fun ActiveToggles(
|
||||
val context = LocalContext.current
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.active_for),
|
||||
text = stringRes(id = R.string.active_for),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
@@ -512,20 +520,21 @@ private fun ActiveToggles(
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_home),
|
||||
stringResource(R.string.home_feed),
|
||||
stringRes(R.string.home_feed),
|
||||
modifier =
|
||||
Modifier.padding(horizontal = 5.dp)
|
||||
Modifier
|
||||
.padding(horizontal = 5.dp)
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = { onToggleFollows(item) },
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.home_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.home_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -545,20 +554,21 @@ private fun ActiveToggles(
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_dm),
|
||||
stringResource(R.string.private_message_feed),
|
||||
stringRes(R.string.private_message_feed),
|
||||
modifier =
|
||||
Modifier.padding(horizontal = 5.dp)
|
||||
Modifier
|
||||
.padding(horizontal = 5.dp)
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = { onTogglePrivateDMs(item) },
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.private_message_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.private_message_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -578,20 +588,21 @@ private fun ActiveToggles(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Groups,
|
||||
contentDescription = stringResource(R.string.public_chat_feed),
|
||||
contentDescription = stringRes(R.string.public_chat_feed),
|
||||
modifier =
|
||||
Modifier.padding(horizontal = 5.dp)
|
||||
Modifier
|
||||
.padding(horizontal = 5.dp)
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = { onTogglePublicChats(item) },
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.public_chat_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.public_chat_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -611,20 +622,21 @@ private fun ActiveToggles(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Public,
|
||||
stringResource(R.string.global_feed),
|
||||
stringRes(R.string.global_feed),
|
||||
modifier =
|
||||
Modifier.padding(horizontal = 5.dp)
|
||||
Modifier
|
||||
.padding(horizontal = 5.dp)
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = { onToggleGlobal(item) },
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.global_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.global_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -672,7 +684,7 @@ private fun FirstLine(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Cancel,
|
||||
contentDescription = stringResource(id = R.string.remove),
|
||||
contentDescription = stringRes(id = R.string.remove),
|
||||
modifier = Modifier.padding(start = 10.dp).size(15.dp),
|
||||
tint = WarningColor,
|
||||
)
|
||||
@@ -691,7 +703,7 @@ fun Kind3RelayEditBox(
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.add_a_relay)) },
|
||||
label = { Text(text = stringRes(R.string.add_a_relay)) },
|
||||
modifier = Modifier.weight(1f),
|
||||
value = url,
|
||||
onValueChange = { url = it },
|
||||
@@ -708,7 +720,7 @@ fun Kind3RelayEditBox(
|
||||
IconButton(onClick = { read = !read }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
contentDescription = stringResource(id = R.string.read_from_relay),
|
||||
contentDescription = stringRes(id = R.string.read_from_relay),
|
||||
modifier = Modifier.size(Size35dp).padding(horizontal = 5.dp),
|
||||
tint =
|
||||
if (read) {
|
||||
@@ -722,7 +734,7 @@ fun Kind3RelayEditBox(
|
||||
IconButton(onClick = { write = !write }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Upload,
|
||||
contentDescription = stringResource(id = R.string.write_to_relay),
|
||||
contentDescription = stringRes(id = R.string.write_to_relay),
|
||||
modifier = Modifier.size(Size35dp).padding(horizontal = 5.dp),
|
||||
tint =
|
||||
if (write) {
|
||||
@@ -761,7 +773,7 @@ fun Kind3RelayEditBox(
|
||||
},
|
||||
),
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.add), color = Color.White)
|
||||
Text(text = stringRes(id = R.string.add), color = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
-1
@@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
+44
-41
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.actions.relays
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -43,16 +42,17 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
|
||||
import com.vitorpamplona.amethyst.service.relays.RelayStats
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
@@ -61,6 +61,7 @@ import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.timeAgo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||
@@ -81,12 +82,13 @@ fun RelayInformationDialog(
|
||||
) {
|
||||
val messages =
|
||||
remember(relayBriefInfo) {
|
||||
RelayStats.get(url = relayBriefInfo.url).messages.snapshot().values.sortedByDescending { it.time }.toImmutableList()
|
||||
}
|
||||
|
||||
val automaticallyShowProfilePicture =
|
||||
remember {
|
||||
accountViewModel.settings.showProfilePictures.value
|
||||
RelayStats
|
||||
.get(url = relayBriefInfo.url)
|
||||
.messages
|
||||
.snapshot()
|
||||
.values
|
||||
.sortedByDescending { it.time }
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
Dialog(
|
||||
@@ -124,10 +126,11 @@ fun RelayInformationDialog(
|
||||
) {
|
||||
Column {
|
||||
RenderRelayIcon(
|
||||
relayBriefInfo.displayUrl,
|
||||
relayInfo.icon ?: relayBriefInfo.favIcon,
|
||||
automaticallyShowProfilePicture,
|
||||
MaterialTheme.colorScheme.largeRelayIconModifier,
|
||||
displayUrl = relayBriefInfo.displayUrl,
|
||||
iconUrl = relayInfo.icon ?: relayBriefInfo.favIcon,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
iconModifier = MaterialTheme.colorScheme.largeRelayIconModifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -141,7 +144,7 @@ fun RelayInformationDialog(
|
||||
}
|
||||
}
|
||||
item {
|
||||
Section(stringResource(R.string.owner))
|
||||
Section(stringRes(R.string.owner))
|
||||
|
||||
relayInfo.pubkey?.let {
|
||||
DisplayOwnerInformation(it, accountViewModel) {
|
||||
@@ -151,16 +154,16 @@ fun RelayInformationDialog(
|
||||
}
|
||||
}
|
||||
item {
|
||||
Section(stringResource(R.string.software))
|
||||
Section(stringRes(R.string.software))
|
||||
|
||||
DisplaySoftwareInformation(relayInfo)
|
||||
|
||||
Section(stringResource(R.string.version))
|
||||
Section(stringRes(R.string.version))
|
||||
|
||||
SectionContent(relayInfo.version ?: "")
|
||||
}
|
||||
item {
|
||||
Section(stringResource(R.string.contact))
|
||||
Section(stringRes(R.string.contact))
|
||||
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
relayInfo.contact?.let {
|
||||
@@ -175,21 +178,21 @@ fun RelayInformationDialog(
|
||||
}
|
||||
}
|
||||
item {
|
||||
Section(stringResource(R.string.supports))
|
||||
Section(stringRes(R.string.supports))
|
||||
|
||||
DisplaySupportedNips(relayInfo)
|
||||
}
|
||||
item {
|
||||
relayInfo.fees?.admission?.let {
|
||||
if (it.isNotEmpty()) {
|
||||
Section(stringResource(R.string.admission_fees))
|
||||
Section(stringRes(R.string.admission_fees))
|
||||
|
||||
it.forEach { item -> SectionContent("${item.amount?.div(1000) ?: 0} sats") }
|
||||
}
|
||||
}
|
||||
|
||||
relayInfo.payments_url?.let {
|
||||
Section(stringResource(R.string.payments_url))
|
||||
Section(stringRes(R.string.payments_url))
|
||||
|
||||
Box(modifier = Modifier.padding(start = 10.dp)) {
|
||||
ClickableUrl(
|
||||
@@ -201,68 +204,68 @@ fun RelayInformationDialog(
|
||||
}
|
||||
item {
|
||||
relayInfo.limitation?.let {
|
||||
Section(stringResource(R.string.limitations))
|
||||
Section(stringRes(R.string.limitations))
|
||||
val authRequiredText =
|
||||
if (it.auth_required ?: false) stringResource(R.string.yes) else stringResource(R.string.no)
|
||||
if (it.auth_required ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
val paymentRequiredText =
|
||||
if (it.payment_required ?: false) stringResource(R.string.yes) else stringResource(R.string.no)
|
||||
if (it.payment_required ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
val restrictedWritesText =
|
||||
if (it.restricted_writes ?: false) stringResource(R.string.yes) else stringResource(R.string.no)
|
||||
if (it.restricted_writes ?: false) stringRes(R.string.yes) else stringRes(R.string.no)
|
||||
|
||||
Column {
|
||||
SectionContent(
|
||||
"${stringResource(R.string.message_length)}: ${it.max_message_length ?: 0}",
|
||||
"${stringRes(R.string.message_length)}: ${it.max_message_length ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringResource(R.string.subscriptions)}: ${it.max_subscriptions ?: 0}",
|
||||
"${stringRes(R.string.subscriptions)}: ${it.max_subscriptions ?: 0}",
|
||||
)
|
||||
SectionContent("${stringResource(R.string.filters)}: ${it.max_filters ?: 0}")
|
||||
SectionContent("${stringRes(R.string.filters)}: ${it.max_filters ?: 0}")
|
||||
SectionContent(
|
||||
"${stringResource(R.string.subscription_id_length)}: ${it.max_subid_length ?: 0}",
|
||||
"${stringRes(R.string.subscription_id_length)}: ${it.max_subid_length ?: 0}",
|
||||
)
|
||||
SectionContent("${stringResource(R.string.minimum_prefix)}: ${it.min_prefix ?: 0}")
|
||||
SectionContent("${stringRes(R.string.minimum_prefix)}: ${it.min_prefix ?: 0}")
|
||||
SectionContent(
|
||||
"${stringResource(R.string.maximum_event_tags)}: ${it.max_event_tags ?: 0}",
|
||||
"${stringRes(R.string.maximum_event_tags)}: ${it.max_event_tags ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringResource(R.string.content_length)}: ${it.max_content_length ?: 0}",
|
||||
"${stringRes(R.string.content_length)}: ${it.max_content_length ?: 0}",
|
||||
)
|
||||
SectionContent(
|
||||
"${stringResource(R.string.max_limit)}: ${it.max_limit ?: 0}",
|
||||
"${stringRes(R.string.max_limit)}: ${it.max_limit ?: 0}",
|
||||
)
|
||||
SectionContent("${stringResource(R.string.minimum_pow)}: ${it.min_pow_difficulty ?: 0}")
|
||||
SectionContent("${stringResource(R.string.auth)}: $authRequiredText")
|
||||
SectionContent("${stringResource(R.string.payment)}: $paymentRequiredText")
|
||||
SectionContent("${stringResource(R.string.restricted_writes)}: $restrictedWritesText")
|
||||
SectionContent("${stringRes(R.string.minimum_pow)}: ${it.min_pow_difficulty ?: 0}")
|
||||
SectionContent("${stringRes(R.string.auth)}: $authRequiredText")
|
||||
SectionContent("${stringRes(R.string.payment)}: $paymentRequiredText")
|
||||
SectionContent("${stringRes(R.string.restricted_writes)}: $restrictedWritesText")
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.relay_countries?.let {
|
||||
Section(stringResource(R.string.countries))
|
||||
Section(stringRes(R.string.countries))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.language_tags?.let {
|
||||
Section(stringResource(R.string.languages))
|
||||
Section(stringRes(R.string.languages))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.tags?.let {
|
||||
Section(stringResource(R.string.tags))
|
||||
Section(stringRes(R.string.tags))
|
||||
|
||||
FlowRow { it.forEach { item -> SectionContent(item) } }
|
||||
}
|
||||
}
|
||||
item {
|
||||
relayInfo.posting_policy?.let {
|
||||
Section(stringResource(R.string.posting_policy))
|
||||
Section(stringRes(R.string.posting_policy))
|
||||
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
ClickableUrl(
|
||||
@@ -274,7 +277,7 @@ fun RelayInformationDialog(
|
||||
}
|
||||
|
||||
item {
|
||||
Section(stringResource(R.string.relay_error_messages))
|
||||
Section(stringRes(R.string.relay_error_messages))
|
||||
}
|
||||
|
||||
items(messages) { msg ->
|
||||
@@ -346,7 +349,7 @@ private fun DisplayOwnerInformation(
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
LoadUser(baseUserHex = userHex, accountViewModel) {
|
||||
Crossfade(it) {
|
||||
CrossfadeIfEnabled(it, accountViewModel = accountViewModel) {
|
||||
if (it != null) {
|
||||
UserCompose(
|
||||
baseUser = it,
|
||||
|
||||
+2
-2
@@ -34,10 +34,10 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.WarningColor
|
||||
import com.vitorpamplona.amethyst.ui.theme.allGoodColor
|
||||
|
||||
@@ -76,7 +76,7 @@ fun RelayNameAndRemoveButton(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Cancel,
|
||||
contentDescription = stringResource(id = R.string.remove),
|
||||
contentDescription = stringRes(id = R.string.remove),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 10.dp)
|
||||
|
||||
@@ -36,11 +36,11 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.allGoodColor
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.ui.theme.warningColor
|
||||
@@ -58,7 +58,7 @@ fun RelayStatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
contentDescription = stringResource(R.string.read_from_relay),
|
||||
contentDescription = stringRes(R.string.read_from_relay),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
@@ -84,7 +84,7 @@ fun RelayStatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Upload,
|
||||
stringResource(R.string.write_to_relay),
|
||||
stringRes(R.string.write_to_relay),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
@@ -110,7 +110,7 @@ fun RelayStatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.SyncProblem,
|
||||
stringResource(R.string.errors),
|
||||
stringRes(R.string.errors),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
@@ -141,7 +141,7 @@ fun RelayStatusRow(
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.DeleteSweep,
|
||||
stringResource(R.string.spam),
|
||||
stringRes(R.string.spam),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(15.dp)
|
||||
@@ -153,12 +153,12 @@ fun RelayStatusRow(
|
||||
R.string.spam_description,
|
||||
)
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.spam),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
stringRes(context, R.string.spam),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -35,9 +35,9 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.relays.RelayStat
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
@@ -49,7 +49,7 @@ fun RelayUrlEditField(onNewRelay: (BasicRelaySetupInfo) -> Unit) {
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Size10dp)) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.add_a_relay)) },
|
||||
label = { Text(text = stringRes(R.string.add_a_relay)) },
|
||||
modifier = Modifier.weight(1f),
|
||||
value = url,
|
||||
onValueChange = { url = it },
|
||||
@@ -81,7 +81,7 @@ fun RelayUrlEditField(onNewRelay: (BasicRelaySetupInfo) -> Unit) {
|
||||
},
|
||||
),
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.add), color = Color.White)
|
||||
Text(text = stringRes(id = R.string.add), color = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -38,13 +38,13 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
|
||||
@@ -86,7 +86,7 @@ fun ChannelFabColumn(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.messages_new_message),
|
||||
text = stringRes(R.string.messages_new_message),
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = Font12SP,
|
||||
@@ -105,7 +105,7 @@ fun ChannelFabColumn(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.messages_create_public_chat),
|
||||
text = stringRes(R.string.messages_create_public_chat),
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = Font12SP,
|
||||
@@ -123,7 +123,7 @@ fun ChannelFabColumn(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Add,
|
||||
contentDescription = stringResource(R.string.messages_create_public_private_chat_description),
|
||||
contentDescription = stringRes(R.string.messages_create_public_private_chat_description),
|
||||
modifier = Modifier.size(26.dp),
|
||||
tint = Color.White,
|
||||
)
|
||||
|
||||
@@ -35,11 +35,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
|
||||
|
||||
@@ -61,7 +61,7 @@ fun NewChannelButton(accountViewModel: AccountViewModel) {
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Add,
|
||||
contentDescription = stringResource(R.string.new_channel),
|
||||
contentDescription = stringRes(R.string.new_channel),
|
||||
modifier = Modifier.size(26.dp),
|
||||
tint = Color.White,
|
||||
)
|
||||
|
||||
@@ -33,13 +33,13 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
|
||||
@Composable
|
||||
@@ -73,7 +73,7 @@ fun NewCommunityNoteButton(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_compose),
|
||||
contentDescription = stringResource(id = R.string.new_community_note),
|
||||
contentDescription = stringRes(id = R.string.new_community_note),
|
||||
modifier = Modifier.size(26.dp),
|
||||
tint = Color.White,
|
||||
)
|
||||
|
||||
@@ -46,7 +46,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -60,6 +59,7 @@ import com.vitorpamplona.amethyst.ui.actions.NewMediaModel
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewMediaView
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -136,7 +136,7 @@ fun NewImageButton(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_compose),
|
||||
contentDescription = stringResource(id = R.string.new_short),
|
||||
contentDescription = stringRes(id = R.string.new_short),
|
||||
modifier = Modifier.size(26.dp),
|
||||
tint = Color.White,
|
||||
)
|
||||
@@ -152,8 +152,7 @@ private fun ShowProgress(postViewModel: NewMediaModel) {
|
||||
animateFloatAsState(
|
||||
targetValue = postViewModel.uploadingPercentage.value,
|
||||
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
|
||||
)
|
||||
.value,
|
||||
).value,
|
||||
modifier =
|
||||
Size55Modifier
|
||||
.clip(CircleShape)
|
||||
|
||||
@@ -33,11 +33,11 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
|
||||
@Composable
|
||||
@@ -59,7 +59,7 @@ fun NewNoteButton(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_compose),
|
||||
contentDescription = stringResource(R.string.new_post),
|
||||
contentDescription = stringRes(R.string.new_post),
|
||||
modifier = Modifier.size(26.dp),
|
||||
tint = Color.White,
|
||||
)
|
||||
|
||||
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.components
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -53,7 +52,6 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
@@ -69,6 +67,7 @@ import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||
import com.vitorpamplona.amethyst.model.ThemeType
|
||||
import com.vitorpamplona.amethyst.service.CachedCashuProcessor
|
||||
import com.vitorpamplona.amethyst.service.CashuToken
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.actions.LoadingAnimation
|
||||
import com.vitorpamplona.amethyst.ui.note.CashuIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.CopyIcon
|
||||
@@ -76,6 +75,7 @@ import com.vitorpamplona.amethyst.ui.note.OpenInNewIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
@@ -106,7 +106,7 @@ fun CashuPreview(
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(targetState = cashuData, label = "CashuPreview") {
|
||||
CrossfadeIfEnabled(targetState = cashuData, label = "CashuPreview", accountViewModel = accountViewModel) {
|
||||
when (it) {
|
||||
is GenericLoadable.Loaded<CashuToken> -> CashuPreview(it.loaded, accountViewModel)
|
||||
is GenericLoadable.Error<CashuToken> ->
|
||||
@@ -190,7 +190,7 @@ fun CashuPreview(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.cashu),
|
||||
text = stringRes(R.string.cashu),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
@@ -200,7 +200,7 @@ fun CashuPreview(
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
Text(
|
||||
text = "${token.totalAmount} ${stringResource(id = R.string.sats)}",
|
||||
text = "${token.totalAmount} ${stringRes(id = R.string.sats)}",
|
||||
fontSize = 25.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier =
|
||||
@@ -239,7 +239,7 @@ fun CashuPreview(
|
||||
Spacer(DoubleHorzSpacer)
|
||||
|
||||
Text(
|
||||
stringResource(id = R.string.cashu_redeem_to_zap),
|
||||
stringRes(id = R.string.cashu_redeem_to_zap),
|
||||
color = Color.White,
|
||||
fontSize = 16.sp,
|
||||
)
|
||||
@@ -256,7 +256,7 @@ fun CashuPreview(
|
||||
startActivity(context, intent, null)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
toast("Cashu", context.getString(R.string.cashu_no_wallet_found))
|
||||
toast(stringRes(context, R.string.cashu), stringRes(context, R.string.cashu_no_wallet_found))
|
||||
}
|
||||
},
|
||||
shape = QuoteBorder,
|
||||
@@ -268,7 +268,7 @@ fun CashuPreview(
|
||||
CashuIcon(Size20Modifier)
|
||||
Spacer(DoubleHorzSpacer)
|
||||
Text(
|
||||
stringResource(id = R.string.cashu_redeem_to_cashu),
|
||||
stringRes(id = R.string.cashu_redeem_to_cashu),
|
||||
color = Color.White,
|
||||
fontSize = 16.sp,
|
||||
)
|
||||
@@ -287,7 +287,7 @@ fun CashuPreview(
|
||||
) {
|
||||
CopyIcon(Size20Modifier, Color.White)
|
||||
Spacer(DoubleHorzSpacer)
|
||||
Text(stringResource(id = R.string.cashu_copy_token), color = Color.White, fontSize = 16.sp)
|
||||
Text(stringRes(id = R.string.cashu_copy_token), color = Color.White, fontSize = 16.sp)
|
||||
}
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
}
|
||||
@@ -328,14 +328,14 @@ fun CashuPreviewNew(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.cashu),
|
||||
text = stringRes(R.string.cashu),
|
||||
fontSize = 12.sp,
|
||||
modifier = Modifier.padding(start = 5.dp, bottom = 1.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "${token.totalAmount} ${stringResource(id = R.string.sats)}",
|
||||
text = "${token.totalAmount} ${stringRes(id = R.string.sats)}",
|
||||
fontSize = 20.sp,
|
||||
modifier = Modifier.padding(top = 5.dp),
|
||||
)
|
||||
@@ -378,7 +378,7 @@ fun CashuPreviewNew(
|
||||
startActivity(context, intent, null)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
toast("Cashu", context.getString(R.string.cashu_no_wallet_found))
|
||||
toast(stringRes(context, R.string.cashu), stringRes(context, R.string.cashu_no_wallet_found))
|
||||
}
|
||||
},
|
||||
shape = SmallishBorder,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size24dp
|
||||
|
||||
@Composable
|
||||
fun ClickableBox(
|
||||
modifier: Modifier,
|
||||
onClick: () -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier.clickable(
|
||||
role = Role.Button,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(bounded = false, radius = Size24dp),
|
||||
onClick = onClick,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
@@ -470,13 +470,12 @@ fun CreateTextWithEmoji(
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val textColor =
|
||||
color.takeOrElse { LocalTextStyle.current.color.takeOrElse { LocalContentColor.current } }
|
||||
|
||||
CustomEmojiChecker(
|
||||
text,
|
||||
tags,
|
||||
onEmojiText = {
|
||||
val textColor =
|
||||
color.takeOrElse { LocalTextStyle.current.color.takeOrElse { LocalContentColor.current } }
|
||||
val style =
|
||||
LocalTextStyle.current
|
||||
.merge(
|
||||
@@ -486,12 +485,13 @@ fun CreateTextWithEmoji(
|
||||
fontWeight = fontWeight,
|
||||
fontSize = fontSize,
|
||||
),
|
||||
)
|
||||
.toSpanStyle()
|
||||
).toSpanStyle()
|
||||
|
||||
InLineIconRenderer(it, style, fontSize, maxLines, overflow, modifier)
|
||||
},
|
||||
onRegularText = {
|
||||
val textColor =
|
||||
color.takeOrElse { LocalTextStyle.current.color.takeOrElse { LocalContentColor.current } }
|
||||
Text(
|
||||
text = it,
|
||||
color = textColor,
|
||||
@@ -536,8 +536,7 @@ fun CreateTextWithEmoji(
|
||||
fontWeight = fontWeight,
|
||||
fontSize = fontSize,
|
||||
),
|
||||
)
|
||||
.toSpanStyle()
|
||||
).toSpanStyle()
|
||||
}
|
||||
|
||||
InLineIconRenderer(it, style, fontSize, maxLines, overflow, modifier)
|
||||
@@ -672,8 +671,7 @@ fun ClickableInLineIconRenderer(
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.associate { it.first to it.second }
|
||||
}.associate { it.first to it.second }
|
||||
|
||||
val annotatedText =
|
||||
buildAnnotatedString {
|
||||
@@ -757,8 +755,7 @@ fun InLineIconRenderer(
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.associate { it.first to it.second }
|
||||
}.associate { it.first to it.second }
|
||||
|
||||
val annotatedText =
|
||||
remember {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -35,21 +34,27 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog
|
||||
import com.vitorpamplona.amethyst.ui.note.payViaIntent
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.encoders.LnWithdrawalUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun MayBeWithdrawal(lnurlWord: String) {
|
||||
fun MayBeWithdrawal(
|
||||
lnurlWord: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
var lnWithdrawal by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
LaunchedEffect(key1 = lnurlWord) {
|
||||
launch(Dispatchers.IO) { lnWithdrawal = LnWithdrawalUtil.findWithdrawal(lnurlWord) }
|
||||
}
|
||||
|
||||
Crossfade(targetState = lnWithdrawal) {
|
||||
CrossfadeIfEnabled(targetState = lnWithdrawal, accountViewModel = accountViewModel) {
|
||||
if (it != null) {
|
||||
ClickableWithdrawal(withdrawalString = it)
|
||||
} else {
|
||||
@@ -71,7 +76,7 @@ fun ClickableWithdrawal(withdrawalString: String) {
|
||||
|
||||
if (showErrorMessageDialog != null) {
|
||||
ErrorMessageDialog(
|
||||
title = context.getString(R.string.error_dialog_pay_withdraw_error),
|
||||
title = stringRes(context, R.string.error_dialog_pay_withdraw_error),
|
||||
textContent = showErrorMessageDialog ?: "",
|
||||
onDismiss = { showErrorMessageDialog = null },
|
||||
)
|
||||
|
||||
+4
-3
@@ -40,11 +40,11 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.richtext.ExpandableTextCutOffCalculator
|
||||
import com.vitorpamplona.amethyst.ui.note.getGradient
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdTopPadding
|
||||
@@ -110,7 +110,8 @@ fun ExpandableRichTextViewer(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier =
|
||||
Modifier.align(Alignment.BottomCenter)
|
||||
Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.background(getGradient(backgroundColor)),
|
||||
) {
|
||||
@@ -135,6 +136,6 @@ fun ShowMoreButton(onClick: () -> Unit) {
|
||||
),
|
||||
contentPadding = ButtonPadding,
|
||||
) {
|
||||
Text(text = stringResource(R.string.show_more), color = Color.White)
|
||||
Text(text = stringRes(R.string.show_more), color = Color.White)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.DefaultAlpha
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
|
||||
/**
|
||||
* Create and return a new [Painter] that wraps [painter] with its [alpha], [colorFilter], or [onDraw] overwritten.
|
||||
*/
|
||||
fun forwardingPainter(
|
||||
painter: Painter,
|
||||
alpha: Float = DefaultAlpha,
|
||||
colorFilter: ColorFilter? = null,
|
||||
onDraw: DrawScope.(ForwardingDrawInfo) -> Unit = DefaultOnDraw,
|
||||
): Painter = ForwardingPainter(painter, alpha, colorFilter, onDraw)
|
||||
|
||||
data class ForwardingDrawInfo(
|
||||
val painter: Painter,
|
||||
val alpha: Float,
|
||||
val colorFilter: ColorFilter?,
|
||||
)
|
||||
|
||||
private class ForwardingPainter(
|
||||
private val painter: Painter,
|
||||
private var alpha: Float,
|
||||
private var colorFilter: ColorFilter?,
|
||||
private val onDraw: DrawScope.(ForwardingDrawInfo) -> Unit,
|
||||
) : Painter() {
|
||||
private var info = newInfo()
|
||||
|
||||
override val intrinsicSize get() = painter.intrinsicSize
|
||||
|
||||
override fun applyAlpha(alpha: Float): Boolean {
|
||||
if (alpha != DefaultAlpha) {
|
||||
this.alpha = alpha
|
||||
this.info = newInfo()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
|
||||
if (colorFilter == null) {
|
||||
this.colorFilter = colorFilter
|
||||
this.info = newInfo()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun DrawScope.onDraw() = onDraw(info)
|
||||
|
||||
private fun newInfo() = ForwardingDrawInfo(painter, alpha, colorFilter)
|
||||
}
|
||||
|
||||
private val DefaultOnDraw: DrawScope.(ForwardingDrawInfo) -> Unit = { info ->
|
||||
with(info.painter) {
|
||||
draw(size, info.alpha, info.colorFilter)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -44,7 +43,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -54,8 +52,11 @@ import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.Lightning
|
||||
import com.vitorpamplona.amethyst.service.lnurl.CachedLnInvoiceParser
|
||||
import com.vitorpamplona.amethyst.service.lnurl.InvoiceAmount
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog
|
||||
import com.vitorpamplona.amethyst.ui.note.payViaIntent
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
@@ -82,9 +83,12 @@ fun LoadValueFromInvoice(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MayBeInvoicePreview(lnbcWord: String) {
|
||||
fun MayBeInvoicePreview(
|
||||
lnbcWord: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
LoadValueFromInvoice(lnbcWord = lnbcWord) { invoiceAmount ->
|
||||
Crossfade(targetState = invoiceAmount, label = "MayBeInvoicePreview") {
|
||||
CrossfadeIfEnabled(targetState = invoiceAmount, label = "MayBeInvoicePreview", accountViewModel = accountViewModel) {
|
||||
if (it != null) {
|
||||
InvoicePreview(it.invoice, it.amount)
|
||||
} else {
|
||||
@@ -108,7 +112,7 @@ fun InvoicePreview(
|
||||
|
||||
if (showErrorMessageDialog != null) {
|
||||
ErrorMessageDialog(
|
||||
title = context.getString(R.string.error_dialog_pay_invoice_error),
|
||||
title = stringRes(context, R.string.error_dialog_pay_invoice_error),
|
||||
textContent = showErrorMessageDialog ?: "",
|
||||
onDismiss = { showErrorMessageDialog = null },
|
||||
)
|
||||
@@ -143,7 +147,7 @@ fun InvoicePreview(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.lightning_invoice),
|
||||
text = stringRes(R.string.lightning_invoice),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
@@ -154,7 +158,7 @@ fun InvoicePreview(
|
||||
|
||||
amount?.let {
|
||||
Text(
|
||||
text = "$it ${stringResource(id = R.string.sats)}",
|
||||
text = "$it ${stringRes(id = R.string.sats)}",
|
||||
fontSize = 25.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier =
|
||||
@@ -176,7 +180,7 @@ fun InvoicePreview(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
) {
|
||||
Text(text = stringResource(R.string.pay), color = Color.White, fontSize = 20.sp)
|
||||
Text(text = stringRes(R.string.pay), color = Color.White, fontSize = 20.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
@@ -56,6 +55,7 @@ import com.vitorpamplona.amethyst.commons.hashtags.Lightning
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
@@ -78,7 +78,8 @@ fun InvoiceRequestCard(
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 30.dp, end = 30.dp)
|
||||
.clip(shape = QuoteBorder)
|
||||
.border(1.dp, MaterialTheme.colorScheme.subtleBorder, QuoteBorder),
|
||||
@@ -126,7 +127,7 @@ fun InvoiceRequest(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = titleText ?: stringResource(R.string.lightning_tips),
|
||||
text = titleText ?: stringRes(R.string.lightning_tips),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
@@ -139,13 +140,13 @@ fun InvoiceRequest(
|
||||
var amount by remember { mutableStateOf(1000L) }
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.note_to_receiver)) },
|
||||
label = { Text(text = stringRes(R.string.note_to_receiver)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = message,
|
||||
onValueChange = { message = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.thank_you_so_much),
|
||||
text = stringRes(R.string.thank_you_so_much),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -157,7 +158,7 @@ fun InvoiceRequest(
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.amount_in_sats)) },
|
||||
label = { Text(text = stringRes(R.string.amount_in_sats)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = amount.toString(),
|
||||
onValueChange = {
|
||||
@@ -199,9 +200,7 @@ fun InvoiceRequest(
|
||||
context = context,
|
||||
)
|
||||
} else {
|
||||
account.createZapRequestFor(toUserPubKeyHex, message, account.defaultZapType) {
|
||||
zapRequest,
|
||||
->
|
||||
account.createZapRequestFor(toUserPubKeyHex, message, account.defaultZapType) { zapRequest ->
|
||||
LocalCache.justConsume(zapRequest, null)
|
||||
LightningAddressResolver()
|
||||
.lnAddressInvoice(
|
||||
@@ -225,7 +224,7 @@ fun InvoiceRequest(
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = buttonText ?: stringResource(R.string.send_sats),
|
||||
text = buttonText ?: stringRes(R.string.send_sats),
|
||||
color = Color.White,
|
||||
fontSize = 20.sp,
|
||||
)
|
||||
|
||||
@@ -20,16 +20,14 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
|
||||
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfVertPadding
|
||||
|
||||
@@ -40,9 +38,7 @@ fun LoadUrlPreview(
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val automaticallyShowUrlPreview = remember { accountViewModel.settings.showUrlPreview.value }
|
||||
|
||||
if (!automaticallyShowUrlPreview) {
|
||||
if (!accountViewModel.settings.showUrlPreview.value) {
|
||||
ClickableUrl(urlText, url)
|
||||
} else {
|
||||
val urlPreviewState by
|
||||
@@ -55,10 +51,10 @@ fun LoadUrlPreview(
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(
|
||||
CrossfadeIfEnabled(
|
||||
targetState = urlPreviewState,
|
||||
animationSpec = tween(durationMillis = 100),
|
||||
label = "UrlPreview",
|
||||
accountViewModel = accountViewModel,
|
||||
) { state ->
|
||||
when (state) {
|
||||
is UrlPreviewState.Loaded -> {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -30,7 +29,6 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.foundation.text.appendInlineContent
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -86,6 +84,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.model.checkForHashtagWithIcon
|
||||
import com.vitorpamplona.amethyst.service.CachedRichTextParser
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.markdown.RenderContentAsMarkdown
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
@@ -103,15 +102,14 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
||||
fun isMarkdown(content: String): Boolean {
|
||||
return content.startsWith("> ") ||
|
||||
fun isMarkdown(content: String): Boolean =
|
||||
content.startsWith("> ") ||
|
||||
content.startsWith("# ") ||
|
||||
content.contains("##") ||
|
||||
content.contains("__") ||
|
||||
content.contains("**") ||
|
||||
content.contains("```") ||
|
||||
content.contains("](")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RichTextViewer(
|
||||
@@ -172,8 +170,8 @@ fun RenderRegularPreview() {
|
||||
// is ImageSegment -> ZoomableContentView(word.segmentText, state, accountViewModel)
|
||||
// is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, accountViewModel)
|
||||
is EmojiSegment -> RenderCustomEmoji(word.segmentText, state)
|
||||
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
|
||||
is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
|
||||
// is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
|
||||
// is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
|
||||
// is CashuSegment -> CashuPreview(word.segmentText, accountViewModel)
|
||||
is EmailSegment -> ClickableEmail(word.segmentText)
|
||||
is PhoneSegment -> ClickablePhone(word.segmentText)
|
||||
@@ -209,8 +207,8 @@ fun RenderRegularPreview2() {
|
||||
// is ImageSegment -> ZoomableContentView(word.segmentText, state, accountViewModel)
|
||||
// is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, accountViewModel)
|
||||
is EmojiSegment -> RenderCustomEmoji(word.segmentText, state)
|
||||
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
|
||||
is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
|
||||
// is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
|
||||
// is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
|
||||
// is CashuSegment -> CashuPreview(word.segmentText, accountViewModel)
|
||||
is EmailSegment -> ClickableEmail(word.segmentText)
|
||||
is PhoneSegment -> ClickablePhone(word.segmentText)
|
||||
@@ -270,8 +268,8 @@ fun RenderRegularPreview3() {
|
||||
// is ImageSegment -> ZoomableContentView(word.segmentText, state, accountViewModel)
|
||||
is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, null, accountViewModel)
|
||||
is EmojiSegment -> RenderCustomEmoji(word.segmentText, state)
|
||||
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
|
||||
is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
|
||||
// is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
|
||||
// is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
|
||||
// is CashuSegment -> CashuPreview(word.segmentText, accountViewModel)
|
||||
is EmailSegment -> ClickableEmail(word.segmentText)
|
||||
is PhoneSegment -> ClickablePhone(word.segmentText)
|
||||
@@ -424,8 +422,8 @@ private fun RenderWordWithPreview(
|
||||
is ImageSegment -> ZoomableContentView(word.segmentText, state, accountViewModel)
|
||||
is LinkSegment -> LoadUrlPreview(word.segmentText, word.segmentText, callbackUri, accountViewModel)
|
||||
is EmojiSegment -> RenderCustomEmoji(word.segmentText, state)
|
||||
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
|
||||
is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
|
||||
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText, accountViewModel)
|
||||
is WithdrawSegment -> MayBeWithdrawal(word.segmentText, accountViewModel)
|
||||
is CashuSegment -> CashuPreview(word.segmentText, accountViewModel)
|
||||
is EmailSegment -> ClickableEmail(word.segmentText)
|
||||
is PhoneSegment -> ClickablePhone(word.segmentText)
|
||||
@@ -598,7 +596,7 @@ fun TagLink(
|
||||
Text(text = word.segmentText)
|
||||
} else {
|
||||
Row {
|
||||
DisplayUserFromTag(it, nav)
|
||||
DisplayUserFromTag(it, accountViewModel, nav)
|
||||
word.extras?.let {
|
||||
Text(text = it)
|
||||
}
|
||||
@@ -683,11 +681,12 @@ private fun DisplayNoteFromTag(
|
||||
@Composable
|
||||
private fun DisplayUserFromTag(
|
||||
baseUser: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
val meta by baseUser.live().userMetadataInfo.observeAsState(baseUser.info)
|
||||
|
||||
Crossfade(targetState = meta, label = "DisplayUserFromTag") {
|
||||
CrossfadeIfEnabled(targetState = meta, label = "DisplayUserFromTag", accountViewModel = accountViewModel) {
|
||||
Row {
|
||||
CreateClickableTextWithEmoji(
|
||||
clickablePart = remember(meta) { it?.bestName() ?: baseUser.pubkeyDisplayHex() },
|
||||
|
||||
@@ -24,11 +24,11 @@ import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Face
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
@@ -51,6 +51,7 @@ import coil.request.Options
|
||||
import com.vitorpamplona.amethyst.commons.robohash.CachedRobohash
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.theme.isLight
|
||||
import com.vitorpamplona.amethyst.ui.theme.onBackgroundColorFilter
|
||||
import java.util.Base64
|
||||
|
||||
@Composable
|
||||
@@ -58,19 +59,22 @@ fun RobohashAsyncImage(
|
||||
robot: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
loadRobohash: Boolean,
|
||||
) {
|
||||
val isLightTheme = MaterialTheme.colorScheme.isLight
|
||||
|
||||
val robotPainter =
|
||||
remember(robot) {
|
||||
CachedRobohash.get(robot, isLightTheme)
|
||||
}
|
||||
|
||||
Image(
|
||||
imageVector = robotPainter,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
)
|
||||
if (loadRobohash) {
|
||||
Image(
|
||||
imageVector = CachedRobohash.get(robot, MaterialTheme.colorScheme.isLight),
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
imageVector = Icons.Default.Face,
|
||||
contentDescription = contentDescription,
|
||||
colorFilter = MaterialTheme.colorScheme.onBackgroundColorFilter,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -85,6 +89,7 @@ fun RobohashFallbackAsyncImage(
|
||||
colorFilter: ColorFilter? = null,
|
||||
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
) {
|
||||
if (model != null && loadProfilePicture) {
|
||||
val isBase64 = model.startsWith("data:image/jpeg;base64,")
|
||||
@@ -106,9 +111,19 @@ fun RobohashFallbackAsyncImage(
|
||||
)
|
||||
} else {
|
||||
val painter =
|
||||
rememberVectorPainter(
|
||||
image = CachedRobohash.get(robot, MaterialTheme.colorScheme.isLight),
|
||||
)
|
||||
if (loadRobohash) {
|
||||
rememberVectorPainter(
|
||||
image = CachedRobohash.get(robot, MaterialTheme.colorScheme.isLight),
|
||||
)
|
||||
} else {
|
||||
forwardingPainter(
|
||||
painter =
|
||||
rememberVectorPainter(
|
||||
image = Icons.Default.Face,
|
||||
),
|
||||
colorFilter = MaterialTheme.colorScheme.onBackgroundColorFilter,
|
||||
)
|
||||
}
|
||||
|
||||
AsyncImage(
|
||||
model = model,
|
||||
@@ -125,16 +140,25 @@ fun RobohashFallbackAsyncImage(
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val robotPainter = CachedRobohash.get(robot, MaterialTheme.colorScheme.isLight)
|
||||
|
||||
Image(
|
||||
imageVector = robotPainter,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
alignment = alignment,
|
||||
contentScale = contentScale,
|
||||
colorFilter = colorFilter,
|
||||
)
|
||||
if (loadRobohash) {
|
||||
Image(
|
||||
imageVector = CachedRobohash.get(robot, MaterialTheme.colorScheme.isLight),
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
alignment = alignment,
|
||||
contentScale = contentScale,
|
||||
colorFilter = colorFilter,
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
imageVector = Icons.Default.Face,
|
||||
contentDescription = contentDescription,
|
||||
colorFilter = MaterialTheme.colorScheme.onBackgroundColorFilter,
|
||||
modifier = modifier,
|
||||
alignment = alignment,
|
||||
contentScale = contentScale,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,11 +37,11 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size24dp
|
||||
|
||||
@@ -74,7 +74,7 @@ fun SelectTextDialog(
|
||||
) {
|
||||
ArrowBackIcon()
|
||||
}
|
||||
Text(text = stringResource(R.string.select_text_dialog_top))
|
||||
Text(text = stringRes(R.string.select_text_dialog_top))
|
||||
}
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
Column(
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -47,14 +46,15 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
|
||||
import com.vitorpamplona.quartz.events.EventInterface
|
||||
@@ -102,7 +102,7 @@ fun SensitivityWarning(
|
||||
var showContentWarningNote by
|
||||
remember(accountState) { mutableStateOf(accountState?.account?.showSensitiveContent != true) }
|
||||
|
||||
Crossfade(targetState = showContentWarningNote) {
|
||||
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
|
||||
if (it) {
|
||||
ContentWarningNote { showContentWarningNote = false }
|
||||
} else {
|
||||
@@ -122,13 +122,13 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Visibility,
|
||||
contentDescription = stringResource(R.string.content_warning),
|
||||
contentDescription = stringRes(R.string.content_warning),
|
||||
modifier = Modifier.size(70.dp).align(Alignment.BottomStart),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Warning,
|
||||
contentDescription = stringResource(R.string.content_warning),
|
||||
contentDescription = stringRes(R.string.content_warning),
|
||||
modifier = Modifier.size(30.dp).align(Alignment.TopEnd),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@@ -137,7 +137,7 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
|
||||
|
||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
Text(
|
||||
text = stringResource(R.string.content_warning),
|
||||
text = stringRes(R.string.content_warning),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 18.sp,
|
||||
)
|
||||
@@ -145,7 +145,7 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = stringResource(R.string.content_warning_explanation),
|
||||
text = stringRes(R.string.content_warning_explanation),
|
||||
color = Color.Gray,
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
@@ -164,7 +164,7 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
|
||||
contentPadding = ButtonPadding,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.show_anyway),
|
||||
text = stringRes(R.string.show_anyway),
|
||||
color = Color.White,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.previews.UrlInfoItem
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.MaxWidthWithHorzPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.innerPostModifier
|
||||
@@ -59,7 +59,7 @@ private fun CopyToClipboard(
|
||||
) {
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.copy_url_to_clipboard)) },
|
||||
text = { Text(stringRes(R.string.copy_url_to_clipboard)) },
|
||||
onClick = {
|
||||
clipboardManager.setText(AnnotatedString(content))
|
||||
onDismiss()
|
||||
@@ -103,7 +103,7 @@ fun UrlPreviewCard(
|
||||
) {
|
||||
AsyncImage(
|
||||
model = previewInfo.imageUrlFullPath,
|
||||
contentDescription = stringResource(R.string.preview_card_image_for, previewInfo.url),
|
||||
contentDescription = previewInfo.title,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = previewCardImageModifier,
|
||||
)
|
||||
|
||||
@@ -81,7 +81,6 @@ import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -108,6 +107,7 @@ import com.vitorpamplona.amethyst.ui.note.LyricsOffIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.MuteIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.MutedIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.PinBottomIconSize
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size110dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size165dp
|
||||
@@ -374,13 +374,15 @@ data class MediaItemData(
|
||||
val artworkUri: String? = null,
|
||||
)
|
||||
|
||||
class MediaItemCache() : GenericBaseCache<MediaItemData, MediaItem>(20) {
|
||||
override suspend fun compute(key: MediaItemData): MediaItem {
|
||||
return MediaItem.Builder()
|
||||
class MediaItemCache : GenericBaseCache<MediaItemData, MediaItem>(20) {
|
||||
override suspend fun compute(key: MediaItemData): MediaItem =
|
||||
MediaItem
|
||||
.Builder()
|
||||
.setMediaId(key.videoUri)
|
||||
.setUri(key.videoUri)
|
||||
.setMediaMetadata(
|
||||
MediaMetadata.Builder()
|
||||
MediaMetadata
|
||||
.Builder()
|
||||
.setArtist(key.authorName?.ifBlank { null })
|
||||
.setTitle(key.title?.ifBlank { null } ?: key.videoUri)
|
||||
.setArtworkUri(
|
||||
@@ -394,11 +396,8 @@ class MediaItemCache() : GenericBaseCache<MediaItemData, MediaItem>(20) {
|
||||
if (e is CancellationException) throw e
|
||||
null
|
||||
},
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
).build(),
|
||||
).build()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -409,7 +408,15 @@ fun GetMediaItem(
|
||||
authorName: String?,
|
||||
inner: @Composable (State<MediaItem>) -> Unit,
|
||||
) {
|
||||
val data = remember(videoUri) { MediaItemData(videoUri, title, artworkUri, authorName) }
|
||||
val data =
|
||||
remember(videoUri) {
|
||||
MediaItemData(
|
||||
videoUri = videoUri,
|
||||
authorName = authorName,
|
||||
title = title,
|
||||
artworkUri = artworkUri,
|
||||
)
|
||||
}
|
||||
val mediaItem by produceCachedState(cache = mediaItemCache, key = data)
|
||||
|
||||
mediaItem?.let {
|
||||
@@ -619,7 +626,7 @@ var keepPlayingMutex: MediaController? = null
|
||||
val trackingVideos = mutableListOf<VisibilityData>()
|
||||
|
||||
@Stable
|
||||
class VisibilityData() {
|
||||
class VisibilityData {
|
||||
var distanceToCenter: Float? = null
|
||||
}
|
||||
|
||||
@@ -645,8 +652,7 @@ fun VideoPlayerActiveMutex(
|
||||
|
||||
val myModifier =
|
||||
remember(videoUri) {
|
||||
Modifier.fillMaxWidth().defaultMinSize(minHeight = 70.dp).onVisiblePositionChanges {
|
||||
distanceToCenter ->
|
||||
Modifier.fillMaxWidth().defaultMinSize(minHeight = 70.dp).onVisiblePositionChanges { distanceToCenter ->
|
||||
myCache.distanceToCenter = distanceToCenter
|
||||
|
||||
if (distanceToCenter != null) {
|
||||
@@ -815,8 +821,7 @@ private fun pollCurrentDuration(controller: MediaController) =
|
||||
emit(controller.currentPosition / controller.duration.toFloat())
|
||||
delay(100)
|
||||
}
|
||||
}
|
||||
.conflate()
|
||||
}.conflate()
|
||||
|
||||
@Composable
|
||||
fun Waveform(
|
||||
@@ -1000,7 +1005,8 @@ private fun MuteButton(
|
||||
) {
|
||||
Box(modifier = VolumeBottomIconSize) {
|
||||
Box(
|
||||
Modifier.clip(CircleShape)
|
||||
Modifier
|
||||
.clip(CircleShape)
|
||||
.fillMaxSize(0.6f)
|
||||
.align(Alignment.Center)
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
@@ -1040,7 +1046,8 @@ private fun KeepPlayingButton(
|
||||
) {
|
||||
Box(modifier = PinBottomIconSize) {
|
||||
Box(
|
||||
Modifier.clip(CircleShape)
|
||||
Modifier
|
||||
.clip(CircleShape)
|
||||
.fillMaxSize(0.6f)
|
||||
.align(Alignment.Center)
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
@@ -1099,7 +1106,8 @@ fun AnimatedShareButton(
|
||||
fun ShareButton(innerAction: @Composable (MutableState<Boolean>, () -> Unit) -> Unit) {
|
||||
Box(modifier = PinBottomIconSize) {
|
||||
Box(
|
||||
Modifier.clip(CircleShape)
|
||||
Modifier
|
||||
.clip(CircleShape)
|
||||
.fillMaxSize(0.6f)
|
||||
.align(Alignment.Center)
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
@@ -1116,7 +1124,7 @@ fun ShareButton(innerAction: @Composable (MutableState<Boolean>, () -> Unit) ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
modifier = Size20Modifier,
|
||||
contentDescription = stringResource(R.string.share_or_save),
|
||||
contentDescription = stringRes(R.string.share_or_save),
|
||||
)
|
||||
|
||||
innerAction(popupExpanded) { popupExpanded.value = false }
|
||||
@@ -1129,7 +1137,8 @@ fun ShareButton(innerAction: @Composable (MutableState<Boolean>, () -> Unit) ->
|
||||
fun SaveButton(onSaveClick: (localContext: Context) -> Unit) {
|
||||
Box(modifier = PinBottomIconSize) {
|
||||
Box(
|
||||
Modifier.clip(CircleShape)
|
||||
Modifier
|
||||
.clip(CircleShape)
|
||||
.fillMaxSize(0.6f)
|
||||
.align(Alignment.Center)
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
@@ -1160,7 +1169,7 @@ fun SaveButton(onSaveClick: (localContext: Context) -> Unit) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
modifier = Size20Modifier,
|
||||
contentDescription = stringResource(R.string.save_to_gallery),
|
||||
contentDescription = stringRes(R.string.save_to_gallery),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -43,6 +42,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.Lightning
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
@@ -67,7 +67,7 @@ fun ZapRaiserRequest(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = titleText ?: stringResource(R.string.zapraiser),
|
||||
text = titleText ?: stringRes(R.string.zapraiser),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(start = 10.dp),
|
||||
@@ -77,13 +77,13 @@ fun ZapRaiserRequest(
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.zapraiser_explainer),
|
||||
text = stringRes(R.string.zapraiser_explainer),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringResource(R.string.zapraiser_target_amount_in_sats)) },
|
||||
label = { Text(text = stringRes(R.string.zapraiser_target_amount_in_sats)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value =
|
||||
if (newPostViewModel.zapRaiserAmount != null) {
|
||||
|
||||
+10
-6
@@ -31,7 +31,6 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -67,7 +66,6 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.core.net.toUri
|
||||
@@ -86,6 +84,7 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
|
||||
import com.vitorpamplona.amethyst.ui.actions.ImageSaver
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size15dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
@@ -234,7 +233,12 @@ private fun DialogContent(
|
||||
exit = remember { fadeOut() },
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = Size15dp, vertical = Size10dp).statusBarsPadding().systemBarsPadding().fillMaxWidth(),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = Size15dp, vertical = Size10dp)
|
||||
.statusBarsPadding()
|
||||
.systemBarsPadding()
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = spacedBy(Size10dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
@@ -245,7 +249,7 @@ private fun DialogContent(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
contentDescription = stringRes(R.string.back),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -262,7 +266,7 @@ private fun DialogContent(
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
modifier = Size20Modifier,
|
||||
contentDescription = stringResource(R.string.quick_action_share),
|
||||
contentDescription = stringRes(R.string.quick_action_share),
|
||||
)
|
||||
|
||||
ShareImageAction(popupExpanded = popupExpanded, myContent, onDismiss = { popupExpanded.value = false })
|
||||
@@ -294,7 +298,7 @@ private fun DialogContent(
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
modifier = Size20Modifier,
|
||||
contentDescription = stringResource(R.string.save_to_gallery),
|
||||
contentDescription = stringRes(R.string.save_to_gallery),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.view.Window
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
@@ -56,7 +55,6 @@ import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.Placeholder
|
||||
import androidx.compose.ui.text.PlaceholderVerticalAlign
|
||||
@@ -64,7 +62,6 @@ import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import coil.annotation.ExperimentalCoilApi
|
||||
import coil.compose.AsyncImage
|
||||
@@ -89,6 +86,7 @@ import com.vitorpamplona.amethyst.ui.note.DownloadForOfflineIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.HashCheckFailedIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.HashCheckIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font17SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size24dp
|
||||
@@ -99,7 +97,7 @@ import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -109,21 +107,20 @@ import kotlinx.coroutines.withContext
|
||||
@Composable
|
||||
fun ZoomableContentView(
|
||||
content: BaseMediaContent,
|
||||
images: ImmutableList<BaseMediaContent> = remember(content) { listOf(content).toImmutableList() },
|
||||
images: ImmutableList<BaseMediaContent> = remember(content) { persistentListOf(content) },
|
||||
roundedCorner: Boolean,
|
||||
isFiniteHeight: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
// store the dialog open or close state
|
||||
var dialogOpen by remember(content) { mutableStateOf(false) }
|
||||
|
||||
val mainImageModifier = Modifier.fillMaxWidth().clickable { dialogOpen = true }
|
||||
val loadedImageModifier = if (roundedCorner) MaterialTheme.colorScheme.imageModifier else Modifier.fillMaxWidth()
|
||||
|
||||
when (content) {
|
||||
is MediaUrlImage ->
|
||||
SensitivityWarning(content.contentWarning != null, accountViewModel) {
|
||||
TwoSecondController(content) { controllerVisible ->
|
||||
val mainImageModifier = Modifier.fillMaxWidth().clickable { dialogOpen = true }
|
||||
val loadedImageModifier = if (roundedCorner) MaterialTheme.colorScheme.imageModifier else Modifier.fillMaxWidth()
|
||||
|
||||
UrlImageView(content, mainImageModifier, loadedImageModifier, isFiniteHeight, controllerVisible, accountViewModel = accountViewModel)
|
||||
}
|
||||
}
|
||||
@@ -148,6 +145,9 @@ fun ZoomableContentView(
|
||||
}
|
||||
is MediaLocalImage ->
|
||||
TwoSecondController(content) { controllerVisible ->
|
||||
val mainImageModifier = Modifier.fillMaxWidth().clickable { dialogOpen = true }
|
||||
val loadedImageModifier = if (roundedCorner) MaterialTheme.colorScheme.imageModifier else Modifier.fillMaxWidth()
|
||||
|
||||
LocalImageView(content, mainImageModifier, loadedImageModifier, isFiniteHeight, controllerVisible, accountViewModel = accountViewModel)
|
||||
}
|
||||
is MediaLocalVideo ->
|
||||
@@ -315,15 +315,13 @@ fun UrlImageView(
|
||||
)
|
||||
}
|
||||
|
||||
val contentScale = if (isFiniteHeight) ContentScale.Fit else ContentScale.FillWidth
|
||||
|
||||
val ratio = remember(content) { aspectRatio(content.dim) }
|
||||
|
||||
if (showImage.value) {
|
||||
SubcomposeAsyncImage(
|
||||
model = content.url,
|
||||
contentDescription = content.description,
|
||||
contentScale = contentScale,
|
||||
contentScale = if (isFiniteHeight) ContentScale.Fit else ContentScale.FillWidth,
|
||||
modifier = mainImageModifier,
|
||||
) {
|
||||
when (painter.state) {
|
||||
@@ -635,7 +633,7 @@ fun ShareImageAction(
|
||||
|
||||
if (videoUri != null && !videoUri.startsWith("file")) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.copy_url_to_clipboard)) },
|
||||
text = { Text(stringRes(R.string.copy_url_to_clipboard)) },
|
||||
onClick = {
|
||||
clipboardManager.setText(AnnotatedString(videoUri))
|
||||
onDismiss()
|
||||
@@ -645,7 +643,7 @@ fun ShareImageAction(
|
||||
|
||||
postNostrUri?.let {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.copy_the_note_id_to_the_clipboard)) },
|
||||
text = { Text(stringRes(R.string.copy_the_note_id_to_the_clipboard)) },
|
||||
onClick = {
|
||||
clipboardManager.setText(AnnotatedString(it))
|
||||
onDismiss()
|
||||
@@ -677,7 +675,7 @@ private fun HashVerificationSymbol(verifiedHash: Boolean) {
|
||||
|
||||
openDialogMsg.value?.let {
|
||||
InformationDialog(
|
||||
title = localContext.getString(R.string.hash_verification_info_title),
|
||||
title = stringRes(localContext, R.string.hash_verification_info_title),
|
||||
textContent = it,
|
||||
) {
|
||||
openDialogMsg.value = null
|
||||
@@ -688,7 +686,7 @@ private fun HashVerificationSymbol(verifiedHash: Boolean) {
|
||||
IconButton(
|
||||
modifier = hashVerifierMark,
|
||||
onClick = {
|
||||
openDialogMsg.value = localContext.getString(R.string.hash_verification_passed)
|
||||
openDialogMsg.value = stringRes(localContext, R.string.hash_verification_passed)
|
||||
},
|
||||
) {
|
||||
HashCheckIcon(Size30dp)
|
||||
@@ -697,7 +695,7 @@ private fun HashVerificationSymbol(verifiedHash: Boolean) {
|
||||
IconButton(
|
||||
modifier = hashVerifierMark,
|
||||
onClick = {
|
||||
openDialogMsg.value = localContext.getString(R.string.hash_verification_failed)
|
||||
openDialogMsg.value = stringRes(localContext, R.string.hash_verification_failed)
|
||||
},
|
||||
) {
|
||||
HashCheckFailedIcon(Size30dp)
|
||||
|
||||
@@ -37,11 +37,11 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.note.NewItemsBubble
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ChatHeadlineBorders
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
@@ -57,7 +57,7 @@ fun ChannelNamePreview() {
|
||||
channelPicture = {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.github),
|
||||
contentDescription = stringResource(id = R.string.profile_banner),
|
||||
contentDescription = stringRes(id = R.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
},
|
||||
@@ -90,7 +90,7 @@ fun ChannelNamePreview() {
|
||||
leadingContent = {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.github),
|
||||
contentDescription = stringResource(id = R.string.profile_banner),
|
||||
contentDescription = stringRes(id = R.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Size55Modifier,
|
||||
)
|
||||
|
||||
@@ -37,7 +37,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -46,6 +45,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.TextCount
|
||||
import com.vitorpamplona.amethyst.ui.note.ZappedIcon
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size16Modifier
|
||||
@@ -66,7 +66,7 @@ fun LeftPictureLayoutPreviewCard() {
|
||||
onImage = {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.github),
|
||||
contentDescription = stringResource(id = R.string.profile_banner),
|
||||
contentDescription = stringRes(id = R.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier.fillMaxSize().clip(QuoteBorder),
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.ui.note.RepostedIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.RepostIcon
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size35Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
@@ -55,7 +55,7 @@ fun GenericRepostLayout(
|
||||
Box(
|
||||
remember { Size18Modifier.align(Alignment.BottomStart).padding(1.dp) },
|
||||
) {
|
||||
RepostedIcon(modifier = Size18Modifier, MaterialTheme.colorScheme.placeholderText)
|
||||
RepostIcon(modifier = Size18Modifier, MaterialTheme.colorScheme.placeholderText)
|
||||
}
|
||||
|
||||
Box(
|
||||
|
||||
+19
-17
@@ -55,7 +55,6 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -64,6 +63,7 @@ import androidx.compose.ui.window.DialogProperties
|
||||
import com.vitorpamplona.amethyst.AccountInfo
|
||||
import com.vitorpamplona.amethyst.LocalPreferences
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
@@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginOrSignupScreen
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.AccountPictureModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
@@ -100,7 +101,7 @@ fun AccountSwitchBottomSheet(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(stringResource(R.string.account_switch_select_account), fontWeight = FontWeight.Bold)
|
||||
Text(stringRes(R.string.account_switch_select_account), fontWeight = FontWeight.Bold)
|
||||
}
|
||||
accounts.forEach { acc -> DisplayAccount(acc, accountViewModel, accountStateViewModel) }
|
||||
Row(
|
||||
@@ -112,7 +113,7 @@ fun AccountSwitchBottomSheet(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TextButton(onClick = { popupExpanded = true }) {
|
||||
Text(stringResource(R.string.account_switch_add_account_btn))
|
||||
Text(stringRes(R.string.account_switch_add_account_btn))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +128,7 @@ fun AccountSwitchBottomSheet(
|
||||
LoginOrSignupScreen(accountStateViewModel, isFirstLogin = false)
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(text = stringResource(R.string.account_switch_add_account_dialog_title))
|
||||
Text(text = stringRes(R.string.account_switch_add_account_dialog_title))
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { popupExpanded = false }) { ArrowBackIcon() }
|
||||
@@ -191,12 +192,11 @@ fun DisplayAccount(
|
||||
.width(55.dp)
|
||||
.padding(0.dp),
|
||||
) {
|
||||
val automaticallyShowProfilePicture =
|
||||
remember {
|
||||
accountViewModel.settings.showProfilePictures.value
|
||||
}
|
||||
|
||||
AccountPicture(it, automaticallyShowProfilePicture)
|
||||
AccountPicture(
|
||||
it,
|
||||
accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Column(modifier = Modifier.weight(1f)) { AccountName(acc, it) }
|
||||
@@ -222,7 +222,7 @@ private fun ActiveMarker(
|
||||
if (isCurrentUser) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.RadioButtonChecked,
|
||||
contentDescription = stringResource(R.string.account_switch_active_account),
|
||||
contentDescription = stringRes(R.string.account_switch_active_account),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
}
|
||||
@@ -232,15 +232,17 @@ private fun ActiveMarker(
|
||||
private fun AccountPicture(
|
||||
user: User,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
) {
|
||||
val profilePicture by user.live().profilePictureChanges.observeAsState()
|
||||
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = user.pubkeyHex,
|
||||
model = profilePicture,
|
||||
contentDescription = stringResource(R.string.profile_image),
|
||||
contentDescription = stringRes(R.string.profile_image),
|
||||
modifier = AccountPictureModifier,
|
||||
loadProfilePicture = loadProfilePicture,
|
||||
loadRobohash = loadRobohash,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -275,8 +277,8 @@ private fun LogoutButton(
|
||||
var logoutDialog by remember { mutableStateOf(false) }
|
||||
if (logoutDialog) {
|
||||
AlertDialog(
|
||||
title = { Text(text = stringResource(R.string.log_out)) },
|
||||
text = { Text(text = stringResource(R.string.are_you_sure_you_want_to_log_out)) },
|
||||
title = { Text(text = stringRes(R.string.log_out)) },
|
||||
text = { Text(text = stringRes(R.string.are_you_sure_you_want_to_log_out)) },
|
||||
onDismissRequest = { logoutDialog = false },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
@@ -285,14 +287,14 @@ private fun LogoutButton(
|
||||
accountStateViewModel.logOff(acc)
|
||||
},
|
||||
) {
|
||||
Text(text = stringResource(R.string.log_out))
|
||||
Text(text = stringRes(R.string.log_out))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { logoutDialog = false },
|
||||
) {
|
||||
Text(text = stringResource(R.string.cancel))
|
||||
Text(text = stringRes(R.string.cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -303,7 +305,7 @@ private fun LogoutButton(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Logout,
|
||||
contentDescription = stringResource(R.string.log_out),
|
||||
contentDescription = stringRes(R.string.log_out),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -44,10 +43,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size0dp
|
||||
@@ -146,12 +145,16 @@ private fun RowScope.HasNewItemsIcon(
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
nav: (Route, Boolean) -> Unit,
|
||||
) {
|
||||
val selected by
|
||||
remember(navEntryState.value) {
|
||||
derivedStateOf { navEntryState.value?.destination?.route?.substringBefore("?") == route.base }
|
||||
}
|
||||
val selected =
|
||||
(
|
||||
navEntryState.value
|
||||
?.destination
|
||||
?.route
|
||||
?.indexOf(route.base) ?: -1
|
||||
) > -1
|
||||
|
||||
NavigationBarItem(
|
||||
alwaysShowLabel = false,
|
||||
icon = {
|
||||
NotifiableIcon(
|
||||
selected,
|
||||
@@ -173,7 +176,7 @@ private fun NotifiableIcon(
|
||||
Box(route.notifSize) {
|
||||
Icon(
|
||||
painter = painterResource(id = route.icon),
|
||||
contentDescription = stringResource(route.contentDescriptor),
|
||||
contentDescription = stringRes(route.contentDescriptor),
|
||||
modifier = route.iconSize,
|
||||
tint = if (selected) MaterialTheme.colorScheme.primary else Color.Unspecified,
|
||||
)
|
||||
|
||||
@@ -41,7 +41,6 @@ import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material3.DrawerState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -58,15 +57,12 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -81,6 +77,7 @@ import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
@@ -130,6 +127,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.RoomNameOnlyDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShortChannelHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.observeAppDefinition
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
@@ -151,16 +149,14 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combineTransform
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun AppTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
@@ -180,7 +176,7 @@ fun AppTopBar(
|
||||
derivedStateOf { navEntryState.value?.arguments?.getString("id") }
|
||||
}
|
||||
|
||||
RenderTopRouteBar(currentRoute, id, followLists, drawerState, accountViewModel, nav, navPopBack)
|
||||
RenderTopRouteBar(currentRoute, id, followLists, openDrawer, accountViewModel, nav, navPopBack)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -188,19 +184,19 @@ private fun RenderTopRouteBar(
|
||||
currentRoute: String?,
|
||||
id: String?,
|
||||
followLists: FollowListViewModel,
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
when (currentRoute) {
|
||||
Route.Home.base -> HomeTopBar(followLists, drawerState, accountViewModel, nav)
|
||||
Route.Video.base -> StoriesTopBar(followLists, drawerState, accountViewModel, nav)
|
||||
Route.Discover.base -> DiscoveryTopBar(followLists, drawerState, accountViewModel, nav)
|
||||
Route.Notification.base -> NotificationTopBar(followLists, drawerState, accountViewModel, nav)
|
||||
Route.Settings.base -> TopBarWithBackButton(stringResource(id = R.string.application_preferences), navPopBack)
|
||||
Route.Bookmarks.base -> TopBarWithBackButton(stringResource(id = R.string.bookmarks), navPopBack)
|
||||
Route.Drafts.base -> TopBarWithBackButton(stringResource(id = R.string.drafts), navPopBack)
|
||||
Route.Home.base -> HomeTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Video.base -> StoriesTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Discover.base -> DiscoveryTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Notification.base -> NotificationTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Settings.base -> TopBarWithBackButton(stringRes(id = R.string.application_preferences), navPopBack)
|
||||
Route.Bookmarks.base -> TopBarWithBackButton(stringRes(id = R.string.bookmarks), navPopBack)
|
||||
Route.Drafts.base -> TopBarWithBackButton(stringRes(id = R.string.drafts), navPopBack)
|
||||
|
||||
else -> {
|
||||
if (id != null) {
|
||||
@@ -213,10 +209,10 @@ private fun RenderTopRouteBar(
|
||||
Route.Geohash.base -> GeoHashTopBar(id, accountViewModel, navPopBack)
|
||||
Route.Note.base -> ThreadTopBar(id, accountViewModel, navPopBack)
|
||||
Route.ContentDiscovery.base -> DvmTopBar(id, accountViewModel, nav, navPopBack)
|
||||
else -> MainTopBar(drawerState, accountViewModel, nav)
|
||||
else -> MainTopBar(openDrawer, accountViewModel, nav)
|
||||
}
|
||||
} else {
|
||||
MainTopBar(drawerState, accountViewModel, nav)
|
||||
MainTopBar(openDrawer, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,7 +225,7 @@ private fun ThreadTopBar(
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = { Text(stringResource(id = R.string.thread_title)) },
|
||||
title = { Text(stringRes(id = R.string.thread_title)) },
|
||||
popBack = navPopBack,
|
||||
)
|
||||
}
|
||||
@@ -381,7 +377,7 @@ private fun RenderRoomTopBar(
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
UsernameDisplay(baseUser, Modifier.weight(1f), fontWeight = FontWeight.Normal)
|
||||
UsernameDisplay(baseUser, Modifier.weight(1f), fontWeight = FontWeight.Normal, accountViewModel = accountViewModel)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -413,7 +409,7 @@ private fun RenderRoomTopBar(
|
||||
.padding(start = 10.dp)
|
||||
.weight(1f),
|
||||
fontWeight = FontWeight.Normal,
|
||||
accountViewModel.userProfile(),
|
||||
accountViewModel,
|
||||
)
|
||||
},
|
||||
extendableRow = {
|
||||
@@ -452,11 +448,11 @@ private fun ChannelTopBar(
|
||||
@Composable
|
||||
fun StoriesTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(drawerState, accountViewModel, nav) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.defaultStoriesFollowList.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithRoutes(
|
||||
@@ -471,11 +467,11 @@ fun StoriesTopBar(
|
||||
@Composable
|
||||
fun HomeTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(drawerState, accountViewModel, nav) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.defaultHomeFollowList.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithRoutes(
|
||||
@@ -494,11 +490,11 @@ fun HomeTopBar(
|
||||
@Composable
|
||||
fun NotificationTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(drawerState, accountViewModel, nav) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.defaultNotificationFollowList.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithoutRoutes(
|
||||
@@ -513,11 +509,11 @@ fun NotificationTopBar(
|
||||
@Composable
|
||||
fun DiscoveryTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(drawerState, accountViewModel, nav) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.defaultDiscoveryFollowList.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithoutRoutes(
|
||||
@@ -531,17 +527,17 @@ fun DiscoveryTopBar(
|
||||
|
||||
@Composable
|
||||
fun MainTopBar(
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(drawerState, accountViewModel, nav) { AmethystClickableIcon() }
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) { AmethystClickableIcon() }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun GenericMainTopBar(
|
||||
drawerState: DrawerState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
@@ -558,8 +554,7 @@ fun GenericMainTopBar(
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
LoggedInUserPictureDrawer(accountViewModel) { coroutineScope.launch { drawerState.open() } }
|
||||
LoggedInUserPictureDrawer(accountViewModel, openDrawer)
|
||||
},
|
||||
actions = { SearchButton { nav(Route.Search.route) } },
|
||||
)
|
||||
@@ -572,7 +567,7 @@ private fun SearchButton(onClick: () -> Unit) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
) {
|
||||
SearchIcon(modifier = Size22Modifier, Color.Unspecified)
|
||||
SearchIcon(modifier = Size22Modifier, MaterialTheme.colorScheme.placeholderText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,10 +587,11 @@ private fun LoggedInUserPictureDrawer(
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = accountViewModel.userProfile().pubkeyHex,
|
||||
model = profilePicture,
|
||||
contentDescription = stringResource(id = R.string.your_profile_image),
|
||||
contentDescription = stringRes(id = R.string.your_profile_image),
|
||||
modifier = HeaderPictureModifier,
|
||||
contentScale = ContentScale.Crop,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -659,7 +655,7 @@ class ResourceName(
|
||||
) : Name() {
|
||||
override fun name() = " $resourceId " // Space to make sure it goes first
|
||||
|
||||
override fun name(context: Context) = context.getString(resourceId)
|
||||
override fun name(context: Context) = stringRes(context, resourceId)
|
||||
}
|
||||
|
||||
class PeopleListName(
|
||||
@@ -800,7 +796,7 @@ fun SimpleTextSpinner(
|
||||
|
||||
val context = LocalContext.current
|
||||
val selectAnOption =
|
||||
stringResource(
|
||||
stringRes(
|
||||
id = R.string.select_an_option,
|
||||
)
|
||||
|
||||
@@ -882,7 +878,7 @@ fun RenderOption(option: Name) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = option.resourceId),
|
||||
text = stringRes(id = option.resourceId),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
@@ -44,7 +43,6 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DrawerState
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
@@ -72,7 +70,6 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -86,6 +83,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.BuildConfig
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
||||
import com.vitorpamplona.amethyst.service.relays.RelayPoolStatus
|
||||
@@ -98,6 +96,7 @@ import com.vitorpamplona.amethyst.ui.qrcode.ShowQRDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font18SP
|
||||
@@ -131,11 +130,6 @@ fun DrawerContent(
|
||||
Unit
|
||||
}
|
||||
|
||||
val automaticallyShowProfilePicture =
|
||||
remember {
|
||||
accountViewModel.settings.showProfilePictures.value
|
||||
}
|
||||
|
||||
ModalDrawerSheet(
|
||||
drawerContainerColor = MaterialTheme.colorScheme.background,
|
||||
drawerTonalElevation = 0.dp,
|
||||
@@ -174,7 +168,8 @@ fun DrawerContent(
|
||||
BottomContent(
|
||||
accountViewModel.account.userProfile(),
|
||||
drawerState,
|
||||
automaticallyShowProfilePicture,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
@@ -217,14 +212,14 @@ fun ProfileContentTemplate(
|
||||
if (profileBanner != null) {
|
||||
AsyncImage(
|
||||
model = profileBanner,
|
||||
contentDescription = stringResource(id = R.string.profile_image),
|
||||
contentDescription = stringRes(id = R.string.profile_image),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = bannerModifier,
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.profile_banner),
|
||||
contentDescription = stringResource(R.string.profile_banner),
|
||||
contentDescription = stringRes(R.string.profile_banner),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = bannerModifier,
|
||||
)
|
||||
@@ -234,7 +229,7 @@ fun ProfileContentTemplate(
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = profilePubHex,
|
||||
model = profilePicture,
|
||||
contentDescription = stringResource(id = R.string.profile_image),
|
||||
contentDescription = stringRes(id = R.string.profile_image),
|
||||
modifier =
|
||||
Modifier
|
||||
.width(100.dp)
|
||||
@@ -243,6 +238,7 @@ fun ProfileContentTemplate(
|
||||
.border(3.dp, MaterialTheme.colorScheme.background, CircleShape)
|
||||
.clickable(onClick = onClick),
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
|
||||
if (bestDisplayName != null) {
|
||||
@@ -295,11 +291,11 @@ fun StatusEditBar(
|
||||
OutlinedTextField(
|
||||
value = currentStatus.value,
|
||||
onValueChange = { currentStatus.value = it },
|
||||
label = { Text(text = stringResource(R.string.status_update)) },
|
||||
label = { Text(text = stringRes(R.string.status_update)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.status_update),
|
||||
text = stringRes(R.string.status_update),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
@@ -401,7 +397,7 @@ private fun FollowingAndFollowerCounts(
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
|
||||
Text(stringResource(R.string.following))
|
||||
Text(stringRes(R.string.following))
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
@@ -410,7 +406,7 @@ private fun FollowingAndFollowerCounts(
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
|
||||
Text(stringResource(R.string.followers))
|
||||
Text(stringRes(R.string.followers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +460,7 @@ fun ListContent(
|
||||
|
||||
Column(modifier) {
|
||||
NavigationRow(
|
||||
title = stringResource(R.string.profile),
|
||||
title = stringRes(R.string.profile),
|
||||
icon = Route.Profile.icon,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
nav = nav,
|
||||
@@ -473,7 +469,7 @@ fun ListContent(
|
||||
)
|
||||
|
||||
NavigationRow(
|
||||
title = stringResource(R.string.bookmarks),
|
||||
title = stringRes(R.string.bookmarks),
|
||||
icon = Route.Bookmarks.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
@@ -482,7 +478,7 @@ fun ListContent(
|
||||
)
|
||||
|
||||
NavigationRow(
|
||||
title = stringResource(R.string.drafts),
|
||||
title = stringRes(R.string.drafts),
|
||||
icon = Route.Drafts.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
@@ -499,7 +495,7 @@ fun ListContent(
|
||||
)
|
||||
|
||||
NavigationRow(
|
||||
title = stringResource(R.string.security_filters),
|
||||
title = stringRes(R.string.security_filters),
|
||||
icon = Route.BlockedUsers.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
@@ -509,7 +505,7 @@ fun ListContent(
|
||||
|
||||
accountViewModel.account.keyPair.privKey?.let {
|
||||
IconRow(
|
||||
title = stringResource(R.string.backup_keys),
|
||||
title = stringRes(R.string.backup_keys),
|
||||
icon = R.drawable.ic_key,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
onClick = {
|
||||
@@ -522,9 +518,9 @@ fun ListContent(
|
||||
IconRow(
|
||||
title =
|
||||
if (checked) {
|
||||
stringResource(R.string.disconnect_from_your_orbot_setup)
|
||||
stringRes(R.string.disconnect_from_your_orbot_setup)
|
||||
} else {
|
||||
stringResource(R.string.connect_via_tor_short)
|
||||
stringRes(R.string.connect_via_tor_short)
|
||||
},
|
||||
icon = R.drawable.ic_tor,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
@@ -543,7 +539,7 @@ fun ListContent(
|
||||
)
|
||||
|
||||
NavigationRow(
|
||||
title = stringResource(R.string.settings),
|
||||
title = stringRes(R.string.settings),
|
||||
icon = Route.Settings.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
@@ -554,7 +550,7 @@ fun ListContent(
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
IconRow(
|
||||
title = stringResource(R.string.drawer_accounts),
|
||||
title = stringRes(R.string.drawer_accounts),
|
||||
icon = R.drawable.manage_accounts,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
onClick = openSheet,
|
||||
@@ -578,7 +574,7 @@ fun ListContent(
|
||||
},
|
||||
onError = {
|
||||
accountViewModel.toast(
|
||||
context.getString(R.string.could_not_connect_to_tor),
|
||||
stringRes(context, R.string.could_not_connect_to_tor),
|
||||
it,
|
||||
)
|
||||
},
|
||||
@@ -588,8 +584,8 @@ fun ListContent(
|
||||
|
||||
if (disconnectTorDialog) {
|
||||
AlertDialog(
|
||||
title = { Text(text = stringResource(R.string.do_you_really_want_to_disable_tor_title)) },
|
||||
text = { Text(text = stringResource(R.string.do_you_really_want_to_disable_tor_text)) },
|
||||
title = { Text(text = stringRes(R.string.do_you_really_want_to_disable_tor_title)) },
|
||||
text = { Text(text = stringRes(R.string.do_you_really_want_to_disable_tor_text)) },
|
||||
onDismissRequest = { disconnectTorDialog = false },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
@@ -599,14 +595,14 @@ fun ListContent(
|
||||
accountViewModel.enableTor(false, proxyPort)
|
||||
},
|
||||
) {
|
||||
Text(text = stringResource(R.string.yes))
|
||||
Text(text = stringRes(R.string.yes))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { disconnectTorDialog = false },
|
||||
) {
|
||||
Text(text = stringResource(R.string.no))
|
||||
Text(text = stringRes(R.string.no))
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -730,7 +726,7 @@ fun IconRowRelays(
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
text = stringResource(id = R.string.relay_setup),
|
||||
text = stringRes(id = R.string.relay_setup),
|
||||
fontSize = 18.sp,
|
||||
)
|
||||
|
||||
@@ -746,6 +742,7 @@ fun BottomContent(
|
||||
user: User,
|
||||
drawerState: DrawerState,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
@@ -792,7 +789,7 @@ fun BottomContent(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_qrcode),
|
||||
contentDescription = stringResource(id = R.string.show_npub_as_a_qr_code),
|
||||
contentDescription = stringRes(id = R.string.show_npub_as_a_qr_code),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
@@ -804,6 +801,7 @@ fun BottomContent(
|
||||
ShowQRDialog(
|
||||
user,
|
||||
loadProfilePicture = loadProfilePicture,
|
||||
loadRobohash = loadRobohash,
|
||||
onScan = {
|
||||
dialogOpen = false
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.os.Bundle
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NamedNavArgument
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
@@ -77,8 +76,7 @@ sealed class Route(
|
||||
nullable = true
|
||||
defaultValue = null
|
||||
},
|
||||
)
|
||||
.toImmutableList(),
|
||||
).toImmutableList(),
|
||||
contentDescriptor = R.string.route_home,
|
||||
hasNewItems = { accountViewModel, newNotes ->
|
||||
HomeLatestItem.hasNewItems(accountViewModel, newNotes)
|
||||
@@ -95,7 +93,7 @@ sealed class Route(
|
||||
object Search :
|
||||
Route(
|
||||
route = "Search",
|
||||
icon = R.drawable.ic_search,
|
||||
icon = R.drawable.ic_moments,
|
||||
contentDescriptor = R.string.route_search,
|
||||
)
|
||||
|
||||
@@ -212,8 +210,7 @@ sealed class Route(
|
||||
nullable = true
|
||||
defaultValue = null
|
||||
},
|
||||
)
|
||||
.toImmutableList(),
|
||||
).toImmutableList(),
|
||||
)
|
||||
|
||||
object RoomByAuthor :
|
||||
@@ -256,9 +253,7 @@ sealed class Route(
|
||||
open class LatestItem {
|
||||
var newestItemPerAccount: Map<String, Note?> = mapOf()
|
||||
|
||||
fun getNewestItem(account: Account): Note? {
|
||||
return newestItemPerAccount[account.userProfile().pubkeyHex]
|
||||
}
|
||||
fun getNewestItem(account: Account): Note? = newestItemPerAccount[account.userProfile().pubkeyHex]
|
||||
|
||||
fun clearNewestItem(account: Account) {
|
||||
val userHex = account.userProfile().pubkeyHex
|
||||
@@ -279,9 +274,10 @@ open class LatestItem {
|
||||
if (newestItem == null || !account.isAcceptable(newestItem)) {
|
||||
filterMore(filter.feed(), account).firstOrNull { it.createdAt() != null && account.isAcceptable(it) }
|
||||
} else {
|
||||
filter.sort(
|
||||
filterMore(filter.applyFilter(newNotes), account) + newestItem,
|
||||
).firstOrNull { it.createdAt() != null && account.isAcceptable(it) }
|
||||
filter
|
||||
.sort(
|
||||
filterMore(filter.applyFilter(newNotes), account) + newestItem,
|
||||
).firstOrNull { it.createdAt() != null && account.isAcceptable(it) }
|
||||
}
|
||||
|
||||
newestItemPerAccount = newestItemPerAccount + Pair(account.userProfile().pubkeyHex, newNewest)
|
||||
@@ -292,16 +288,12 @@ open class LatestItem {
|
||||
open fun filterMore(
|
||||
newItems: Set<Note>,
|
||||
account: Account,
|
||||
): Set<Note> {
|
||||
return newItems
|
||||
}
|
||||
): Set<Note> = newItems
|
||||
|
||||
open fun filterMore(
|
||||
newItems: List<Note>,
|
||||
account: Account,
|
||||
): List<Note> {
|
||||
return newItems
|
||||
}
|
||||
): List<Note> = newItems
|
||||
}
|
||||
|
||||
object HomeLatestItem : LatestItem() {
|
||||
@@ -382,16 +374,12 @@ object MessagesLatestItem : LatestItem() {
|
||||
override fun filterMore(
|
||||
newItems: Set<Note>,
|
||||
account: Account,
|
||||
): Set<Note> {
|
||||
return newItems.filter { isNew(it, account) }.toSet()
|
||||
}
|
||||
): Set<Note> = newItems.filter { isNew(it, account) }.toSet()
|
||||
|
||||
override fun filterMore(
|
||||
newItems: List<Note>,
|
||||
account: Account,
|
||||
): List<Note> {
|
||||
return newItems.filter { isNew(it, account) }
|
||||
}
|
||||
): List<Note> = newItems.filter { isNew(it, account) }
|
||||
}
|
||||
|
||||
fun getRouteWithArguments(navController: NavHostController): String? {
|
||||
@@ -399,9 +387,7 @@ fun getRouteWithArguments(navController: NavHostController): String? {
|
||||
return getRouteWithArguments(currentEntry.destination, currentEntry.arguments)
|
||||
}
|
||||
|
||||
fun getRouteWithArguments(navState: State<NavBackStackEntry?>): String? {
|
||||
return navState.value?.let { getRouteWithArguments(it.destination, it.arguments) }
|
||||
}
|
||||
fun getRouteWithArguments(navState: State<NavBackStackEntry?>): String? = navState.value?.let { getRouteWithArguments(it.destination, it.arguments) }
|
||||
|
||||
private fun getRouteWithArguments(
|
||||
destination: NavDestination,
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -31,32 +31,25 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MilitaryTech
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.NoteDropDownMenu
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
|
||||
import com.vitorpamplona.amethyst.ui.note.types.BadgeDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.BadgeCard
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@@ -67,16 +60,14 @@ fun BadgeCompose(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
val noteState by likeSetCard.note.live().metadata.observeAsState()
|
||||
val noteState by likeSetCard.note
|
||||
.live()
|
||||
.metadata
|
||||
.observeAsState()
|
||||
val note = noteState?.note
|
||||
|
||||
val context = LocalContext.current.applicationContext
|
||||
|
||||
val popupExpanded = remember { mutableStateOf(false) }
|
||||
val enablePopup = remember { { popupExpanded.value = true } }
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
if (note == null) {
|
||||
BlankNote(Modifier)
|
||||
} else {
|
||||
@@ -89,18 +80,15 @@ fun BadgeCompose(
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.background(backgroundColor.value)
|
||||
.combinedClickable(
|
||||
Modifier
|
||||
.background(backgroundColor.value)
|
||||
.clickable(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
routeFor(
|
||||
note,
|
||||
accountViewModel.userProfile(),
|
||||
)
|
||||
?.let { nav(it) }
|
||||
}
|
||||
routeFor(
|
||||
note,
|
||||
accountViewModel.userProfile(),
|
||||
)?.let { nav(it) }
|
||||
},
|
||||
onLongClick = enablePopup,
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
@@ -128,7 +116,7 @@ fun BadgeCompose(
|
||||
Column(modifier = Modifier.padding(start = if (!isInnerNote) 10.dp else 0.dp)) {
|
||||
Row {
|
||||
Text(
|
||||
stringResource(R.string.new_badge_award_notif),
|
||||
stringRes(R.string.new_badge_award_notif),
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(bottom = 5.dp).weight(1f),
|
||||
)
|
||||
@@ -139,19 +127,7 @@ fun BadgeCompose(
|
||||
maxLines = 1,
|
||||
)
|
||||
|
||||
IconButton(
|
||||
modifier = Modifier.then(Modifier.size(24.dp)),
|
||||
onClick = enablePopup,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = stringResource(id = R.string.more_options),
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
|
||||
NoteDropDownMenu(note, popupExpanded, null, accountViewModel, nav)
|
||||
}
|
||||
MoreOptionsButton(note, null, accountViewModel, nav)
|
||||
}
|
||||
|
||||
note.replyTo?.firstOrNull()?.let {
|
||||
|
||||
@@ -34,7 +34,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -42,6 +41,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.mockAccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||
@@ -75,7 +75,7 @@ fun BlankNote(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.post_not_found) + if (idHex != null) ": $idHex" else "",
|
||||
text = stringRes(R.string.post_not_found) + if (idHex != null) ": $idHex" else "",
|
||||
modifier = Modifier.padding(30.dp),
|
||||
color = Color.Gray,
|
||||
textAlign = TextAlign.Center,
|
||||
@@ -125,7 +125,7 @@ fun HiddenNote(
|
||||
modifier = Modifier.padding(30.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.post_was_flagged_as_inappropriate_by),
|
||||
text = stringRes(R.string.post_was_flagged_as_inappropriate_by),
|
||||
color = Color.Gray,
|
||||
)
|
||||
FlowRow(modifier = Modifier.padding(top = 10.dp)) {
|
||||
@@ -157,7 +157,7 @@ fun HiddenNote(
|
||||
),
|
||||
contentPadding = ButtonPadding,
|
||||
) {
|
||||
Text(text = stringResource(R.string.show_anyway), color = Color.White)
|
||||
Text(text = stringRes(R.string.show_anyway), color = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ fun HiddenNoteByMe(
|
||||
modifier = Modifier.padding(30.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.post_was_hidden),
|
||||
text = stringRes(R.string.post_was_hidden),
|
||||
color = Color.Gray,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
@@ -203,7 +203,7 @@ fun HiddenNoteByMe(
|
||||
),
|
||||
contentPadding = ButtonPadding,
|
||||
) {
|
||||
Text(text = stringResource(R.string.show_anyway), color = Color.White)
|
||||
Text(text = stringRes(R.string.show_anyway), color = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -68,6 +67,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.ParticipantListBuilder
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
|
||||
@@ -472,7 +472,7 @@ fun RenderLiveActivityThumb(
|
||||
} ?: run { DisplayAuthorBanner(baseNote) }
|
||||
|
||||
Box(Modifier.padding(10.dp)) {
|
||||
Crossfade(targetState = card.status, label = "RenderLiveActivityThumb") {
|
||||
CrossfadeIfEnabled(targetState = card.status, label = "RenderLiveActivityThumb", accountViewModel = accountViewModel) {
|
||||
when (it) {
|
||||
STATUS_LIVE -> {
|
||||
val url = card.media
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -46,7 +45,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -61,12 +59,15 @@ import androidx.lifecycle.map
|
||||
import com.patrykandpatrick.vico.core.extension.forEachIndexedExtended
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Channel
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.layouts.ChatHeaderLayout
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.AccountPictureModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||
@@ -136,7 +137,7 @@ private fun ChatroomPrivateMessages(
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(userRoom, label = "ChatroomPrivateMessages") { room ->
|
||||
CrossfadeIfEnabled(targetState = userRoom, label = "ChatroomPrivateMessages", accountViewModel = accountViewModel) { room ->
|
||||
if (room != null) {
|
||||
UserRoomCompose(baseNote, room, accountViewModel, nav)
|
||||
} else {
|
||||
@@ -182,20 +183,15 @@ private fun ChannelRoomCompose(
|
||||
|
||||
val description =
|
||||
if (noteEvent is ChannelCreateEvent) {
|
||||
stringResource(R.string.channel_created)
|
||||
stringRes(R.string.channel_created)
|
||||
} else if (noteEvent is ChannelMetadataEvent) {
|
||||
"${stringResource(R.string.channel_information_changed_to)} "
|
||||
"${stringRes(R.string.channel_information_changed_to)} "
|
||||
} else {
|
||||
noteEvent?.content()
|
||||
}
|
||||
|
||||
val hasNewMessages = remember { mutableStateOf<Boolean>(false) }
|
||||
|
||||
val automaticallyShowProfilePicture =
|
||||
remember {
|
||||
accountViewModel.settings.showProfilePictures.value
|
||||
}
|
||||
|
||||
WatchNotificationChanges(note, route, accountViewModel) { newHasNewMessages ->
|
||||
if (hasNewMessages.value != newHasNewMessages) {
|
||||
hasNewMessages.value = newHasNewMessages
|
||||
@@ -209,7 +205,8 @@ private fun ChannelRoomCompose(
|
||||
channelLastTime = remember(note) { note.createdAt() },
|
||||
channelLastContent = remember(note, authorState) { "$authorName: $description" },
|
||||
hasNewMessages = hasNewMessages,
|
||||
loadProfilePicture = automaticallyShowProfilePicture,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
onClick = { nav(route) },
|
||||
)
|
||||
}
|
||||
@@ -219,7 +216,7 @@ private fun ChannelTitleWithLabelInfo(
|
||||
channelName: String,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
val label = stringResource(id = R.string.public_chat)
|
||||
val label = stringRes(id = R.string.public_chat)
|
||||
val placeHolderColor = MaterialTheme.colorScheme.placeholderText
|
||||
val channelNameAndBoostInfo =
|
||||
remember(channelName) {
|
||||
@@ -305,7 +302,7 @@ fun RoomNameDisplay(
|
||||
.distinctUntilChanged()
|
||||
.observeAsState(accountViewModel.userProfile().privateChatrooms[room]?.subject)
|
||||
|
||||
Crossfade(targetState = roomSubject, modifier, label = "RoomNameDisplay") {
|
||||
CrossfadeIfEnabled(targetState = roomSubject, modifier, label = "RoomNameDisplay", accountViewModel = accountViewModel) {
|
||||
if (!it.isNullOrBlank()) {
|
||||
if (room.users.size > 1) {
|
||||
DisplayRoomSubject(it)
|
||||
@@ -337,7 +334,7 @@ private fun DisplayUserAndSubject(
|
||||
maxLines = 1,
|
||||
)
|
||||
LoadUser(baseUserHex = user, accountViewModel = accountViewModel) {
|
||||
it?.let { UsernameDisplay(it, Modifier.weight(1f)) }
|
||||
it?.let { UsernameDisplay(it, Modifier.weight(1f), accountViewModel = accountViewModel) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,14 +351,14 @@ fun DisplayUserSetAsSubject(
|
||||
// Regular Design
|
||||
Row {
|
||||
LoadUser(baseUserHex = userList[0], accountViewModel) {
|
||||
it?.let { UsernameDisplay(it, Modifier.weight(1f), fontWeight = fontWeight) }
|
||||
it?.let { UsernameDisplay(it, Modifier.weight(1f), fontWeight = fontWeight, accountViewModel = accountViewModel) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Row {
|
||||
userList.take(4).forEachIndexedExtended { index, isFirst, isLast, value ->
|
||||
LoadUser(baseUserHex = value, accountViewModel) {
|
||||
it?.let { ShortUsernameDisplay(baseUser = it, fontWeight = fontWeight) }
|
||||
it?.let { ShortUsernameDisplay(baseUser = it, fontWeight = fontWeight, accountViewModel = accountViewModel) }
|
||||
}
|
||||
|
||||
if (!isLast) {
|
||||
@@ -395,6 +392,7 @@ fun ShortUsernameDisplay(
|
||||
baseUser: User,
|
||||
weight: Modifier = Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Bold,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val userName by
|
||||
baseUser
|
||||
@@ -404,7 +402,7 @@ fun ShortUsernameDisplay(
|
||||
.distinctUntilChanged()
|
||||
.observeAsState(baseUser.toBestShortFirstName())
|
||||
|
||||
Crossfade(targetState = userName, modifier = weight) {
|
||||
CrossfadeIfEnabled(targetState = userName, modifier = weight, accountViewModel = accountViewModel) {
|
||||
CreateTextWithEmoji(
|
||||
text = it,
|
||||
tags = baseUser.info?.tags,
|
||||
@@ -459,6 +457,7 @@ fun ChannelName(
|
||||
channelLastContent: String?,
|
||||
hasNewMessages: MutableState<Boolean>,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
ChannelName(
|
||||
@@ -466,9 +465,10 @@ fun ChannelName(
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = channelIdHex,
|
||||
model = channelPicture,
|
||||
contentDescription = stringResource(R.string.channel_image),
|
||||
contentDescription = stringRes(R.string.channel_image),
|
||||
modifier = AccountPictureModifier,
|
||||
loadProfilePicture = loadProfilePicture,
|
||||
loadRobohash = loadRobohash,
|
||||
)
|
||||
},
|
||||
channelTitle,
|
||||
@@ -506,7 +506,7 @@ fun ChannelName(
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
stringResource(R.string.referenced_event_not_found),
|
||||
stringRes(R.string.referenced_event_not_found),
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
||||
@@ -46,7 +46,6 @@ 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.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -54,7 +53,6 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.vitorpamplona.amethyst.R
|
||||
@@ -65,6 +63,7 @@ import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ChatBubbleMaxSizeModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.ChatBubbleShapeMe
|
||||
import com.vitorpamplona.amethyst.ui.theme.ChatBubbleShapeThem
|
||||
@@ -78,7 +77,7 @@ import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
|
||||
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing5dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size5dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size5Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdTopPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.chatAuthorBox
|
||||
@@ -173,7 +172,7 @@ fun NormalChatNote(
|
||||
ChatBubbleLayout(
|
||||
isLoggedInUser = isLoggedInUser,
|
||||
innerQuote = innerQuote,
|
||||
isSimplified = accountViewModel.settings.featureSet == FeatureSetType.SIMPLIFIED,
|
||||
isComplete = accountViewModel.settings.featureSet == FeatureSetType.COMPLETE,
|
||||
hasDetailsToShow = note.zaps.isNotEmpty() || note.zapPayments.isNotEmpty() || note.reactions.isNotEmpty(),
|
||||
drawAuthorInfo = drawAuthorInfo,
|
||||
parentBackgroundColor = parentBackgroundColor,
|
||||
@@ -249,7 +248,7 @@ fun NormalChatNote(
|
||||
fun ChatBubbleLayout(
|
||||
isLoggedInUser: Boolean,
|
||||
innerQuote: Boolean,
|
||||
isSimplified: Boolean,
|
||||
isComplete: Boolean,
|
||||
hasDetailsToShow: Boolean,
|
||||
drawAuthorInfo: Boolean,
|
||||
parentBackgroundColor: MutableState<Color>? = null,
|
||||
@@ -300,10 +299,10 @@ fun ChatBubbleLayout(
|
||||
val showDetails =
|
||||
remember {
|
||||
mutableStateOf(
|
||||
if (isSimplified) {
|
||||
hasDetailsToShow
|
||||
} else {
|
||||
if (isComplete) {
|
||||
true
|
||||
} else {
|
||||
hasDetailsToShow
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -313,7 +312,7 @@ fun ChatBubbleLayout(
|
||||
Modifier.combinedClickable(
|
||||
onClick = {
|
||||
if (!onClick()) {
|
||||
if (isSimplified) {
|
||||
if (!isComplete) {
|
||||
showDetails.value = !showDetails.value
|
||||
}
|
||||
}
|
||||
@@ -376,7 +375,7 @@ private fun BubblePreview() {
|
||||
ChatBubbleLayout(
|
||||
isLoggedInUser = false,
|
||||
innerQuote = false,
|
||||
isSimplified = false,
|
||||
isComplete = true,
|
||||
hasDetailsToShow = true,
|
||||
drawAuthorInfo = true,
|
||||
parentBackgroundColor = backgroundBubbleColor,
|
||||
@@ -406,7 +405,7 @@ private fun BubblePreview() {
|
||||
ChatBubbleLayout(
|
||||
isLoggedInUser = true,
|
||||
innerQuote = false,
|
||||
isSimplified = false,
|
||||
isComplete = true,
|
||||
hasDetailsToShow = true,
|
||||
drawAuthorInfo = true,
|
||||
parentBackgroundColor = backgroundBubbleColor,
|
||||
@@ -623,10 +622,8 @@ fun IncognitoBadge(baseNote: Note) {
|
||||
|
||||
@Composable
|
||||
fun ChatTimeAgo(baseNote: Note) {
|
||||
val nowStr = stringResource(id = R.string.now)
|
||||
|
||||
val time by
|
||||
remember(baseNote) { derivedStateOf { timeAgoShort(baseNote.createdAt() ?: 0, nowStr) } }
|
||||
val nowStr = stringRes(id = R.string.now)
|
||||
val time = remember(baseNote) { timeAgoShort(baseNote.createdAt() ?: 0, nowStr) }
|
||||
|
||||
Text(
|
||||
text = time,
|
||||
@@ -668,7 +665,7 @@ private fun RenderRegularTextNote(
|
||||
}
|
||||
} else {
|
||||
TranslatableRichTextViewer(
|
||||
content = stringResource(id = R.string.could_not_decrypt_the_message),
|
||||
content = stringRes(id = R.string.could_not_decrypt_the_message),
|
||||
canPreview = true,
|
||||
quotesLeft = 0,
|
||||
modifier = HalfTopPadding,
|
||||
@@ -690,11 +687,11 @@ private fun RenderChangeChannelMetadataNote(note: Note) {
|
||||
val channelInfo = noteEvent.channelInfo()
|
||||
val text =
|
||||
note.author?.toBestDisplayName().toString() +
|
||||
" ${stringResource(R.string.changed_chat_name_to)} '" +
|
||||
" ${stringRes(R.string.changed_chat_name_to)} '" +
|
||||
(channelInfo.name ?: "") +
|
||||
"', ${stringResource(R.string.description_to)} '" +
|
||||
"', ${stringRes(R.string.description_to)} '" +
|
||||
(channelInfo.about ?: "") +
|
||||
"', ${stringResource(R.string.and_picture_to)} '" +
|
||||
"', ${stringRes(R.string.and_picture_to)} '" +
|
||||
(channelInfo.picture ?: "") +
|
||||
"'"
|
||||
|
||||
@@ -711,11 +708,11 @@ private fun RenderCreateChannelNote(note: Note) {
|
||||
|
||||
val text =
|
||||
note.author?.toBestDisplayName().toString() +
|
||||
" ${stringResource(R.string.created)} " +
|
||||
" ${stringRes(R.string.created)} " +
|
||||
(channelInfo.name ?: "") +
|
||||
" ${stringResource(R.string.with_description_of)} '" +
|
||||
" ${stringRes(R.string.with_description_of)} '" +
|
||||
(channelInfo.about ?: "") +
|
||||
"', ${stringResource(R.string.and_picture)} '" +
|
||||
"', ${stringRes(R.string.and_picture)} '" +
|
||||
(channelInfo.picture ?: "") +
|
||||
"'"
|
||||
|
||||
@@ -769,7 +766,11 @@ private fun WatchAndDisplayUser(
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
|
||||
ObserveAndDisplayFollowingMark(author.pubkeyHex, Size5dp, accountViewModel)
|
||||
WatchUserFollows(author.pubkeyHex, accountViewModel) { newFollowingState ->
|
||||
if (newFollowingState) {
|
||||
FollowingIcon(Size5Modifier)
|
||||
}
|
||||
}
|
||||
},
|
||||
name = {
|
||||
if (userState != null) {
|
||||
|
||||
@@ -43,10 +43,8 @@ import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.PushPin
|
||||
import androidx.compose.material.icons.filled.Report
|
||||
import androidx.compose.material.icons.materialIcon
|
||||
import androidx.compose.material.icons.materialPath
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material.icons.outlined.ArrowForwardIos
|
||||
import androidx.compose.material.icons.outlined.BarChart
|
||||
import androidx.compose.material.icons.outlined.Bolt
|
||||
import androidx.compose.material.icons.outlined.PlayCircle
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -56,9 +54,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -66,9 +62,18 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.Cashu
|
||||
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
|
||||
import com.vitorpamplona.amethyst.commons.labels.Following
|
||||
import com.vitorpamplona.amethyst.commons.icons.Following
|
||||
import com.vitorpamplona.amethyst.commons.icons.Like
|
||||
import com.vitorpamplona.amethyst.commons.icons.Liked
|
||||
import com.vitorpamplona.amethyst.commons.icons.Reply
|
||||
import com.vitorpamplona.amethyst.commons.icons.Repost
|
||||
import com.vitorpamplona.amethyst.commons.icons.Reposted
|
||||
import com.vitorpamplona.amethyst.commons.icons.Search
|
||||
import com.vitorpamplona.amethyst.commons.icons.Zap
|
||||
import com.vitorpamplona.amethyst.commons.icons.ZapSplit
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size19Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||
@@ -79,7 +84,7 @@ import com.vitorpamplona.amethyst.ui.theme.subtleButton
|
||||
fun AmethystIcon(iconSize: Dp) {
|
||||
Icon(
|
||||
imageVector = CustomHashTagIcons.Amethyst,
|
||||
contentDescription = stringResource(id = R.string.app_logo),
|
||||
contentDescription = stringRes(id = R.string.app_logo),
|
||||
modifier = Modifier.size(iconSize),
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
@@ -87,10 +92,15 @@ fun AmethystIcon(iconSize: Dp) {
|
||||
|
||||
@Composable
|
||||
fun FollowingIcon(iconSize: Dp) {
|
||||
FollowingIcon(Modifier.size(iconSize))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FollowingIcon(modifier: Modifier) {
|
||||
Icon(
|
||||
imageVector = Following,
|
||||
contentDescription = stringResource(id = R.string.following),
|
||||
modifier = Modifier.size(iconSize),
|
||||
contentDescription = stringRes(id = R.string.following),
|
||||
modifier = modifier,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
@@ -99,7 +109,7 @@ fun FollowingIcon(iconSize: Dp) {
|
||||
fun ArrowBackIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
contentDescription = stringRes(R.string.back),
|
||||
tint = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
}
|
||||
@@ -121,7 +131,7 @@ fun DownloadForOfflineIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.DownloadForOffline,
|
||||
contentDescription = stringResource(id = R.string.accessibility_download_for_offline),
|
||||
contentDescription = stringRes(id = R.string.accessibility_download_for_offline),
|
||||
modifier = remember(iconSize) { Modifier.size(iconSize) },
|
||||
tint = tint,
|
||||
)
|
||||
@@ -131,7 +141,7 @@ fun DownloadForOfflineIcon(
|
||||
fun HashCheckIcon(iconSize: Dp) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.original),
|
||||
contentDescription = stringResource(id = R.string.hash_verification_passed),
|
||||
contentDescription = stringRes(id = R.string.hash_verification_passed),
|
||||
modifier = remember(iconSize) { Modifier.size(iconSize) },
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
@@ -141,19 +151,22 @@ fun HashCheckIcon(iconSize: Dp) {
|
||||
fun HashCheckFailedIcon(iconSize: Dp) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Report,
|
||||
contentDescription = stringResource(id = R.string.hash_verification_failed),
|
||||
contentDescription = stringRes(id = R.string.hash_verification_failed),
|
||||
modifier = remember(iconSize) { Modifier.size(iconSize) },
|
||||
tint = Color.Red,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LikedIcon(modifier: Modifier) {
|
||||
fun LikedIcon(
|
||||
modifier: Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_liked),
|
||||
null,
|
||||
imageVector = Liked,
|
||||
stringRes(id = R.string.like_description),
|
||||
modifier = modifier,
|
||||
tint = Color.Unspecified,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -163,21 +176,34 @@ fun LikeIcon(
|
||||
grayTint: Color,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_like),
|
||||
contentDescription = stringResource(id = R.string.like_description),
|
||||
imageVector = Like,
|
||||
contentDescription = stringRes(id = R.string.like_description),
|
||||
modifier = iconSizeModifier,
|
||||
tint = grayTint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RepostIcon(
|
||||
modifier: Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Repost,
|
||||
contentDescription = stringRes(id = R.string.boost_or_quote_description),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RepostedIcon(
|
||||
modifier: Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_retweeted),
|
||||
contentDescription = stringResource(id = R.string.boost_or_quote_description),
|
||||
imageVector = Reposted,
|
||||
contentDescription = stringRes(id = R.string.boost_or_quote_description),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -190,7 +216,7 @@ fun LightningAddressIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Bolt,
|
||||
contentDescription = stringResource(R.string.lightning_address),
|
||||
contentDescription = stringRes(R.string.lightning_address),
|
||||
tint = tint,
|
||||
modifier = modifier,
|
||||
)
|
||||
@@ -213,8 +239,10 @@ fun ReactionRowIconPreview() {
|
||||
CommentIcon(Size20Modifier, Color.Unspecified)
|
||||
RepostedIcon(Size20Modifier)
|
||||
LikeIcon(Size20Modifier, Color.Unspecified)
|
||||
OutlinedZapIcon(Size20Modifier)
|
||||
ZapIcon(Size20Modifier)
|
||||
ZappedIcon(Size20Modifier)
|
||||
ShareIcon(Size20Modifier, Color.Unspecified)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,17 +254,44 @@ fun ZapIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Bolt,
|
||||
contentDescription = stringResource(contentDescriptor),
|
||||
contentDescription = stringRes(contentDescriptor),
|
||||
tint = tint,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OutlinedZapIcon(
|
||||
modifier: Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
contentDescriptor: Int = R.string.zap_description,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Zap,
|
||||
contentDescription = stringRes(contentDescriptor),
|
||||
tint = tint,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShareIcon(
|
||||
modifier: Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
modifier = modifier,
|
||||
contentDescription = stringRes(R.string.share_or_save),
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CashuIcon(modifier: Modifier) {
|
||||
Icon(
|
||||
imageVector = CustomHashTagIcons.Cashu,
|
||||
"Cashu",
|
||||
stringRes(R.string.cashu),
|
||||
tint = Color.Unspecified,
|
||||
modifier = modifier,
|
||||
)
|
||||
@@ -249,7 +304,7 @@ fun CopyIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ContentCopy,
|
||||
stringResource(id = R.string.copy_to_clipboard),
|
||||
stringRes(id = R.string.copy_to_clipboard),
|
||||
tint = tint,
|
||||
modifier = modifier,
|
||||
)
|
||||
@@ -262,7 +317,7 @@ fun OpenInNewIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
|
||||
stringResource(id = R.string.copy_to_clipboard),
|
||||
stringRes(id = R.string.copy_to_clipboard),
|
||||
tint = tint,
|
||||
modifier = modifier,
|
||||
)
|
||||
@@ -275,7 +330,7 @@ fun ExpandLessIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ExpandLess,
|
||||
contentDescription = stringResource(id = contentDescriptor),
|
||||
contentDescription = stringRes(id = contentDescriptor),
|
||||
modifier = modifier,
|
||||
tint = MaterialTheme.colorScheme.subtleButton,
|
||||
)
|
||||
@@ -288,7 +343,7 @@ fun ExpandMoreIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ExpandMore,
|
||||
contentDescription = stringResource(id = contentDescriptor),
|
||||
contentDescription = stringRes(id = contentDescriptor),
|
||||
modifier = modifier,
|
||||
tint = MaterialTheme.colorScheme.subtleButton,
|
||||
)
|
||||
@@ -300,31 +355,18 @@ fun CommentIcon(
|
||||
tint: Color,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_comment),
|
||||
contentDescription = stringResource(id = R.string.reply_description),
|
||||
imageVector = Reply,
|
||||
contentDescription = stringRes(id = R.string.reply_description),
|
||||
modifier = iconSizeModifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ViewCountIcon(
|
||||
modifier: Modifier,
|
||||
tint: Color = Color.Unspecified,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.BarChart,
|
||||
null,
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PollIcon() {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_poll),
|
||||
contentDescription = stringResource(id = R.string.poll),
|
||||
contentDescription = stringRes(id = R.string.poll),
|
||||
modifier = Size20Modifier,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@@ -334,7 +376,7 @@ fun PollIcon() {
|
||||
fun RegularPostIcon() {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_lists),
|
||||
contentDescription = stringResource(id = R.string.disable_poll),
|
||||
contentDescription = stringRes(id = R.string.disable_poll),
|
||||
modifier = Size20Modifier,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@@ -344,7 +386,7 @@ fun RegularPostIcon() {
|
||||
fun CancelIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Cancel,
|
||||
contentDescription = stringResource(id = R.string.cancel),
|
||||
contentDescription = stringRes(id = R.string.cancel),
|
||||
modifier = Size30Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
@@ -354,7 +396,7 @@ fun CancelIcon() {
|
||||
fun CloseIcon() {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_close),
|
||||
contentDescription = stringResource(id = R.string.cancel),
|
||||
contentDescription = stringRes(id = R.string.cancel),
|
||||
modifier = Size20Modifier,
|
||||
)
|
||||
}
|
||||
@@ -363,7 +405,7 @@ fun CloseIcon() {
|
||||
fun MutedIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.VolumeOff,
|
||||
contentDescription = stringResource(id = R.string.muted_button),
|
||||
contentDescription = stringRes(id = R.string.muted_button),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Size30Modifier,
|
||||
)
|
||||
@@ -373,7 +415,7 @@ fun MutedIcon() {
|
||||
fun MuteIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.VolumeUp,
|
||||
contentDescription = stringResource(id = R.string.mute_button),
|
||||
contentDescription = stringRes(id = R.string.mute_button),
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Size30Modifier,
|
||||
)
|
||||
@@ -385,8 +427,8 @@ fun SearchIcon(
|
||||
tint: Color = Color.Unspecified,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_search),
|
||||
contentDescription = stringResource(id = R.string.search_button),
|
||||
imageVector = Search,
|
||||
contentDescription = stringRes(id = R.string.search_button),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -399,7 +441,7 @@ fun PlayIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.PlayCircle,
|
||||
contentDescription = stringResource(id = R.string.accessibility_play_username),
|
||||
contentDescription = stringRes(id = R.string.accessibility_play_username),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -425,7 +467,7 @@ fun LyricsIcon(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.lyrics_on),
|
||||
contentDescription = stringResource(id = R.string.accessibility_lyrics_on),
|
||||
contentDescription = stringRes(id = R.string.accessibility_lyrics_on),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -438,7 +480,7 @@ fun LyricsOffIcon(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.lyrics_off),
|
||||
contentDescription = stringResource(id = R.string.accessibility_lyrics_off),
|
||||
contentDescription = stringRes(id = R.string.accessibility_lyrics_off),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -448,7 +490,7 @@ fun LyricsOffIcon(
|
||||
fun ClearTextIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Clear,
|
||||
contentDescription = stringResource(R.string.clear),
|
||||
contentDescription = stringRes(R.string.clear),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -459,18 +501,18 @@ fun LinkIcon(
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Link,
|
||||
contentDescription = stringResource(R.string.website),
|
||||
contentDescription = stringRes(R.string.website),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VerticalDotsIcon(contentDescriptor: Int? = null) {
|
||||
fun VerticalDotsIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = contentDescriptor?.let { stringResource(id = it) },
|
||||
modifier = Size18Modifier,
|
||||
contentDescription = stringRes(id = R.string.note_options),
|
||||
modifier = Size19Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
@@ -479,7 +521,7 @@ fun VerticalDotsIcon(contentDescriptor: Int? = null) {
|
||||
fun NIP05CheckingIcon(modifier: Modifier) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Downloading,
|
||||
contentDescription = stringResource(id = R.string.nip05_checking),
|
||||
contentDescription = stringRes(id = R.string.nip05_checking),
|
||||
modifier = modifier,
|
||||
tint = Color.Yellow,
|
||||
)
|
||||
@@ -489,7 +531,7 @@ fun NIP05CheckingIcon(modifier: Modifier) {
|
||||
fun NIP05VerifiedIcon(modifier: Modifier) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.nip_05),
|
||||
contentDescription = stringResource(id = R.string.nip05_verified),
|
||||
contentDescription = stringRes(id = R.string.nip05_verified),
|
||||
modifier = modifier,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
@@ -499,7 +541,7 @@ fun NIP05VerifiedIcon(modifier: Modifier) {
|
||||
fun NIP05FailedVerification(modifier: Modifier) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Report,
|
||||
contentDescription = stringResource(id = R.string.nip05_failed),
|
||||
contentDescription = stringRes(id = R.string.nip05_failed),
|
||||
modifier = modifier,
|
||||
tint = Color.Red,
|
||||
)
|
||||
@@ -512,7 +554,7 @@ fun IncognitoIconOn(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.incognito),
|
||||
contentDescription = stringResource(id = R.string.accessibility_turn_off_sealed_message),
|
||||
contentDescription = stringRes(id = R.string.accessibility_turn_off_sealed_message),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -525,7 +567,7 @@ fun IncognitoIconOff(
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.incognito_off),
|
||||
contentDescription = stringResource(id = R.string.accessibility_turn_on_sealed_message),
|
||||
contentDescription = stringRes(id = R.string.accessibility_turn_on_sealed_message),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -537,8 +579,8 @@ fun ZapSplitIcon(
|
||||
tint: Color = BitcoinOrange,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ZapSplitVector,
|
||||
contentDescription = stringResource(id = R.string.zap_split_title),
|
||||
imageVector = ZapSplit,
|
||||
contentDescription = stringRes(id = R.string.zap_split_title),
|
||||
modifier = modifier,
|
||||
tint = tint,
|
||||
)
|
||||
@@ -549,56 +591,29 @@ fun ZapSplitIcon(
|
||||
fun ZapSplitPreview() {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Box(
|
||||
Modifier.height(20.dp).width(25.dp),
|
||||
Modifier
|
||||
.height(20.dp)
|
||||
.width(25.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Bolt,
|
||||
contentDescription = stringResource(id = R.string.zaps),
|
||||
modifier = Modifier.size(20.dp).align(Alignment.CenterStart),
|
||||
contentDescription = stringRes(id = R.string.zaps),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(20.dp)
|
||||
.align(Alignment.CenterStart),
|
||||
tint = BitcoinOrange,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.ArrowForwardIos,
|
||||
contentDescription = stringResource(id = R.string.zaps),
|
||||
modifier = Modifier.size(13.dp).align(Alignment.CenterEnd),
|
||||
contentDescription = stringRes(id = R.string.zaps),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(13.dp)
|
||||
.align(Alignment.CenterEnd),
|
||||
tint = BitcoinOrange,
|
||||
)
|
||||
}
|
||||
ZapSplitIcon(tint = BitcoinOrange)
|
||||
}
|
||||
}
|
||||
|
||||
public val ZapSplitVector: ImageVector
|
||||
get() {
|
||||
if (zapSplit != null) {
|
||||
return zapSplit!!
|
||||
}
|
||||
zapSplit =
|
||||
materialIcon(name = "ZapSplit") {
|
||||
materialPath {
|
||||
moveTo(7.0f, 21.0f)
|
||||
horizontalLineToRelative(-1.0f)
|
||||
lineToRelative(1.0f, -7.0f)
|
||||
horizontalLineTo(3.5f)
|
||||
curveToRelative(-0.88f, 0.0f, -0.33f, -0.75f, -0.31f, -0.78f)
|
||||
curveTo(4.48f, 10.94f, 6.42f, 7.54f, 9.01f, 3.0f)
|
||||
horizontalLineToRelative(1.0f)
|
||||
lineToRelative(-1.0f, 7.0f)
|
||||
horizontalLineToRelative(3.51f)
|
||||
curveToRelative(0.4f, 0.0f, 0.62f, 0.19f, 0.4f, 0.66f)
|
||||
curveTo(8.97f, 17.55f, 7.0f, 21.0f, 7.0f, 21.0f)
|
||||
close()
|
||||
moveTo(14.59f, 16.59f)
|
||||
lineTo(19.17f, 12.0f)
|
||||
lineTo(14.59f, 7.41f)
|
||||
lineTo(16.0f, 6.0f)
|
||||
lineToRelative(6.0f, 6.0f)
|
||||
lineToRelative(-6.0f, 6.0f)
|
||||
lineToRelative(-1.41f, -1.41f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
return zapSplit!!
|
||||
}
|
||||
|
||||
private var zapSplit: ImageVector? = null
|
||||
|
||||
@@ -68,25 +68,23 @@ fun MessageSetCompose(
|
||||
|
||||
val columnModifier =
|
||||
remember(backgroundColor.value) {
|
||||
Modifier.background(backgroundColor.value)
|
||||
Modifier
|
||||
.background(backgroundColor.value)
|
||||
.padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = 10.dp,
|
||||
)
|
||||
.combinedClickable(
|
||||
).combinedClickable(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
routeFor(
|
||||
baseNote,
|
||||
accountViewModel.userProfile(),
|
||||
)
|
||||
?.let { nav(it) }
|
||||
)?.let { nav(it) }
|
||||
}
|
||||
},
|
||||
onLongClick = enablePopup,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
).fillMaxWidth()
|
||||
}
|
||||
|
||||
Column(columnModifier) {
|
||||
@@ -111,7 +109,9 @@ fun MessageSetCompose(
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
NoteDropDownMenu(baseNote, popupExpanded, null, accountViewModel, nav)
|
||||
if (popupExpanded.value) {
|
||||
NoteDropDownMenu(baseNote, { popupExpanded.value = false }, null, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,12 +53,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.NoteState
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
@@ -71,6 +71,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.NoteDropDownMenu
|
||||
import com.vitorpamplona.amethyst.ui.screen.CombinedZap
|
||||
import com.vitorpamplona.amethyst.ui.screen.MultiSetCard
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfTopPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.NotificationIconModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.NotificationIconModifierSmaller
|
||||
@@ -105,7 +106,6 @@ fun MultiSetCompose(
|
||||
val baseNote = remember { multiSetCard.note }
|
||||
|
||||
val popupExpanded = remember { mutableStateOf(false) }
|
||||
val enablePopup = remember { { popupExpanded.value = true } }
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -125,7 +125,7 @@ fun MultiSetCompose(
|
||||
onClick = {
|
||||
scope.launch { routeFor(baseNote, accountViewModel.userProfile())?.let { nav(it) } }
|
||||
},
|
||||
onLongClick = enablePopup,
|
||||
onLongClick = { popupExpanded.value = true },
|
||||
).padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
@@ -151,7 +151,9 @@ fun MultiSetCompose(
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
NoteDropDownMenu(baseNote, popupExpanded, null, accountViewModel, nav)
|
||||
if (popupExpanded.value) {
|
||||
NoteDropDownMenu(baseNote, { popupExpanded.value = false }, null, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,7 +495,7 @@ private fun BoxedAuthor(
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Box(modifier = Size35Modifier.clickable(onClick = { nav(authorRouteFor(note)) })) {
|
||||
WatchAuthorWithBlank(note, Size35Modifier) { author ->
|
||||
WatchAuthorWithBlank(note, Size35Modifier, accountViewModel) { author ->
|
||||
WatchUserMetadataAndFollowsAndRenderUserProfilePictureOrDefaultAuthor(
|
||||
author,
|
||||
accountViewModel,
|
||||
@@ -510,7 +512,7 @@ fun WatchUserMetadataAndFollowsAndRenderUserProfilePictureOrDefaultAuthor(
|
||||
if (author != null) {
|
||||
WatchUserMetadataAndFollowsAndRenderUserProfilePicture(author, accountViewModel)
|
||||
} else {
|
||||
DisplayBlankAuthor(Size35dp)
|
||||
DisplayBlankAuthor(Size35dp, accountViewModel = accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,26 +522,23 @@ fun WatchUserMetadataAndFollowsAndRenderUserProfilePicture(
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
WatchUserMetadata(author) { baseUserPicture ->
|
||||
// Crossfade(targetState = baseUserPicture) { userPicture ->
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = author.pubkeyHex,
|
||||
model = baseUserPicture,
|
||||
contentDescription = stringResource(id = R.string.profile_image),
|
||||
contentDescription = stringRes(id = R.string.profile_image),
|
||||
modifier = MaterialTheme.colorScheme.profile35dpModifier,
|
||||
contentScale = ContentScale.Crop,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
// }
|
||||
}
|
||||
|
||||
WatchUserFollows(author.pubkeyHex, accountViewModel) { isFollowing ->
|
||||
// Crossfade(targetState = isFollowing) {
|
||||
if (isFollowing) {
|
||||
Box(modifier = Size35Modifier, contentAlignment = Alignment.TopEnd) {
|
||||
FollowingIcon(Size10dp)
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -54,6 +53,7 @@ import com.vitorpamplona.amethyst.commons.hashtags.Tunestr
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadStatuses
|
||||
@@ -132,10 +132,11 @@ fun ObserveDisplayNip05Status(
|
||||
val nip05 by baseUser.live().nip05Changes.observeAsState(baseUser.nip05())
|
||||
|
||||
LoadStatuses(baseUser, accountViewModel) { statuses ->
|
||||
Crossfade(
|
||||
CrossfadeIfEnabled(
|
||||
targetState = nip05,
|
||||
modifier = columnModifier,
|
||||
label = "ObserveDisplayNip05StatusCrossfade",
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
VerifyAndDisplayNIP05OrStatusLine(
|
||||
it,
|
||||
@@ -165,11 +166,11 @@ private fun VerifyAndDisplayNIP05OrStatusLine(
|
||||
nip05VerificationAsAState(baseUser.info!!, baseUser.pubkeyHex, accountViewModel)
|
||||
|
||||
if (nip05Verified.value != true) {
|
||||
DisplayNIP05(nip05, nip05Verified)
|
||||
DisplayNIP05(nip05, nip05Verified, accountViewModel)
|
||||
} else if (!statuses.isEmpty()) {
|
||||
RotateStatuses(statuses, accountViewModel, nav)
|
||||
} else {
|
||||
DisplayNIP05(nip05, nip05Verified)
|
||||
DisplayNIP05(nip05, nip05Verified, accountViewModel)
|
||||
}
|
||||
} else {
|
||||
if (!statuses.isEmpty()) {
|
||||
@@ -271,8 +272,7 @@ fun DisplayStatus(
|
||||
routeFor(
|
||||
note,
|
||||
accountViewModel.userProfile(),
|
||||
)
|
||||
?.let { nav(it) }
|
||||
)?.let { nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
@@ -294,8 +294,7 @@ fun DisplayStatus(
|
||||
routeFor(
|
||||
it,
|
||||
accountViewModel.userProfile(),
|
||||
)
|
||||
?.let { nav(it) }
|
||||
)?.let { nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
@@ -314,6 +313,7 @@ fun DisplayStatus(
|
||||
private fun DisplayNIP05(
|
||||
nip05: String,
|
||||
nip05Verified: MutableState<Boolean?>,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val uri = LocalUriHandler.current
|
||||
val (user, domain) =
|
||||
@@ -336,7 +336,7 @@ private fun DisplayNIP05(
|
||||
)
|
||||
}
|
||||
|
||||
NIP05VerifiedSymbol(nip05Verified, NIP05IconSize)
|
||||
NIP05VerifiedSymbol(nip05Verified, NIP05IconSize, accountViewModel)
|
||||
|
||||
ClickableText(
|
||||
text = remember(nip05) { AnnotatedString(domain) },
|
||||
@@ -352,8 +352,9 @@ private fun DisplayNIP05(
|
||||
private fun NIP05VerifiedSymbol(
|
||||
nip05Verified: MutableState<Boolean?>,
|
||||
modifier: Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Crossfade(targetState = nip05Verified.value) {
|
||||
CrossfadeIfEnabled(targetState = nip05Verified.value, accountViewModel = accountViewModel) {
|
||||
when (it) {
|
||||
null -> NIP05CheckingIcon(modifier = modifier)
|
||||
true -> NIP05VerifiedIcon(modifier = modifier)
|
||||
@@ -373,7 +374,7 @@ fun DisplayNip05ProfileStatus(
|
||||
if (nip05.split("@").size <= 2) {
|
||||
val nip05Verified = nip05VerificationAsAState(user.info!!, user.pubkeyHex, accountViewModel)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
NIP05VerifiedSymbol(nip05Verified, Size16Modifier)
|
||||
NIP05VerifiedSymbol(nip05Verified, Size16Modifier, accountViewModel)
|
||||
var domainPadStart = 5.dp
|
||||
|
||||
val (user, domain) =
|
||||
|
||||
@@ -46,7 +46,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
@@ -113,6 +112,7 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderWikiContent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.VideoDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.RenderChannelHeader
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfDoubleVertSpacer
|
||||
@@ -461,7 +461,14 @@ fun InnerNoteWithReactions(
|
||||
horizontalArrangement = RowColSpacing10dp,
|
||||
) {
|
||||
if (notBoostedNorQuote) {
|
||||
AuthorAndRelayInformation(baseNote, accountViewModel, nav)
|
||||
Column(WidthAuthorPictureModifier, verticalArrangement = RowColSpacing5dp) {
|
||||
// Draws the boosted picture outside the boosted card.
|
||||
Box(modifier = Size55Modifier, contentAlignment = Alignment.BottomEnd) {
|
||||
RenderAuthorImages(baseNote, nav, accountViewModel)
|
||||
}
|
||||
|
||||
BadgeBox(baseNote, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
@@ -470,7 +477,7 @@ fun InnerNoteWithReactions(
|
||||
baseNote.event !is GenericRepostEvent &&
|
||||
!isBoostedNote &&
|
||||
!isQuotedNote &&
|
||||
accountViewModel.settings.featureSet != FeatureSetType.SIMPLIFIED
|
||||
accountViewModel.settings.featureSet == FeatureSetType.COMPLETE
|
||||
NoteBody(
|
||||
baseNote = baseNote,
|
||||
showAuthorPicture = isQuotedNote,
|
||||
@@ -968,9 +975,9 @@ fun FirstUserInfoRow(
|
||||
if (showAuthorPicture) {
|
||||
NoteAuthorPicture(baseNote, nav, accountViewModel, Size25dp)
|
||||
Spacer(HalfPadding)
|
||||
NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor)
|
||||
NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor, accountViewModel = accountViewModel)
|
||||
} else {
|
||||
NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor)
|
||||
NoteUsernameDisplay(baseNote, Modifier.weight(1f), textColor = textColor, accountViewModel = accountViewModel)
|
||||
}
|
||||
|
||||
if (isCommunityPost) {
|
||||
@@ -1051,29 +1058,13 @@ fun observeEdits(
|
||||
return editState
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AuthorAndRelayInformation(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
Column(WidthAuthorPictureModifier, verticalArrangement = RowColSpacing5dp) {
|
||||
// Draws the boosted picture outside the boosted card.
|
||||
Box(modifier = Size55Modifier, contentAlignment = Alignment.BottomEnd) {
|
||||
RenderAuthorImages(baseNote, nav, accountViewModel)
|
||||
}
|
||||
|
||||
BadgeBox(baseNote, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BadgeBox(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
if (accountViewModel.settings.featureSet != FeatureSetType.SIMPLIFIED) {
|
||||
if (accountViewModel.settings.featureSet == FeatureSetType.COMPLETE) {
|
||||
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
|
||||
baseNote.replyTo?.lastOrNull()?.let { RelayBadges(it, accountViewModel, nav) }
|
||||
} else {
|
||||
@@ -1088,9 +1079,7 @@ private fun RenderAuthorImages(
|
||||
nav: (String) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val isRepost = baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent
|
||||
|
||||
if (isRepost) {
|
||||
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
|
||||
val baseRepost = baseNote.replyTo?.lastOrNull()
|
||||
if (baseRepost != null) {
|
||||
RepostNoteAuthorPicture(baseNote, baseRepost, accountViewModel, nav)
|
||||
@@ -1108,6 +1097,7 @@ private fun RenderAuthorImages(
|
||||
ChannelNotePicture(
|
||||
channel,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1118,6 +1108,7 @@ private fun RenderAuthorImages(
|
||||
private fun ChannelNotePicture(
|
||||
baseChannel: Channel,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
) {
|
||||
val model by
|
||||
baseChannel.live
|
||||
@@ -1129,9 +1120,10 @@ private fun ChannelNotePicture(
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = baseChannel.idHex,
|
||||
model = model,
|
||||
contentDescription = stringResource(R.string.group_picture),
|
||||
contentDescription = stringRes(R.string.group_picture),
|
||||
modifier = MaterialTheme.colorScheme.channelNotePictureModifier,
|
||||
loadProfilePicture = loadProfilePicture,
|
||||
loadRobohash = loadRobohash,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -89,6 +88,7 @@ import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.components.SelectTextDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.WarningColor
|
||||
import com.vitorpamplona.amethyst.ui.theme.isLight
|
||||
import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground
|
||||
@@ -260,12 +260,12 @@ private fun RenderMainPopup(
|
||||
MaterialTheme.colorScheme.secondaryButtonBackground
|
||||
}
|
||||
|
||||
val showToast = { stringResource: Int ->
|
||||
val showToast = { stringRes: Int ->
|
||||
scope.launch {
|
||||
Toast
|
||||
.makeText(
|
||||
context,
|
||||
context.getString(stringResource),
|
||||
stringRes(context, stringRes),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
@@ -284,7 +284,7 @@ private fun RenderMainPopup(
|
||||
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
|
||||
NoteQuickActionItem(
|
||||
icon = Icons.Default.ContentCopy,
|
||||
label = stringResource(R.string.quick_action_copy_text),
|
||||
label = stringRes(R.string.quick_action_copy_text),
|
||||
) {
|
||||
accountViewModel.decrypt(note) {
|
||||
clipboardManager.setText(AnnotatedString(it))
|
||||
@@ -296,7 +296,7 @@ private fun RenderMainPopup(
|
||||
VerticalDivider(color = primaryLight)
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.AlternateEmail,
|
||||
stringResource(R.string.quick_action_copy_user_id),
|
||||
stringRes(R.string.quick_action_copy_user_id),
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
clipboardManager.setText(AnnotatedString("nostr:${note.author?.pubkeyNpub()}"))
|
||||
@@ -307,7 +307,7 @@ private fun RenderMainPopup(
|
||||
VerticalDivider(color = primaryLight)
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.FormatQuote,
|
||||
stringResource(R.string.quick_action_copy_note_id),
|
||||
stringRes(R.string.quick_action_copy_note_id),
|
||||
) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
clipboardManager.setText(AnnotatedString("nostr:${note.toNEvent()}"))
|
||||
@@ -321,7 +321,7 @@ private fun RenderMainPopup(
|
||||
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.Block,
|
||||
stringResource(R.string.quick_action_block),
|
||||
stringRes(R.string.quick_action_block),
|
||||
) {
|
||||
if (accountViewModel.hideBlockAlertDialog) {
|
||||
note.author?.let { accountViewModel.hide(it) }
|
||||
@@ -339,7 +339,7 @@ private fun RenderMainPopup(
|
||||
if (isOwnNote) {
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.Delete,
|
||||
stringResource(R.string.quick_action_delete),
|
||||
stringRes(R.string.quick_action_delete),
|
||||
) {
|
||||
if (accountViewModel.hideDeleteRequestDialog) {
|
||||
accountViewModel.delete(note)
|
||||
@@ -351,7 +351,7 @@ private fun RenderMainPopup(
|
||||
} else if (isFollowingUser) {
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.PersonRemove,
|
||||
stringResource(R.string.quick_action_unfollow),
|
||||
stringRes(R.string.quick_action_unfollow),
|
||||
) {
|
||||
accountViewModel.unfollow(note.author!!)
|
||||
onDismiss()
|
||||
@@ -359,7 +359,7 @@ private fun RenderMainPopup(
|
||||
} else {
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.PersonAdd,
|
||||
stringResource(R.string.quick_action_follow),
|
||||
stringRes(R.string.quick_action_follow),
|
||||
) {
|
||||
accountViewModel.follow(note.author!!)
|
||||
onDismiss()
|
||||
@@ -369,7 +369,7 @@ private fun RenderMainPopup(
|
||||
VerticalDivider(color = primaryLight)
|
||||
NoteQuickActionItem(
|
||||
icon = ImageVector.vectorResource(id = R.drawable.relays),
|
||||
label = stringResource(R.string.broadcast),
|
||||
label = stringRes(R.string.broadcast),
|
||||
) {
|
||||
accountViewModel.broadcast(note)
|
||||
// showSelectTextDialog = true
|
||||
@@ -379,7 +379,7 @@ private fun RenderMainPopup(
|
||||
if (isOwnNote && note.isDraft()) {
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.Edit,
|
||||
stringResource(R.string.edit_draft),
|
||||
stringRes(R.string.edit_draft),
|
||||
) {
|
||||
onDismiss()
|
||||
onWantsToEditDraft()
|
||||
@@ -387,7 +387,7 @@ private fun RenderMainPopup(
|
||||
} else {
|
||||
NoteQuickActionItem(
|
||||
icon = Icons.Default.Share,
|
||||
label = stringResource(R.string.quick_action_share),
|
||||
label = stringRes(R.string.quick_action_share),
|
||||
) {
|
||||
val sendIntent =
|
||||
Intent().apply {
|
||||
@@ -399,14 +399,14 @@ private fun RenderMainPopup(
|
||||
)
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
context.getString(R.string.quick_action_share_browser_link),
|
||||
stringRes(context, R.string.quick_action_share_browser_link),
|
||||
)
|
||||
}
|
||||
|
||||
val shareIntent =
|
||||
Intent.createChooser(
|
||||
sendIntent,
|
||||
context.getString(R.string.quick_action_share),
|
||||
stringRes(context, R.string.quick_action_share),
|
||||
)
|
||||
ContextCompat.startActivity(context, shareIntent, null)
|
||||
onDismiss()
|
||||
@@ -418,7 +418,7 @@ private fun RenderMainPopup(
|
||||
|
||||
NoteQuickActionItem(
|
||||
Icons.Default.Report,
|
||||
stringResource(R.string.quick_action_report),
|
||||
stringRes(R.string.quick_action_report),
|
||||
) {
|
||||
showReportDialog.value = true
|
||||
}
|
||||
@@ -463,10 +463,10 @@ fun DeleteAlertDialog(
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
QuickActionAlertDialog(
|
||||
title = stringResource(R.string.quick_action_request_deletion_alert_title),
|
||||
textContent = stringResource(R.string.quick_action_request_deletion_alert_body),
|
||||
title = stringRes(R.string.quick_action_request_deletion_alert_title),
|
||||
textContent = stringRes(R.string.quick_action_request_deletion_alert_body),
|
||||
buttonIcon = Icons.Default.Delete,
|
||||
buttonText = stringResource(R.string.quick_action_delete_dialog_btn),
|
||||
buttonText = stringRes(R.string.quick_action_delete_dialog_btn),
|
||||
onClickDoOnce = {
|
||||
accountViewModel.delete(note)
|
||||
onDismiss()
|
||||
@@ -486,10 +486,10 @@ private fun BlockAlertDialog(
|
||||
accountViewModel: AccountViewModel,
|
||||
onDismiss: () -> Unit,
|
||||
) = QuickActionAlertDialog(
|
||||
title = stringResource(R.string.report_dialog_block_hide_user_btn),
|
||||
textContent = stringResource(R.string.report_dialog_blocking_a_user),
|
||||
title = stringRes(R.string.report_dialog_block_hide_user_btn),
|
||||
textContent = stringRes(R.string.report_dialog_blocking_a_user),
|
||||
buttonIcon = Icons.Default.Block,
|
||||
buttonText = stringResource(R.string.quick_action_block_dialog_btn),
|
||||
buttonText = stringRes(R.string.quick_action_block_dialog_btn),
|
||||
buttonColors =
|
||||
ButtonDefaults.buttonColors(
|
||||
containerColor = WarningColor,
|
||||
@@ -587,7 +587,7 @@ fun QuickActionAlertDialog(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
TextButton(onClick = onClickDontShowAgain) {
|
||||
Text(stringResource(R.string.quick_action_dont_show_again_button))
|
||||
Text(stringRes(R.string.quick_action_dont_show_again_button))
|
||||
}
|
||||
Button(
|
||||
onClick = onClickDoOnce,
|
||||
|
||||
@@ -61,7 +61,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -76,6 +75,7 @@ import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.StringToastMsg
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BigPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
@@ -437,7 +437,7 @@ fun ZapVote(
|
||||
zappingProgress = 0f
|
||||
showErrorMessageDialog =
|
||||
StringToastMsg(
|
||||
context.getString(R.string.error_dialog_zap_error),
|
||||
stringRes(context, R.string.error_dialog_zap_error),
|
||||
it,
|
||||
)
|
||||
}
|
||||
@@ -446,7 +446,7 @@ fun ZapVote(
|
||||
scope.launch {
|
||||
showErrorMessageDialog =
|
||||
StringToastMsg(
|
||||
context.getString(R.string.error_dialog_zap_error),
|
||||
stringRes(context, R.string.error_dialog_zap_error),
|
||||
it,
|
||||
)
|
||||
}
|
||||
@@ -471,7 +471,7 @@ fun ZapVote(
|
||||
zappingProgress = 1f
|
||||
Icon(
|
||||
imageVector = Icons.Default.Bolt,
|
||||
contentDescription = stringResource(R.string.zaps),
|
||||
contentDescription = stringRes(R.string.zaps),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = BitcoinOrange,
|
||||
)
|
||||
@@ -479,7 +479,7 @@ fun ZapVote(
|
||||
if (zappingProgress < 0.1 || zappingProgress > 0.99) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Bolt,
|
||||
contentDescription = stringResource(id = R.string.zaps),
|
||||
contentDescription = stringRes(id = R.string.zaps),
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.ContentTransform
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
@@ -82,11 +82,8 @@ import androidx.compose.ui.Alignment.Companion.CenterStart
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -95,27 +92,28 @@ import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.map
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.EditState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.DarkerGreen
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfDoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Height24dpModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.ModifierWidth3dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.NoSoTinyBorders
|
||||
import com.vitorpamplona.amethyst.ui.theme.ReactionRowExpandButton
|
||||
@@ -124,8 +122,8 @@ import com.vitorpamplona.amethyst.ui.theme.ReactionRowHeightWithPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.ReactionRowZapraiser
|
||||
import com.vitorpamplona.amethyst.ui.theme.ReactionRowZapraiserWithPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size16Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size17Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size18dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size19Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||
@@ -134,7 +132,6 @@ import com.vitorpamplona.amethyst.ui.theme.Size24dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.TinyBorders
|
||||
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter
|
||||
import com.vitorpamplona.quartz.encoders.Nip30CustomEmoji
|
||||
import com.vitorpamplona.quartz.events.BaseTextNoteEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -182,7 +179,7 @@ private fun InnerReactionRow(
|
||||
addPadding = addPadding,
|
||||
one = {
|
||||
WatchReactionsZapsBoostsAndDisplayIfExists(baseNote) {
|
||||
RenderShowIndividualReactionsButton(wantsToSeeReactions)
|
||||
RenderShowIndividualReactionsButton(wantsToSeeReactions, accountViewModel)
|
||||
}
|
||||
},
|
||||
two = {
|
||||
@@ -209,15 +206,51 @@ private fun InnerReactionRow(
|
||||
ZapReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav = nav)
|
||||
},
|
||||
six = {
|
||||
ViewCountReaction(
|
||||
ShareReaction(
|
||||
note = baseNote,
|
||||
grayTint = MaterialTheme.colorScheme.placeholderText,
|
||||
viewCountColorFilter = MaterialTheme.colorScheme.placeholderTextColorFilter,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShareReaction(
|
||||
note: Note,
|
||||
grayTint: Color,
|
||||
barChartModifier: Modifier = Size19Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
ClickableBox(
|
||||
modifier = barChartModifier,
|
||||
onClick = {
|
||||
val sendIntent =
|
||||
Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
putExtra(
|
||||
Intent.EXTRA_TEXT,
|
||||
externalLinkForNote(note),
|
||||
)
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
stringRes(context, R.string.quick_action_share_browser_link),
|
||||
)
|
||||
}
|
||||
|
||||
val shareIntent =
|
||||
Intent.createChooser(
|
||||
sendIntent,
|
||||
stringRes(context, R.string.quick_action_share),
|
||||
)
|
||||
ContextCompat.startActivity(context, shareIntent, null)
|
||||
},
|
||||
) {
|
||||
ShareIcon(barChartModifier, grayTint)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GenericInnerReactionRow(
|
||||
showReactionDetail: Boolean,
|
||||
@@ -251,7 +284,7 @@ private fun GenericInnerReactionRow(
|
||||
|
||||
Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) { five() }
|
||||
|
||||
Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) { six() }
|
||||
Row(verticalAlignment = CenterVertically, modifier = Modifier) { six() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +362,7 @@ fun RenderZapRaiser(
|
||||
|
||||
Text(
|
||||
text =
|
||||
stringResource(id = R.string.sats_to_complete, totalPercentage, zapraiserStatus.left),
|
||||
stringRes(id = R.string.sats_to_complete, totalPercentage, zapraiserStatus.left),
|
||||
modifier = NoSoTinyBorders,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
fontSize = Font14SP,
|
||||
@@ -382,14 +415,18 @@ fun <T, K, P, R> LiveData<T>.combineWith(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderShowIndividualReactionsButton(wantsToSeeReactions: MutableState<Boolean>) {
|
||||
IconButton(
|
||||
private fun RenderShowIndividualReactionsButton(
|
||||
wantsToSeeReactions: MutableState<Boolean>,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
ClickableBox(
|
||||
onClick = { wantsToSeeReactions.value = !wantsToSeeReactions.value },
|
||||
modifier = Size20Modifier,
|
||||
) {
|
||||
Crossfade(
|
||||
CrossfadeIfEnabled(
|
||||
targetState = wantsToSeeReactions.value,
|
||||
label = "RenderShowIndividualReactionsButton",
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
if (it) {
|
||||
ExpandLessIcon(modifier = Size22Modifier, R.string.close_all_reactions_to_this_post)
|
||||
@@ -585,10 +622,10 @@ fun ReplyReaction(
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
showCounter: Boolean = true,
|
||||
iconSizeModifier: Modifier = Size17Modifier,
|
||||
iconSizeModifier: Modifier = Size19Modifier,
|
||||
onPress: () -> Unit,
|
||||
) {
|
||||
IconButton(
|
||||
ClickableBox(
|
||||
modifier = iconSizeModifier,
|
||||
onClick = {
|
||||
if (baseNote.isDraft()) {
|
||||
@@ -596,15 +633,15 @@ fun ReplyReaction(
|
||||
R.string.draft_note,
|
||||
R.string.it_s_not_possible_to_reply_to_a_draft_note,
|
||||
)
|
||||
return@IconButton
|
||||
}
|
||||
if (accountViewModel.isWriteable()) {
|
||||
onPress()
|
||||
} else {
|
||||
accountViewModel.toast(
|
||||
R.string.read_only_user,
|
||||
R.string.login_with_a_private_key_to_be_able_to_reply,
|
||||
)
|
||||
if (accountViewModel.isWriteable()) {
|
||||
onPress()
|
||||
} else {
|
||||
accountViewModel.toast(
|
||||
R.string.read_only_user,
|
||||
R.string.login_with_a_private_key_to_be_able_to_reply,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
@@ -612,7 +649,7 @@ fun ReplyReaction(
|
||||
}
|
||||
|
||||
if (showCounter) {
|
||||
ReplyCounter(baseNote, grayTint)
|
||||
ReplyCounter(baseNote, grayTint, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,23 +657,29 @@ fun ReplyReaction(
|
||||
fun ReplyCounter(
|
||||
baseNote: Note,
|
||||
textColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val repliesState by baseNote.live().replyCount.observeAsState(baseNote.replies.size)
|
||||
|
||||
SlidingAnimationCount(repliesState, textColor)
|
||||
SlidingAnimationCount(repliesState, textColor, accountViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SlidingAnimationCount(
|
||||
baseCount: Int,
|
||||
textColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
AnimatedContent<Int>(
|
||||
targetState = baseCount,
|
||||
transitionSpec = AnimatedContentTransitionScope<Int>::transitionSpec,
|
||||
label = "SlidingAnimationCount",
|
||||
) { count ->
|
||||
TextCount(count, textColor)
|
||||
if (accountViewModel.settings.featureSet == FeatureSetType.PERFORMANCE) {
|
||||
TextCount(baseCount, textColor)
|
||||
} else {
|
||||
AnimatedContent<Int>(
|
||||
targetState = baseCount,
|
||||
transitionSpec = AnimatedContentTransitionScope<Int>::transitionSpec,
|
||||
label = "SlidingAnimationCount",
|
||||
) { count ->
|
||||
TextCount(count, textColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,18 +717,28 @@ fun TextCount(
|
||||
fun SlidingAnimationAmount(
|
||||
amount: String,
|
||||
textColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
AnimatedContent(
|
||||
targetState = amount,
|
||||
transitionSpec = AnimatedContentTransitionScope<String>::transitionSpec,
|
||||
label = "SlidingAnimationAmount",
|
||||
) { count ->
|
||||
if (accountViewModel.settings.featureSet == FeatureSetType.PERFORMANCE) {
|
||||
Text(
|
||||
text = count,
|
||||
text = amount,
|
||||
fontSize = Font14SP,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
)
|
||||
} else {
|
||||
AnimatedContent(
|
||||
targetState = amount,
|
||||
transitionSpec = AnimatedContentTransitionScope<String>::transitionSpec,
|
||||
label = "SlidingAnimationAmount",
|
||||
) { count ->
|
||||
Text(
|
||||
text = count,
|
||||
fontSize = Font14SP,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,16 +747,18 @@ fun BoostReaction(
|
||||
baseNote: Note,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
iconSizeModifier: Modifier = Size20Modifier,
|
||||
iconSizeModifier: Modifier = Size19Modifier,
|
||||
iconSize: Dp = Size20dp,
|
||||
onQuotePress: () -> Unit,
|
||||
onForkPress: () -> Unit,
|
||||
) {
|
||||
var wantsToBoost by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(
|
||||
ClickableBox(
|
||||
modifier = iconSizeModifier,
|
||||
onClick = { accountViewModel.tryBoost(baseNote) { wantsToBoost = true } },
|
||||
onClick = {
|
||||
accountViewModel.tryBoost(baseNote) { wantsToBoost = true }
|
||||
},
|
||||
) {
|
||||
ObserveBoostIcon(baseNote, accountViewModel) { hasBoosted ->
|
||||
RepostedIcon(iconSizeModifier, if (hasBoosted) Color.Unspecified else grayTint)
|
||||
@@ -730,7 +785,7 @@ fun BoostReaction(
|
||||
}
|
||||
}
|
||||
|
||||
BoostText(baseNote, grayTint)
|
||||
BoostText(baseNote, grayTint, accountViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -757,10 +812,11 @@ fun ObserveBoostIcon(
|
||||
fun BoostText(
|
||||
baseNote: Note,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val boostState by baseNote.live().boostCount.observeAsState(baseNote.boosts.size)
|
||||
|
||||
SlidingAnimationCount(boostState, grayTint)
|
||||
SlidingAnimationCount(boostState, grayTint, accountViewModel)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@@ -770,8 +826,8 @@ fun LikeReaction(
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
iconSize: Dp = Size20dp,
|
||||
heartSizeModifier: Modifier = Size16Modifier,
|
||||
iconSize: Dp = Size18dp,
|
||||
heartSizeModifier: Modifier = Size18Modifier,
|
||||
iconFontSize: TextUnit = Font14SP,
|
||||
) {
|
||||
var wantsToChangeReactionSymbol by remember { mutableStateOf(false) }
|
||||
@@ -797,7 +853,7 @@ fun LikeReaction(
|
||||
),
|
||||
) {
|
||||
ObserveLikeIcon(baseNote, accountViewModel) { reactionType ->
|
||||
Crossfade(targetState = reactionType, label = "LikeIcon") {
|
||||
CrossfadeIfEnabled(targetState = reactionType, label = "LikeIcon", accountViewModel = accountViewModel) {
|
||||
if (it != null) {
|
||||
RenderReactionType(it, heartSizeModifier, iconFontSize)
|
||||
} else {
|
||||
@@ -828,7 +884,7 @@ fun LikeReaction(
|
||||
}
|
||||
}
|
||||
|
||||
ObserveLikeText(baseNote) { reactionCount -> SlidingAnimationCount(reactionCount, grayTint) }
|
||||
ObserveLikeText(baseNote) { reactionCount -> SlidingAnimationCount(reactionCount, grayTint, accountViewModel) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1001,7 +1057,7 @@ fun ZapReaction(
|
||||
if (showErrorMessageDialog.isNotEmpty()) {
|
||||
val msg = showErrorMessageDialog.joinToString("\n")
|
||||
ErrorMessageDialog(
|
||||
title = stringResource(id = R.string.error_dialog_zap_error),
|
||||
title = stringRes(id = R.string.error_dialog_zap_error),
|
||||
textContent = msg,
|
||||
onClickStartMessage = {
|
||||
baseNote.author?.let {
|
||||
@@ -1076,11 +1132,11 @@ fun ZapReaction(
|
||||
baseNote,
|
||||
accountViewModel,
|
||||
) { wasZappedByLoggedInUser ->
|
||||
Crossfade(targetState = wasZappedByLoggedInUser.value, label = "ZapIcon") {
|
||||
CrossfadeIfEnabled(targetState = wasZappedByLoggedInUser.value, label = "ZapIcon", accountViewModel = accountViewModel) {
|
||||
if (it) {
|
||||
ZappedIcon(iconSizeModifier)
|
||||
} else {
|
||||
ZapIcon(iconSizeModifier, grayTint)
|
||||
OutlinedZapIcon(iconSizeModifier, grayTint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1088,7 +1144,7 @@ fun ZapReaction(
|
||||
}
|
||||
|
||||
ObserveZapAmountText(baseNote, accountViewModel) { zapAmountTxt ->
|
||||
SlidingAnimationAmount(zapAmountTxt, grayTint)
|
||||
SlidingAnimationAmount(zapAmountTxt, grayTint, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1111,13 +1167,13 @@ fun zapClick(
|
||||
|
||||
if (accountViewModel.account.zapAmountChoices.isEmpty()) {
|
||||
accountViewModel.toast(
|
||||
context.getString(R.string.error_dialog_zap_error),
|
||||
context.getString(R.string.no_zap_amount_setup_long_press_to_change),
|
||||
R.string.error_dialog_zap_error,
|
||||
R.string.no_zap_amount_setup_long_press_to_change,
|
||||
)
|
||||
} else if (!accountViewModel.isWriteable()) {
|
||||
accountViewModel.toast(
|
||||
context.getString(R.string.error_dialog_zap_error),
|
||||
context.getString(R.string.login_with_a_private_key_to_be_able_to_send_zaps),
|
||||
R.string.error_dialog_zap_error,
|
||||
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
|
||||
)
|
||||
} else if (accountViewModel.account.zapAmountChoices.size == 1) {
|
||||
accountViewModel.zap(
|
||||
@@ -1187,42 +1243,6 @@ fun ObserveZapAmountText(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ViewCountReaction(
|
||||
note: Note,
|
||||
grayTint: Color,
|
||||
barChartModifier: Modifier = Size19Modifier,
|
||||
numberSizeModifier: Modifier = Height24dpModifier,
|
||||
viewCountColorFilter: ColorFilter,
|
||||
) {
|
||||
ViewCountIcon(barChartModifier, grayTint)
|
||||
DrawViewCount(note, numberSizeModifier, viewCountColorFilter)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DrawViewCount(
|
||||
note: Note,
|
||||
iconModifier: Modifier = Modifier,
|
||||
viewCountColorFilter: ColorFilter,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
AsyncImage(
|
||||
model =
|
||||
remember(note) {
|
||||
ImageRequest
|
||||
.Builder(context)
|
||||
.data("https://counter.amethyst.social/${note.idHex}.svg?label=+&color=00000000")
|
||||
.diskCachePolicy(CachePolicy.DISABLED)
|
||||
.memoryCachePolicy(CachePolicy.ENABLED)
|
||||
.build()
|
||||
},
|
||||
contentDescription = context.getString(R.string.view_count),
|
||||
modifier = iconModifier,
|
||||
colorFilter = viewCountColorFilter,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun BoostTypeChoicePopup(
|
||||
@@ -1259,7 +1279,7 @@ private fun BoostTypeChoicePopup(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
) {
|
||||
Text(stringResource(R.string.boost), color = Color.White, textAlign = TextAlign.Center)
|
||||
Text(stringRes(R.string.boost), color = Color.White, textAlign = TextAlign.Center)
|
||||
}
|
||||
|
||||
Button(
|
||||
@@ -1271,7 +1291,7 @@ private fun BoostTypeChoicePopup(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
) {
|
||||
Text(stringResource(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
|
||||
Text(stringRes(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
|
||||
}
|
||||
|
||||
Button(
|
||||
@@ -1283,7 +1303,7 @@ private fun BoostTypeChoicePopup(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
) {
|
||||
Text(stringResource(R.string.fork), color = Color.White, textAlign = TextAlign.Center)
|
||||
Text(stringRes(R.string.fork), color = Color.White, textAlign = TextAlign.Center)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1389,12 +1409,8 @@ private fun ActionableReactionButton(
|
||||
} else {
|
||||
when (reactionType) {
|
||||
"+" -> {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_liked),
|
||||
null,
|
||||
modifier = remember { thisModifier.size(16.dp) },
|
||||
tint = Color.White,
|
||||
)
|
||||
LikedIcon(modifier = thisModifier.size(16.dp), tint = Color.White)
|
||||
|
||||
Text(
|
||||
text = removeSymbol,
|
||||
color = Color.White,
|
||||
|
||||
@@ -38,13 +38,13 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.RelayInfo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||
@@ -90,7 +90,7 @@ fun RelayCompose(
|
||||
}
|
||||
|
||||
Text(
|
||||
"${relay.counter} ${stringResource(R.string.posts_received)}",
|
||||
"${relay.counter} ${stringRes(R.string.posts_received)}",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -114,7 +114,8 @@ private fun RelayOptions(
|
||||
|
||||
val isNotUsingRelay =
|
||||
remember(userState) {
|
||||
accountViewModel.account.connectToRelays.value.none { it.url == relay.url }
|
||||
accountViewModel.account.connectToRelays.value
|
||||
.none { it.url == relay.url }
|
||||
}
|
||||
|
||||
if (isNotUsingRelay) {
|
||||
@@ -136,7 +137,7 @@ fun AddRelayButton(onClick: () -> Unit) {
|
||||
),
|
||||
contentPadding = ButtonPadding,
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.add), color = Color.White)
|
||||
Text(text = stringRes(id = R.string.add), color = Color.White)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,12 +153,12 @@ fun RemoveRelayButton(onClick: () -> Unit) {
|
||||
),
|
||||
contentPadding = ButtonPadding,
|
||||
) {
|
||||
Text(text = stringResource(R.string.remove), color = Color.White)
|
||||
Text(text = stringRes(R.string.remove), color = Color.White)
|
||||
}
|
||||
}
|
||||
|
||||
fun formattedDateTime(timestamp: Long): String {
|
||||
return Instant.ofEpochSecond(timestamp)
|
||||
fun formattedDateTime(timestamp: Long): String =
|
||||
Instant
|
||||
.ofEpochSecond(timestamp)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.format(DateTimeFormatter.ofPattern("MMM d, uuuu hh:mm a"))
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -47,13 +46,14 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonBoxModifer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size17Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@@ -69,7 +69,7 @@ fun RelayBadges(
|
||||
) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
|
||||
Crossfade(expanded.value, modifier = noteComposeRelayBox, label = "RelayBadges") {
|
||||
CrossfadeIfEnabled(expanded.value, modifier = noteComposeRelayBox, label = "RelayBadges", accountViewModel = accountViewModel) {
|
||||
if (it) {
|
||||
RenderAllRelayList(baseNote, Modifier.fillMaxWidth(), accountViewModel = accountViewModel, nav = nav)
|
||||
} else {
|
||||
@@ -131,7 +131,7 @@ fun WatchAndRenderRelay(
|
||||
baseNote.relays.getOrNull(relayIndex),
|
||||
)
|
||||
|
||||
Crossfade(targetState = noteRelays, label = "RenderRelay", modifier = Size17Modifier) {
|
||||
CrossfadeIfEnabled(targetState = noteRelays, label = "RenderRelay", modifier = Size17Modifier, accountViewModel = accountViewModel) {
|
||||
if (it != null) {
|
||||
RenderRelay(it, accountViewModel, nav)
|
||||
}
|
||||
@@ -239,7 +239,7 @@ private fun ShowMoreRelaysButton(onClick: () -> Unit) {
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ExpandMore,
|
||||
contentDescription = stringResource(id = R.string.expand_relay_list),
|
||||
contentDescription = stringRes(id = R.string.expand_relay_list),
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ChevronRight
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -37,16 +36,18 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.RelayBriefInfoCache
|
||||
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.RelayInformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size17dp
|
||||
@@ -87,13 +88,13 @@ fun ShouldShowExpandButton(
|
||||
|
||||
@Composable
|
||||
fun ChatRelayExpandButton(onClick: () -> Unit) {
|
||||
IconButton(
|
||||
ClickableBox(
|
||||
modifier = Size15Modifier,
|
||||
onClick = onClick,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ChevronRight,
|
||||
contentDescription = stringResource(id = R.string.expand_relay_list),
|
||||
contentDescription = stringRes(id = R.string.expand_relay_list),
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
@@ -178,6 +179,7 @@ fun RenderRelay(
|
||||
displayUrl = relay.displayUrl,
|
||||
iconUrl = relayInfo?.icon ?: relay.favIcon,
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -187,14 +189,16 @@ fun RenderRelayIcon(
|
||||
displayUrl: String,
|
||||
iconUrl: String?,
|
||||
loadProfilePicture: Boolean,
|
||||
loadRobohash: Boolean,
|
||||
iconModifier: Modifier = MaterialTheme.colorScheme.relayIconModifier,
|
||||
) {
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = displayUrl,
|
||||
model = iconUrl,
|
||||
contentDescription = stringResource(id = R.string.relay_info, displayUrl),
|
||||
contentDescription = stringRes(id = R.string.relay_info, displayUrl),
|
||||
colorFilter = RelayIconFilter,
|
||||
modifier = iconModifier,
|
||||
loadProfilePicture = loadProfilePicture,
|
||||
loadRobohash = loadRobohash,
|
||||
)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user