Compare commits

..
32 Commits
Author SHA1 Message Date
Vitor Pamplona 227aac5b76 v0.79.2 2023-10-01 12:07:51 -04:00
Vitor Pamplona 82ea72d2d1 Removes last mention to a string 2023-10-01 12:07:17 -04:00
Vitor Pamplona f1637c3d20 v0.79.1 2023-10-01 11:42:53 -04:00
Vitor Pamplona 07be841246 waits 3 seconds before starting the rest. 2023-10-01 11:42:47 -04:00
Vitor Pamplona 3ce5d92556 Making sure shared flows don't suspend. 2023-10-01 11:42:31 -04:00
Vitor Pamplona 8f11396e00 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2023-10-01 11:29:30 -04:00
Vitor PamplonaandGitHub 2259523387 Merge pull request #630 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2023-10-01 11:29:25 -04:00
Vitor Pamplona d67711b123 Speeds up cold startup. 2023-10-01 11:28:59 -04:00
Vitor Pamplona c89ca51a1b Updates dependencies 2023-10-01 11:10:37 -04:00
Crowdin Bot c5c951dd59 New Crowdin translations by GitHub Action 2023-10-01 15:09:21 +00:00
Vitor Pamplona 9f68f0227e Moves many Toasts to better designed Information Dialogs. 2023-10-01 11:06:05 -04:00
Vitor Pamplona 7faed2ddd3 stringResource seems to be slower than context.getString 2023-09-30 18:46:21 -04:00
Vitor Pamplona 7dceb701b2 Moves check if it is online to the viewModel scope 2023-09-30 18:45:58 -04:00
Vitor Pamplona e454ccc326 Uses the "host" property as the creator of the stream. 2023-09-30 17:43:46 -04:00
Vitor Pamplona bac50668e8 Speeds the composition of the Topbar 2023-09-30 17:23:27 -04:00
Vitor Pamplona ff9e1556d5 Increases the performance of the Bottom bar's Notification dot calculations 2023-09-30 16:24:51 -04:00
Vitor Pamplona dffb49b071 Uses Lists name instead of d-tag on the Top Bar 2023-09-30 11:45:46 -04:00
Vitor Pamplona 7387d9060f Improves error zap message screens for Material3 2023-09-30 11:37:23 -04:00
Vitor Pamplona 52e1842174 Fixes the repeat payments when the NWC is not setup correctly. 2023-09-30 11:17:17 -04:00
Vitor Pamplona d738e5cbaf v0.79.0 2023-09-29 16:27:04 -04:00
Vitor Pamplona 386b6db5ff Fixes relay type loading from the default list when they are not present in the user's account yet. 2023-09-29 16:23:42 -04:00
Vitor Pamplona 47d57e5a44 Only adds search relays if none of the current relays are active for search. 2023-09-29 16:09:11 -04:00
Vitor Pamplona 92068ca64f Fixing the inclusion of search relays 2023-09-29 16:03:59 -04:00
Vitor Pamplona 349510e900 Updates gradle plugin 2023-09-29 15:48:24 -04:00
Vitor PamplonaandGitHub 948d0f4bea Merge pull request #627 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2023-09-29 14:41:14 -04:00
Crowdin Bot b9a5586167 New Crowdin translations by GitHub Action 2023-09-29 18:30:00 +00:00
Vitor Pamplona 54edddbd2d Migrates to Material 3 2023-09-29 13:57:10 -04:00
Vitor PamplonaandGitHub b9c83426dc Merge pull request #624 from greenart7c3/main
add settings to load profile pictures only on wifi,never or always
2023-09-27 14:30:44 -04:00
greenart7c3 40dae875b8 Merge branch 'main' of https://github.com/greenart7c3/amethyst 2023-09-27 10:48:53 -03:00
greenart7c3 e762ad5e18 add settings to load profile pictures 2023-09-27 10:48:26 -03:00
Vitor PamplonaandGitHub 9b1bd2127f Merge pull request #623 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2023-09-26 11:07:27 -04:00
Crowdin Bot 88701a97c7 New Crowdin translations by GitHub Action 2023-09-26 15:03:50 +00:00
156 changed files with 3913 additions and 2876 deletions
+5 -5
View File
@@ -13,8 +13,8 @@ android {
applicationId "com.vitorpamplona.amethyst"
minSdk 26
targetSdk 34
versionCode 308
versionName "0.78.2"
versionCode 311
versionName "0.79.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@@ -102,7 +102,7 @@ dependencies {
// Observe Live data as State
implementation "androidx.compose.runtime:runtime-livedata:$compose_ui_version"
implementation "androidx.compose.material:material:$compose_ui_version"
implementation "androidx.compose.material3:material3:${material3_version}"
implementation "androidx.compose.material:material-icons-extended:$compose_ui_version"
// Lifecycle
@@ -155,7 +155,7 @@ dependencies {
// Markdown (With fix for full-image bleeds)
implementation('com.github.vitorpamplona.compose-richtext:richtext-ui:a0954aba63')
implementation('com.github.vitorpamplona.compose-richtext:richtext-ui-material:a0954aba63')
implementation('com.github.vitorpamplona.compose-richtext:richtext-ui-material3:a0954aba63')
implementation('com.github.vitorpamplona.compose-richtext:richtext-commonmark:a0954aba63')
// Language picker and Theme chooser
@@ -171,7 +171,7 @@ dependencies {
playImplementation 'com.google.mlkit:translate:17.0.1'
// PushNotifications
playImplementation platform('com.google.firebase:firebase-bom:32.2.3')
playImplementation platform('com.google.firebase:firebase-bom:32.3.1')
playImplementation 'com.google.firebase:firebase-messaging-ktx'
// Charts
@@ -0,0 +1,120 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.material3.pullrefresh
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.Drag
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.inspectable
import androidx.compose.ui.unit.Velocity
/**
* A nested scroll modifier that provides scroll events to [state].
*
* Note that this modifier must be added above a scrolling container, such as a lazy column, in
* order to receive scroll events. For example:
*
* @sample androidx.compose.material.samples.PullRefreshSample
*
* @param state The [PullRefreshState] associated with this pull-to-refresh component.
* The state will be updated by this modifier.
* @param enabled If not enabled, all scroll delta and fling velocity will be ignored.
*/
// TODO(b/244423199): Move pullRefresh into its own material library similar to material-ripple.
fun Modifier.pullRefresh(
state: PullRefreshState,
enabled: Boolean = true
) = inspectable(
inspectorInfo = debugInspectorInfo {
name = "pullRefresh"
properties["state"] = state
properties["enabled"] = enabled
}
) {
Modifier.pullRefresh(state::onPull, state::onRelease, enabled)
}
/**
* A nested scroll modifier that provides [onPull] and [onRelease] callbacks to aid building custom
* pull refresh components.
*
* Note that this modifier must be added above a scrolling container, such as a lazy column, in
* order to receive scroll events. For example:
*
* @sample androidx.compose.material.samples.CustomPullRefreshSample
*
* @param onPull Callback for dispatching vertical scroll delta, takes float pullDelta as argument.
* Positive delta (pulling down) is dispatched only if the child does not consume it (i.e. pulling
* down despite being at the top of a scrollable component), whereas negative delta (swiping up) is
* dispatched first (in case it is needed to push the indicator back up), and then the unconsumed
* delta is passed on to the child. The callback returns how much delta was consumed.
* @param onRelease Callback for when drag is released, takes float flingVelocity as argument.
* The callback returns how much velocity was consumed - in most cases this should only consume
* velocity if pull refresh has been dragged already and the velocity is positive (the fling is
* downwards), as an upwards fling should typically still scroll a scrollable component beneath the
* pullRefresh. This is invoked before any remaining velocity is passed to the child.
* @param enabled If not enabled, all scroll delta and fling velocity will be ignored and neither
* [onPull] nor [onRelease] will be invoked.
*/
fun Modifier.pullRefresh(
onPull: (pullDelta: Float) -> Float,
onRelease: suspend (flingVelocity: Float) -> Float,
enabled: Boolean = true
) = inspectable(
inspectorInfo = debugInspectorInfo {
name = "pullRefresh"
properties["onPull"] = onPull
properties["onRelease"] = onRelease
properties["enabled"] = enabled
}
) {
Modifier.nestedScroll(PullRefreshNestedScrollConnection(onPull, onRelease, enabled))
}
private class PullRefreshNestedScrollConnection(
private val onPull: (pullDelta: Float) -> Float,
private val onRelease: suspend (flingVelocity: Float) -> Float,
private val enabled: Boolean
) : NestedScrollConnection {
override fun onPreScroll(
available: Offset,
source: NestedScrollSource
): Offset = when {
!enabled -> Offset.Zero
source == Drag && available.y < 0 -> Offset(0f, onPull(available.y)) // Swiping up
else -> Offset.Zero
}
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset = when {
!enabled -> Offset.Zero
source == Drag && available.y > 0 -> Offset(0f, onPull(available.y)) // Pulling down
else -> Offset.Zero
}
override suspend fun onPreFling(available: Velocity): Velocity {
return Velocity(0f, onRelease(available.y))
}
}
@@ -0,0 +1,238 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.material3.pullrefresh
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
/**
* The default indicator for Compose pull-to-refresh, based on Android's SwipeRefreshLayout.
*
* @sample androidx.compose.material.samples.PullRefreshSample
*
* @param refreshing A boolean representing whether a refresh is occurring.
* @param state The [PullRefreshState] which controls where and how the indicator will be drawn.
* @param modifier Modifiers for the indicator.
* @param backgroundColor The color of the indicator's background.
* @param contentColor The color of the indicator's arc and arrow.
* @param scale A boolean controlling whether the indicator's size scales with pull progress or not.
*/
// TODO(b/244423199): Consider whether the state parameter should be replaced with lambdas to
// enable people to use this indicator with custom pull-to-refresh components.
@Composable
fun PullRefreshIndicator(
refreshing: Boolean,
state: PullRefreshState,
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(backgroundColor),
scale: Boolean = false
) {
val showElevation by remember(refreshing, state) {
derivedStateOf { refreshing || state.position > 0.5f }
}
Surface(
modifier = modifier
.size(IndicatorSize)
.pullRefreshIndicatorTransform(state, scale),
shape = SpinnerShape,
color = backgroundColor,
shadowElevation = if (showElevation) Elevation else 0.dp
) {
Crossfade(
targetState = refreshing,
animationSpec = tween(durationMillis = CrossfadeDurationMs)
) { refreshing ->
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
val spinnerSize = (ArcRadius + StrokeWidth).times(2)
if (refreshing) {
CircularProgressIndicator(
color = contentColor,
strokeWidth = StrokeWidth,
modifier = Modifier.size(spinnerSize)
)
} else {
CircularArrowIndicator(state, contentColor, Modifier.size(spinnerSize))
}
}
}
}
}
/**
* Modifier.size MUST be specified.
*/
@Composable
private fun CircularArrowIndicator(
state: PullRefreshState,
color: Color,
modifier: Modifier
) {
val path = remember { Path().apply { fillType = PathFillType.EvenOdd } }
val targetAlpha by remember(state) {
derivedStateOf {
if (state.progress >= 1f) MaxAlpha else MinAlpha
}
}
val alphaState = animateFloatAsState(targetValue = targetAlpha, animationSpec = AlphaTween)
// Empty semantics for tests
Canvas(modifier.semantics {}) {
val values = ArrowValues(state.progress)
val alpha = alphaState.value
rotate(degrees = values.rotation) {
val arcRadius = ArcRadius.toPx() + StrokeWidth.toPx() / 2f
val arcBounds = Rect(
size.center.x - arcRadius,
size.center.y - arcRadius,
size.center.x + arcRadius,
size.center.y + arcRadius
)
drawArc(
color = color,
alpha = alpha,
startAngle = values.startAngle,
sweepAngle = values.endAngle - values.startAngle,
useCenter = false,
topLeft = arcBounds.topLeft,
size = arcBounds.size,
style = Stroke(
width = StrokeWidth.toPx(),
cap = StrokeCap.Square
)
)
drawArrow(path, arcBounds, color, alpha, values)
}
}
}
@Immutable
private class ArrowValues(
val rotation: Float,
val startAngle: Float,
val endAngle: Float,
val scale: Float
)
private fun ArrowValues(progress: Float): ArrowValues {
// Discard first 40% of progress. Scale remaining progress to full range between 0 and 100%.
val adjustedPercent = max(min(1f, progress) - 0.4f, 0f) * 5 / 3
// How far beyond the threshold pull has gone, as a percentage of the threshold.
val overshootPercent = abs(progress) - 1.0f
// Limit the overshoot to 200%. Linear between 0 and 200.
val linearTension = overshootPercent.coerceIn(0f, 2f)
// Non-linear tension. Increases with linearTension, but at a decreasing rate.
val tensionPercent = linearTension - linearTension.pow(2) / 4
// Calculations based on SwipeRefreshLayout specification.
val endTrim = adjustedPercent * MaxProgressArc
val rotation = (-0.25f + 0.4f * adjustedPercent + tensionPercent) * 0.5f
val startAngle = rotation * 360
val endAngle = (rotation + endTrim) * 360
val scale = min(1f, adjustedPercent)
return ArrowValues(rotation, startAngle, endAngle, scale)
}
private fun DrawScope.drawArrow(
arrow: Path,
bounds: Rect,
color: Color,
alpha: Float,
values: ArrowValues
) {
arrow.reset()
arrow.moveTo(0f, 0f) // Move to left corner
arrow.lineTo(x = ArrowWidth.toPx() * values.scale, y = 0f) // Line to right corner
// Line to tip of arrow
arrow.lineTo(
x = ArrowWidth.toPx() * values.scale / 2,
y = ArrowHeight.toPx() * values.scale
)
val radius = min(bounds.width, bounds.height) / 2f
val inset = ArrowWidth.toPx() * values.scale / 2f
arrow.translate(
Offset(
x = radius + bounds.center.x - inset,
y = bounds.center.y + StrokeWidth.toPx() / 2f
)
)
arrow.close()
rotate(degrees = values.endAngle) {
drawPath(path = arrow, color = color, alpha = alpha)
}
}
private const val CrossfadeDurationMs = 100
private const val MaxProgressArc = 0.8f
private val IndicatorSize = 40.dp
private val SpinnerShape = CircleShape
private val ArcRadius = 7.5.dp
private val StrokeWidth = 2.5.dp
private val ArrowWidth = 10.dp
private val ArrowHeight = 5.dp
private val Elevation = 6.dp
// Values taken from SwipeRefreshLayout
private const val MinAlpha = 0.3f
private const val MaxAlpha = 1f
private val AlphaTween = tween<Float>(300, easing = LinearEasing)
@@ -0,0 +1,75 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.material3.pullrefresh
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.inspectable
/**
* A modifier for translating the position and scaling the size of a pull-to-refresh indicator
* based on the given [PullRefreshState].
*
* @sample androidx.compose.material.samples.PullRefreshIndicatorTransformSample
*
* @param state The [PullRefreshState] which determines the position of the indicator.
* @param scale A boolean controlling whether the indicator's size scales with pull progress or not.
*/
// TODO: Consider whether the state parameter should be replaced with lambdas.
fun Modifier.pullRefreshIndicatorTransform(
state: PullRefreshState,
scale: Boolean = false
) = inspectable(
inspectorInfo = debugInspectorInfo {
name = "pullRefreshIndicatorTransform"
properties["state"] = state
properties["scale"] = scale
}
) {
Modifier
// Essentially we only want to clip the at the top, so the indicator will not appear when
// the position is 0. It is preferable to clip the indicator as opposed to the layout that
// contains the indicator, as this would also end up clipping shadows drawn by items in a
// list for example - so we leave the clipping to the scrolling container. We use MAX_VALUE
// for the other dimensions to allow for more room for elevation / arbitrary indicators - we
// only ever really want to clip at the top edge.
.drawWithContent {
clipRect(
top = 0f,
left = -Float.MAX_VALUE,
right = Float.MAX_VALUE,
bottom = Float.MAX_VALUE
) {
this@drawWithContent.drawContent()
}
}
.graphicsLayer {
translationY = state.position - size.height
if (scale && !state.refreshing) {
val scaleFraction = LinearOutSlowInEasing
.transform(state.position / state.threshold)
.coerceIn(0f, 1f)
scaleX = scaleFraction
scaleY = scaleFraction
}
}
}
@@ -0,0 +1,228 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.material3.pullrefresh
import androidx.compose.animation.core.animate
import androidx.compose.foundation.MutatorMutex
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
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
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlin.math.abs
import kotlin.math.pow
/**
* Creates a [PullRefreshState] that is remembered across compositions.
*
* Changes to [refreshing] will result in [PullRefreshState] being updated.
*
* @sample androidx.compose.material.samples.PullRefreshSample
*
* @param refreshing A boolean representing whether a refresh is currently occurring.
* @param onRefresh The function to be called to trigger a refresh.
* @param refreshThreshold The threshold below which, if a release
* occurs, [onRefresh] will be called.
* @param refreshingOffset The offset at which the indicator will be drawn while refreshing. This
* offset corresponds to the position of the bottom of the indicator.
*/
@Composable
fun rememberPullRefreshState(
refreshing: Boolean,
onRefresh: () -> Unit,
refreshThreshold: Dp = PullRefreshDefaults.RefreshThreshold,
refreshingOffset: Dp = PullRefreshDefaults.RefreshingOffset
): PullRefreshState {
require(refreshThreshold > 0.dp) { "The refresh trigger must be greater than zero!" }
val scope = rememberCoroutineScope()
val onRefreshState = rememberUpdatedState(onRefresh)
val thresholdPx: Float
val refreshingOffsetPx: Float
with(LocalDensity.current) {
thresholdPx = refreshThreshold.toPx()
refreshingOffsetPx = refreshingOffset.toPx()
}
val state = remember(scope) {
PullRefreshState(scope, onRefreshState, refreshingOffsetPx, thresholdPx)
}
SideEffect {
state.setRefreshing(refreshing)
state.setThreshold(thresholdPx)
state.setRefreshingOffset(refreshingOffsetPx)
}
return state
}
/**
* A state object that can be used in conjunction with [pullRefresh] to add pull-to-refresh
* behaviour to a scroll component. Based on Android's SwipeRefreshLayout.
*
* Provides [progress], a float representing how far the user has pulled as a percentage of the
* refreshThreshold. Values of one or less indicate that the user has not yet pulled past the
* threshold. Values greater than one indicate how far past the threshold the user has pulled.
*
* Can be used in conjunction with [pullRefreshIndicatorTransform] to implement Android-like
* pull-to-refresh behaviour with a custom indicator.
*
* Should be created using [rememberPullRefreshState].
*/
class PullRefreshState internal constructor(
private val animationScope: CoroutineScope,
private val onRefreshState: State<() -> Unit>,
refreshingOffset: Float,
threshold: Float
) {
/**
* A float representing how far the user has pulled as a percentage of the refreshThreshold.
*
* If the component has not been pulled at all, progress is zero. If the pull has reached
* halfway to the threshold, progress is 0.5f. A value greater than 1 indicates that pull has
* gone beyond the refreshThreshold - e.g. a value of 2f indicates that the user has pulled to
* two times the refreshThreshold.
*/
val progress get() = adjustedDistancePulled / threshold
internal val refreshing get() = _refreshing
internal val position get() = _position
internal val threshold get() = _threshold
private val adjustedDistancePulled by derivedStateOf { distancePulled * DragMultiplier }
private var _refreshing by mutableStateOf(false)
private var _position by mutableStateOf(0f)
private var distancePulled by mutableStateOf(0f)
private var _threshold by mutableStateOf(threshold)
private var _refreshingOffset by mutableStateOf(refreshingOffset)
internal fun onPull(pullDelta: Float): Float {
if (_refreshing) return 0f // Already refreshing, do nothing.
val newOffset = (distancePulled + pullDelta).coerceAtLeast(0f)
val dragConsumed = newOffset - distancePulled
distancePulled = newOffset
_position = calculateIndicatorPosition()
return dragConsumed
}
internal fun onRelease(velocity: Float): Float {
if (refreshing) return 0f // Already refreshing, do nothing
if (adjustedDistancePulled > threshold) {
onRefreshState.value()
}
animateIndicatorTo(0f)
val consumed = when {
// We are flinging without having dragged the pull refresh (for example a fling inside
// a list) - don't consume
distancePulled == 0f -> 0f
// If the velocity is negative, the fling is upwards, and we don't want to prevent the
// the list from scrolling
velocity < 0f -> 0f
// We are showing the indicator, and the fling is downwards - consume everything
else -> velocity
}
distancePulled = 0f
return consumed
}
internal fun setRefreshing(refreshing: Boolean) {
if (_refreshing != refreshing) {
_refreshing = refreshing
distancePulled = 0f
animateIndicatorTo(if (refreshing) _refreshingOffset else 0f)
}
}
internal fun setThreshold(threshold: Float) {
_threshold = threshold
}
internal fun setRefreshingOffset(refreshingOffset: Float) {
if (_refreshingOffset != refreshingOffset) {
_refreshingOffset = refreshingOffset
if (refreshing) animateIndicatorTo(refreshingOffset)
}
}
// Make sure to cancel any existing animations when we launch a new one. We use this instead of
// Animatable as calling snapTo() on every drag delta has a one frame delay, and some extra
// overhead of running through the animation pipeline instead of directly mutating the state.
private val mutatorMutex = MutatorMutex()
private fun animateIndicatorTo(offset: Float) = animationScope.launch {
mutatorMutex.mutate {
animate(initialValue = _position, targetValue = offset) { value, _ ->
_position = value
}
}
}
private fun calculateIndicatorPosition(): Float = when {
// If drag hasn't gone past the threshold, the position is the adjustedDistancePulled.
adjustedDistancePulled <= threshold -> adjustedDistancePulled
else -> {
// How far beyond the threshold pull has gone, as a percentage of the threshold.
val overshootPercent = abs(progress) - 1.0f
// Limit the overshoot to 200%. Linear between 0 and 200.
val linearTension = overshootPercent.coerceIn(0f, 2f)
// Non-linear tension. Increases with linearTension, but at a decreasing rate.
val tensionPercent = linearTension - linearTension.pow(2) / 4
// The additional offset beyond the threshold.
val extraOffset = threshold * tensionPercent
threshold + extraOffset
}
}
}
/**
* Default parameter values for [rememberPullRefreshState].
*/
object PullRefreshDefaults {
/**
* If the indicator is below this threshold offset when it is released, a refresh
* will be triggered.
*/
val RefreshThreshold = 80.dp
/**
* The offset at which the indicator should be rendered whilst a refresh is occurring.
*/
val RefreshingOffset = 56.dp
}
/**
* The distance pulled is multiplied by this value to give us the adjusted distance pulled, which
* is used in calculating the indicator position (when the adjusted distance pulled is less than
* the refresh threshold, it is the indicator position, otherwise the indicator position is
* derived from the progress).
*/
private const val DragMultiplier = 0.5f
@@ -80,6 +80,7 @@ private object PrefKeys {
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"
}
object LocalPreferences {
@@ -280,6 +281,12 @@ object LocalPreferences {
} else {
putBoolean(PrefKeys.AUTOMATICALLY_HIDE_NAV_BARS, account.settings.automaticallyHideNavigationBars.prefCode!!)
}
if (account.settings.automaticallyShowProfilePictures.prefCode == null) {
remove(PrefKeys.AUTOMATICALLY_SHOW_PROFILE_PICTURE)
} else {
putBoolean(PrefKeys.AUTOMATICALLY_SHOW_PROFILE_PICTURE, account.settings.automaticallyShowProfilePictures.prefCode!!)
}
putString(PrefKeys.PREFERRED_LANGUAGE, account.settings.preferredLanguage ?: "")
}.apply()
}
@@ -431,6 +438,12 @@ object LocalPreferences {
BooleanType.ALWAYS
}
settings.automaticallyShowProfilePictures = if (contains(PrefKeys.AUTOMATICALLY_SHOW_PROFILE_PICTURE)) {
parseConnectivityType(getBoolean(PrefKeys.AUTOMATICALLY_SHOW_PROFILE_PICTURE, false))
} else {
ConnectivityType.ALWAYS
}
settings.preferredLanguage = getString(PrefKeys.PREFERRED_LANGUAGE, "")
}
@@ -33,6 +33,10 @@ import com.vitorpamplona.amethyst.service.NostrVideoDataSource
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.actions.ImageUploader
import com.vitorpamplona.quartz.encoders.decodePublicKeyAsHexOrNull
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
object ServiceManager {
@@ -87,9 +91,12 @@ object ServiceManager {
// Notification Elements
NostrHomeDataSource.start()
NostrAccountDataSource.start()
NostrChatroomListDataSource.start()
NostrDiscoveryDataSource.start()
NostrVideoDataSource.start()
GlobalScope.launch(Dispatchers.IO) {
delay(3000)
NostrChatroomListDataSource.start()
NostrDiscoveryDataSource.start()
NostrVideoDataSource.start()
}
// More Info Data Sources
NostrSingleEventDataSource.start()
@@ -98,7 +98,6 @@ class Account(
// Observers line up here.
val live: AccountLiveData = AccountLiveData(this)
val liveLanguages: AccountLiveData = AccountLiveData(this)
val liveLastRead: AccountLiveData = AccountLiveData(this)
val saveable: AccountLiveData = AccountLiveData(this)
@Immutable
@@ -185,6 +184,14 @@ class Account(
saveable.invalidateData()
}
fun updateAutomaticallyShowProfilePicture(
automaticallyShowProfilePicture: ConnectivityType
) {
settings.automaticallyShowProfilePictures = automaticallyShowProfilePicture
live.invalidateData()
saveable.invalidateData()
}
fun updateAutomaticallyHideHavBars(
automaticallyHideHavBars: BooleanType
) {
@@ -888,7 +895,7 @@ class Account(
return returningContactList
}
fun follow(user: User) {
suspend fun follow(user: User) {
if (!isWriteable() && !loginWithExternalSigner) return
val contactList = migrateCommunitiesAndChannelsIfNeeded(userProfile().latestContactList)
@@ -1057,7 +1064,7 @@ class Account(
LocalCache.consume(event)
}
fun unfollow(user: User) {
suspend fun unfollow(user: User) {
if (!isWriteable() && !loginWithExternalSigner) return
val contactList = migrateCommunitiesAndChannelsIfNeeded(userProfile().latestContactList)
@@ -2947,7 +2954,10 @@ class Account(
// Takes a User's relay list and adds the types of feeds they are active for.
fun activeRelays(): Array<Relay>? {
var usersRelayList = userProfile().latestContactList?.relays()?.map {
val localFeedTypes = localRelays.firstOrNull() { localRelay -> localRelay.url == it.key }?.feedTypes ?: FeedType.values().toSet()
val localFeedTypes = localRelays.firstOrNull() { localRelay -> localRelay.url == it.key }?.feedTypes
?: Constants.defaultRelays.filter { defaultRelay -> defaultRelay.url == it.key }.firstOrNull()?.feedTypes
?: FeedType.values().toSet()
Relay(it.key, it.value.read, it.value.write, localFeedTypes, proxy)
} ?: return null
@@ -3102,12 +3112,14 @@ class Account(
live.invalidateData()
}
fun markAsRead(route: String, timestampInSecs: Long) {
fun markAsRead(route: String, timestampInSecs: Long): Boolean {
val lastTime = lastReadPerRoute[route]
if (lastTime == null || timestampInSecs > lastTime) {
return if (lastTime == null || timestampInSecs > lastTime) {
lastReadPerRoute = lastReadPerRoute + Pair(route, timestampInSecs)
saveable.invalidateData()
liveLastRead.invalidateData()
true
} else {
false
}
}
@@ -22,6 +22,7 @@ import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import java.io.File
@@ -348,7 +349,12 @@ object LocalCache {
val channel = getOrCreateChannel(note.idHex) {
LiveActivitiesChannel(note.address)
} as? LiveActivitiesChannel
channel?.updateChannelInfo(author, event, event.createdAt)
val creator = event.host()?.ifBlank { null }?.let {
checkGetOrCreateUser(it)
} ?: author
channel?.updateChannelInfo(creator, event, event.createdAt)
refreshObservers(note)
}
@@ -1548,7 +1554,7 @@ object LocalCache {
@Stable
class LocalCacheLiveData {
private val _newEventBundles = MutableSharedFlow<Set<Note>>()
private val _newEventBundles = MutableSharedFlow<Set<Note>>(0, 10, BufferOverflow.DROP_OLDEST)
val newEventBundles = _newEventBundles.asSharedFlow() // read-only public view
// Refreshes observers in batches.
@@ -9,7 +9,8 @@ class Settings(
var automaticallyShowImages: ConnectivityType = ConnectivityType.ALWAYS,
var automaticallyStartPlayback: ConnectivityType = ConnectivityType.ALWAYS,
var automaticallyShowUrlPreview: ConnectivityType = ConnectivityType.ALWAYS,
var automaticallyHideNavigationBars: BooleanType = BooleanType.ALWAYS
var automaticallyHideNavigationBars: BooleanType = BooleanType.ALWAYS,
var automaticallyShowProfilePictures: ConnectivityType = ConnectivityType.ALWAYS
)
enum class ConnectivityType(val prefCode: Boolean?, val screenCode: Int, val reourceId: Int) {
@@ -1,8 +1,10 @@
package com.vitorpamplona.amethyst.service
import android.content.Context
import androidx.compose.runtime.Immutable
import com.fasterxml.jackson.databind.JsonNode
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.quartz.events.Event
@@ -45,7 +47,7 @@ class CashuProcessor {
}
}
suspend fun melt(token: CashuToken, lud16: String, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
suspend fun melt(token: CashuToken, lud16: String, onSuccess: (String, String) -> Unit, onError: (String, String) -> Unit, context: Context) {
checkNotInMainThread()
runCatching {
@@ -54,16 +56,17 @@ class CashuProcessor {
milliSats = token.redeemInvoiceAmount * 1000, // Make invoice and leave room for fees
message = "Redeem Cashu",
onSuccess = { invoice ->
meltInvoice(token, invoice, onSuccess, onError)
meltInvoice(token, invoice, onSuccess, onError, context)
},
onProgress = {
},
onError = onError
onError = onError,
context = context
)
}
}
private fun meltInvoice(token: CashuToken, invoice: String, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
private fun meltInvoice(token: CashuToken, invoice: String, onSuccess: (String, String) -> Unit, onError: (String, String) -> Unit, context: Context) {
try {
val client = HttpClient.getHttpClient()
val url = token.mint + "/melt" // Melt cashu tokens at Mint
@@ -88,13 +91,24 @@ class CashuProcessor {
val successful = tree?.get("paid")?.asText() == "true"
if (successful) {
onSuccess("Redeemed ${token.totalAmount} Sats" + " (Fees: ${token.fees} Sats)")
onSuccess(
context.getString(R.string.cashu_sucessful_redemption),
context.getString(R.string.cashu_sucessful_redemption_explainer, token.totalAmount.toString(), token.fees.toString())
)
} else {
onError(tree?.get("detail")?.asText()?.split('.')?.getOrNull(0) ?: "Cashu: Tokens already spent.")
val msg = tree?.get("detail")?.asText()?.split('.')?.getOrNull(0)?.ifBlank { null }
onError(
context.getString(R.string.cashu_failed_redemption),
if (msg != null) {
context.getString(R.string.cashu_failed_redemption_explainer_error_msg, msg)
} else {
context.getString(R.string.cashu_failed_redemption_explainer_error_msg)
}
)
}
}
} catch (e: Exception) {
onError("Token melt failure: " + e.message)
onError(context.getString(R.string.cashu_sucessful_redemption), context.getString(R.string.cashu_failed_redemption_explainer_error_msg, e.message))
}
}
}
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.service
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.relays.COMMON_FEED_TYPES
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.service.relays.EOSEAccount
@@ -34,6 +35,7 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
lateinit var account: Account
val latestEOSEs = EOSEAccount()
val hasLoadedTheBasics = mutableMapOf<User, Boolean>()
fun createAccountContactListFilter(): TypedFilter {
return TypedFilter(
@@ -141,7 +143,11 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
)
val accountChannel = requestNewChannel { time, relayUrl ->
latestEOSEs.addOrUpdate(account.userProfile(), account.defaultNotificationFollowList, relayUrl, time)
if (hasLoadedTheBasics[account.userProfile()] != null) {
latestEOSEs.addOrUpdate(account.userProfile(), account.defaultNotificationFollowList, relayUrl, time)
} else {
hasLoadedTheBasics[account.userProfile()] = true
}
}
override fun consume(event: Event, relay: Relay) {
@@ -201,18 +207,28 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
}
override fun updateChannelFilters() {
// gets everthing about the user logged in
accountChannel.typedFilters = listOf(
createAccountMetadataFilter(),
createAccountContactListFilter(),
createAccountRelayListFilter(),
createNotificationFilter(),
createGiftWrapsToMeFilter(),
createAccountReportsFilter(),
createAccountAcceptedAwardsFilter(),
createAccountBookmarkListFilter(),
createAccountLastPostsListFilter()
).ifEmpty { null }
return if (hasLoadedTheBasics[account.userProfile()] != null) {
// gets everthing about the user logged in
accountChannel.typedFilters = listOf(
createAccountMetadataFilter(),
createAccountContactListFilter(),
createAccountRelayListFilter(),
createNotificationFilter(),
createGiftWrapsToMeFilter(),
createAccountReportsFilter(),
createAccountAcceptedAwardsFilter(),
createAccountBookmarkListFilter(),
createAccountLastPostsListFilter()
).ifEmpty { null }
} else {
// just the basics.
accountChannel.typedFilters = listOf(
createAccountMetadataFilter(),
createAccountContactListFilter(),
createAccountRelayListFilter(),
createAccountBookmarkListFilter()
).ifEmpty { null }
}
}
override fun auth(relay: Relay, challenge: String) {
@@ -35,7 +35,7 @@ class ZapPaymentHandler(val account: Account) {
pollOption: Int?,
message: String,
context: Context,
onError: (String) -> Unit,
onError: (String, String) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<Payable>) -> Unit,
zapType: LnZapEvent.ZapType
@@ -48,7 +48,10 @@ class ZapPaymentHandler(val account: Account) {
val lud16 = note.author?.info?.lud16?.trim() ?: note.author?.info?.lud06?.trim()
if (lud16.isNullOrBlank()) {
onError(context.getString(R.string.user_does_not_have_a_lightning_address_setup_to_receive_sats))
onError(
context.getString(R.string.missing_lud16),
context.getString(R.string.user_does_not_have_a_lightning_address_setup_to_receive_sats)
)
return@withContext
}
@@ -121,6 +124,9 @@ class ZapPaymentHandler(val account: Account) {
)
} else {
onError(
context.getString(
R.string.missing_lud16
),
context.getString(
R.string.user_x_does_not_have_a_lightning_address_setup_to_receive_sats,
user?.toBestDisplayName() ?: value.lnAddressOrPubKeyHex
@@ -149,7 +155,7 @@ class ZapPaymentHandler(val account: Account) {
pollOption: Int?,
message: String,
context: Context,
onError: (String) -> Unit,
onError: (String, String) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayInvoiceThroughIntent: (String) -> Unit,
zapType: LnZapEvent.ZapType,
@@ -181,9 +187,13 @@ class ZapPaymentHandler(val account: Account) {
if (response is PayInvoiceErrorResponse) {
onProgress(0.0f)
onError(
response.error?.message
?: response.error?.code?.toString()
?: "Error parsing error message"
context.getString(R.string.error_dialog_pay_invoice_error),
context.getString(
R.string.wallet_connect_pay_invoice_error_error,
response.error?.message
?: response.error?.code?.toString()
?: "Error parsing error message"
)
)
} else {
onProgress(1f)
@@ -192,16 +202,13 @@ class ZapPaymentHandler(val account: Account) {
)
onProgress(0.8f)
} else {
try {
onPayInvoiceThroughIntent(it)
} catch (e: Exception) {
onError(context.getString(R.string.lightning_wallets_not_found2))
}
onPayInvoiceThroughIntent(it)
onProgress(0f)
}
},
onError = onError,
onProgress = onProgress
onProgress = onProgress,
context = context
)
}
}
@@ -1,7 +1,9 @@
package com.vitorpamplona.amethyst.service.lnurl
import android.content.Context
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.HttpClient
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
@@ -31,13 +33,24 @@ class LightningAddressResolver() {
return null
}
private suspend fun fetchLightningAddressJson(lnaddress: String, onSuccess: suspend (String) -> Unit, onError: (String) -> Unit) = withContext(Dispatchers.IO) {
private suspend fun fetchLightningAddressJson(
lnaddress: String,
onSuccess: suspend (String) -> Unit,
onError: (String, String) -> Unit,
context: Context
) = withContext(Dispatchers.IO) {
checkNotInMainThread()
val url = assembleUrl(lnaddress)
if (url == null) {
onError("Could not assemble LNUrl from Lightning Address \"${lnaddress}\". Check the user's setup")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(
R.string.could_not_assemble_lnurl_from_lightning_address_check_the_user_s_setup,
lnaddress
)
)
return@withContext
}
@@ -51,16 +64,39 @@ class LightningAddressResolver() {
if (it.isSuccessful) {
onSuccess(it.body.string())
} else {
onError("The receiver's lightning service at $url is not available. It was calculated from the lightning address \"${lnaddress}\". Error: ${it.code}. Check if the server is up and if the lightning address is correct")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(
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,
lnaddress,
it.code.toString()
)
)
}
}
} catch (e: Exception) {
e.printStackTrace()
onError("Could not resolve $url. Check if the server is up and if the lightning address $lnaddress is correct")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(
R.string.could_not_resolve_check_if_you_are_connected_if_the_server_is_up_and_if_the_lightning_address_is_correct,
url,
lnaddress
)
)
}
}
suspend fun fetchLightningInvoice(lnCallback: String, milliSats: Long, message: String, nostrRequest: String? = null, onSuccess: suspend (String) -> Unit, onError: (String) -> Unit) = withContext(Dispatchers.IO) {
suspend fun fetchLightningInvoice(
lnCallback: String,
milliSats: Long,
message: String,
nostrRequest: String? = null,
onSuccess: suspend (String) -> Unit,
onError: (String, String) -> Unit,
context: Context
) = withContext(Dispatchers.IO) {
val encodedMessage = URLEncoder.encode(message, "utf-8")
val urlBinder = if (lnCallback.contains("?")) "&" else "?"
@@ -80,18 +116,22 @@ class LightningAddressResolver() {
if (it.isSuccessful) {
onSuccess(it.body.string())
} else {
onError("Could not fetch invoice from $lnCallback")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(R.string.could_not_fetch_invoice_from, lnCallback)
)
}
}
}
suspend fun lnAddressToLnUrl(lnaddress: String, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
suspend fun lnAddressToLnUrl(lnaddress: String, onSuccess: (String) -> Unit, onError: (String, String) -> Unit, context: Context) {
fetchLightningAddressJson(
lnaddress,
onSuccess = {
onSuccess(it.toByteArray().toLnUrl())
},
onError = onError
onError = onError,
context = context
)
}
@@ -101,8 +141,9 @@ class LightningAddressResolver() {
message: String,
nostrRequest: String? = null,
onSuccess: suspend (String) -> Unit,
onError: (String) -> Unit,
onProgress: (percent: Float) -> Unit
onError: (String, String) -> Unit,
onProgress: (percent: Float) -> Unit,
context: Context
) {
val mapper = jacksonObjectMapper()
@@ -114,14 +155,20 @@ class LightningAddressResolver() {
val lnurlp = try {
mapper.readTree(lnAddressJson)
} catch (t: Throwable) {
onError("Error Parsing JSON from Lightning Address. Check the user's lightning setup")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(R.string.error_parsing_json_from_lightning_address_check_the_user_s_lightning_setup)
)
null
}
val callback = lnurlp?.get("callback")?.asText()
if (callback == null) {
onError("Callback URL not found in the User's lightning address server configuration")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(R.string.callback_url_not_found_in_the_user_s_lightning_address_server_configuration)
)
}
val allowsNostr = lnurlp?.get("allowsNostr")?.asBoolean() ?: false
@@ -138,7 +185,10 @@ class LightningAddressResolver() {
val lnInvoice = try {
mapper.readTree(it)
} catch (t: Throwable) {
onError("Error Parsing JSON from Lightning Address's invoice fetch. Check the user's lightning setup")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(R.string.error_parsing_json_from_lightning_address_s_invoice_fetch_check_the_user_s_lightning_setup)
)
null
}
@@ -151,21 +201,40 @@ class LightningAddressResolver() {
onSuccess(pr)
} else {
onProgress(0.0f)
onError("Incorrect invoice amount (${invoiceAmount.toLong()} sats) from $lnaddress. It should have been $expectedAmountInSats")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(
R.string.incorrect_invoice_amount_sats_from_it_should_have_been,
invoiceAmount.toLong().toString(),
lnaddress,
expectedAmountInSats.toString()
)
)
}
} ?: lnInvoice?.get("reason")?.asText()?.ifBlank { null }?.let { reason ->
onProgress(0.0f)
onError("Unable to create a lightning invoice before sending the zap. The receiver's lightning wallet sent the following error: $reason")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(
R.string.unable_to_create_a_lightning_invoice_before_sending_the_zap_the_receiver_s_lightning_wallet_sent_the_following_error,
reason
)
)
} ?: run {
onProgress(0.0f)
onError("nable to create a lightning invoice before sending the zap. Element pr not found in the resulting JSON.")
onError(
context.getString(R.string.error_unable_to_fetch_invoice),
context.getString(R.string.unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json)
)
}
},
onError = onError
onError = onError,
context
)
}
},
onError = onError
onError = onError,
context
)
}
}
@@ -16,8 +16,8 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -70,7 +70,7 @@ class MainActivity : AppCompatActivity() {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
color = MaterialTheme.colorScheme.background
) {
val accountStateViewModel: AccountStateViewModel = viewModel {
AccountStateViewModel(this@MainActivity)
@@ -0,0 +1,54 @@
package com.vitorpamplona.amethyst.ui.actions
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Done
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
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.theme.Size16dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
@Composable
fun InformationDialog(
title: String,
textContent: String,
buttonColors: ButtonColors = ButtonDefaults.buttonColors(),
onDismiss: () -> Unit
) {
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(title)
},
text = {
SelectionContainer {
Text(textContent)
}
},
confirmButton = {
Button(onClick = onDismiss, colors = buttonColors, contentPadding = PaddingValues(horizontal = Size16dp)) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Outlined.Done,
contentDescription = null
)
Spacer(StdHorzSpacer)
Text(stringResource(R.string.error_dialog_button_ok))
}
}
}
)
}
@@ -16,15 +16,15 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -54,10 +54,12 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.note.ChannelName
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
import com.vitorpamplona.amethyst.ui.note.SearchIcon
@@ -133,7 +135,7 @@ fun JoinUserOrChannelView(searchBarViewModel: SearchBarViewModel, onClose: () ->
Text(
text = "",
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
fontWeight = FontWeight.Bold
)
}
@@ -270,7 +272,7 @@ private fun SearchEditTextForJoin(
placeholder = {
Text(
text = stringResource(R.string.channel_list_user_or_group_id_demo),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
trailingIcon = {
@@ -303,6 +305,13 @@ private fun RenderSearchResults(
val users by searchBarViewModel.searchResultsUsers.collectAsState()
val channels by searchBarViewModel.searchResultsChannels.collectAsState()
val scope = rememberCoroutineScope()
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
Row(
modifier = Modifier
@@ -337,7 +346,7 @@ private fun RenderSearchResults(
channels,
key = { _, item -> "c" + item.idHex }
) { _, item ->
RenderChannel(item) {
RenderChannel(item, automaticallyShowProfilePicture) {
nav("Channel/${item.idHex}")
searchBarViewModel.clear()
}
@@ -350,6 +359,7 @@ private fun RenderSearchResults(
@Composable
private fun RenderChannel(
item: com.vitorpamplona.amethyst.model.Channel,
loadProfilePicture: Boolean,
onClick: () -> Unit
) {
val hasNewMessages = remember {
@@ -368,7 +378,8 @@ private fun RenderChannel(
channelLastTime = null,
channelLastContent = item.summary(),
hasNewMessages,
onClick = onClick
onClick = onClick,
loadProfilePicture = loadProfilePicture
)
}
@@ -426,7 +437,7 @@ private fun DisplayUserAboutInfo(baseUser: User) {
Text(
text = about,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -10,11 +10,11 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -75,7 +75,7 @@ fun NewChannelView(onClose: () -> Unit, accountViewModel: AccountViewModel, chan
placeholder = {
Text(
text = stringResource(R.string.my_awesome_group),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -94,7 +94,7 @@ fun NewChannelView(onClose: () -> Unit, accountViewModel: AccountViewModel, chan
placeholder = {
Text(
text = "http://mygroup.com/logo.jpg",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -111,7 +111,7 @@ fun NewChannelView(onClose: () -> Unit, accountViewModel: AccountViewModel, chan
placeholder = {
Text(
text = stringResource(R.string.about_us),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -14,6 +14,7 @@ import com.vitorpamplona.amethyst.service.FileHeader
import com.vitorpamplona.amethyst.service.relays.Relay
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
@@ -22,7 +23,7 @@ open class NewMediaModel : ViewModel() {
var account: Account? = null
var isUploadingImage by mutableStateOf(false)
val imageUploadingError = MutableSharedFlow<String?>()
val imageUploadingError = MutableSharedFlow<String?>(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
var mediaType by mutableStateOf<String?>(null)
var selectedServer by mutableStateOf<ServersAvailable?>(null)
@@ -21,12 +21,12 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -135,7 +135,7 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac
painter = painterResource(R.drawable.relays),
contentDescription = null,
modifier = Modifier.height(25.dp),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
}
@@ -292,7 +292,7 @@ fun ImageVideoPost(postViewModel: NewMediaModel, accountViewModel: AccountViewMo
placeholder = {
Text(
text = stringResource(R.string.content_description_example),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -5,10 +5,10 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -38,13 +38,13 @@ fun NewPollClosing(pollViewModel: NewPostViewModel) {
} catch (e: Exception) { pollViewModel.isValidClosedAt.value = false }
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
val colorInValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.placeholderText
val colorValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.placeholderText
)
Row(
@@ -60,13 +60,13 @@ fun NewPollClosing(pollViewModel: NewPostViewModel) {
label = {
Text(
text = stringResource(R.string.poll_closing_time),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
placeholder = {
Text(
text = stringResource(R.string.poll_closing_time_days),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -5,10 +5,10 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -38,13 +38,13 @@ fun NewPollConsensusThreshold(pollViewModel: NewPostViewModel) {
} catch (e: Exception) { pollViewModel.isValidConsensusThreshold.value = false }
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
val colorInValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.placeholderText
val colorValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.placeholderText
)
Row(
@@ -60,13 +60,13 @@ fun NewPollConsensusThreshold(pollViewModel: NewPostViewModel) {
label = {
Text(
text = stringResource(R.string.poll_consensus_threshold),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
placeholder = {
Text(
text = stringResource(R.string.poll_consensus_threshold_percent),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -2,9 +2,9 @@ package com.vitorpamplona.amethyst.ui.actions
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -36,13 +36,13 @@ fun NewPollOption(pollViewModel: NewPostViewModel, optionIndex: Int) {
label = {
Text(
text = stringResource(R.string.poll_option_index).format(optionIndex + 1),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
placeholder = {
Text(
text = stringResource(R.string.poll_option_description),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -3,7 +3,7 @@ package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -33,13 +33,13 @@ fun NewPollPrimaryDescription(pollViewModel: NewPostViewModel) {
isInputValid = false
}
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
val colorInValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.placeholderText
val colorValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.placeholderText
)
OutlinedTextField(
@@ -50,7 +50,7 @@ fun NewPollPrimaryDescription(pollViewModel: NewPostViewModel) {
label = {
Text(
text = stringResource(R.string.poll_primary_description),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -68,11 +68,11 @@ fun NewPollPrimaryDescription(pollViewModel: NewPostViewModel) {
placeholder = {
Text(
text = stringResource(R.string.poll_primary_description),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
colors = if (isInputValid) colorValid else colorInValid,
visualTransformation = UrlUserTagTransformation(MaterialTheme.colors.primary),
visualTransformation = UrlUserTagTransformation(MaterialTheme.colorScheme.primary),
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
@@ -1,9 +1,9 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
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
@@ -30,13 +30,13 @@ fun NewPollRecipientsField(pollViewModel: NewPostViewModel, account: Account) {
label = {
Text(
text = stringResource(R.string.poll_zap_recipients),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
placeholder = {
Text(
text = stringResource(R.string.poll_zap_recipients),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
@@ -7,10 +7,10 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
@@ -26,13 +26,13 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
@Composable
fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) {
val colorInValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.error,
val colorInValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.error,
unfocusedBorderColor = Color.Red
)
val colorValid = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colors.primary,
unfocusedBorderColor = MaterialTheme.colors.placeholderText
val colorValid = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.placeholderText
)
Row(
@@ -48,13 +48,13 @@ fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) {
label = {
Text(
text = stringResource(R.string.poll_zap_value_min),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
placeholder = {
Text(
text = stringResource(R.string.sats),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -70,13 +70,13 @@ fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) {
label = {
Text(
text = stringResource(R.string.poll_zap_value_max),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
placeholder = {
Text(
text = stringResource(R.string.sats),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -88,7 +88,7 @@ fun NewPollVoteValueRange(pollViewModel: NewPostViewModel) {
) {
Text(
text = stringResource(R.string.poll_zap_value_min_max_explainer),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(vertical = 10.dp)
)
}
@@ -19,7 +19,6 @@ import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowForwardIos
import androidx.compose.material.icons.filled.Bolt
@@ -32,6 +31,7 @@ import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material.icons.outlined.ArrowForwardIos
import androidx.compose.material.icons.outlined.Bolt
import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -46,7 +46,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@@ -120,6 +119,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.Math.round
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NewPostView(
onClose: () -> Unit,
@@ -188,9 +188,7 @@ fun NewPostView(
TopAppBar(
title = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(end = 10.dp),
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
@@ -207,7 +205,7 @@ fun NewPostView(
painter = painterResource(R.drawable.relays),
contentDescription = null,
modifier = Modifier.height(25.dp),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
}
@@ -224,17 +222,20 @@ fun NewPostView(
}
},
navigationIcon = {
Spacer(modifier = StdHorzSpacer)
CloseButton(onPress = {
postViewModel.cancel()
scope.launch {
delay(100)
onClose()
}
})
Row() {
Spacer(modifier = StdHorzSpacer)
CloseButton(onPress = {
postViewModel.cancel()
scope.launch {
delay(100)
onClose()
}
})
}
},
backgroundColor = MaterialTheme.colors.surface,
elevation = 0.dp
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface
)
)
}
) { pad ->
@@ -255,7 +256,6 @@ fun NewPostView(
) {
Column(
modifier = Modifier
.padding(top = 10.dp)
.imePadding()
.weight(1f)
) {
@@ -276,7 +276,7 @@ fun NewPostView(
makeItShort = true,
unPackReply = false,
isQuotedNote = true,
modifier = MaterialTheme.colors.replyModifier,
modifier = MaterialTheme.colorScheme.replyModifier,
accountViewModel = accountViewModel,
nav = nav
)
@@ -380,6 +380,9 @@ fun NewPostView(
},
onClose = {
postViewModel.wantsInvoice = false
},
onError = { title, message ->
accountViewModel.toast(title, message)
}
)
}
@@ -412,7 +415,7 @@ fun NewPostView(
.clip(shape = QuoteBorder)
.border(
1.dp,
MaterialTheme.colors.subtleBorder,
MaterialTheme.colorScheme.subtleBorder,
QuoteBorder
)
)
@@ -422,7 +425,7 @@ fun NewPostView(
UrlPreview(myUrlPreview, myUrlPreview, accountViewModel)
}
} else if (startsWithNIP19Scheme(myUrlPreview)) {
val bgColor = MaterialTheme.colors.background
val bgColor = MaterialTheme.colorScheme.background
val backgroundColor = remember {
mutableStateOf(bgColor)
}
@@ -469,7 +472,7 @@ fun NewPostView(
) {
UploadFromGallery(
isUploading = postViewModel.isUploadingImage,
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
) {
postViewModel.selectImage(it)
@@ -533,10 +536,10 @@ private fun PollField(postViewModel: NewPostViewModel) {
},
border = BorderStroke(
1.dp,
MaterialTheme.colors.placeholderText
MaterialTheme.colorScheme.placeholderText
),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colors.placeholderText
containerColor = MaterialTheme.colorScheme.placeholderText
)
) {
Image(
@@ -548,7 +551,7 @@ private fun PollField(postViewModel: NewPostViewModel) {
}
}
@OptIn(ExperimentalComposeUiApi::class)
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun MessageField(
postViewModel: NewPostViewModel
@@ -575,7 +578,7 @@ private fun MessageField(
.fillMaxWidth()
.border(
width = 1.dp,
color = MaterialTheme.colors.surface,
color = MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(8.dp)
)
.focusRequester(focusRequester)
@@ -587,15 +590,14 @@ private fun MessageField(
placeholder = {
Text(
text = stringResource(R.string.what_s_on_your_mind),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
colors = TextFieldDefaults
.outlinedTextFieldColors(
unfocusedBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent
),
visualTransformation = UrlUserTagTransformation(MaterialTheme.colors.primary),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = Color.Transparent,
unfocusedBorderColor = Color.Transparent
),
visualTransformation = UrlUserTagTransformation(MaterialTheme.colorScheme.primary),
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
@@ -646,7 +648,7 @@ fun ContentSensitivityExplainer(postViewModel: NewPostViewModel) {
Text(
text = stringResource(R.string.add_sensitive_content_explainer),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(vertical = 10.dp)
)
}
@@ -676,18 +678,16 @@ fun SendDirectMessageTo(postViewModel: NewPostViewModel) {
placeholder = {
Text(
text = stringResource(R.string.messages_new_message_to_caption),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
visualTransformation = UrlUserTagTransformation(
MaterialTheme.colors.primary
MaterialTheme.colorScheme.primary
),
colors = TextFieldDefaults
.outlinedTextFieldColors(
unfocusedBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent
),
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
colors = OutlinedTextFieldDefaults.colors(
unfocusedBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent
)
)
}
@@ -713,18 +713,16 @@ fun SendDirectMessageTo(postViewModel: NewPostViewModel) {
placeholder = {
Text(
text = stringResource(R.string.messages_new_message_subject_caption),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
visualTransformation = UrlUserTagTransformation(
MaterialTheme.colors.primary
MaterialTheme.colorScheme.primary
),
colors = TextFieldDefaults
.outlinedTextFieldColors(
unfocusedBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent
),
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
colors = OutlinedTextFieldDefaults.colors(
unfocusedBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent
)
)
}
@@ -778,7 +776,7 @@ fun FowardZapTo(postViewModel: NewPostViewModel, accountViewModel: AccountViewMo
Text(
text = stringResource(R.string.zap_split_explainer),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(vertical = 10.dp)
)
@@ -823,12 +821,12 @@ fun FowardZapTo(postViewModel: NewPostViewModel, accountViewModel: AccountViewMo
placeholder = {
Text(
text = stringResource(R.string.zap_split_serarch_and_add_user_placeholder),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true,
visualTransformation = UrlUserTagTransformation(
MaterialTheme.colors.primary
MaterialTheme.colorScheme.primary
),
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
@@ -876,7 +874,7 @@ fun LocationAsHash(postViewModel: NewPostViewModel) {
imageVector = Icons.Default.LocationOn,
null,
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}
@@ -896,7 +894,7 @@ fun LocationAsHash(postViewModel: NewPostViewModel) {
Text(
text = stringResource(R.string.geohash_explainer),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(vertical = 10.dp)
)
}
@@ -957,7 +955,7 @@ fun Notifying(baseMentions: ImmutableList<User>?, onClick: (User) -> Unit) {
Text(
stringResource(R.string.reply_notify),
fontWeight = FontWeight.Bold,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.align(CenterVertically)
)
@@ -971,7 +969,7 @@ fun Notifying(baseMentions: ImmutableList<User>?, onClick: (User) -> Unit) {
Button(
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.mediumImportanceLink
containerColor = MaterialTheme.colorScheme.mediumImportanceLink
),
onClick = {
onClick(myUser)
@@ -1030,7 +1028,7 @@ private fun AddZapraiserButton(
modifier = Modifier
.size(20.dp)
.align(Alignment.TopStart),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
Icon(
imageVector = Icons.Default.Bolt,
@@ -1038,7 +1036,7 @@ private fun AddZapraiserButton(
modifier = Modifier
.size(13.dp)
.align(Alignment.BottomEnd),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
} else {
Icon(
@@ -1074,14 +1072,14 @@ fun AddGeoHash(postViewModel: NewPostViewModel, onClick: () -> Unit) {
imageVector = Icons.Default.LocationOff,
null,
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
} else {
Icon(
imageVector = Icons.Default.LocationOn,
null,
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}
}
@@ -1102,7 +1100,7 @@ private fun AddLnInvoiceButton(
imageVector = Icons.Default.CurrencyBitcoin,
null,
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
} else {
Icon(
@@ -1137,7 +1135,7 @@ private fun ForwardZapTo(
modifier = Modifier
.size(20.dp)
.align(Alignment.CenterStart),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
Icon(
imageVector = Icons.Default.ArrowForwardIos,
@@ -1145,7 +1143,7 @@ private fun ForwardZapTo(
modifier = Modifier
.size(13.dp)
.align(Alignment.CenterEnd),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
} else {
Icon(
@@ -1191,7 +1189,7 @@ private fun MarkAsSensitive(
modifier = Modifier
.size(18.dp)
.align(Alignment.BottomStart),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
Icon(
imageVector = Icons.Rounded.Warning,
@@ -1199,7 +1197,7 @@ private fun MarkAsSensitive(
modifier = Modifier
.size(10.dp)
.align(Alignment.TopEnd),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
} else {
Icon(
@@ -1225,13 +1223,9 @@ private fun MarkAsSensitive(
@Composable
fun CloseButton(onPress: () -> Unit) {
Button(
OutlinedButton(
onClick = onPress,
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = Color.Gray
)
contentPadding = PaddingValues(horizontal = Size5dp)
) {
CloseIcon()
}
@@ -1241,17 +1235,11 @@ fun CloseButton(onPress: () -> Unit) {
fun PostButton(onPost: () -> Unit = {}, isActive: Boolean, modifier: Modifier = Modifier) {
Button(
modifier = modifier,
enabled = isActive,
onClick = {
if (isActive) {
onPost()
}
onPost()
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray
),
contentPadding = PaddingValues(0.dp)
shape = ButtonBorder
) {
Text(text = stringResource(R.string.post), color = Color.White)
}
@@ -1260,19 +1248,11 @@ fun PostButton(onPost: () -> Unit = {}, isActive: Boolean, modifier: Modifier =
@Composable
fun SaveButton(onPost: () -> Unit = {}, isActive: Boolean, modifier: Modifier = Modifier) {
Button(
enabled = isActive,
modifier = modifier,
onClick = {
if (isActive) {
onPost()
}
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray
)
onClick = onPost
) {
Text(text = stringResource(R.string.save), color = Color.White)
Text(text = stringResource(R.string.save))
}
}
@@ -1286,10 +1266,9 @@ fun CreateButton(onPost: () -> Unit = {}, isActive: Boolean, modifier: Modifier
}
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray
)
colors = ButtonDefaults.buttonColors(
containerColor = if (isActive) MaterialTheme.colorScheme.primary else Color.Gray
)
) {
Text(text = stringResource(R.string.create), color = Color.White)
}
@@ -1337,7 +1316,7 @@ fun ImageVideoDescription(
.clip(shape = QuoteBorder)
.border(
1.dp,
MaterialTheme.colors.subtleBorder,
MaterialTheme.colorScheme.subtleBorder,
QuoteBorder
)
) {
@@ -1478,7 +1457,7 @@ fun ImageVideoDescription(
placeholder = {
Text(
text = stringResource(R.string.content_description_example),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -1497,7 +1476,7 @@ fun ImageVideoDescription(
},
shape = QuoteBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = stringResource(R.string.add_content), color = Color.White, fontSize = 20.sp)
@@ -1531,21 +1510,17 @@ fun SettingSwitchItem(
modifier = Modifier.weight(1.0f),
verticalArrangement = Arrangement.spacedBy(3.dp)
) {
val contentAlpha = if (enabled) ContentAlpha.high else ContentAlpha.disabled
Text(
text = stringResource(id = title),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.alpha(contentAlpha)
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(id = description),
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
color = Color.Gray,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.alpha(contentAlpha)
overflow = TextOverflow.Ellipsis
)
}
@@ -33,6 +33,7 @@ import com.vitorpamplona.quartz.events.PrivateDmEvent
import com.vitorpamplona.quartz.events.TextNoteEvent
import com.vitorpamplona.quartz.events.ZapSplitSetup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.mapLatest
@@ -58,7 +59,7 @@ open class NewPostViewModel() : ViewModel() {
var message by mutableStateOf(TextFieldValue(""))
var urlPreview by mutableStateOf<String?>(null)
var isUploadingImage by mutableStateOf(false)
val imageUploadingError = MutableSharedFlow<String?>()
val imageUploadingError = MutableSharedFlow<String?>(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
var userSuggestions by mutableStateOf<List<User>>(emptyList())
var userSuggestionAnchor: TextRange? = null
@@ -14,16 +14,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.DeleteSweep
@@ -34,6 +24,18 @@ import androidx.compose.material.icons.filled.Public
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.SyncProblem
import androidx.compose.material.icons.filled.Upload
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -56,9 +58,11 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.RelayBriefInfo
import com.vitorpamplona.amethyst.model.RelaySetupInfo
import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.service.relays.Constants
import com.vitorpamplona.amethyst.service.relays.Constants.defaultRelays
import com.vitorpamplona.amethyst.service.relays.FeedType
@@ -81,6 +85,7 @@ import com.vitorpamplona.amethyst.ui.theme.warningColor
import kotlinx.coroutines.launch
import java.lang.Math.round
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, relayToAdd: String = "", nav: (String) -> Unit) {
val postViewModel: NewRelayListViewModel = viewModel()
@@ -135,8 +140,9 @@ fun NewRelayListView(onClose: () -> Unit, accountViewModel: AccountViewModel, re
onClose()
})
},
backgroundColor = MaterialTheme.colors.surface,
elevation = 0.dp
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface
)
)
}
) { pad ->
@@ -210,7 +216,7 @@ fun ServerConfigHeader() {
maxLines = 1,
fontSize = Font14SP,
modifier = Modifier.weight(1.2f),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
Spacer(modifier = Modifier.size(5.dp))
@@ -220,7 +226,7 @@ fun ServerConfigHeader() {
maxLines = 1,
fontSize = Font14SP,
modifier = Modifier.weight(1.2f),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
Spacer(modifier = Modifier.size(5.dp))
@@ -230,7 +236,7 @@ fun ServerConfigHeader() {
maxLines = 1,
fontSize = Font14SP,
modifier = Modifier.weight(1f),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
Spacer(modifier = Modifier.size(5.dp))
@@ -240,7 +246,7 @@ fun ServerConfigHeader() {
maxLines = 1,
fontSize = Font14SP,
modifier = Modifier.weight(1f),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
Spacer(modifier = Modifier.size(2.dp))
@@ -258,6 +264,7 @@ fun ServerConfigHeader() {
@Composable
fun ServerConfigPreview() {
ServerConfigClickableLine(
loadProfilePicture = true,
item = RelaySetupInfo(
url = "nostr.mom",
read = true,
@@ -310,7 +317,16 @@ fun ServerConfig(
)
}
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
ServerConfigClickableLine(
loadProfilePicture = automaticallyShowProfilePicture,
item = item,
onToggleDownload = onToggleDownload,
onToggleUpload = onToggleUpload,
@@ -334,15 +350,10 @@ fun ServerConfig(
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
}
scope.launch {
Toast
.makeText(
context,
msg,
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
context.getString(R.string.unable_to_download_relay_document),
msg
)
}
)
}
@@ -351,6 +362,7 @@ fun ServerConfig(
@Composable
fun ServerConfigClickableLine(
loadProfilePicture: Boolean,
item: RelaySetupInfo,
onToggleDownload: (RelaySetupInfo) -> Unit,
onToggleUpload: (RelaySetupInfo) -> Unit,
@@ -368,7 +380,7 @@ fun ServerConfigClickableLine(
modifier = Modifier.padding(vertical = 5.dp)
) {
Column(Modifier.clickable(onClick = onClick)) {
RenderRelayIcon(iconUrl = item.briefInfo.favIcon, Size55dp)
RenderRelayIcon(iconUrl = item.briefInfo.favIcon, loadProfilePicture, Size55dp)
}
Spacer(modifier = HalfHorzPadding)
@@ -441,9 +453,9 @@ private fun RenderStatusRow(
}
),
tint = if (item.read) {
MaterialTheme.colors.allGoodColor
MaterialTheme.colorScheme.allGoodColor
} else {
MaterialTheme.colors.onSurface.copy(
MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.32f
)
}
@@ -454,7 +466,7 @@ private fun RenderStatusRow(
maxLines = 1,
fontSize = 12.sp,
modifier = modifier,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
Icon(
@@ -477,9 +489,9 @@ private fun RenderStatusRow(
}
),
tint = if (item.write) {
MaterialTheme.colors.allGoodColor
MaterialTheme.colorScheme.allGoodColor
} else {
MaterialTheme.colors.onSurface.copy(
MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.32f
)
}
@@ -490,7 +502,7 @@ private fun RenderStatusRow(
maxLines = 1,
fontSize = 12.sp,
modifier = modifier,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
Icon(
@@ -512,7 +524,7 @@ private fun RenderStatusRow(
}
}
),
tint = if (item.errorCount > 0) MaterialTheme.colors.warningColor else MaterialTheme.colors.allGoodColor
tint = if (item.errorCount > 0) MaterialTheme.colorScheme.warningColor else MaterialTheme.colorScheme.allGoodColor
)
Text(
@@ -520,7 +532,7 @@ private fun RenderStatusRow(
maxLines = 1,
fontSize = 12.sp,
modifier = modifier,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
Icon(
@@ -542,7 +554,7 @@ private fun RenderStatusRow(
}
}
),
tint = if (item.spamCount > 0) MaterialTheme.colors.warningColor else MaterialTheme.colors.allGoodColor
tint = if (item.spamCount > 0) MaterialTheme.colorScheme.warningColor else MaterialTheme.colorScheme.allGoodColor
)
Text(
@@ -550,7 +562,7 @@ private fun RenderStatusRow(
maxLines = 1,
fontSize = 12.sp,
modifier = modifier,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
@@ -571,7 +583,7 @@ private fun RenderActiveToggles(
text = stringResource(id = R.string.active_for),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 2.dp, end = 5.dp),
fontSize = 14.sp
)
@@ -601,9 +613,9 @@ private fun RenderActiveToggles(
}
),
tint = if (item.feedTypes.contains(FeedType.FOLLOWS)) {
MaterialTheme.colors.allGoodColor
MaterialTheme.colorScheme.allGoodColor
} else {
MaterialTheme.colors.onSurface.copy(
MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.32f
)
}
@@ -634,9 +646,9 @@ private fun RenderActiveToggles(
}
),
tint = if (item.feedTypes.contains(FeedType.PRIVATE_DMS)) {
MaterialTheme.colors.allGoodColor
MaterialTheme.colorScheme.allGoodColor
} else {
MaterialTheme.colors.onSurface.copy(
MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.32f
)
}
@@ -667,9 +679,9 @@ private fun RenderActiveToggles(
}
),
tint = if (item.feedTypes.contains(FeedType.PUBLIC_CHATS)) {
MaterialTheme.colors.allGoodColor
MaterialTheme.colorScheme.allGoodColor
} else {
MaterialTheme.colors.onSurface.copy(
MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.32f
)
}
@@ -700,9 +712,9 @@ private fun RenderActiveToggles(
}
),
tint = if (item.feedTypes.contains(FeedType.GLOBAL)) {
MaterialTheme.colors.allGoodColor
MaterialTheme.colorScheme.allGoodColor
} else {
MaterialTheme.colors.onSurface.copy(
MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.32f
)
}
@@ -734,9 +746,9 @@ private fun RenderActiveToggles(
}
),
tint = if (item.feedTypes.contains(FeedType.SEARCH)) {
MaterialTheme.colors.allGoodColor
MaterialTheme.colorScheme.allGoodColor
} else {
MaterialTheme.colors.onSurface.copy(
MaterialTheme.colorScheme.onSurface.copy(
alpha = 0.32f
)
}
@@ -767,7 +779,7 @@ private fun FirstLine(
modifier = Modifier
.padding(start = 5.dp, top = 1.dp)
.size(14.dp),
tint = MaterialTheme.colors.allGoodColor
tint = MaterialTheme.colorScheme.allGoodColor
)
}
}
@@ -803,7 +815,7 @@ fun EditableServerConfig(relayToAdd: String, onNewRelay: (RelaySetupInfo) -> Uni
placeholder = {
Text(
text = "server.com",
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
},
@@ -817,7 +829,7 @@ fun EditableServerConfig(relayToAdd: String, onNewRelay: (RelaySetupInfo) -> Uni
modifier = Modifier
.size(Size35dp)
.padding(horizontal = 5.dp),
tint = if (read) MaterialTheme.colors.allGoodColor else MaterialTheme.colors.placeholderText
tint = if (read) MaterialTheme.colorScheme.allGoodColor else MaterialTheme.colorScheme.placeholderText
)
}
@@ -828,7 +840,7 @@ fun EditableServerConfig(relayToAdd: String, onNewRelay: (RelaySetupInfo) -> Uni
modifier = Modifier
.size(Size35dp)
.padding(horizontal = 5.dp),
tint = if (write) MaterialTheme.colors.allGoodColor else MaterialTheme.colors.placeholderText
tint = if (write) MaterialTheme.colorScheme.allGoodColor else MaterialTheme.colorScheme.placeholderText
)
}
@@ -846,7 +858,7 @@ fun EditableServerConfig(relayToAdd: String, onNewRelay: (RelaySetupInfo) -> Uni
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = if (url.isNotBlank()) MaterialTheme.colors.primary else MaterialTheme.colors.placeholderText
containerColor = if (url.isNotBlank()) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.placeholderText
)
) {
Text(text = stringResource(id = R.string.add), color = Color.White)
@@ -57,21 +57,30 @@ class NewRelayListViewModel : ViewModel() {
_relays.update {
var relayFile = account.userProfile().latestContactList?.relays()
// Ugly, but forces nostr.band as the only search-supporting relay today.
// TODO: Remove when search becomes more available.
if (relayFile?.none { it.key.removeSuffix("/") in Constants.forcedRelaysForSearchSet } == true) {
relayFile = relayFile + Constants.forcedRelayForSearch.map {
Pair(
it.url,
ContactListEvent.ReadWrite(it.read, it.write)
)
}
}
if (relayFile != null) {
// Ugly, but forces nostr.band as the only search-supporting relay today.
// TODO: Remove when search becomes more available.
val needsSearchRelay = relayFile.none {
it.key.removeSuffix("/") in Constants.forcedRelaysForSearchSet
} && relayFile.none {
account.localRelays.filter { localRelay -> localRelay.url == it.key }.firstOrNull()?.feedTypes?.contains(FeedType.SEARCH) ?: false
}
if (needsSearchRelay) {
relayFile = relayFile + Constants.forcedRelayForSearch.map {
Pair(
it.url,
ContactListEvent.ReadWrite(it.read, it.write)
)
}
}
relayFile.map {
val liveRelay = RelayPool.getRelay(it.key)
val localInfoFeedTypes = account.localRelays.filter { localRelay -> localRelay.url == it.key }.firstOrNull()?.feedTypes ?: FeedType.values().toSet().toImmutableSet()
val localInfoFeedTypes = account.localRelays.filter { localRelay -> localRelay.url == it.key }.firstOrNull()?.feedTypes
?: Constants.defaultRelays.filter { defaultRelay -> defaultRelay.url == it.key }.firstOrNull()?.feedTypes
?: FeedType.values().toSet().toImmutableSet()
val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
@@ -11,10 +11,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@@ -94,7 +94,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = stringResource(R.string.my_display_name),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -115,7 +115,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = stringResource(id = R.string.about_me),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -134,13 +134,13 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = "https://mywebsite.com/me.jpg",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
leadingIcon = {
UploadFromGallery(
isUploading = postViewModel.isUploadingImageForPicture,
tint = MaterialTheme.colors.placeholderText,
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 5.dp)
) {
postViewModel.uploadForPicture(it, context)
@@ -159,13 +159,13 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = "https://mywebsite.com/mybanner.jpg",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
leadingIcon = {
UploadFromGallery(
isUploading = postViewModel.isUploadingImageForBanner,
tint = MaterialTheme.colors.placeholderText,
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 5.dp)
) {
postViewModel.uploadForBanner(it, context)
@@ -184,7 +184,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = "https://mywebsite.com",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true
@@ -200,7 +200,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = "_@mywebsite.com",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true
@@ -215,7 +215,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = "me@mylightiningnode.com",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true
@@ -231,7 +231,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = stringResource(R.string.lnurl),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -246,7 +246,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = stringResource(R.string.twitter_proof_url_template),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -261,7 +261,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = stringResource(R.string.mastodon_proof_url_template),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -276,7 +276,7 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
placeholder = {
Text(
text = stringResource(R.string.github_proof_url_template),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
)
@@ -15,6 +15,7 @@ import com.vitorpamplona.quartz.events.GitHubIdentity
import com.vitorpamplona.quartz.events.MastodonIdentity
import com.vitorpamplona.quartz.events.TwitterIdentity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import java.io.ByteArrayInputStream
@@ -41,7 +42,7 @@ class NewUserMetadataViewModel : ViewModel() {
var isUploadingImageForPicture by mutableStateOf(false)
var isUploadingImageForBanner by mutableStateOf(false)
val imageUploadingError = MutableSharedFlow<String?>()
val imageUploadingError = MutableSharedFlow<String?>(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
fun load(account: Account) {
this.account = account
@@ -13,9 +13,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -25,8 +26,10 @@ 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.ConnectivityType
import com.vitorpamplona.amethyst.model.RelayBriefInfo
import com.vitorpamplona.amethyst.model.RelayInformation
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
import com.vitorpamplona.amethyst.ui.note.LoadUser
@@ -47,6 +50,14 @@ fun RelayInformationDialog(
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
Dialog(
onDismissRequest = { onClose() },
properties = DialogProperties(
@@ -77,6 +88,7 @@ fun RelayInformationDialog(
Column() {
RenderRelayIcon(
relayBriefInfo.favIcon,
automaticallyShowProfilePicture,
Size55dp
)
}
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst.ui.actions
import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@@ -12,19 +11,18 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.Surface
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.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 androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
@@ -34,7 +32,6 @@ import com.vitorpamplona.amethyst.model.RelayInformation
import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.service.relays.Relay
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.launch
data class RelayList(
val relay: Relay,
@@ -55,7 +52,6 @@ fun RelaySelectionDialog(
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
var relays by remember {
@@ -69,6 +65,13 @@ fun RelaySelectionDialog(
}
)
}
val hasSelectedRelay by remember {
derivedStateOf {
relays.any { it.isSelected }
}
}
var relayInfo: RelayInfoDialog? by remember { mutableStateOf(null) }
relayInfo?.let {
@@ -121,21 +124,15 @@ fun RelaySelectionDialog(
SaveButton(
onPost = {
val selectedRelays = relays.filter { it.isSelected }
if (selectedRelays.isEmpty()) {
scope.launch {
Toast.makeText(context, context.getString(R.string.select_a_relay_to_continue), Toast.LENGTH_SHORT).show()
}
return@SaveButton
}
onPost(selectedRelays.map { it.relay })
onClose()
},
isActive = true
isActive = hasSelectedRelay
)
}
RelaySwitch(
text = stringResource(R.string.select_deselect_all),
text = context.getString(R.string.select_deselect_all),
checked = selected,
onClick = {
selected = !selected
@@ -181,15 +178,10 @@ fun RelaySelectionDialog(
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
}
scope.launch {
Toast
.makeText(
context,
msg,
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
context.getString(R.string.unable_to_download_relay_document),
msg
)
}
)
}
@@ -3,12 +3,10 @@ package com.vitorpamplona.amethyst.ui.actions
import android.Manifest
import android.os.Build
import android.widget.Toast
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -66,21 +64,16 @@ fun SaveToGallery(url: String) {
}
}
Button(
OutlinedButton(
onClick = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q || writeStoragePermissionState.status.isGranted) {
saveImage()
} else {
writeStoragePermissionState.launchPermissionRequest()
}
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = Color.Gray
)
}
) {
Text(text = stringResource(id = R.string.save), color = Color.White)
Text(text = stringResource(id = R.string.save))
}
}
@@ -126,7 +119,7 @@ fun SaveToGallery(localFile: File, mimeType: String?) {
}
}
Button(
OutlinedButton(
onClick = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q || writeStoragePermissionState.status.isGranted) {
saveImage()
@@ -134,12 +127,8 @@ fun SaveToGallery(localFile: File, mimeType: String?) {
writeStoragePermissionState.launchPermissionRequest()
}
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = Color.Gray
)
shape = ButtonBorder
) {
Text(text = stringResource(id = R.string.save), color = Color.White)
Text(text = stringResource(id = R.string.save))
}
}
@@ -11,9 +11,9 @@ import androidx.compose.animation.core.tween
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddPhotoAlternate
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -144,7 +144,7 @@ fun LoadingAnimation(
),
progress = 1f,
strokeWidth = 1.dp,
color = MaterialTheme.colors.background // Set background color
color = MaterialTheme.colorScheme.background // Set background color
)
}
@@ -2,13 +2,12 @@ package com.vitorpamplona.amethyst.ui.buttons
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -20,6 +19,7 @@ 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.theme.Font12SP
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
@Composable
fun ChannelFabColumn(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
@@ -46,12 +46,11 @@ fun ChannelFabColumn(accountViewModel: AccountViewModel, nav: (String) -> Unit)
Column() {
if (isOpen) {
OutlinedButton(
FloatingActionButton(
onClick = { wantsToSendNewMessage = true; isOpen = false },
modifier = Modifier.size(55.dp),
modifier = Size55Modifier,
shape = CircleShape,
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
contentPadding = PaddingValues(bottom = 3.dp)
containerColor = MaterialTheme.colorScheme.primary
) {
Text(
text = stringResource(R.string.messages_new_message),
@@ -63,12 +62,11 @@ fun ChannelFabColumn(accountViewModel: AccountViewModel, nav: (String) -> Unit)
Spacer(modifier = Modifier.height(20.dp))
OutlinedButton(
FloatingActionButton(
onClick = { wantsToCreateChannel = true; isOpen = false },
modifier = Modifier.size(55.dp),
modifier = Size55Modifier,
shape = CircleShape,
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
contentPadding = PaddingValues(bottom = 3.dp)
containerColor = MaterialTheme.colorScheme.primary
) {
Text(
text = stringResource(R.string.messages_create_public_chat),
@@ -81,12 +79,11 @@ fun ChannelFabColumn(accountViewModel: AccountViewModel, nav: (String) -> Unit)
Spacer(modifier = Modifier.height(20.dp))
}
OutlinedButton(
FloatingActionButton(
onClick = { isOpen = !isOpen },
modifier = Modifier.size(55.dp),
modifier = Size55Modifier,
shape = CircleShape,
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
contentPadding = PaddingValues(0.dp)
containerColor = MaterialTheme.colorScheme.primary
) {
Icon(
imageVector = Icons.Outlined.Add,
@@ -3,12 +3,12 @@ package com.vitorpamplona.amethyst.buttons
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -36,7 +36,7 @@ fun NewChannelButton(accountViewModel: AccountViewModel) {
onClick = { wantsToPost = true },
modifier = Modifier.size(55.dp),
shape = CircleShape,
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
colors = ButtonDefaults.outlinedButtonColors(containerColor = MaterialTheme.colorScheme.primary),
contentPadding = PaddingValues(0.dp)
) {
Icon(
@@ -1,12 +1,10 @@
package com.vitorpamplona.amethyst.ui.buttons
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -21,6 +19,7 @@ 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.theme.Size55Modifier
@Composable
fun NewCommunityNoteButton(communityIdHex: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
@@ -41,12 +40,11 @@ fun NewCommunityNoteButton(note: Note, accountViewModel: AccountViewModel, nav:
NewPostView({ wantsToPost = false }, note, accountViewModel = accountViewModel, nav = nav)
}
OutlinedButton(
FloatingActionButton(
onClick = { wantsToPost = true },
modifier = Modifier.size(55.dp),
modifier = Size55Modifier,
shape = CircleShape,
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
contentPadding = PaddingValues(0.dp)
containerColor = MaterialTheme.colorScheme.primary
) {
Icon(
painter = painterResource(R.drawable.ic_compose),
@@ -6,16 +6,14 @@ import android.os.Build
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.ProgressIndicatorDefaults
import androidx.compose.material.Text
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -41,6 +39,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.theme.Size55Modifier
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -112,12 +111,11 @@ fun NewImageButton(accountViewModel: AccountViewModel, nav: (String) -> Unit, na
if (postViewModel.isUploadingImage) {
ShowProgress(postViewModel)
} else {
OutlinedButton(
FloatingActionButton(
onClick = { wantsToPost = true },
modifier = Modifier.size(55.dp),
modifier = Size55Modifier,
shape = CircleShape,
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
contentPadding = PaddingValues(0.dp)
containerColor = MaterialTheme.colorScheme.primary
) {
Icon(
painter = painterResource(R.drawable.ic_compose),
@@ -137,16 +135,15 @@ private fun ShowProgress(postViewModel: NewMediaModel) {
targetValue = postViewModel.uploadingPercentage.value,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
).value,
modifier = Modifier
.size(55.dp)
modifier = Size55Modifier
.clip(CircleShape)
.background(MaterialTheme.colors.background),
.background(MaterialTheme.colorScheme.background),
strokeWidth = 5.dp
)
postViewModel.uploadingDescription.value?.let {
Text(
it,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.colorScheme.onSurface,
fontSize = 10.sp,
textAlign = TextAlign.Center
)
@@ -1,12 +1,10 @@
package com.vitorpamplona.amethyst.ui.buttons
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -19,6 +17,7 @@ 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.theme.Size55Modifier
@Composable
fun NewNoteButton(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
@@ -30,12 +29,11 @@ fun NewNoteButton(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
NewPostView({ wantsToPost = false }, accountViewModel = accountViewModel, nav = nav)
}
OutlinedButton(
FloatingActionButton(
onClick = { wantsToPost = true },
modifier = Modifier.size(55.dp),
modifier = Size55Modifier,
shape = CircleShape,
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MaterialTheme.colors.primary),
contentPadding = PaddingValues(0.dp)
containerColor = MaterialTheme.colorScheme.primary
) {
Icon(
painter = painterResource(R.drawable.ic_compose),
@@ -6,7 +6,7 @@ import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -55,7 +55,7 @@ fun AudioWaveformReadOnly(
amplitudes: List<Int>,
onProgressChange: (Float) -> Unit
) {
val backgroundColor = MaterialTheme.colors.background
val backgroundColor = MaterialTheme.colorScheme.background
val _progress = remember(progress) { progress.coerceIn(MinProgress, MaxProgress) }
val _spikeWidth = remember(spikeWidth) { spikeWidth.coerceIn(MinSpikeWidthDp, MaxSpikeWidthDp) }
val _spikePadding = remember(spikePadding) { spikePadding.coerceIn(MinSpikePaddingDp, MaxSpikePaddingDp) }
@@ -2,7 +2,6 @@ package com.vitorpamplona.amethyst.ui.components
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Column
@@ -10,7 +9,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -76,7 +75,7 @@ fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
.fillMaxWidth()
.padding(start = 30.dp, end = 30.dp)
.clip(shape = QuoteBorder)
.border(1.dp, MaterialTheme.colors.subtleBorder, QuoteBorder)
.border(1.dp, MaterialTheme.colorScheme.subtleBorder, QuoteBorder)
) {
Column(
modifier = Modifier
@@ -133,31 +132,25 @@ fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
CashuProcessor().melt(
token,
lud16,
onSuccess = {
scope.launch {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}
onSuccess = { title, message ->
accountViewModel.toast(title, message)
},
onError = {
scope.launch {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
}
}
onError = { title, message ->
accountViewModel.toast(title, message)
},
context
)
}
} else {
scope.launch {
Toast.makeText(
context,
context.getString(R.string.no_lightning_address_set),
Toast.LENGTH_SHORT
).show()
}
accountViewModel.toast(
context.getString(R.string.no_lightning_address_set),
context.getString(R.string.user_x_does_not_have_a_lightning_address_setup_to_receive_sats, accountViewModel.account.userProfile().toBestDisplayName())
)
}
},
shape = QuoteBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(
@@ -177,16 +170,12 @@ fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
startActivity(context, intent, null)
} else {
// Copying the token to clipboard for now
var orignaltoken = token.token
clipboardManager.setText(AnnotatedString("$orignaltoken"))
scope.launch {
Toast.makeText(context, context.getString(R.string.copied_token_to_clipboard), Toast.LENGTH_SHORT).show()
}
clipboardManager.setText(AnnotatedString(token.token))
}
},
shape = QuoteBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text("", color = Color.White, fontSize = 18.sp)
@@ -4,8 +4,8 @@ import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@@ -19,7 +19,7 @@ fun ClickableEmail(email: String) {
ClickableText(
text = remember { AnnotatedString(stripped) },
onClick = { runCatching { context.sendMail(stripped) } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
}
@@ -1,8 +1,8 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.AnnotatedString
import com.vitorpamplona.amethyst.model.Note
@@ -16,6 +16,6 @@ fun ClickableNoteTag(
ClickableText(
text = AnnotatedString("@${baseNote.idNote().toShortenHex()}"),
onClick = { nav("Note/${baseNote.idHex}") },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
}
@@ -4,8 +4,8 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@@ -18,7 +18,7 @@ fun ClickablePhone(phone: String) {
ClickableText(
text = remember { AnnotatedString(phone) },
onClick = { runCatching { context.dial(phone) } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
}
@@ -7,11 +7,10 @@ import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
@@ -307,8 +306,8 @@ fun CreateClickableText(
nav: (String) -> Unit
) {
val currentStyle = LocalTextStyle.current
val primaryColor = MaterialTheme.colors.primary
val onBackgroundColor = MaterialTheme.colors.onBackground
val primaryColor = MaterialTheme.colorScheme.primary
val onBackgroundColor = MaterialTheme.colorScheme.onBackground
val clickablePartStyle = remember(primaryColor, overrideColor) {
currentStyle.copy(color = overrideColor ?: primaryColor, fontWeight = fontWeight).toSpanStyle()
@@ -368,7 +367,7 @@ fun CreateTextWithEmoji(
val textColor = color.takeOrElse {
LocalTextStyle.current.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
LocalContentColor.current
}
}
@@ -424,7 +423,7 @@ fun CreateTextWithEmoji(
val textColor = color.takeOrElse {
LocalTextStyle.current.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
LocalContentColor.current
}
}
@@ -537,14 +536,14 @@ fun CreateClickableTextWithEmoji(
ClickableInLineIconRenderer(
emojiLists!!.part1,
maxLines,
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.primary, fontWeight = fontWeight).toSpanStyle()
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colorScheme.primary, fontWeight = fontWeight).toSpanStyle()
) {
nav(route)
}
InLineIconRenderer(
emojiLists!!.part2,
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.onBackground, fontWeight = fontWeight).toSpanStyle(),
LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colorScheme.onBackground, fontWeight = fontWeight).toSpanStyle(),
maxLines = maxLines
)
}
@@ -1,8 +1,8 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalUriHandler
@@ -23,6 +23,6 @@ fun ClickableUrl(urlText: String, url: String) {
ClickableText(
text = text,
onClick = { runCatching { uri.openUri(doubleCheckedUrl) } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
}
@@ -1,8 +1,8 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@@ -28,6 +28,6 @@ fun ClickableUserTag(
ClickableText(
text = userName,
onClick = { nav(route) },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
}
@@ -1,19 +1,17 @@
package com.vitorpamplona.amethyst.ui.components
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextDirection
import androidx.core.content.ContextCompat
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog
import com.vitorpamplona.amethyst.ui.note.payViaIntent
import com.vitorpamplona.quartz.encoders.LnWithdrawalUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -43,29 +41,28 @@ fun MayBeWithdrawal(lnurlWord: String) {
@Composable
fun ClickableWithdrawal(withdrawalString: String) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val withdraw = remember(withdrawalString) {
AnnotatedString("$withdrawalString ")
}
var showErrorMessageDialog by remember { mutableStateOf<String?>(null) }
if (showErrorMessageDialog != null) {
ErrorMessageDialog(
title = context.getString(R.string.error_dialog_pay_withdraw_error),
textContent = showErrorMessageDialog ?: "",
onDismiss = { showErrorMessageDialog = null }
)
}
ClickableText(
text = withdraw,
onClick = {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("lightning:$withdrawalString"))
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
ContextCompat.startActivity(context, intent, null)
} catch (e: Exception) {
scope.launch {
Toast.makeText(
context,
context.getString(R.string.lightning_wallets_not_found),
Toast.LENGTH_LONG
).show()
}
payViaIntent(withdrawalString, context) {
showErrorMessageDialog = it
}
},
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
}
@@ -7,10 +7,10 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
@@ -97,7 +97,7 @@ fun ExpandableRichTextViewer(
.fillMaxWidth()
.background(getGradient(backgroundColor))
) {
ShowMoreButton() {
ShowMoreButton {
showFullText = !showFullText
}
}
@@ -112,7 +112,7 @@ fun ShowMoreButton(onClick: () -> Unit) {
onClick = onClick,
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.secondaryButtonBackground
containerColor = MaterialTheme.colorScheme.secondaryButtonBackground
),
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
) {
@@ -1,8 +1,5 @@
package com.vitorpamplona.amethyst.ui.components
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Column
@@ -10,7 +7,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -23,8 +20,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat.startActivity
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog
import com.vitorpamplona.amethyst.ui.note.payViaIntent
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
@@ -67,14 +65,23 @@ fun MayBeInvoicePreview(lnbcWord: String) {
@Composable
fun InvoicePreview(lnInvoice: String, amount: String?) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
var showErrorMessageDialog by remember { mutableStateOf<String?>(null) }
if (showErrorMessageDialog != null) {
ErrorMessageDialog(
title = context.getString(R.string.error_dialog_pay_invoice_error),
textContent = showErrorMessageDialog ?: "",
onDismiss = { showErrorMessageDialog = null }
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 30.dp, end = 30.dp)
.clip(shape = QuoteBorder)
.border(1.dp, MaterialTheme.colors.subtleBorder, QuoteBorder)
.border(1.dp, MaterialTheme.colorScheme.subtleBorder, QuoteBorder)
) {
Column(
modifier = Modifier
@@ -120,23 +127,13 @@ fun InvoicePreview(lnInvoice: String, amount: String?) {
.fillMaxWidth()
.padding(vertical = 10.dp),
onClick = {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("lightning:$lnInvoice"))
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(context, intent, null)
} catch (e: Exception) {
scope.launch {
Toast.makeText(
context,
context.getString(R.string.lightning_wallets_not_found),
Toast.LENGTH_LONG
).show()
}
payViaIntent(lnInvoice, context) {
showErrorMessageDialog = it
}
},
shape = QuoteBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = stringResource(R.string.pay), color = Color.White, fontSize = 20.sp)
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst.ui.components
import android.widget.Toast
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -8,13 +7,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -50,21 +49,22 @@ fun InvoiceRequestCard(
titleText: String? = null,
buttonText: String? = null,
onSuccess: (String) -> Unit,
onClose: () -> Unit
onClose: () -> Unit,
onError: (String, String) -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 30.dp, end = 30.dp)
.clip(shape = QuoteBorder)
.border(1.dp, MaterialTheme.colors.subtleBorder, QuoteBorder)
.border(1.dp, MaterialTheme.colorScheme.subtleBorder, QuoteBorder)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(30.dp)
) {
InvoiceRequest(lud16, toUserPubKeyHex, account, titleText, buttonText, onSuccess, onClose)
InvoiceRequest(lud16, toUserPubKeyHex, account, titleText, buttonText, onSuccess, onClose, onError)
}
}
}
@@ -77,7 +77,8 @@ fun InvoiceRequest(
titleText: String? = null,
buttonText: String? = null,
onSuccess: (String) -> Unit,
onClose: () -> Unit
onClose: () -> Unit,
onError: (String, String) -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
@@ -116,7 +117,7 @@ fun InvoiceRequest(
placeholder = {
Text(
text = stringResource(R.string.thank_you_so_much),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -141,7 +142,7 @@ fun InvoiceRequest(
placeholder = {
Text(
text = "1000",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -162,20 +163,16 @@ fun InvoiceRequest(
message,
zapRequest?.toJson(),
onSuccess = onSuccess,
onError = {
scope.launch {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
onClose()
}
},
onError = onError,
onProgress = {
}
},
context = context
)
}
},
shape = QuoteBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = buttonText ?: stringResource(R.string.send_sats), color = Color.White, fontSize = 20.sp)
@@ -6,13 +6,12 @@ import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
@@ -30,7 +29,7 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.em
import com.halilibo.richtext.markdown.Markdown
import com.halilibo.richtext.markdown.MarkdownParseOptions
import com.halilibo.richtext.ui.material.MaterialRichText
import com.halilibo.richtext.ui.material3.Material3RichText
import com.vitorpamplona.amethyst.model.HashtagIcon
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
@@ -137,7 +136,7 @@ private fun RenderRegular(
val textStyle = currentTextStyle.copy(
lineHeight = 1.4.em,
color = currentTextStyle.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
LocalContentColor.current
}
)
@@ -333,8 +332,8 @@ private fun RenderContentAsMarkdown(content: String, tags: ImmutableListOfLists<
}
ProvideTextStyle(MarkdownTextStyle) {
MaterialRichText(
style = MaterialTheme.colors.markdownStyle
Material3RichText(
style = MaterialTheme.colorScheme.markdownStyle
) {
RefreshableContent(content, tags, accountViewModel) {
Markdown(
@@ -511,7 +510,7 @@ private fun DisplayFullNote(
NoteCompose(
baseNote = it,
accountViewModel = accountViewModel,
modifier = MaterialTheme.colors.replyModifier,
modifier = MaterialTheme.colorScheme.replyModifier,
parentBackgroundColor = backgroundColor,
isQuotedNote = true,
nav = nav
@@ -540,12 +539,12 @@ private fun RenderHashtag(
segment: HashTagSegment,
nav: (String) -> Unit
) {
val primary = MaterialTheme.colors.primary
val primary = MaterialTheme.colorScheme.primary
val hashtagIcon = remember(segment.hashtag) { checkForHashtagWithIcon(segment.hashtag, primary) }
ClickableText(
text = buildAnnotatedString {
withStyle(
LocalTextStyle.current.copy(color = MaterialTheme.colors.primary).toSpanStyle()
LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary).toSpanStyle()
) {
append("#${segment.hashtag}")
}
@@ -557,7 +556,7 @@ private fun RenderHashtag(
val myId = "inlineContent"
val emptytext = buildAnnotatedString {
withStyle(
LocalTextStyle.current.copy(color = MaterialTheme.colors.primary).toSpanStyle()
LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary).toSpanStyle()
) {
append("")
appendInlineContent(myId, "[icon]")
@@ -656,7 +655,7 @@ private fun DisplayNoteFromTag(
NoteCompose(
baseNote = baseNote,
accountViewModel = accountViewModel,
modifier = MaterialTheme.colors.innerPostModifier,
modifier = MaterialTheme.colorScheme.innerPostModifier,
parentBackgroundColor = backgroundColor,
isQuotedNote = true,
nav = nav
@@ -58,14 +58,15 @@ fun RobohashFallbackAsyncImage(
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
loadProfilePicture: Boolean
) {
val context = LocalContext.current
val painter = rememberAsyncImagePainter(
model = Robohash.imageRequest(context, robot)
)
if (model != null) {
if (model != null && loadProfilePicture) {
AsyncImage(
model = model,
contentDescription = contentDescription,
@@ -101,7 +102,8 @@ fun RobohashAsyncImageProxy(
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
loadProfilePicture: Boolean
) {
RobohashFallbackAsyncImage(
robot = robot,
@@ -112,6 +114,7 @@ fun RobohashAsyncImageProxy(
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality
filterQuality = filterQuality,
loadProfilePicture = loadProfilePicture
)
}
@@ -9,10 +9,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.Divider
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material3.Card
import androidx.compose.material3.Divider
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -11,14 +11,14 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
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
@@ -104,7 +104,7 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
modifier = Modifier
.size(70.dp)
.align(Alignment.BottomStart),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
Icon(
imageVector = Icons.Rounded.Warning,
@@ -112,7 +112,7 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
modifier = Modifier
.size(30.dp)
.align(Alignment.TopEnd),
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
}
@@ -141,7 +141,7 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
),
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
) {
@@ -14,8 +14,8 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -66,8 +66,8 @@ fun DotsIndicator(
modifier: Modifier = Modifier,
totalDots: Int,
selectedIndex: Int,
selectedColor: Color = MaterialTheme.colors.primary /* Color.Yellow */,
unSelectedColor: Color = MaterialTheme.colors.placeholderText /* Color.Gray */,
selectedColor: Color = MaterialTheme.colorScheme.primary /* Color.Yellow */,
unSelectedColor: Color = MaterialTheme.colorScheme.placeholderText /* Color.Gray */,
dotSize: Dp
) {
LazyRow(
@@ -14,11 +14,11 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
@@ -98,7 +98,7 @@ fun SpinnerSelectionDialog(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
Text(text = item.title, color = MaterialTheme.colors.onSurface)
Text(text = item.title, color = MaterialTheme.colorScheme.onSurface)
}
item.explainer?.let {
Spacer(modifier = Modifier.height(5.dp))
@@ -5,8 +5,8 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -46,7 +46,7 @@ fun UrlPreviewCard(
val uri = LocalUriHandler.current
Row(
modifier = MaterialTheme.colors.innerPostModifier
modifier = MaterialTheme.colorScheme.innerPostModifier
.clickable {
runCatching { uri.openUri(url) }
}
@@ -63,7 +63,7 @@ fun UrlPreviewCard(
Text(
text = previewInfo.verifiedUrl?.host ?: previewInfo.url,
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
modifier = MaxWidthWithHorzPadding,
color = Color.Gray,
maxLines = 1,
@@ -72,7 +72,7 @@ fun UrlPreviewCard(
Text(
text = previewInfo.title,
style = MaterialTheme.typography.body2,
style = MaterialTheme.typography.bodyMedium,
modifier = MaxWidthWithHorzPadding,
maxLines = 1,
overflow = TextOverflow.Ellipsis
@@ -80,7 +80,7 @@ fun UrlPreviewCard(
Text(
text = previewInfo.description,
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
modifier = MaxWidthWithHorzPadding,
color = Color.Gray,
maxLines = 3,
@@ -22,8 +22,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -527,7 +527,7 @@ private fun RenderVideoPlayer(
}
BoxWithConstraints() {
val borders = MaterialTheme.colors.imageModifier
val borders = MaterialTheme.colorScheme.imageModifier
val myModifier = remember {
if (roundedCorner) {
@@ -797,7 +797,7 @@ private fun MuteButton(
.clip(CircleShape)
.fillMaxSize(0.6f)
.align(Alignment.Center)
.background(MaterialTheme.colors.background)
.background(MaterialTheme.colorScheme.background)
)
IconButton(
@@ -838,7 +838,7 @@ private fun KeepPlayingButton(
.clip(CircleShape)
.fillMaxSize(0.6f)
.align(Alignment.Center)
.background(MaterialTheme.colors.background)
.background(MaterialTheme.colorScheme.background)
)
IconButton(
@@ -849,9 +849,9 @@ private fun KeepPlayingButton(
modifier = Size50Modifier
) {
if (keepPlaying.value) {
LyricsIcon(Size22Modifier, MaterialTheme.colors.onBackground)
LyricsIcon(Size22Modifier, MaterialTheme.colorScheme.onBackground)
} else {
LyricsOffIcon(Size22Modifier, MaterialTheme.colors.onBackground)
LyricsOffIcon(Size22Modifier, MaterialTheme.colorScheme.onBackground)
}
}
}
@@ -5,11 +5,11 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
@@ -60,7 +60,7 @@ fun ZapRaiserRequest(
Text(
text = stringResource(R.string.zapraiser_explainer),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(vertical = 10.dp)
)
@@ -84,7 +84,7 @@ fun ZapRaiserRequest(
placeholder = {
Text(
text = "1000",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -6,7 +6,6 @@ import android.content.ContextWrapper
import android.os.Build
import android.util.Log
import android.view.Window
import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
@@ -16,6 +15,7 @@ import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -29,18 +29,17 @@ import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
@@ -49,11 +48,9 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
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.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
@@ -79,6 +76,7 @@ import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.service.BlurHashRequester
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.actions.CloseButton
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
import com.vitorpamplona.amethyst.ui.actions.LoadingAnimation
import com.vitorpamplona.amethyst.ui.actions.SaveToGallery
import com.vitorpamplona.amethyst.ui.note.BlankNote
@@ -86,15 +84,14 @@ 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.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.Font17SP
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.Size20dp
import com.vitorpamplona.amethyst.ui.theme.Size24dp
import com.vitorpamplona.amethyst.ui.theme.Size30dp
import com.vitorpamplona.amethyst.ui.theme.Size5dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.imageModifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.toHexKey
import kotlinx.collections.immutable.ImmutableList
@@ -212,7 +209,7 @@ fun ZoomableContentView(
}
var mainImageModifier = if (roundedCorner) {
MaterialTheme.colors.imageModifier
MaterialTheme.colorScheme.imageModifier
} else {
Modifier.fillMaxWidth()
}
@@ -553,7 +550,7 @@ private fun DisplayUrlWithLoadingSymbolWait(content: ZoomableContent) {
val myId = "inlineContent"
val emptytext = buildAnnotatedString {
withStyle(
LocalTextStyle.current.copy(color = MaterialTheme.colors.primary).toSpanStyle()
LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary).toSpanStyle()
) {
append("")
appendInlineContent(myId, "[icon]")
@@ -637,7 +634,7 @@ fun ZoomableImageDialog(
decorFitsSystemWindows = false
)
) {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
val pagerState: PagerState = rememberPagerState() { allImages.size }
@@ -671,6 +668,7 @@ fun ZoomableImageDialog(
if (myContent is ZoomableUrlContent) {
Row() {
CopyToClipboard(content = myContent)
Spacer(modifier = StdHorzSpacer)
SaveToGallery(url = myContent.url)
}
} else if (myContent is ZoomableLocalImage && myContent.localFile != null) {
@@ -691,17 +689,11 @@ private fun CopyToClipboard(
) {
val popupExpanded = remember { mutableStateOf(false) }
Button(
OutlinedButton(
modifier = Modifier.padding(horizontal = Size5dp),
onClick = { popupExpanded.value = true },
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.placeholderText
)
onClick = { popupExpanded.value = true }
) {
Icon(
tint = Color.White,
imageVector = Icons.Default.Share,
modifier = Size20Modifier,
contentDescription = stringResource(R.string.copy_url_to_clipboard)
@@ -725,20 +717,35 @@ private fun ShareImageAction(
val clipboardManager = LocalClipboardManager.current
if (content is ZoomableUrlContent) {
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(content.url)); onDismiss() }) {
Text(stringResource(R.string.copy_url_to_clipboard))
}
if (content.uri != null) {
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(content.uri)); onDismiss() }) {
Text(stringResource(R.string.copy_the_note_id_to_the_clipboard))
DropdownMenuItem(
text = {
Text(stringResource(R.string.copy_url_to_clipboard))
},
onClick = {
clipboardManager.setText(AnnotatedString(content.url)); onDismiss()
}
)
if (content.uri != null) {
DropdownMenuItem(
text = {
Text(stringResource(R.string.copy_the_note_id_to_the_clipboard))
},
onClick = {
clipboardManager.setText(AnnotatedString(content.uri)); onDismiss()
}
)
}
}
if (content is ZoomablePreloadedContent) {
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(content.uri)); onDismiss() }) {
Text(stringResource(R.string.copy_the_note_id_to_the_clipboard))
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.copy_the_note_id_to_the_clipboard))
},
onClick = {
clipboardManager.setText(AnnotatedString(content.uri)); onDismiss()
}
)
}
}
}
@@ -800,7 +807,17 @@ private fun verifyHash(content: ZoomableUrlContent, context: Context): Boolean?
@Composable
private fun HashVerificationSymbol(verifiedHash: Boolean, modifier: Modifier) {
val localContext = LocalContext.current
val scope = rememberCoroutineScope()
val openDialogMsg = remember { mutableStateOf<String?>(null) }
openDialogMsg.value?.let {
InformationDialog(
title = localContext.getString(R.string.hash_verification_info_title),
textContent = it
) {
openDialogMsg.value = null
}
}
Box(
modifier
@@ -811,13 +828,7 @@ private fun HashVerificationSymbol(verifiedHash: Boolean, modifier: Modifier) {
if (verifiedHash) {
IconButton(
onClick = {
scope.launch {
Toast.makeText(
localContext,
localContext.getString(R.string.hash_verification_passed),
Toast.LENGTH_LONG
).show()
}
openDialogMsg.value = localContext.getString(R.string.hash_verification_passed)
}
) {
HashCheckIcon(Size30dp)
@@ -825,13 +836,7 @@ private fun HashVerificationSymbol(verifiedHash: Boolean, modifier: Modifier) {
} else {
IconButton(
onClick = {
scope.launch {
Toast.makeText(
localContext,
localContext.getString(R.string.hash_verification_failed),
Toast.LENGTH_LONG
).show()
}
openDialogMsg.value = localContext.getString(R.string.hash_verification_failed)
}
) {
HashCheckFailedIcon(Size30dp)
@@ -12,17 +12,19 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.AlertDialog
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Logout
import androidx.compose.material.icons.filled.RadioButtonChecked
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@@ -33,7 +35,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.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -44,8 +45,10 @@ import androidx.lifecycle.map
import com.vitorpamplona.amethyst.AccountInfo
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
@@ -60,6 +63,7 @@ import com.vitorpamplona.quartz.events.toImmutableListOfLists
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AccountSwitchBottomSheet(
accountViewModel: AccountViewModel,
@@ -111,8 +115,9 @@ fun AccountSwitchBottomSheet(
ArrowBackIcon()
}
},
backgroundColor = Color.Transparent,
elevation = 0.dp
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface
)
)
}
}
@@ -136,6 +141,14 @@ fun DisplayAccount(
)
}
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
if (baseUser == null) {
LaunchedEffect(key1 = acc.npub) {
launch(Dispatchers.IO) {
@@ -173,7 +186,7 @@ fun DisplayAccount(
.width(55.dp)
.padding(0.dp)
) {
AccountPicture(it)
AccountPicture(it, automaticallyShowProfilePicture)
}
Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f)) {
@@ -202,22 +215,21 @@ private fun ActiveMarker(acc: AccountInfo, accountViewModel: AccountViewModel) {
Icon(
imageVector = Icons.Default.RadioButtonChecked,
contentDescription = stringResource(R.string.account_switch_active_account),
tint = MaterialTheme.colors.secondary
tint = MaterialTheme.colorScheme.secondary
)
}
}
@Composable
private fun AccountPicture(user: User) {
val profilePicture by user.live().metadata.map {
it.user.profilePicture()
}.observeAsState()
private fun AccountPicture(user: User, loadProfilePicture: Boolean) {
val profilePicture by user.live().profilePictureChanges.observeAsState()
RobohashAsyncImageProxy(
robot = remember(user) { user.pubkeyHex },
model = profilePicture,
contentDescription = stringResource(R.string.profile_image),
modifier = AccountPictureModifier
modifier = AccountPictureModifier,
loadProfilePicture = loadProfilePicture
)
}
@@ -293,7 +305,7 @@ private fun LogoutButton(
Icon(
imageVector = Icons.Default.Logout,
contentDescription = stringResource(R.string.log_out),
tint = MaterialTheme.colors.onSurface
tint = MaterialTheme.colorScheme.onSurface
)
}
}
@@ -6,28 +6,23 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.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
@@ -39,13 +34,14 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavBackStackEntry
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.Size0dp
import com.vitorpamplona.amethyst.ui.theme.Size10dp
import kotlinx.collections.immutable.persistentListOf
val bottomNavigationItems = listOf(
val bottomNavigationItems = persistentListOf(
Route.Home,
Route.Message,
Route.Video,
@@ -61,6 +57,7 @@ enum class Keyboard {
fun keyboardAsState(): State<Keyboard> {
val keyboardState = remember { mutableStateOf(Keyboard.Closed) }
val view = LocalView.current
DisposableEffect(view) {
val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
@@ -104,13 +101,9 @@ private fun RenderBottomMenu(
) {
Column(modifier = BottomTopHeight) {
Divider(
thickness = 0.25.dp
thickness = DividerThickness
)
BottomNavigation(
modifier = Modifier,
elevation = 0.dp,
backgroundColor = MaterialTheme.colors.background
) {
NavigationBar(tonalElevation = Size0dp) {
bottomNavigationItems.forEach { item ->
HasNewItemsIcon(item, accountViewModel, navEntryState, nav)
}
@@ -125,16 +118,12 @@ private fun RowScope.HasNewItemsIcon(
navEntryState: State<NavBackStackEntry?>,
nav: (Route, Boolean) -> Unit
) {
var hasNewItems by remember { mutableStateOf(false) }
WatchPossibleNotificationChanges(route, accountViewModel) {
if (it != hasNewItems) {
hasNewItems = it
val selected by remember(navEntryState.value) {
derivedStateOf {
navEntryState.value?.destination?.route?.substringBefore("?") == route.base
}
}
val scope = rememberCoroutineScope()
val size = remember {
if ("Home" == route.base) 25.dp else 23.dp
}
@@ -142,78 +131,12 @@ private fun RowScope.HasNewItemsIcon(
if ("Home" == route.base) 24.dp else 20.dp
}
BottomIcon(
icon = route.icon,
size = size,
iconSize = iconSize,
base = route.base,
hasNewItems = hasNewItems,
navEntryState = navEntryState
) { selected ->
scope.launch {
nav(route, selected)
}
}
}
@Composable
fun WatchPossibleNotificationChanges(
route: Route,
accountViewModel: AccountViewModel,
onChange: (Boolean) -> Unit
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val notifState by accountViewModel.accountLastReadLiveData.observeAsState()
LaunchedEffect(key1 = notifState, key2 = accountState) {
launch(Dispatchers.IO) {
onChange(route.hasNewItems(accountViewModel.account, emptySet()))
}
}
LaunchedEffect(Unit) {
launch(Dispatchers.IO) {
LocalCache.live.newEventBundles.collect {
launch(Dispatchers.IO) {
onChange(route.hasNewItems(accountViewModel.account, it))
}
}
}
}
}
@Composable
private fun RowScope.BottomIcon(
icon: Int,
size: Dp,
iconSize: Dp,
base: String,
hasNewItems: Boolean,
navEntryState: State<NavBackStackEntry?>,
onClick: (Boolean) -> Unit
) {
val selected by remember(navEntryState.value) {
derivedStateOf {
navEntryState.value?.destination?.route?.substringBefore("?") == base
}
}
NavigationIcon(icon, size, iconSize, selected, hasNewItems, onClick)
}
@Composable
private fun RowScope.NavigationIcon(
icon: Int,
size: Dp,
iconSize: Dp,
selected: Boolean,
hasNewItems: Boolean,
onClick: (Boolean) -> Unit
) {
BottomNavigationItem(
NavigationBarItem(
icon = {
val hasNewItems = accountViewModel.notificationDots.hasNewItems[route]?.collectAsState()
NotifiableIcon(
icon,
route.icon,
size,
iconSize,
selected,
@@ -221,51 +144,56 @@ private fun RowScope.NavigationIcon(
)
},
selected = selected,
onClick = { onClick(selected) }
onClick = { nav(route, selected) }
)
}
@Composable
private fun NotifiableIcon(icon: Int, size: Dp, iconSize: Dp, selected: Boolean, hasNewItems: Boolean) {
private fun NotifiableIcon(
icon: Int,
size: Dp,
iconSize: Dp,
selected: Boolean,
hasNewItems: State<Boolean>?
) {
Box(remember { Modifier.size(size) }) {
Icon(
painter = painterResource(id = icon),
contentDescription = null,
modifier = remember { Modifier.size(iconSize) },
tint = if (selected) MaterialTheme.colors.primary else Color.Unspecified
tint = if (selected) MaterialTheme.colorScheme.primary else Color.Unspecified
)
if (hasNewItems) {
Box(
remember {
Modifier
.width(10.dp)
.height(10.dp)
.align(Alignment.TopEnd)
}
) {
Box(
modifier = remember {
Modifier
.width(10.dp)
.height(10.dp)
.clip(shape = CircleShape)
}.background(MaterialTheme.colors.primary),
contentAlignment = Alignment.TopEnd
) {
Text(
"",
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 12.sp,
modifier = remember {
Modifier
.wrapContentHeight()
.align(Alignment.TopEnd)
}
)
}
}
if (hasNewItems?.value == true) {
NotificationDotIcon(
Modifier.align(Alignment.TopEnd)
)
}
}
}
@Composable
private fun NotificationDotIcon(modifier: Modifier) {
Box(modifier.size(Size10dp)) {
Box(
modifier = remember {
Modifier
.size(Size10dp)
.clip(shape = CircleShape)
}.background(MaterialTheme.colorScheme.primary),
contentAlignment = Alignment.TopEnd
) {
Text(
"",
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 12.sp,
modifier = remember {
Modifier
.wrapContentHeight()
.align(Alignment.TopEnd)
}
)
}
}
}
@@ -17,22 +17,18 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.AppBarDefaults
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.contentColorFor
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.Divider
import androidx.compose.material3.DrawerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
@@ -51,7 +47,6 @@ 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.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@@ -62,6 +57,7 @@ import coil.Coil
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS
import com.vitorpamplona.amethyst.model.LocalCache
@@ -82,6 +78,7 @@ import com.vitorpamplona.amethyst.service.NostrThreadDataSource
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.service.relays.RelayPool
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
@@ -111,6 +108,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.theme.BottomTopHeight
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.HalfVertSpacer
import com.vitorpamplona.amethyst.ui.theme.HeaderPictureModifier
@@ -134,7 +132,7 @@ import kotlinx.coroutines.launch
fun AppTopBar(
followLists: FollowListViewModel,
navEntryState: State<NavBackStackEntry?>,
scaffoldState: ScaffoldState,
drawerState: DrawerState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
navPopBack: () -> Unit
@@ -151,7 +149,7 @@ fun AppTopBar(
}
}
RenderTopRouteBar(currentRoute, id, followLists, scaffoldState, accountViewModel, nav, navPopBack)
RenderTopRouteBar(currentRoute, id, followLists, drawerState, accountViewModel, nav, navPopBack)
}
@Composable
@@ -159,16 +157,16 @@ private fun RenderTopRouteBar(
currentRoute: String?,
id: String?,
followLists: FollowListViewModel,
scaffoldState: ScaffoldState,
drawerState: DrawerState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
navPopBack: () -> Unit
) {
when (currentRoute) {
Route.Home.base -> HomeTopBar(followLists, scaffoldState, accountViewModel, nav)
Route.Video.base -> StoriesTopBar(followLists, scaffoldState, accountViewModel, nav)
Route.Discover.base -> DiscoveryTopBar(followLists, scaffoldState, accountViewModel, nav)
Route.Notification.base -> NotificationTopBar(followLists, scaffoldState, accountViewModel, nav)
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)
else -> {
if (id != null) {
@@ -179,10 +177,10 @@ private fun RenderTopRouteBar(
Route.Community.base -> CommunityTopBar(id, accountViewModel, nav, navPopBack)
Route.Hashtag.base -> HashTagTopBar(id, accountViewModel, navPopBack)
Route.Geohash.base -> GeoHashTopBar(id, accountViewModel, navPopBack)
else -> MainTopBar(scaffoldState, accountViewModel, nav)
else -> MainTopBar(drawerState, accountViewModel, nav)
}
} else {
MainTopBar(scaffoldState, accountViewModel, nav)
MainTopBar(drawerState, accountViewModel, nav)
}
}
}
@@ -375,15 +373,13 @@ fun NoTopBar() {
}
@Composable
fun StoriesTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(scaffoldState, accountViewModel, nav) { accountViewModel ->
fun StoriesTopBar(followLists: FollowListViewModel, drawerState: DrawerState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(drawerState, accountViewModel, nav) { accountViewModel ->
val list by accountViewModel.storiesListLiveData.observeAsState(GLOBAL_FOLLOWS)
FollowList(
FollowListWithRoutes(
followListsModel = followLists,
listName = list,
withGlobal = true,
withRoutes = false
listName = list
) { listName ->
accountViewModel.account.changeDefaultStoriesFollowList(listName.code)
}
@@ -391,15 +387,13 @@ fun StoriesTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState
}
@Composable
fun HomeTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(scaffoldState, accountViewModel, nav) { accountViewModel ->
fun HomeTopBar(followLists: FollowListViewModel, drawerState: DrawerState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(drawerState, accountViewModel, nav) { accountViewModel ->
val list by accountViewModel.homeListLiveData.observeAsState(KIND3_FOLLOWS)
FollowList(
FollowListWithRoutes(
followListsModel = followLists,
listName = list,
withGlobal = true,
withRoutes = true
listName = list
) { listName ->
if (listName.type == CodeNameType.ROUTE) {
nav(listName.code)
@@ -411,15 +405,13 @@ fun HomeTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, a
}
@Composable
fun NotificationTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(scaffoldState, accountViewModel, nav) { accountViewModel ->
fun NotificationTopBar(followLists: FollowListViewModel, drawerState: DrawerState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(drawerState, accountViewModel, nav) { accountViewModel ->
val list by accountViewModel.notificationListLiveData.observeAsState(GLOBAL_FOLLOWS)
FollowList(
FollowListWithoutRoutes(
followListsModel = followLists,
listName = list,
withGlobal = true,
withRoutes = false
listName = list
) { listName ->
accountViewModel.account.changeDefaultNotificationFollowList(listName.code)
}
@@ -427,15 +419,13 @@ fun NotificationTopBar(followLists: FollowListViewModel, scaffoldState: Scaffold
}
@Composable
fun DiscoveryTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(scaffoldState, accountViewModel, nav) { accountViewModel ->
fun DiscoveryTopBar(followLists: FollowListViewModel, drawerState: DrawerState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(drawerState, accountViewModel, nav) { accountViewModel ->
val list by accountViewModel.discoveryListLiveData.observeAsState(GLOBAL_FOLLOWS)
FollowList(
FollowListWithoutRoutes(
followListsModel = followLists,
listName = list,
withGlobal = true,
withRoutes = false
listName = list
) { listName ->
accountViewModel.account.changeDefaultDiscoveryFollowList(listName.code)
}
@@ -443,24 +433,25 @@ fun DiscoveryTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldSta
}
@Composable
fun MainTopBar(scaffoldState: ScaffoldState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(scaffoldState, accountViewModel, nav) {
fun MainTopBar(drawerState: DrawerState, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
GenericMainTopBar(drawerState, accountViewModel, nav) {
AmethystClickableIcon()
}
}
@OptIn(coil.annotation.ExperimentalCoilApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GenericMainTopBar(
scaffoldState: ScaffoldState,
drawerState: DrawerState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
content: @Composable (AccountViewModel) -> Unit
) {
Column(modifier = BottomTopHeight) {
MyTopAppBar(
elevation = 0.dp,
backgroundColor = MaterialTheme.colors.surface,
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface
),
title = {
Column(
modifier = Modifier.fillMaxWidth(),
@@ -483,7 +474,7 @@ fun GenericMainTopBar(
val coroutineScope = rememberCoroutineScope()
LoggedInUserPictureDrawer(accountViewModel) {
coroutineScope.launch {
scaffoldState.drawerState.open()
drawerState.open()
}
}
},
@@ -493,7 +484,7 @@ fun GenericMainTopBar(
}
}
)
Divider(thickness = 0.25.dp)
Divider(thickness = DividerThickness)
}
}
@@ -512,9 +503,16 @@ private fun LoggedInUserPictureDrawer(
onClick: () -> Unit
) {
val profilePicture by accountViewModel.account.userProfile().live().profilePictureChanges.observeAsState()
val pubkeyHex = remember { accountViewModel.userProfile().pubkeyHex }
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
IconButton(
onClick = onClick
) {
@@ -523,48 +521,42 @@ private fun LoggedInUserPictureDrawer(
model = profilePicture,
contentDescription = stringResource(id = R.string.profile_image),
modifier = HeaderPictureModifier,
contentScale = ContentScale.Crop
contentScale = ContentScale.Crop,
loadProfilePicture = automaticallyShowProfilePicture
)
}
}
@Composable
fun FollowList(
fun FollowListWithRoutes(
followListsModel: FollowListViewModel,
listName: String,
withGlobal: Boolean,
withRoutes: Boolean,
onChange: (CodeName) -> Unit
) {
val context = LocalContext.current
val kind3Follow = CodeName(KIND3_FOLLOWS, ResourceName(R.string.follow_list_kind3follows, context), CodeNameType.HARDCODED)
val globalFollow = CodeName(GLOBAL_FOLLOWS, ResourceName(R.string.follow_list_global, context), CodeNameType.HARDCODED)
val defaultOptions = if (withGlobal) listOf(kind3Follow, globalFollow) else listOf(kind3Follow)
val followLists by followListsModel.peopleLists.collectAsState()
val routeList by followListsModel.routes.collectAsState()
val allLists = remember(followLists) {
if (withRoutes) {
(defaultOptions + followLists + routeList)
} else {
(defaultOptions + followLists)
}
}
val followNames by remember(followLists) {
derivedStateOf {
allLists.map { it.name }.toImmutableList()
}
}
val allLists by followListsModel.kind3GlobalPeopleRoutes.collectAsState()
SimpleTextSpinner(
placeholder = allLists.firstOrNull { it.code == listName }?.name?.name() ?: "Select an Option",
options = followNames,
placeholderCode = listName,
options = allLists,
onSelect = {
onChange(allLists.getOrNull(it) ?: kind3Follow)
onChange(allLists.getOrNull(it) ?: followListsModel.kind3Follow)
}
)
}
@Composable
fun FollowListWithoutRoutes(
followListsModel: FollowListViewModel,
listName: String,
onChange: (CodeName) -> Unit
) {
val allLists by followListsModel.kind3GlobalPeople.collectAsState()
SimpleTextSpinner(
placeholderCode = listName,
options = allLists,
onSelect = {
onChange(allLists.getOrNull(it) ?: followListsModel.kind3Follow)
}
)
}
@@ -575,6 +567,7 @@ enum class CodeNameType {
abstract class Name {
abstract fun name(): String
open fun name(context: Context) = name()
}
class GeoHashName(val geoHashTag: String) : Name() {
@@ -583,12 +576,13 @@ class GeoHashName(val geoHashTag: String) : Name() {
class HashtagName(val hashTag: String) : Name() {
override fun name() = "#$hashTag"
}
class ResourceName(val resourceId: Int, val context: Context) : Name() {
override fun name() = context.getString(resourceId)
class ResourceName(val resourceId: Int) : Name() {
override fun name() = " $resourceId " // Space to make sure it goes first
override fun name(context: Context) = context.getString(resourceId)
}
class PeopleListName(val note: AddressableNote) : Name() {
override fun name() = note.dTag() ?: ""
override fun name() = (note.event as? PeopleListEvent)?.nameOrTitle() ?: note.dTag() ?: ""
}
class CommunityName(val note: AddressableNote) : Name() {
override fun name() = "/n/${(note.dTag() ?: "")}"
@@ -599,11 +593,14 @@ data class CodeName(val code: String, val name: Name, val type: CodeNameType)
@Stable
class FollowListViewModel(val account: Account) : ViewModel() {
private var _peopleLists = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList())
val peopleLists = _peopleLists.asStateFlow()
val kind3Follow = CodeName(KIND3_FOLLOWS, ResourceName(R.string.follow_list_kind3follows), CodeNameType.HARDCODED)
val globalFollow = CodeName(GLOBAL_FOLLOWS, ResourceName(R.string.follow_list_global), CodeNameType.HARDCODED)
private var _routes = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList())
val routes = _routes.asStateFlow()
private var _kind3GlobalPeopleRoutes = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList())
val kind3GlobalPeopleRoutes = _kind3GlobalPeopleRoutes.asStateFlow()
private var _kind3GlobalPeople = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList())
val kind3GlobalPeople = _kind3GlobalPeople.asStateFlow()
fun refresh() {
viewModelScope.launch(Dispatchers.Default) {
@@ -625,11 +622,7 @@ class FollowListViewModel(val account: Account) : ViewModel() {
} else {
null
}
}.sortedBy { it.name.name() }.toImmutableList()
if (!equalImmutableLists(_peopleLists.value, newFollowLists)) {
_peopleLists.emit(newFollowLists)
}
}.sortedBy { it.name.name() }
val communities = account.userProfile().cachedFollowingCommunitiesSet().mapNotNull {
LocalCache.checkGetOrCreateAddressableNote(it)?.let { communityNote ->
@@ -645,10 +638,18 @@ class FollowListViewModel(val account: Account) : ViewModel() {
CodeName("Geohash/$it", GeoHashName(it), CodeNameType.ROUTE)
}
val routeList = (communities + hashtags + geotags).sortedBy { it.name.name() }.toImmutableList()
val routeList = (communities + hashtags + geotags).sortedBy { it.name.name() }
if (!equalImmutableLists(_routes.value, routeList)) {
_routes.emit(routeList)
val kind3GlobalPeopleRouteList = listOf(listOf(kind3Follow, globalFollow), newFollowLists, routeList).flatten().toImmutableList()
if (!equalImmutableLists(_kind3GlobalPeopleRoutes.value, kind3GlobalPeopleRouteList)) {
_kind3GlobalPeopleRoutes.emit(kind3GlobalPeopleRouteList)
}
val kind3GlobalPeopleList = listOf(listOf(kind3Follow, globalFollow), newFollowLists).flatten().toImmutableList()
if (!equalImmutableLists(_kind3GlobalPeople.value, kind3GlobalPeopleList)) {
_kind3GlobalPeople.emit(kind3GlobalPeopleList)
}
}
@@ -684,14 +685,24 @@ class FollowListViewModel(val account: Account) : ViewModel() {
@Composable
fun SimpleTextSpinner(
placeholder: String,
options: ImmutableList<Name>,
placeholderCode: String,
options: ImmutableList<CodeName>,
onSelect: (Int) -> Unit,
modifier: Modifier = Modifier
) {
val interactionSource = remember { MutableInteractionSource() }
var optionsShowing by remember { mutableStateOf(false) }
var currentText by remember(placeholder) { mutableStateOf(placeholder) }
val context = LocalContext.current
val selectAnOption = stringResource(
id = R.string.select_an_option
)
var currentText by remember {
mutableStateOf(
options.firstOrNull { it.code == placeholderCode }?.name?.name(context) ?: selectAnOption
)
}
Box(
modifier = modifier,
@@ -699,12 +710,12 @@ fun SimpleTextSpinner(
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Spacer(modifier = Modifier.size(20.dp))
Text(placeholder)
Text(currentText)
Icon(
imageVector = Icons.Default.ExpandMore,
null,
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
Box(
@@ -725,27 +736,27 @@ fun SimpleTextSpinner(
options = options,
onDismiss = { optionsShowing = false },
onSelect = {
currentText = options[it].name()
currentText = options[it].name.name(context)
optionsShowing = false
onSelect(it)
}
) {
RenderOption(it)
RenderOption(it.name)
}
}
}
}
@Composable
fun RenderOption(it: Name) {
when (it) {
fun RenderOption(option: Name) {
when (option) {
is GeoHashName -> {
LoadCityName(it.geoHashTag) {
LoadCityName(option.geoHashTag) {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "/g/$it", color = MaterialTheme.colors.onSurface)
Text(text = "/g/$it", color = MaterialTheme.colorScheme.onSurface)
}
}
}
@@ -754,7 +765,7 @@ fun RenderOption(it: Name) {
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
Text(text = it.name(), color = MaterialTheme.colors.onSurface)
Text(text = option.name(), color = MaterialTheme.colorScheme.onSurface)
}
}
is ResourceName -> {
@@ -762,7 +773,7 @@ fun RenderOption(it: Name) {
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(id = it.resourceId), color = MaterialTheme.colors.onSurface)
Text(text = stringResource(id = option.resourceId), color = MaterialTheme.colorScheme.onSurface)
}
}
is PeopleListName -> {
@@ -770,7 +781,7 @@ fun RenderOption(it: Name) {
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
Text(text = it.name(), color = MaterialTheme.colors.onSurface)
Text(text = option.name(), color = MaterialTheme.colorScheme.onSurface)
}
}
is CommunityName -> {
@@ -778,21 +789,21 @@ fun RenderOption(it: Name) {
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
val name by it.note.live().metadata.map {
val name by option.note.live().metadata.map {
"/n/" + (it.note as? AddressableNote)?.dTag()
}.observeAsState()
Text(text = name ?: "", color = MaterialTheme.colors.onSurface)
Text(text = name ?: "", color = MaterialTheme.colorScheme.onSurface)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopBarWithBackButton(caption: String, popBack: () -> Unit) {
Column(modifier = BottomTopHeight) {
MyTopAppBar(
elevation = 0.dp,
TopAppBar(
title = { Text(caption) },
navigationIcon = {
IconButton(
@@ -810,15 +821,12 @@ fun TopBarWithBackButton(caption: String, popBack: () -> Unit) {
@Composable
fun FlexibleTopBarWithBackButton(
prefixRow: (@Composable () -> Unit)? = null,
title: @Composable RowScope.() -> Unit,
extendableRow: (@Composable () -> Unit)? = null,
popBack: () -> Unit
) {
Column() {
MyExtensibleTopAppBar(
elevation = 0.dp,
prefixRow = prefixRow,
title = title,
extendableRow = extendableRow,
navigationIcon = {
@@ -896,135 +904,53 @@ fun debugState(context: Context) {
}
}
@Composable
fun MyTopAppBar(
title: @Composable RowScope.() -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable (() -> Unit)? = null,
actions: @Composable RowScope.() -> Unit = {},
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = AppBarDefaults.TopAppBarElevation
) {
Surface(
contentColor = contentColor,
elevation = elevation,
modifier = modifier
) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Row(
Modifier
.fillMaxWidth()
.padding(AppBarDefaults.ContentPadding),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
if (navigationIcon == null) {
Spacer(TitleInsetWithoutIcon)
} else {
Row(TitleIconModifier, verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.high,
content = navigationIcon
)
}
}
Row(
Modifier.weight(1f),
verticalAlignment = Alignment.CenterVertically
) {
ProvideTextStyle(MaterialTheme.typography.h6) {
title()
}
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Row(
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
content = actions
)
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyExtensibleTopAppBar(
prefixRow: (@Composable () -> Unit)? = null,
title: @Composable RowScope.() -> Unit,
extendableRow: (@Composable () -> Unit)? = null,
modifier: Modifier = Modifier,
navigationIcon: @Composable (() -> Unit)? = null,
actions: @Composable RowScope.() -> Unit = {},
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = AppBarDefaults.TopAppBarElevation
actions: @Composable RowScope.() -> Unit = {}
) {
val expanded = remember { mutableStateOf(false) }
Surface(
color = backgroundColor,
contentColor = contentColor,
elevation = elevation,
modifier = modifier.clickable {
Column(
Modifier.clickable {
expanded.value = !expanded.value
}
) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Column(Modifier.fillMaxWidth()) {
Row(
Modifier
.fillMaxWidth()
.padding(AppBarDefaults.ContentPadding),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Row(modifier = BottomTopHeight) {
TopAppBar(
title = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
title()
}
},
modifier = modifier,
navigationIcon = {
if (navigationIcon == null) {
Spacer(TitleInsetWithoutIcon)
} else {
Row(TitleIconModifier, verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.high,
content = navigationIcon
)
navigationIcon()
}
}
},
actions = actions
)
}
Row(
Modifier.weight(1f),
verticalAlignment = Alignment.CenterVertically
) {
ProvideTextStyle(MaterialTheme.typography.h6) {
title()
}
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Row(
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
content = actions
)
}
}
if (expanded.value && extendableRow != null) {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Column {
extendableRow()
}
}
}
if (prefixRow != null) {
prefixRow()
if (expanded.value && extendableRow != null) {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Column {
extendableRow()
}
}
}
@@ -22,21 +22,20 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.AlertDialog
import androidx.compose.material.Divider
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.DrawerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -70,8 +69,10 @@ import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.HttpClient
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.service.relays.RelayPool
import com.vitorpamplona.amethyst.service.relays.RelayPoolStatus
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
@@ -92,17 +93,25 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DrawerContent(
nav: (String) -> Unit,
scaffoldState: ScaffoldState,
sheetState: ModalBottomSheetState,
drawerState: DrawerState,
openSheet: () -> Unit,
accountViewModel: AccountViewModel
) {
Surface(
modifier = Modifier.fillMaxWidth(),
color = MaterialTheme.colors.background
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
ModalDrawerSheet(
drawerContainerColor = MaterialTheme.colorScheme.background,
drawerTonalElevation = 0.dp
) {
Column() {
ProfileContent(
@@ -111,7 +120,7 @@ fun DrawerContent(
.fillMaxWidth()
.padding(horizontal = 25.dp)
.padding(top = 70.dp),
scaffoldState,
drawerState,
accountViewModel,
nav
)
@@ -121,15 +130,15 @@ fun DrawerContent(
)
ListContent(
nav,
scaffoldState,
sheetState,
drawerState,
openSheet,
modifier = Modifier
.fillMaxWidth()
.weight(1f),
accountViewModel
)
BottomContent(accountViewModel.account.userProfile(), scaffoldState, nav)
BottomContent(accountViewModel.account.userProfile(), drawerState, automaticallyShowProfilePicture, nav)
}
}
}
@@ -138,7 +147,7 @@ fun DrawerContent(
fun ProfileContent(
baseAccountUser: User,
modifier: Modifier = Modifier,
scaffoldState: ScaffoldState,
drawerState: DrawerState,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
@@ -150,11 +159,19 @@ fun ProfileContent(
val profileBanner = remember(accountUserState) { accountUserState?.user?.info?.banner?.ifBlank { null } }
val profilePicture = remember(accountUserState) { accountUserState?.user?.profilePicture() }
val bestUserName = remember(accountUserState) { accountUserState?.user?.bestUsername() }
val bestDisplayName = remember(accountUserState) { accountUserState?.user?.bestDisplayName() }
// val bestUserName = remember(accountUserState) { accountUserState?.user?.bestUsername() }
val bestDisplayName = remember(accountUserState) { accountUserState?.user?.toBestDisplayName() }
val tags = remember(accountUserState) { accountUserState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() }
val route = remember(accountUserState) { "User/${accountUserState?.user?.pubkeyHex}" }
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
Box {
if (profileBanner != null) {
AsyncImage(
@@ -170,8 +187,7 @@ fun ProfileContent(
painter = painterResource(R.drawable.profile_banner),
contentDescription = stringResource(R.string.profile_banner),
contentScale = ContentScale.FillWidth,
modifier = Modifier
.fillMaxWidth()
modifier = Modifier.fillMaxWidth()
.height(120.dp)
)
}
@@ -185,14 +201,15 @@ fun ProfileContent(
.width(100.dp)
.height(100.dp)
.clip(shape = CircleShape)
.border(3.dp, MaterialTheme.colors.background, CircleShape)
.background(MaterialTheme.colors.background)
.border(3.dp, MaterialTheme.colorScheme.background, CircleShape)
.background(MaterialTheme.colorScheme.background)
.clickable(onClick = {
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
})
}),
loadProfilePicture = automaticallyShowProfilePicture
)
if (bestDisplayName != null) {
@@ -204,7 +221,7 @@ fun ProfileContent(
.clickable(onClick = {
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
}),
fontWeight = FontWeight.Bold,
@@ -213,24 +230,6 @@ fun ProfileContent(
overflow = TextOverflow.Ellipsis
)
}
if (bestUserName != null) {
CreateTextWithEmoji(
text = remember { " @$bestUserName" },
tags = tags,
color = Color.LightGray,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.clickable(
onClick = {
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
}
}
)
)
}
Row(Modifier.padding(top = Size10dp)) {
EditStatusBox(baseAccountUser, accountViewModel)
@@ -242,7 +241,7 @@ fun ProfileContent(
.clickable(onClick = {
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
})
) {
@@ -276,7 +275,7 @@ private fun EditStatusBox(baseAccountUser: User, accountViewModel: AccountViewMo
placeholder = {
Text(
text = stringResource(R.string.status_update),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -322,7 +321,7 @@ private fun EditStatusBox(baseAccountUser: User, accountViewModel: AccountViewMo
placeholder = {
Text(
text = stringResource(R.string.status_update),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
keyboardOptions = KeyboardOptions.Default.copy(
@@ -365,7 +364,7 @@ fun SendButton(onClick: () -> Unit) {
imageVector = Icons.Default.Send,
null,
modifier = Size20Modifier,
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
}
@@ -380,7 +379,7 @@ fun UserStatusDeleteButton(onClick: () -> Unit) {
imageVector = Icons.Default.Delete,
null,
modifier = Size20Modifier,
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
}
@@ -441,12 +440,11 @@ fun WatchFollower(baseAccountUser: User, onReady: (String) -> Unit) {
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ListContent(
nav: (String) -> Unit,
scaffoldState: ScaffoldState,
sheetState: ModalBottomSheetState,
drawerState: DrawerState,
openSheet: () -> Unit,
modifier: Modifier,
accountViewModel: AccountViewModel
) {
@@ -474,18 +472,18 @@ fun ListContent(
NavigationRow(
title = stringResource(R.string.profile),
icon = Route.Profile.icon,
tint = MaterialTheme.colors.primary,
tint = MaterialTheme.colorScheme.primary,
nav = nav,
scaffoldState = scaffoldState,
drawerState = drawerState,
route = route
)
NavigationRow(
title = stringResource(R.string.bookmarks),
icon = Route.Bookmarks.icon,
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
nav = nav,
scaffoldState = scaffoldState,
drawerState = drawerState,
route = Route.Bookmarks.route
)
@@ -493,7 +491,7 @@ fun ListContent(
accountViewModel = accountViewModel,
onClick = {
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
wantsToEditRelays = true
}
@@ -502,9 +500,9 @@ fun ListContent(
NavigationRow(
title = stringResource(R.string.security_filters),
icon = Route.BlockedUsers.icon,
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
nav = nav,
scaffoldState = scaffoldState,
drawerState = drawerState,
route = Route.BlockedUsers.route
)
@@ -512,10 +510,10 @@ fun ListContent(
IconRow(
title = stringResource(R.string.backup_keys),
icon = R.drawable.ic_key,
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
onClick = {
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
backupDialogOpen = true
}
@@ -526,10 +524,10 @@ fun ListContent(
IconRow(
title = textTorProxy,
icon = R.drawable.ic_tor,
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
onLongClick = {
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
conectOrbotDialogOpen = true
},
@@ -538,7 +536,7 @@ fun ListContent(
disconnectTorDialog = true
} else {
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
conectOrbotDialogOpen = true
}
@@ -548,9 +546,9 @@ fun ListContent(
NavigationRow(
title = stringResource(R.string.settings),
icon = Route.Settings.icon,
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
nav = nav,
scaffoldState = scaffoldState,
drawerState = drawerState,
route = Route.Settings.route
)
@@ -559,8 +557,8 @@ fun ListContent(
IconRow(
title = stringResource(R.string.drawer_accounts),
icon = R.drawable.manage_accounts,
tint = MaterialTheme.colors.onBackground,
onClick = { coroutineScope.launch { sheetState.show() } }
tint = MaterialTheme.colorScheme.onBackground,
onClick = openSheet
)
}
@@ -568,7 +566,7 @@ fun ListContent(
NewRelayListView({ wantsToEditRelays = false }, accountViewModel, nav = nav)
}
if (backupDialogOpen) {
AccountBackupDialog(accountViewModel.account, onClose = { backupDialogOpen = false })
AccountBackupDialog(accountViewModel, onClose = { backupDialogOpen = false })
}
if (conectOrbotDialogOpen) {
ConnectOrbotDialog(
@@ -579,6 +577,12 @@ fun ListContent(
checked = true
enableTor(accountViewModel.account, true, proxyPort, context, coroutineScope)
},
onError = {
accountViewModel.toast(
context.getString(R.string.could_not_connect_to_tor),
it
)
},
proxyPort
)
}
@@ -651,7 +655,7 @@ private fun RenderRelayStatus(
}
}
val placeHolder = MaterialTheme.colors.placeholderText
val placeHolder = MaterialTheme.colorScheme.placeholderText
val color by remember(relayPool) {
derivedStateOf {
@@ -666,7 +670,7 @@ private fun RenderRelayStatus(
Text(
text = text,
color = color,
style = MaterialTheme.typography.subtitle1
style = MaterialTheme.typography.titleMedium
)
}
@@ -676,14 +680,14 @@ fun NavigationRow(
icon: Int,
tint: Color,
nav: (String) -> Unit,
scaffoldState: ScaffoldState,
drawerState: DrawerState,
route: String
) {
val coroutineScope = rememberCoroutineScope()
IconRow(title, icon, tint, onClick = {
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
})
}
@@ -737,7 +741,7 @@ fun IconRowRelays(accountViewModel: AccountViewModel, onClick: () -> Unit) {
painter = painterResource(R.drawable.relays),
null,
modifier = Modifier.size(22.dp),
tint = MaterialTheme.colors.onSurface
tint = MaterialTheme.colorScheme.onSurface
)
Text(
@@ -754,7 +758,7 @@ fun IconRowRelays(accountViewModel: AccountViewModel, onClick: () -> Unit) {
}
@Composable
fun BottomContent(user: User, scaffoldState: ScaffoldState, nav: (String) -> Unit) {
fun BottomContent(user: User, drawerState: DrawerState, loadProfilePicture: Boolean, nav: (String) -> Unit) {
val coroutineScope = rememberCoroutineScope()
// store the dialog open or close state
@@ -793,21 +797,21 @@ fun BottomContent(user: User, scaffoldState: ScaffoldState, nav: (String) -> Uni
painter = painterResource(R.drawable.ic_theme),
null,
modifier = Modifier.size(24.dp),
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}*/
Box(modifier = Modifier.weight(1F))
IconButton(onClick = {
dialogOpen = true
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
}) {
Icon(
painter = painterResource(R.drawable.ic_qrcode),
null,
modifier = Modifier.size(24.dp),
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}
}
@@ -816,10 +820,11 @@ fun BottomContent(user: User, scaffoldState: ScaffoldState, nav: (String) -> Uni
if (dialogOpen) {
ShowQRDialog(
user,
loadProfilePicture = loadProfilePicture,
onScan = {
dialogOpen = false
coroutineScope.launch {
scaffoldState.drawerState.close()
drawerState.close()
}
nav(it)
},
@@ -10,10 +10,10 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -138,7 +138,7 @@ fun AddBountyAmountDialog(bounty: Note, accountViewModel: AccountViewModel, onCl
placeholder = {
Text(
text = "10000, 50000, 5000000",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true
@@ -9,14 +9,14 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MilitaryTech
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Divider
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.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -38,7 +38,6 @@ import com.vitorpamplona.amethyst.ui.screen.BadgeCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@@ -59,16 +58,12 @@ fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForL
if (note == null) {
BlankNote(Modifier, isInnerNote)
} else {
val defaultBackgroundColor = MaterialTheme.colors.background
val defaultBackgroundColor = MaterialTheme.colorScheme.background
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
val newItemColor = MaterialTheme.colors.newItemBackgroundColor
val newItemColor = MaterialTheme.colorScheme.newItemBackgroundColor
LaunchedEffect(key1 = likeSetCard) {
scope.launch(Dispatchers.IO) {
val isNew = likeSetCard.createdAt() > accountViewModel.account.loadLastRead(routeForLastRead)
accountViewModel.account.markAsRead(routeForLastRead, likeSetCard.createdAt())
accountViewModel.loadAndMarkAsRead(routeForLastRead, likeSetCard.createdAt()) { isNew ->
val newBackgroundColor = if (isNew) {
newItemColor.compositeOver(defaultBackgroundColor)
} else {
@@ -117,7 +112,7 @@ fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForL
modifier = Modifier
.size(25.dp)
.align(Alignment.TopEnd),
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}
}
@@ -134,7 +129,7 @@ fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForL
Text(
timeAgo(note.createdAt(), context = context),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
@@ -146,7 +141,7 @@ fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForL
imageVector = Icons.Default.MoreVert,
null,
modifier = Modifier.size(15.dp),
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
NoteDropDownMenu(note, popupExpanded, accountViewModel)
@@ -7,11 +7,11 @@ import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -108,7 +108,7 @@ fun HiddenNote(
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
contentColor = MaterialTheme.colorScheme.primary
),
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
) {
@@ -15,9 +15,9 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -61,6 +61,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.OfflineFlag
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ScheduledFlag
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
@@ -275,39 +276,26 @@ private fun CheckNewAndRenderChannelCard(
showPopup: () -> Unit,
nav: (String) -> Unit
) {
val newItemColor = MaterialTheme.colors.newItemBackgroundColor
val defaultBackgroundColor = MaterialTheme.colors.background
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
val newItemColor = MaterialTheme.colorScheme.newItemBackgroundColor
val defaultBackgroundColor = MaterialTheme.colorScheme.background
val backgroundColor = remember {
mutableStateOf<Color>(
parentBackgroundColor?.value ?: defaultBackgroundColor
)
}
LaunchedEffect(key1 = routeForLastRead, key2 = parentBackgroundColor?.value) {
launch(Dispatchers.IO) {
routeForLastRead?.let {
val lastTime = accountViewModel.account.loadLastRead(it)
val createdAt = baseNote.createdAt()
if (createdAt != null) {
accountViewModel.account.markAsRead(it, createdAt)
val isNew = createdAt > lastTime
val newBackgroundColor = if (isNew) {
if (parentBackgroundColor != null) {
newItemColor.compositeOver(parentBackgroundColor.value)
} else {
newItemColor.compositeOver(defaultBackgroundColor)
}
routeForLastRead?.let {
accountViewModel.loadAndMarkAsRead(routeForLastRead, baseNote.createdAt()) { isNew ->
val newBackgroundColor = if (isNew) {
if (parentBackgroundColor != null) {
newItemColor.compositeOver(parentBackgroundColor.value)
} else {
parentBackgroundColor?.value ?: defaultBackgroundColor
}
if (newBackgroundColor != backgroundColor.value) {
launch(Dispatchers.Main) {
backgroundColor.value = newBackgroundColor
}
newItemColor.compositeOver(defaultBackgroundColor)
}
} else {
parentBackgroundColor?.value ?: defaultBackgroundColor
}
} ?: run {
val newBackgroundColor = parentBackgroundColor?.value ?: defaultBackgroundColor
if (newBackgroundColor != backgroundColor.value) {
launch(Dispatchers.Main) {
@@ -315,6 +303,13 @@ private fun CheckNewAndRenderChannelCard(
}
}
}
} ?: run {
val newBackgroundColor = parentBackgroundColor?.value ?: defaultBackgroundColor
if (newBackgroundColor != backgroundColor.value) {
launch(Dispatchers.Main) {
backgroundColor.value = newBackgroundColor
}
}
}
}
@@ -510,6 +505,8 @@ fun RenderLiveActivityThumb(
}
}
Spacer(modifier = DoubleVertSpacer)
ChannelHeader(
channelHex = remember { baseNote.idHex },
showVideo = false,
@@ -614,9 +611,9 @@ fun RenderCommunitiesThumb(baseNote: Note, accountViewModel: AccountViewModel, n
)
Spacer(modifier = StdHorzSpacer)
LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel, nav)
LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav)
Spacer(modifier = StdHorzSpacer)
ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel, nav = nav)
ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav)
}
description?.let {
@@ -624,7 +621,7 @@ fun RenderCommunitiesThumb(baseNote: Note, accountViewModel: AccountViewModel, n
Row() {
Text(
text = it,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
fontSize = 14.sp
@@ -716,7 +713,9 @@ fun RenderChannelThumb(baseNote: Note, channel: Channel, accountViewModel: Accou
Spacer(modifier = DoubleHorzSpacer)
Column(
modifier = Modifier.fillMaxWidth().fillMaxHeight()
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
@@ -728,9 +727,9 @@ fun RenderChannelThumb(baseNote: Note, channel: Channel, accountViewModel: Accou
)
Spacer(modifier = StdHorzSpacer)
LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel, nav)
LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav)
Spacer(modifier = StdHorzSpacer)
ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel, nav = nav)
ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav)
}
description?.let {
@@ -738,7 +737,7 @@ fun RenderChannelThumb(baseNote: Note, channel: Channel, accountViewModel: Accou
Row() {
Text(
text = it,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
fontSize = 14.sp
@@ -768,7 +767,7 @@ fun Gallery(users: List<User>, accountViewModel: AccountViewModel) {
Text(
text = remember(users) { " + " + (showCount(users.size - 6)).toString() },
fontSize = 13.sp,
color = MaterialTheme.colors.onSurface
color = MaterialTheme.colorScheme.onSurface
)
}
}
@@ -14,10 +14,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Divider
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -47,8 +47,10 @@ 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.ConnectivityType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -185,6 +187,14 @@ private fun ChannelRoomCompose(
val hasNewMessages = remember { mutableStateOf<Boolean>(false) }
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
WatchNotificationChanges(note, route, accountViewModel) { newHasNewMessages ->
if (hasNewMessages.value != newHasNewMessages) {
hasNewMessages.value = newHasNewMessages
@@ -200,6 +210,7 @@ private fun ChannelRoomCompose(
channelLastTime = remember(note) { note.createdAt() },
channelLastContent = remember(note) { "$authorName: $description" },
hasNewMessages = hasNewMessages,
loadProfilePicture = automaticallyShowProfilePicture,
onClick = { nav(route) }
)
}
@@ -207,7 +218,7 @@ private fun ChannelRoomCompose(
@Composable
private fun ChannelTitleWithLabelInfo(channelName: String, modifier: Modifier) {
val label = stringResource(id = R.string.public_chat)
val placeHolderColor = MaterialTheme.colors.placeholderText
val placeHolderColor = MaterialTheme.colorScheme.placeholderText
val channelNameAndBoostInfo = remember(channelName) {
buildAnnotatedString {
withStyle(
@@ -404,9 +415,7 @@ private fun WatchNotificationChanges(
accountViewModel: AccountViewModel,
onNewStatus: (Boolean) -> Unit
) {
val cacheState by accountViewModel.accountLastReadLiveData.observeAsState()
LaunchedEffect(key1 = note, cacheState) {
LaunchedEffect(key1 = note, accountViewModel.accountMarkAsReadUpdates.value) {
launch(Dispatchers.IO) {
note.event?.createdAt()?.let {
val lastTime = accountViewModel.account.loadLastRead(route)
@@ -443,6 +452,7 @@ fun ChannelName(
channelLastTime: Long?,
channelLastContent: String?,
hasNewMessages: MutableState<Boolean>,
loadProfilePicture: Boolean,
onClick: () -> Unit
) {
ChannelName(
@@ -456,7 +466,8 @@ fun ChannelName(
.width(Size55dp)
.height(Size55dp)
.clip(shape = CircleShape)
}
},
loadProfilePicture = loadProfilePicture
)
},
channelTitle,
@@ -517,7 +528,7 @@ private fun SecondRow(channelLastContent: String?, hasNewMessages: MutableState<
if (channelLastContent != null) {
Text(
channelLastContent,
color = MaterialTheme.colors.grayText,
color = MaterialTheme.colorScheme.grayText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
@@ -526,7 +537,7 @@ private fun SecondRow(channelLastContent: String?, hasNewMessages: MutableState<
} else {
Text(
stringResource(R.string.referenced_event_not_found),
color = MaterialTheme.colors.grayText,
color = MaterialTheme.colorScheme.grayText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = modifier
@@ -560,7 +571,7 @@ private fun TimeAgo(channelLastTime: Long?) {
}
Text(
text = timeAgo,
color = MaterialTheme.colors.grayText,
color = MaterialTheme.colorScheme.grayText,
maxLines = 1
)
}
@@ -573,7 +584,7 @@ fun NewItemsBubble() {
.width(10.dp)
.height(10.dp)
.clip(shape = CircleShape)
.background(MaterialTheme.colors.primary),
.background(MaterialTheme.colorScheme.primary),
contentAlignment = Alignment.Center
) {
Text(
@@ -15,10 +15,10 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -43,8 +43,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.map
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
@@ -71,8 +73,6 @@ import com.vitorpamplona.quartz.events.EmptyTagList
import com.vitorpamplona.quartz.events.ImmutableListOfLists
import com.vitorpamplona.quartz.events.PrivateDmEvent
import com.vitorpamplona.quartz.events.toImmutableListOfLists
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -230,9 +230,9 @@ fun NormalChatNote(
}
}
val loggedInColors = MaterialTheme.colors.mediumImportanceLink
val otherColors = MaterialTheme.colors.subtleBorder
val defaultBackground = MaterialTheme.colors.background
val loggedInColors = MaterialTheme.colorScheme.mediumImportanceLink
val otherColors = MaterialTheme.colorScheme.subtleBorder
val defaultBackground = MaterialTheme.colorScheme.background
val backgroundBubbleColor = remember {
if (accountViewModel.isLoggedUser(note.author)) {
@@ -258,12 +258,7 @@ fun NormalChatNote(
if (routeForLastRead != null) {
LaunchedEffect(key1 = routeForLastRead) {
launch(Dispatchers.IO) {
val createdAt = note.createdAt()
if (createdAt != null) {
accountViewModel.account.markAsRead(routeForLastRead, createdAt)
}
}
accountViewModel.loadAndMarkAsRead(routeForLastRead, note.createdAt()) { }
}
}
@@ -400,10 +395,19 @@ private fun MessageBubbleLines(
bubbleSize: MutableState<Int>,
availableBubbleSize: MutableState<Int>
) {
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
if (drawAuthorInfo) {
DrawAuthorInfo(
baseNote,
alignment,
automaticallyShowProfilePicture,
nav
)
} else {
@@ -561,13 +565,13 @@ private fun StatusRow(
Column(modifier = ReactionRowHeightChat) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = ReactionRowHeightChat) {
LikeReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel, nav)
LikeReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav)
Spacer(modifier = StdHorzSpacer)
ZapReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel, nav = nav)
ZapReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav = nav)
Spacer(modifier = DoubleHorzSpacer)
ReplyReaction(
baseNote = baseNote,
grayTint = MaterialTheme.colors.placeholderText,
grayTint = MaterialTheme.colorScheme.placeholderText,
accountViewModel = accountViewModel,
showCounter = false,
iconSize = Size15dp
@@ -588,7 +592,7 @@ fun IncognitoBadge(baseNote: Note) {
modifier = Modifier
.padding(top = 1.dp)
.size(14.dp),
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
} else if (baseNote.event is PrivateDmEvent) {
Icon(
@@ -597,7 +601,7 @@ fun IncognitoBadge(baseNote: Note) {
modifier = Modifier
.padding(top = 1.dp)
.size(14.dp),
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
}
@@ -614,7 +618,7 @@ fun ChatTimeAgo(baseNote: Note) {
Text(
text = time,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
fontSize = Font12SP,
maxLines = 1
)
@@ -712,6 +716,7 @@ private fun RenderCreateChannelNote(note: Note) {
private fun DrawAuthorInfo(
baseNote: Note,
alignment: Arrangement.Horizontal,
loadProfilePicture: Boolean,
nav: (String) -> Unit
) {
Row(
@@ -719,26 +724,28 @@ private fun DrawAuthorInfo(
horizontalArrangement = alignment,
modifier = Modifier.padding(top = 5.dp)
) {
DisplayAndWatchNoteAuthor(baseNote, nav)
DisplayAndWatchNoteAuthor(baseNote, loadProfilePicture, nav)
}
}
@Composable
private fun DisplayAndWatchNoteAuthor(
baseNote: Note,
loadProfilePicture: Boolean,
nav: (String) -> Unit
) {
val author = remember {
baseNote.author
}
author?.let {
WatchAndDisplayUser(it, nav)
WatchAndDisplayUser(it, loadProfilePicture, nav)
}
}
@Composable
private fun WatchAndDisplayUser(
author: User,
loadProfilePicture: Boolean,
nav: (String) -> Unit
) {
val pubkeyHex = remember { author.pubkeyHex }
@@ -764,7 +771,7 @@ private fun WatchAndDisplayUser(
}
}
UserIcon(pubkeyHex, userProfilePicture, nav, route)
UserIcon(pubkeyHex, userProfilePicture, loadProfilePicture, nav, route)
userDisplayName?.let {
DisplayMessageUsername(it, userTags, route, nav)
@@ -775,6 +782,7 @@ private fun WatchAndDisplayUser(
private fun UserIcon(
pubkeyHex: String,
userProfilePicture: String?,
loadProfilePicture: Boolean,
nav: (String) -> Unit,
route: String
) {
@@ -782,6 +790,7 @@ private fun UserIcon(
robot = pubkeyHex,
model = userProfilePicture,
contentDescription = stringResource(id = R.string.profile_image),
loadProfilePicture = loadProfilePicture,
modifier = remember {
Modifier
.width(Size25dp)
@@ -808,7 +817,7 @@ private fun DisplayMessageUsername(
maxLines = 1,
tags = userTags,
fontWeight = FontWeight.Bold,
overrideColor = MaterialTheme.colors.onBackground, // we do not want clickable names in purple here.
overrideColor = MaterialTheme.colorScheme.onBackground, // we do not want clickable names in purple here.
route = route,
nav = nav
)
@@ -1,8 +1,6 @@
package com.vitorpamplona.amethyst.ui.note
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Bolt
@@ -20,6 +18,8 @@ import androidx.compose.material.icons.filled.VolumeOff
import androidx.compose.material.icons.filled.VolumeUp
import androidx.compose.material.icons.outlined.BarChart
import androidx.compose.material.icons.outlined.PlayCircle
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -61,7 +61,7 @@ fun ArrowBackIcon() {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = stringResource(R.string.back),
tint = MaterialTheme.colors.grayText
tint = MaterialTheme.colorScheme.grayText
)
}
@@ -71,7 +71,7 @@ fun MessageIcon(modifier: Modifier) {
painter = painterResource(R.drawable.ic_dm),
null,
modifier = modifier,
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}
@@ -81,7 +81,7 @@ fun DownloadForOfflineIcon(iconSize: Dp) {
imageVector = Icons.Default.DownloadForOffline,
null,
modifier = remember(iconSize) { Modifier.size(iconSize) },
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}
@@ -181,7 +181,7 @@ fun ExpandLessIcon(modifier: Modifier) {
imageVector = Icons.Default.ExpandLess,
null,
modifier = modifier,
tint = MaterialTheme.colors.subtleButton
tint = MaterialTheme.colorScheme.subtleButton
)
}
@@ -191,7 +191,7 @@ fun ExpandMoreIcon(modifier: Modifier) {
imageVector = Icons.Default.ExpandMore,
null,
modifier = modifier,
tint = MaterialTheme.colors.subtleButton
tint = MaterialTheme.colorScheme.subtleButton
)
}
@@ -226,7 +226,7 @@ fun PollIcon() {
painter = painterResource(R.drawable.ic_poll),
null,
modifier = Size20Modifier,
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
@@ -236,7 +236,7 @@ fun RegularPostIcon() {
painter = painterResource(R.drawable.ic_lists),
null,
modifier = Size20Modifier,
tint = MaterialTheme.colors.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
@@ -246,7 +246,7 @@ fun CancelIcon() {
imageVector = Icons.Default.Cancel,
null,
modifier = Size30Modifier,
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
@@ -255,8 +255,7 @@ fun CloseIcon() {
Icon(
painter = painterResource(id = R.drawable.ic_close),
contentDescription = stringResource(id = R.string.cancel),
modifier = Size20Modifier,
tint = Color.White
modifier = Size20Modifier
)
}
@@ -265,7 +264,7 @@ fun MutedIcon() {
Icon(
imageVector = Icons.Default.VolumeOff,
contentDescription = stringResource(id = R.string.muted_button),
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
modifier = Size30Modifier
)
}
@@ -275,7 +274,7 @@ fun MuteIcon() {
Icon(
imageVector = Icons.Default.VolumeUp,
contentDescription = stringResource(id = R.string.mute_button),
tint = MaterialTheme.colors.onBackground,
tint = MaterialTheme.colorScheme.onBackground,
modifier = Size30Modifier
)
}
@@ -354,7 +353,7 @@ fun VerticalDotsIcon() {
imageVector = Icons.Default.MoreVert,
null,
modifier = Size18Modifier,
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
@@ -10,8 +10,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -28,7 +28,6 @@ import com.vitorpamplona.amethyst.ui.screen.MessageSetCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@@ -43,16 +42,12 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, routeForLastRead: String,
val scope = rememberCoroutineScope()
val defaultBackgroundColor = MaterialTheme.colors.background
val defaultBackgroundColor = MaterialTheme.colorScheme.background
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
val newItemColor = MaterialTheme.colors.newItemBackgroundColor
val newItemColor = MaterialTheme.colorScheme.newItemBackgroundColor
LaunchedEffect(key1 = messageSetCard) {
launch(Dispatchers.IO) {
val isNew = messageSetCard.createdAt() > accountViewModel.account.loadLastRead(routeForLastRead)
accountViewModel.account.markAsRead(routeForLastRead, messageSetCard.createdAt())
accountViewModel.loadAndMarkAsRead(routeForLastRead, messageSetCard.createdAt()) { isNew ->
val newBackgroundColor = if (isNew) {
newItemColor.compositeOver(defaultBackgroundColor)
} else {
@@ -17,9 +17,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
@@ -43,8 +43,10 @@ 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.ConnectivityType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
@@ -89,16 +91,12 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, showHi
val scope = rememberCoroutineScope()
val defaultBackgroundColor = MaterialTheme.colors.background
val defaultBackgroundColor = MaterialTheme.colorScheme.background
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
val newItemColor = MaterialTheme.colors.newItemBackgroundColor
val newItemColor = MaterialTheme.colorScheme.newItemBackgroundColor
LaunchedEffect(key1 = multiSetCard) {
launch(Dispatchers.IO) {
val isNew = multiSetCard.maxCreatedAt > accountViewModel.account.loadLastRead(routeForLastRead)
accountViewModel.account.markAsRead(routeForLastRead, multiSetCard.maxCreatedAt)
accountViewModel.loadAndMarkAsRead(routeForLastRead, multiSetCard.maxCreatedAt) { isNew ->
val newBackgroundColor = if (isNew) {
newItemColor.compositeOver(defaultBackgroundColor)
} else {
@@ -439,7 +437,7 @@ fun CrossfadeToDisplayAmount(amount: String) {
modifier = amountBoxModifier,
contentAlignment = Alignment.BottomCenter
) {
val backgroundColor = MaterialTheme.colors.overPictureBackground
val backgroundColor = MaterialTheme.colorScheme.overPictureBackground
Box(
modifier = remember {
Modifier
@@ -451,7 +449,7 @@ fun CrossfadeToDisplayAmount(amount: String) {
Text(
text = amount,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colors.bitcoinColor,
color = MaterialTheme.colorScheme.bitcoinColor,
fontSize = commentTextSize,
modifier = bottomPadding1dp
)
@@ -525,14 +523,23 @@ fun WatchUserMetadataAndFollowsAndRenderUserProfilePicture(
author: User,
accountViewModel: AccountViewModel
) {
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
WatchUserMetadata(author) { baseUserPicture ->
// Crossfade(targetState = baseUserPicture) { userPicture ->
RobohashAsyncImageProxy(
robot = author.pubkeyHex,
model = baseUserPicture,
contentDescription = stringResource(id = R.string.profile_image),
modifier = MaterialTheme.colors.profile35dpModifier,
contentScale = ContentScale.Crop
modifier = MaterialTheme.colorScheme.profile35dpModifier,
contentScale = ContentScale.Crop,
loadProfilePicture = automaticallyShowProfilePicture
)
// }
}
@@ -6,13 +6,13 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -172,7 +172,7 @@ fun DisplayUsersNpub(npub: String) {
Text(
text = npub,
fontSize = 14.sp,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -205,7 +205,7 @@ fun DisplayStatus(
painter = painterResource(id = R.drawable.tunestr),
null,
modifier = Size15Modifier.padding(end = Size5dp),
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
else -> {}
}
@@ -213,7 +213,7 @@ fun DisplayStatus(
Text(
text = content,
fontSize = Font14SP,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -229,7 +229,7 @@ fun DisplayStatus(
imageVector = Icons.Default.OpenInNew,
null,
modifier = Size15Modifier,
tint = MaterialTheme.colors.lessImportantLink
tint = MaterialTheme.colorScheme.lessImportantLink
)
}
} else if (nostrATag != null) {
@@ -249,7 +249,7 @@ fun DisplayStatus(
imageVector = Icons.Default.OpenInNew,
null,
modifier = Size15Modifier,
tint = MaterialTheme.colors.lessImportantLink
tint = MaterialTheme.colorScheme.lessImportantLink
)
}
}
@@ -271,7 +271,7 @@ fun DisplayStatus(
imageVector = Icons.Default.OpenInNew,
null,
modifier = Size15Modifier,
tint = MaterialTheme.colors.lessImportantLink
tint = MaterialTheme.colorScheme.lessImportantLink
)
}
}
@@ -298,7 +298,7 @@ private fun DisplayNIP05(
Text(
text = remember(nip05) { AnnotatedString(user) },
fontSize = Font14SP,
color = MaterialTheme.colors.nip05,
color = MaterialTheme.colorScheme.nip05,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -309,7 +309,7 @@ private fun DisplayNIP05(
ClickableText(
text = remember(nip05) { AnnotatedString(domain) },
onClick = { runCatching { uri.openUri("https://$domain") } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.nip05, fontSize = Font14SP),
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.nip05, fontSize = Font14SP),
maxLines = 1,
overflow = TextOverflow.Visible
)
@@ -349,7 +349,7 @@ fun DisplayNip05ProfileStatus(user: User, accountViewModel: AccountViewModel) {
if (user != "_") {
Text(
text = remember { AnnotatedString(user + "@") },
color = MaterialTheme.colors.primary,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis
@@ -360,7 +360,7 @@ fun DisplayNip05ProfileStatus(user: User, accountViewModel: AccountViewModel) {
ClickableText(
text = AnnotatedString(domain),
onClick = { nip05.let { runCatching { uri.openUri("https://${it.split("@")[1]}") } } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary),
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary),
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = domainPadStart),
maxLines = 1,
overflow = TextOverflow.Ellipsis
@@ -28,19 +28,19 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.darkColors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowForwardIos
import androidx.compose.material.icons.outlined.Bolt
import androidx.compose.material.lightColors
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -88,7 +88,6 @@ import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.RelayBriefInfo
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.OnlineChecker
import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
@@ -162,6 +161,7 @@ import com.vitorpamplona.amethyst.ui.theme.replyBackground
import com.vitorpamplona.amethyst.ui.theme.replyModifier
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toNpub
import com.vitorpamplona.quartz.events.AppDefinitionEvent
import com.vitorpamplona.quartz.events.AudioHeaderEvent
@@ -624,7 +624,7 @@ fun LongCommunityHeader(
Modifier.weight(1f)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
val defaultBackground = MaterialTheme.colors.background
val defaultBackground = MaterialTheme.colorScheme.background
val background = remember {
mutableStateOf(defaultBackground)
}
@@ -728,6 +728,14 @@ fun ShortCommunityHeader(baseNote: AddressableNote, fontWeight: FontWeight = Fon
val noteState by baseNote.live().metadata.observeAsState()
val noteEvent = remember(noteState) { noteState?.note?.event as? CommunityDefinitionEvent } ?: return
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
Row(verticalAlignment = Alignment.CenterVertically) {
noteEvent.image()?.let {
RobohashAsyncImageProxy(
@@ -735,7 +743,8 @@ fun ShortCommunityHeader(baseNote: AddressableNote, fontWeight: FontWeight = Fon
model = it,
contentDescription = stringResource(R.string.profile_image),
contentScale = ContentScale.Crop,
modifier = HeaderPictureModifier
modifier = HeaderPictureModifier,
loadProfilePicture = automaticallyShowProfilePicture
)
}
@@ -774,9 +783,9 @@ private fun ShortCommunityActionOptions(
nav: (String) -> Unit
) {
Spacer(modifier = StdHorzSpacer)
LikeReaction(baseNote = note, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel, nav = nav)
LikeReaction(baseNote = note, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav)
Spacer(modifier = StdHorzSpacer)
ZapReaction(baseNote = note, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel, nav = nav)
ZapReaction(baseNote = note, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav)
WatchAddressableNoteFollows(note, accountViewModel) { isFollowing ->
if (!isFollowing) {
@@ -826,9 +835,9 @@ private fun CheckNewAndRenderNote(
showPopup: () -> Unit,
nav: (String) -> Unit
) {
val newItemColor = MaterialTheme.colors.newItemBackgroundColor
val defaultBackgroundColor = MaterialTheme.colors.background
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
val newItemColor = MaterialTheme.colorScheme.newItemBackgroundColor
val defaultBackgroundColor = MaterialTheme.colorScheme.background
val backgroundColor = remember(baseNote) { mutableStateOf<Color>(parentBackgroundColor?.value ?: defaultBackgroundColor) }
LaunchedEffect(key1 = routeForLastRead, key2 = parentBackgroundColor?.value) {
routeForLastRead?.let {
@@ -1103,7 +1112,7 @@ fun DisplayZapSplits(noteEvent: EventInterface, accountViewModel: AccountViewMod
ClickableText(
text = AnnotatedString(it.lnAddressOrPubKeyHex),
onClick = { },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
} else {
UserPicture(userHex = it.lnAddressOrPubKeyHex, size = 25.dp, accountViewModel = accountViewModel, nav = nav)
@@ -1277,8 +1286,8 @@ fun routeFor(note: Note, loggedIn: User): String? {
return null
}
fun routeToMessage(user: User, draftMessage: String?, accountViewModel: AccountViewModel): String {
val withKey = ChatroomKey(persistentSetOf(user.pubkeyHex))
fun routeToMessage(user: HexKey, draftMessage: String?, accountViewModel: AccountViewModel): String {
val withKey = ChatroomKey(persistentSetOf(user))
accountViewModel.account.userProfile().createChatroom(withKey)
return if (draftMessage != null) {
"Room/${withKey.hashCode()}?message=$draftMessage"
@@ -1287,6 +1296,10 @@ fun routeToMessage(user: User, draftMessage: String?, accountViewModel: AccountV
}
}
fun routeToMessage(user: User, draftMessage: String?, accountViewModel: AccountViewModel): String {
return routeToMessage(user.pubkeyHex, draftMessage, accountViewModel)
}
fun routeFor(note: Channel): String {
return "Channel/${note.idHex}"
}
@@ -1325,7 +1338,7 @@ fun RenderTextEvent(
if (makeItShort && isAuthorTheLoggedUser) {
Text(
text = eventContent,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
@@ -1369,7 +1382,7 @@ fun RenderPoll(
if (makeItShort && accountViewModel.isLoggedUser(note.author)) {
Text(
text = eventContent,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
@@ -1485,12 +1498,12 @@ fun RenderAppDefinition(
modifier = Modifier
.border(
3.dp,
MaterialTheme.colors.background,
MaterialTheme.colorScheme.background,
CircleShape
)
.clip(shape = CircleShape)
.fillMaxSize()
.background(MaterialTheme.colors.background)
.background(MaterialTheme.colorScheme.background)
.combinedClickable(
onClick = { zoomImageDialogOpen = true },
onLongClick = {
@@ -1534,12 +1547,12 @@ fun RenderAppDefinition(
val website = remember(it) { it.website }
if (!website.isNullOrEmpty()) {
Row(verticalAlignment = Alignment.CenterVertically) {
LinkIcon(Size16Modifier, MaterialTheme.colors.placeholderText)
LinkIcon(Size16Modifier, MaterialTheme.colorScheme.placeholderText)
ClickableText(
text = AnnotatedString(website.removePrefix("https://")),
onClick = { website.let { runCatching { uri.openUri(it) } } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary),
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary),
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp, start = 5.dp)
)
}
@@ -1550,7 +1563,7 @@ fun RenderAppDefinition(
modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)
) {
val tags = remember(note) { note.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList }
val bgColor = MaterialTheme.colors.background
val bgColor = MaterialTheme.colorScheme.background
val backgroundColor = remember {
mutableStateOf(bgColor)
}
@@ -1628,7 +1641,7 @@ private fun RenderPrivateMessage(
if (makeItShort && isAuthorTheLoggedUser) {
Text(
text = eventContent!!,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
@@ -2025,7 +2038,7 @@ fun RenderPostApproval(
note.replyTo?.forEach {
NoteCompose(
it,
modifier = MaterialTheme.colors.replyModifier,
modifier = MaterialTheme.colorScheme.replyModifier,
unPackReply = false,
makeItShort = true,
isQuotedNote = true,
@@ -2230,10 +2243,9 @@ fun RemoveButton(onClick: () -> Unit) {
modifier = Modifier.padding(start = 3.dp),
onClick = onClick,
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
),
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)
) {
Text(text = stringResource(R.string.remove), color = Color.White)
@@ -2255,10 +2267,9 @@ fun AddButton(
}
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray
),
colors = ButtonDefaults.buttonColors(
containerColor = if (isActive) MaterialTheme.colorScheme.primary else Color.Gray
),
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)
) {
Text(text = stringResource(text), color = Color.White, textAlign = TextAlign.Center)
@@ -2302,7 +2313,7 @@ fun RenderPinListEvent(
FlowRow(modifier = Modifier.padding(top = 5.dp)) {
pinsToShow.forEach { pin ->
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = CenterVertically) {
PinIcon(modifier = Size15Modifier, tint = MaterialTheme.colors.onBackground.copy(0.32f))
PinIcon(modifier = Size15Modifier, tint = MaterialTheme.colorScheme.onBackground.copy(0.32f))
Spacer(modifier = Modifier.width(5.dp))
@@ -2425,7 +2436,7 @@ private fun RenderReport(
.clip(shape = QuoteBorder)
.border(
1.dp,
MaterialTheme.colors.subtleBorder,
MaterialTheme.colorScheme.subtleBorder,
QuoteBorder
),
unPackReply = false,
@@ -2469,7 +2480,7 @@ private fun ReplyRow(
showVideo = false,
showBottomDiviser = false,
sendToChannel = true,
modifier = MaterialTheme.colors.replyModifier.padding(10.dp),
modifier = MaterialTheme.colorScheme.replyModifier.padding(10.dp),
accountViewModel = accountViewModel,
nav = nav
)
@@ -2502,7 +2513,7 @@ private fun ReplyNoteComposition(
val replyBackgroundColor = remember {
mutableStateOf(backgroundColor.value)
}
val defaultReplyBackground = MaterialTheme.colors.replyBackground
val defaultReplyBackground = MaterialTheme.colorScheme.replyBackground
LaunchedEffect(key1 = backgroundColor.value, key2 = defaultReplyBackground) {
launch(Dispatchers.Default) {
@@ -2517,7 +2528,7 @@ private fun ReplyNoteComposition(
NoteCompose(
baseNote = replyingDirectlyTo,
isQuotedNote = true,
modifier = MaterialTheme.colors.replyModifier,
modifier = MaterialTheme.colorScheme.replyModifier,
unPackReply = false,
makeItShort = true,
parentBackgroundColor = replyBackgroundColor,
@@ -2604,7 +2615,7 @@ fun DisplayLocation(geohash: String, nav: (String) -> Unit) {
text = AnnotatedString(cityName),
onClick = { nav("Geohash/$geohash") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
),
fontSize = Font14SP,
@@ -2635,7 +2646,7 @@ fun FirstUserInfoRow(
}
}
val textColor = if (isRepost) MaterialTheme.colors.grayText else Color.Unspecified
val textColor = if (isRepost) MaterialTheme.colorScheme.grayText else Color.Unspecified
if (showAuthorPicture) {
NoteAuthorPicture(baseNote, nav, accountViewModel, Size25dp)
@@ -2664,7 +2675,7 @@ private fun BoostedMark() {
Text(
stringResource(id = R.string.boosted),
fontWeight = FontWeight.Bold,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
modifier = HalfStartPadding
)
@@ -2707,7 +2718,7 @@ fun TimeAgo(time: Long) {
Text(
text = timeStr,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
}
@@ -2770,11 +2781,19 @@ private fun RenderAuthorImages(
val isChannel = baseNote.event is ChannelMessageEvent && baseNote.channelHex() != null
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
if (isChannel) {
val baseChannelHex = remember { baseNote.channelHex() }
if (baseChannelHex != null) {
LoadChannel(baseChannelHex, accountViewModel) { channel ->
ChannelNotePicture(channel)
ChannelNotePicture(channel, loadProfilePicture = automaticallyShowProfilePicture)
}
}
}
@@ -2802,12 +2821,12 @@ fun LoadChannel(baseChannelHex: String, accountViewModel: AccountViewModel, cont
}
@Composable
private fun ChannelNotePicture(baseChannel: Channel) {
private fun ChannelNotePicture(baseChannel: Channel, loadProfilePicture: Boolean) {
val model by baseChannel.live.map {
it.channel.profilePicture()
}.distinctUntilChanged().observeAsState()
val backgroundColor = MaterialTheme.colors.background
val backgroundColor = MaterialTheme.colorScheme.background
val modifier = remember {
Modifier
@@ -2827,7 +2846,8 @@ private fun ChannelNotePicture(baseChannel: Channel) {
robot = baseChannel.idHex,
model = model,
contentDescription = stringResource(R.string.group_picture),
modifier = modifier
modifier = modifier,
loadProfilePicture = loadProfilePicture
)
}
}
@@ -2850,7 +2870,7 @@ private fun RepostNoteAuthorPicture(
}
Box(Size18Modifier.align(Alignment.BottomStart).padding(1.dp)) {
RepostedIcon(modifier = Size18Modifier, MaterialTheme.colors.placeholderText)
RepostedIcon(modifier = Size18Modifier, MaterialTheme.colorScheme.placeholderText)
}
Box(Size35Modifier.align(Alignment.BottomEnd)) {
@@ -2953,7 +2973,7 @@ private fun LoadAndDisplayPost(postAddress: ATag, accountViewModel: AccountViewM
nav(it)
}
},
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
}
}
@@ -3055,7 +3075,7 @@ private fun DisplayTagList(firstTag: String, nav: (String) -> Unit) {
text = displayTag,
onClick = { nav(route) },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
),
@@ -3076,7 +3096,7 @@ private fun DisplayCommunity(note: Note, nav: (String) -> Unit) {
text = displayTag,
onClick = { nav(route) },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
),
@@ -3118,7 +3138,7 @@ fun DisplayUncitedHashtags(
text = remember { AnnotatedString("#$hashtag ") },
onClick = { nav("Hashtag/$hashtag") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
)
@@ -3138,7 +3158,7 @@ fun DisplayPoW(
Text(
powStr,
color = MaterialTheme.colors.lessImportantLink,
color = MaterialTheme.colorScheme.lessImportantLink,
fontSize = Font14SP,
fontWeight = FontWeight.Bold,
maxLines = 1
@@ -3166,7 +3186,7 @@ fun DisplayReward(
text = AnnotatedString("#bounty"),
onClick = { nav("Hashtag/bounty") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
)
@@ -3222,19 +3242,19 @@ private fun RenderPledgeAmount(
if (hasPledge) {
ZappedIcon(modifier = Size20Modifier)
} else {
ZapIcon(modifier = Size20Modifier, MaterialTheme.colors.placeholderText)
ZapIcon(modifier = Size20Modifier, MaterialTheme.colorScheme.placeholderText)
}
Text(
text = reward,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
}
@Composable
fun BadgeDisplay(baseNote: Note) {
val background = MaterialTheme.colors.background
val background = MaterialTheme.colorScheme.background
val badgeData = baseNote.event as? BadgeDefinitionEvent ?: return
val image = remember { badgeData.thumb()?.ifBlank { null } ?: badgeData.image() }
@@ -3250,9 +3270,9 @@ fun BadgeDisplay(baseNote: Note) {
val backgroundColor = it.drawable.toBitmap(200, 200).copy(Bitmap.Config.ARGB_8888, false).get(0, 199)
val colorFromImage = Color(backgroundColor)
val textBackground = if (colorFromImage.luminance() > 0.5) {
lightColors().onBackground
lightColorScheme().onBackground
} else {
darkColors().onBackground
darkColorScheme().onBackground
}
launch(Dispatchers.Main) {
@@ -3268,7 +3288,7 @@ fun BadgeDisplay(baseNote: Note) {
.clip(shape = CutCornerShape(20, 20, 20, 20))
.border(
5.dp,
MaterialTheme.colors.mediumImportanceLink,
MaterialTheme.colorScheme.mediumImportanceLink,
CutCornerShape(20)
)
.background(backgroundFromImage.first)
@@ -3311,7 +3331,7 @@ private fun RenderBadge(
name?.let {
Text(
text = it,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
@@ -3323,7 +3343,7 @@ private fun RenderBadge(
description?.let {
Text(
text = it,
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
@@ -3494,7 +3514,7 @@ fun AudioTrackHeader(noteEvent: AudioTrackEvent, note: Note, accountViewModel: A
it.first.role?.let {
Text(
text = it.capitalize(Locale.ROOT),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
}
@@ -3535,7 +3555,7 @@ fun AudioHeader(noteEvent: AudioHeaderEvent, note: Note, accountViewModel: Accou
val waveform = remember { noteEvent.wavefrom()?.toImmutableList()?.ifEmpty { null } }
val content = remember { noteEvent.content().ifBlank { null } }
val defaultBackground = MaterialTheme.colors.background
val defaultBackground = MaterialTheme.colorScheme.background
val background = remember { mutableStateOf(defaultBackground) }
val tags = remember(noteEvent) { noteEvent?.tags()?.toImmutableListOfLists() ?: EmptyTagList }
@@ -3623,8 +3643,10 @@ fun RenderLiveActivityEventInner(baseNote: Note, accountViewModel: AccountViewMo
var isOnline by remember { mutableStateOf(false) }
LaunchedEffect(key1 = media) {
launch(Dispatchers.IO) {
isOnline = OnlineChecker.isOnline(media)
accountViewModel.checkIsOnline(media) { newIsOnline ->
if (isOnline != newIsOnline) {
isOnline = newIsOnline
}
}
}
@@ -3690,7 +3712,7 @@ fun RenderLiveActivityEventInner(baseNote: Note, accountViewModel: AccountViewMo
it.first.role?.let {
Text(
text = it.capitalize(Locale.ROOT),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
}
@@ -3722,7 +3744,7 @@ fun RenderLiveActivityEventInner(baseNote: Note, accountViewModel: AccountViewMo
) {
Text(
text = stringResource(id = R.string.live_stream_is_offline),
color = MaterialTheme.colors.onBackground,
color = MaterialTheme.colorScheme.onBackground,
fontWeight = FontWeight.Bold
)
}
@@ -3736,7 +3758,7 @@ fun RenderLiveActivityEventInner(baseNote: Note, accountViewModel: AccountViewMo
) {
Text(
text = stringResource(id = R.string.live_stream_has_ended),
color = MaterialTheme.colors.onBackground,
color = MaterialTheme.colorScheme.onBackground,
fontWeight = FontWeight.Bold
)
}
@@ -3756,7 +3778,7 @@ private fun LongFormHeader(noteEvent: LongTextNoteEvent, note: Note, accountView
.clip(shape = QuoteBorder)
.border(
1.dp,
MaterialTheme.colors.subtleBorder,
MaterialTheme.colorScheme.subtleBorder,
QuoteBorder
)
) {
@@ -3786,7 +3808,7 @@ private fun LongFormHeader(noteEvent: LongTextNoteEvent, note: Note, accountView
title?.let {
Text(
text = it,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier
.fillMaxWidth()
.padding(start = 10.dp, end = 10.dp, top = 10.dp)
@@ -3796,7 +3818,7 @@ private fun LongFormHeader(noteEvent: LongTextNoteEvent, note: Note, accountView
summary?.let {
Text(
text = it,
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier
.fillMaxWidth()
.padding(start = 10.dp, end = 10.dp, bottom = 10.dp),
@@ -3822,7 +3844,7 @@ private fun RenderClassifieds(noteEvent: ClassifiedsEvent, note: Note, accountVi
.clip(shape = QuoteBorder)
.border(
1.dp,
MaterialTheme.colors.subtleBorder,
MaterialTheme.colorScheme.subtleBorder,
QuoteBorder
)
) {
@@ -3845,7 +3867,7 @@ private fun RenderClassifieds(noteEvent: ClassifiedsEvent, note: Note, accountVi
title?.let {
Text(
text = it,
style = MaterialTheme.typography.body1,
style = MaterialTheme.typography.bodyLarge,
maxLines = 1,
modifier = Modifier.weight(1f)
)
@@ -3865,7 +3887,7 @@ private fun RenderClassifieds(noteEvent: ClassifiedsEvent, note: Note, accountVi
Text(
text = priceTag,
maxLines = 1,
color = MaterialTheme.colors.primary,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Bold,
modifier = remember {
Modifier
@@ -3882,7 +3904,7 @@ private fun RenderClassifieds(noteEvent: ClassifiedsEvent, note: Note, accountVi
summary?.let {
Text(
text = it,
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier
.weight(1f),
color = Color.Gray,
@@ -3894,7 +3916,7 @@ private fun RenderClassifieds(noteEvent: ClassifiedsEvent, note: Note, accountVi
location?.let {
Text(
text = it,
style = MaterialTheme.typography.caption,
style = MaterialTheme.typography.bodySmall,
color = Color.Gray,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
@@ -15,16 +16,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AlternateEmail
import androidx.compose.material.icons.filled.Block
@@ -35,6 +26,17 @@ import androidx.compose.material.icons.filled.PersonAdd
import androidx.compose.material.icons.filled.PersonRemove
import androidx.compose.material.icons.filled.Report
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
@@ -65,6 +67,7 @@ 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.theme.WarningColor
import com.vitorpamplona.amethyst.ui.theme.isLight
import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground
import com.vitorpamplona.quartz.events.AudioTrackEvent
import com.vitorpamplona.quartz.events.FileHeaderEvent
@@ -177,15 +180,15 @@ private fun RenderMainPopup(
showReportDialog: MutableState<Boolean>
) {
val context = LocalContext.current
val primaryLight = lightenColor(MaterialTheme.colors.primary, 0.1f)
val primaryLight = lightenColor(MaterialTheme.colorScheme.primary, 0.1f)
val cardShape = RoundedCornerShape(5.dp)
val clipboardManager = LocalClipboardManager.current
val scope = rememberCoroutineScope()
val backgroundColor = if (MaterialTheme.colors.isLight) {
MaterialTheme.colors.primary
val backgroundColor = if (MaterialTheme.colorScheme.isLight) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colors.secondaryButtonBackground
MaterialTheme.colorScheme.secondaryButtonBackground
}
val showToast = { stringResource: Int ->
@@ -205,7 +208,7 @@ private fun RenderMainPopup(
Card(
modifier = Modifier.shadow(elevation = 6.dp, shape = cardShape),
shape = cardShape,
backgroundColor = backgroundColor
colors = CardDefaults.cardColors(containerColor = backgroundColor)
) {
Column(modifier = Modifier.width(IntrinsicSize.Min)) {
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
@@ -288,20 +291,16 @@ private fun RenderMainPopup(
Icons.Default.PersonRemove,
stringResource(R.string.quick_action_unfollow)
) {
scope.launch(Dispatchers.IO) {
accountViewModel.unfollow(note.author!!)
onDismiss()
}
accountViewModel.unfollow(note.author!!)
onDismiss()
}
} else {
NoteQuickActionItem(
Icons.Default.PersonAdd,
stringResource(R.string.quick_action_follow)
) {
scope.launch(Dispatchers.IO) {
accountViewModel.follow(note.author!!)
onDismiss()
}
accountViewModel.follow(note.author!!)
onDismiss()
}
}
@@ -413,7 +412,7 @@ private fun BlockAlertDialog(note: Note, accountViewModel: AccountViewModel, onD
buttonIcon = Icons.Default.Block,
buttonText = stringResource(R.string.quick_action_block_dialog_btn),
buttonColors = ButtonDefaults.buttonColors(
backgroundColor = WarningColor,
containerColor = WarningColor,
contentColor = Color.White
),
onClickDoOnce = {
@@ -503,7 +502,7 @@ fun QuickActionAlertDialog(
text = {
Text(textContent)
},
buttons = {
confirmButton = {
Row(
modifier = Modifier
.padding(all = 8.dp)
@@ -513,7 +512,7 @@ fun QuickActionAlertDialog(
TextButton(onClick = onClickDontShowAgain) {
Text(stringResource(R.string.quick_action_dont_show_again_button))
}
Button(onClick = onClickDoOnce, colors = buttonColors) {
Button(onClick = onClickDoOnce, colors = buttonColors, contentPadding = PaddingValues(horizontal = 16.dp)) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
@@ -1,12 +1,13 @@
package com.vitorpamplona.amethyst.ui.note
import android.widget.Toast
import androidx.compose.foundation.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.outlined.Bolt
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
@@ -27,6 +28,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.StringToastMsg
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.Font14SP
@@ -128,7 +130,7 @@ private fun OptionNote(
val color = if (poolOption.consensusThreadhold) {
Color.Green.copy(alpha = 0.32f)
} else {
MaterialTheme.colors.mediumImportanceLink
MaterialTheme.colorScheme.mediumImportanceLink
}
ZapVote(
@@ -252,7 +254,7 @@ private fun RenderOptionBeforeVote(
.clip(shape = QuoteBorder)
.border(
2.dp,
MaterialTheme.colors.primary,
MaterialTheme.colorScheme.primary,
QuoteBorder
)
) {
@@ -294,7 +296,7 @@ fun ZapVote(
}
var zappingProgress by remember { mutableStateOf(0f) }
var showErrorMessageDialog by remember { mutableStateOf<String?>(null) }
var showErrorMessageDialog by remember { mutableStateOf<StringToastMsg?>(null) }
val context = LocalContext.current
val scope = rememberCoroutineScope()
@@ -305,50 +307,30 @@ fun ZapVote(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.combinedClickable(
role = Role.Button,
// interactionSource = remember { MutableInteractionSource() },
// indication = rememberRipple(bounded = false, radius = 24.dp),
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false, radius = 24.dp),
onClick = {
if (!accountViewModel.isWriteable() && !accountViewModel.loggedInWithExternalSigner()) {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.login_with_a_private_key_to_be_able_to_send_zaps),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_send_zaps
)
} else if (pollViewModel.isPollClosed()) {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.poll_is_closed),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.poll_unable_to_vote,
R.string.poll_is_closed_explainer
)
} else if (isLoggedUser) {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.poll_author_no_vote),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.poll_unable_to_vote,
R.string.poll_author_no_vote
)
} else if (pollViewModel.isVoteAmountAtomic() && poolOption.zappedByLoggedIn) {
// only allow one vote per option when min==max, i.e. atomic vote amount specified
scope.launch {
Toast
.makeText(
context,
R.string.one_vote_per_user_on_atomic_votes,
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.poll_unable_to_vote,
R.string.one_vote_per_user_on_atomic_votes
)
return@combinedClickable
} else if (accountViewModel.account.zapAmountChoices.size == 1 &&
pollViewModel.isValidInputVoteAmount(accountViewModel.account.zapAmountChoices.first())
@@ -359,13 +341,9 @@ fun ZapVote(
poolOption.option,
"",
context,
onError = {
scope.launch {
zappingProgress = 0f
Toast
.makeText(context, it, Toast.LENGTH_SHORT)
.show()
}
onError = { title, message ->
zappingProgress = 0f
showErrorMessageDialog = StringToastMsg(title, message)
},
onProgress = {
scope.launch(Dispatchers.Main) {
@@ -395,11 +373,9 @@ fun ZapVote(
onChangeAmount = {
wantsToZap = false
},
onError = {
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = it
}
onError = { title, message ->
showErrorMessageDialog = StringToastMsg(title, message)
zappingProgress = 0f
},
onProgress = {
scope.launch(Dispatchers.Main) {
@@ -423,19 +399,22 @@ fun ZapVote(
wantsToPay = persistentListOf()
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = it
showErrorMessageDialog = StringToastMsg(
context.getString(R.string.error_dialog_zap_error),
it
)
}
}
)
}
if (showErrorMessageDialog != null) {
showErrorMessageDialog?.let { toast ->
ErrorMessageDialog(
title = stringResource(id = R.string.error_dialog_zap_error),
textContent = showErrorMessageDialog ?: "",
title = toast.title,
textContent = toast.msg,
onClickStartMessage = {
baseNote.author?.let {
nav(routeToMessage(it, showErrorMessageDialog, accountViewModel))
nav(routeToMessage(it, toast.msg, accountViewModel))
}
},
onDismiss = { showErrorMessageDialog = null }
@@ -458,7 +437,7 @@ fun ZapVote(
imageVector = Icons.Outlined.Bolt,
contentDescription = stringResource(id = R.string.zaps),
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
} else {
Spacer(Modifier.width(3.dp))
@@ -479,7 +458,7 @@ fun ZapVote(
Text(
text = amountStr,
fontSize = Font14SP,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
modifier = modifier
)
}
@@ -494,7 +473,7 @@ fun FilteredZapAmountChoicePopup(
pollOption: Int,
onDismiss: () -> Unit,
onChangeAmount: () -> Unit,
onError: (text: String) -> Unit,
onError: (title: String, text: String) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit
) {
@@ -543,7 +522,7 @@ fun FilteredZapAmountChoicePopup(
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(
@@ -2,7 +2,6 @@ package com.vitorpamplona.amethyst.ui.note
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
@@ -29,16 +28,16 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProgressIndicatorDefaults
import androidx.compose.material.Text
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
@@ -110,7 +109,6 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.math.BigDecimal
@@ -172,7 +170,7 @@ private fun InnerReactionRow(
) {
val (value, elapsed) = measureTimedValue {
Row(verticalAlignment = CenterVertically) {
ReplyReactionWithDialog(baseNote, MaterialTheme.colors.placeholderText, accountViewModel, nav)
ReplyReactionWithDialog(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav)
}
}
Log.d("Rendering Metrics", "Reaction Reply: ${baseNote.event?.content()?.split("\n")?.getOrNull(0)?.take(15)}.. $elapsed")
@@ -186,7 +184,7 @@ private fun InnerReactionRow(
Row(verticalAlignment = CenterVertically) {
BoostWithDialog(
baseNote,
MaterialTheme.colors.placeholderText,
MaterialTheme.colorScheme.placeholderText,
accountViewModel,
nav
)
@@ -201,7 +199,7 @@ private fun InnerReactionRow(
) {
val (value, elapsed) = measureTimedValue {
Row(verticalAlignment = CenterVertically) {
LikeReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel, nav)
LikeReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav)
}
}
Log.d("Rendering Metrics", "Reaction Likes: ${baseNote.event?.content()?.split("\n")?.getOrNull(0)?.take(15)}.. $elapsed")
@@ -213,7 +211,7 @@ private fun InnerReactionRow(
) {
val (value, elapsed) = measureTimedValue {
Row(verticalAlignment = CenterVertically) {
ZapReaction(baseNote, MaterialTheme.colors.placeholderText, accountViewModel, nav = nav)
ZapReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav = nav)
}
}
Log.d("Rendering Metrics", "Reaction Zaps: ${baseNote.event?.content()?.split("\n")?.getOrNull(0)?.take(15)}.. $elapsed")
@@ -227,8 +225,8 @@ private fun InnerReactionRow(
Row(verticalAlignment = CenterVertically) {
ViewCountReaction(
note = baseNote,
grayTint = MaterialTheme.colors.placeholderText,
viewCountColorFilter = MaterialTheme.colors.placeholderTextColorFilter
grayTint = MaterialTheme.colorScheme.placeholderText,
viewCountColorFilter = MaterialTheme.colorScheme.placeholderTextColorFilter
)
}
}
@@ -285,7 +283,7 @@ fun RenderZapRaiser(baseNote: Note, zapraiserAmount: Long, details: Boolean, acc
val color = if (zapraiserStatus.progress > 0.99) {
DarkerGreen
} else {
MaterialTheme.colors.mediumImportanceLink
MaterialTheme.colorScheme.mediumImportanceLink
}
LinearProgressIndicator(
@@ -310,7 +308,7 @@ fun RenderZapRaiser(baseNote: Note, zapraiserAmount: Long, details: Boolean, acc
Text(
text = stringResource(id = R.string.sats_to_complete, totalPercentage, zapraiserStatus.left),
modifier = NoSoTinyBorders,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
fontSize = Font14SP,
maxLines = 1
)
@@ -389,7 +387,7 @@ private fun ReactionDetailGallery(
nav: (String) -> Unit,
accountViewModel: AccountViewModel
) {
val defaultBackgroundColor = MaterialTheme.colors.background
val defaultBackgroundColor = MaterialTheme.colorScheme.background
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
val hasReactions by baseNote.live().hasReactions.observeAsState(
@@ -540,9 +538,6 @@ fun ReplyReaction(
iconSize: Dp = Size17dp,
onPress: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
IconButton(
modifier = remember {
Modifier.size(iconSize)
@@ -554,13 +549,10 @@ fun ReplyReaction(
if (accountViewModel.loggedInWithExternalSigner()) {
onPress()
} else {
scope.launch {
Toast.makeText(
context,
context.getString(R.string.login_with_a_private_key_to_be_able_to_reply),
Toast.LENGTH_SHORT
).show()
}
accountViewModel.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_reply
)
}
}
}
@@ -643,9 +635,6 @@ fun BoostReaction(
iconSize: Dp = 20.dp,
onQuotePress: () -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
var wantsToBoost by remember { mutableStateOf(false) }
val iconButtonModifier = remember {
@@ -657,29 +646,22 @@ fun BoostReaction(
onClick = {
if (accountViewModel.isWriteable()) {
if (accountViewModel.hasBoosted(baseNote)) {
scope.launch(Dispatchers.IO) {
accountViewModel.deleteBoostsTo(baseNote)
}
accountViewModel.deleteBoostsTo(baseNote)
} else {
wantsToBoost = true
}
} else {
if (accountViewModel.loggedInWithExternalSigner()) {
if (accountViewModel.hasBoosted(baseNote)) {
scope.launch(Dispatchers.IO) {
accountViewModel.deleteBoostsTo(baseNote)
}
accountViewModel.deleteBoostsTo(baseNote)
} else {
wantsToBoost = true
}
} else {
scope.launch {
Toast.makeText(
context,
context.getString(R.string.login_with_a_private_key_to_be_able_to_boost_posts),
Toast.LENGTH_SHORT
).show()
}
accountViewModel.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_boost_posts
)
}
}
}
@@ -699,9 +681,7 @@ fun BoostReaction(
onQuotePress()
},
onRepost = {
scope.launch(Dispatchers.IO) {
accountViewModel.boost(baseNote)
}
accountViewModel.boost(baseNote)
}
)
}
@@ -741,9 +721,6 @@ fun LikeReaction(
heartSize: Dp = 16.dp,
iconFontSize: TextUnit = Font14SP
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val iconButtonModifier = remember {
Modifier.size(iconSize)
}
@@ -761,21 +738,12 @@ fun LikeReaction(
likeClick(
baseNote,
accountViewModel,
scope,
context,
onMultipleChoices = {
wantsToReact = true
},
onWantsToSignReaction = {
if (accountViewModel.account.reactionChoices.size == 1) {
scope.launch(Dispatchers.IO) {
val reaction = accountViewModel.account.reactionChoices.first()
if (accountViewModel.hasReactedTo(baseNote, reaction)) {
accountViewModel.deleteReactionTo(baseNote, reaction)
} else {
accountViewModel.reactTo(baseNote, reaction)
}
}
accountViewModel.reactToOrDelete(baseNote)
} else if (accountViewModel.account.reactionChoices.size > 1) {
wantsToReact = true
}
@@ -896,44 +864,25 @@ fun LikeText(baseNote: Note, grayTint: Color) {
private fun likeClick(
baseNote: Note,
accountViewModel: AccountViewModel,
scope: CoroutineScope,
context: Context,
onMultipleChoices: () -> Unit,
onWantsToSignReaction: () -> Unit
) {
if (accountViewModel.account.reactionChoices.isEmpty()) {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.no_reaction_type_setup_long_press_to_change),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.no_reactions_setup,
R.string.no_reaction_type_setup_long_press_to_change
)
} else if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithExternalSigner()) {
onWantsToSignReaction()
} else {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.login_with_a_private_key_to_like_posts),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_like_posts
)
}
} else if (accountViewModel.account.reactionChoices.size == 1) {
scope.launch(Dispatchers.IO) {
val reaction = accountViewModel.account.reactionChoices.first()
if (accountViewModel.hasReactedTo(baseNote, reaction)) {
accountViewModel.deleteReactionTo(baseNote, reaction)
} else {
accountViewModel.reactTo(baseNote, reaction)
}
}
accountViewModel.reactToOrDelete(baseNote)
} else if (accountViewModel.account.reactionChoices.size > 1) {
onMultipleChoices()
}
@@ -976,18 +925,19 @@ fun ZapReaction(
zapClick(
baseNote,
accountViewModel,
scope,
context,
onZappingProgress = { progress: Float ->
zappingProgress = progress
scope.launch {
zappingProgress = progress
}
},
onMultipleChoices = {
wantsToZap = true
},
onError = {
onError = { title, message ->
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = it
showErrorMessageDialog = message
}
},
onPayViaIntent = {
@@ -1015,10 +965,10 @@ fun ZapReaction(
wantsToZap = false
wantsToChangeZapAmount = true
},
onError = {
onError = { title, message ->
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = it
showErrorMessageDialog = message
}
},
onProgress = {
@@ -1075,10 +1025,10 @@ fun ZapReaction(
if (wantsToSetCustomZap) {
ZapCustomDialog(
onClose = { wantsToSetCustomZap = false },
onError = {
onError = { title, message ->
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = it
showErrorMessageDialog = message
}
},
onProgress = {
@@ -1121,33 +1071,22 @@ fun ZapReaction(
private fun zapClick(
baseNote: Note,
accountViewModel: AccountViewModel,
scope: CoroutineScope,
context: Context,
onZappingProgress: (Float) -> Unit,
onMultipleChoices: () -> Unit,
onError: (String) -> Unit,
onError: (String, String) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit
) {
if (accountViewModel.account.zapAmountChoices.isEmpty()) {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.no_zap_amount_setup_long_press_to_change),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
context.getString(R.string.error_dialog_zap_error),
context.getString(R.string.no_zap_amount_setup_long_press_to_change)
)
} else if (!accountViewModel.isWriteable() && !accountViewModel.loggedInWithExternalSigner()) {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.login_with_a_private_key_to_be_able_to_send_zaps),
Toast.LENGTH_SHORT
)
.show()
}
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)
)
} else if (accountViewModel.account.zapAmountChoices.size == 1) {
accountViewModel.zap(
baseNote,
@@ -1157,9 +1096,7 @@ private fun zapClick(
context,
onError = onError,
onProgress = {
scope.launch(Dispatchers.Main) {
onZappingProgress(it)
}
onZappingProgress(it)
},
zapType = accountViewModel.account.defaultZapType,
onPayViaIntent = onPayViaIntent
@@ -1270,7 +1207,7 @@ private fun DrawViewCount(
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
},
contentDescription = stringResource(R.string.view_count),
contentDescription = context.getString(R.string.view_count),
modifier = iconModifier,
colorFilter = viewCountColorFilter
)
@@ -1289,15 +1226,12 @@ private fun BoostTypeChoicePopup(baseNote: Note, iconSize: Dp, accountViewModel:
onDismissRequest = { onDismiss() }
) {
FlowRow {
val scope = rememberCoroutineScope()
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = {
if (accountViewModel.isWriteable()) {
scope.launch(Dispatchers.IO) {
accountViewModel.boost(baseNote)
onDismiss()
}
accountViewModel.boost(baseNote)
onDismiss()
} else {
onRepost()
onDismiss()
@@ -1306,7 +1240,7 @@ private fun BoostTypeChoicePopup(baseNote: Note, iconSize: Dp, accountViewModel:
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(stringResource(R.string.boost), color = Color.White, textAlign = TextAlign.Center)
@@ -1318,7 +1252,7 @@ private fun BoostTypeChoicePopup(baseNote: Note, iconSize: Dp, accountViewModel:
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(stringResource(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
@@ -1387,10 +1321,9 @@ private fun ActionableReactionButton(
onDismiss()
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
)
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
)
) {
val thisModifier = remember(reactionType) {
Modifier.combinedClickable(
@@ -1471,12 +1404,11 @@ fun ZapAmountChoicePopup(
accountViewModel: AccountViewModel,
onDismiss: () -> Unit,
onChangeAmount: () -> Unit,
onError: (text: String) -> Unit,
onError: (title: String, text: String) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val zapMessage = ""
@@ -1507,7 +1439,7 @@ fun ZapAmountChoicePopup(
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(
@@ -5,11 +5,11 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -73,7 +73,7 @@ fun RelayCompose(
Text(
"${relay.counter} ${stringResource(R.string.posts_received)}",
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -117,10 +117,9 @@ fun AddRelayButton(onClick: () -> Unit) {
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onClick,
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
),
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
) {
Text(text = stringResource(id = R.string.add), color = Color.White)
@@ -133,10 +132,9 @@ fun RemoveRelayButton(onClick: () -> Unit) {
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onClick,
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
),
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
) {
Text(text = stringResource(R.string.remove), color = Color.White)
@@ -5,11 +5,11 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
@@ -88,7 +88,7 @@ private fun ShowMoreRelaysButton(onClick: () -> Unit) {
imageVector = Icons.Default.ExpandMore,
null,
modifier = ShowMoreRelaysButtonIconModifier,
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
}
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst.ui.note
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -10,12 +9,12 @@ import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
@@ -33,10 +32,12 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.map
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.RelayBriefInfo
import com.vitorpamplona.amethyst.model.RelayInformation
import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.actions.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -47,7 +48,6 @@ import com.vitorpamplona.amethyst.ui.theme.Size15dp
import com.vitorpamplona.amethyst.ui.theme.StdStartPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.launch
@Composable
public fun RelayBadgesHorizontal(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
@@ -100,7 +100,7 @@ fun ChatRelayExpandButton(onClick: () -> Unit) {
imageVector = Icons.Default.ChevronRight,
null,
modifier = Size15Modifier,
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
}
@@ -123,9 +123,18 @@ fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav:
val context = LocalContext.current
val scope = rememberCoroutineScope()
val interactionSource = remember { MutableInteractionSource() }
val ripple = rememberRipple(bounded = false, radius = Size15dp)
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
val clickableModifier = remember(relay) {
Modifier
.padding(1.dp)
@@ -148,15 +157,10 @@ fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav:
Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS -> context.getString(R.string.relay_information_document_error_assemble_url, url, exceptionMessage)
}
scope.launch {
Toast
.makeText(
context,
msg,
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
context.getString(R.string.unable_to_download_relay_document),
msg
)
}
)
}
@@ -166,13 +170,13 @@ fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav:
Box(
modifier = clickableModifier
) {
RenderRelayIcon(relay.favIcon)
RenderRelayIcon(relay.favIcon, automaticallyShowProfilePicture)
}
}
@Composable
fun RenderRelayIcon(iconUrl: String, size: Dp = Size13dp) {
val backgroundColor = MaterialTheme.colors.background
fun RenderRelayIcon(iconUrl: String, loadProfilePicture: Boolean, size: Dp = Size13dp) {
val backgroundColor = MaterialTheme.colorScheme.background
val iconModifier = remember {
Modifier
@@ -186,6 +190,7 @@ fun RenderRelayIcon(iconUrl: String, size: Dp = Size13dp) {
model = iconUrl,
contentDescription = stringResource(id = R.string.relay_icon),
colorFilter = RelayIconFilter,
modifier = iconModifier
modifier = iconModifier,
loadProfilePicture = loadProfilePicture
)
}
@@ -4,9 +4,9 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -73,7 +73,7 @@ private fun ReplyInformation(
Text(
stringResource(R.string.replying_to),
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
repliesToDisplay.forEachIndexed { idx, user ->
@@ -84,13 +84,13 @@ private fun ReplyInformation(
Text(
", ",
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
} else if (idx < repliesToDisplay.size - 1) {
Text(
stringResource(R.string.and),
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
} else {
@@ -98,25 +98,25 @@ private fun ReplyInformation(
Text(
", ",
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
} else if (idx < repliesToDisplay.size) {
Text(
stringResource(R.string.and),
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
ClickableText(
AnnotatedString("${sortedMentions.size - 2}"),
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.lessImportantLink, fontSize = 13.sp),
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.lessImportantLink, fontSize = 13.sp),
onClick = { expanded = true }
)
Text(
" ${stringResource(R.string.others)}",
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
}
@@ -176,7 +176,7 @@ fun ReplyInformationChannel(
Text(
stringResource(id = R.string.replying_to),
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
mentions.forEachIndexed { idx, user ->
@@ -186,13 +186,13 @@ fun ReplyInformationChannel(
Text(
", ",
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
} else if (idx < mentions.size - 1) {
Text(
" ${stringResource(id = R.string.and)} ",
fontSize = 13.sp,
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
}
}
@@ -213,7 +213,7 @@ private fun ReplyInfoMention(
clickablePart = remember(innerUserState) { "$prefix${innerUserState?.user?.toBestDisplayName()}" },
tags = remember(innerUserState) { innerUserState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.lessImportantLink,
color = MaterialTheme.colorScheme.lessImportantLink,
fontSize = 13.sp
),
onClick = { onUserTagClick(user) }
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -19,13 +20,13 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -212,7 +213,7 @@ fun UpdateReactionTypeDialog(
placeholder = {
Text(
text = "\uD83D\uDCAF, \uD83C\uDF89, \uD83D\uDC4E",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true,
@@ -225,7 +226,7 @@ fun UpdateReactionTypeDialog(
onClick = { postViewModel.addChoice() },
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = stringResource(R.string.add), color = Color.White)
@@ -254,11 +255,12 @@ private fun RenderReactionOption(
modifier = Modifier.padding(horizontal = 3.dp),
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
),
onClick = {
postViewModel.removeChoice(reactionType)
}
},
contentPadding = PaddingValues(horizontal = 5.dp)
) {
if (reactionType.startsWith(":")) {
val noStartColon = reactionType.removePrefix(":")
@@ -331,7 +333,7 @@ private fun EmojiSelector(accountViewModel: AccountViewModel, nav: (String) -> U
@Composable
fun EmojiCollectionGallery(emojiCollections: List<ATag>, accountViewModel: AccountViewModel, nav: (String) -> Unit, onClick: ((EmojiUrl) -> Unit)? = null) {
val color = MaterialTheme.colors.background
val color = MaterialTheme.colorScheme.background
val bgColor = remember { mutableStateOf(color) }
val listState = rememberLazyListState()
@@ -5,7 +5,6 @@ import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.Toast
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
@@ -27,18 +26,18 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Visibility
import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -82,7 +81,6 @@ import com.vitorpamplona.quartz.encoders.decodePublicKey
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.LnZapEvent
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import androidx.compose.runtime.rememberCoroutineScope as rememberCoroutineScope
@@ -228,9 +226,16 @@ fun UpdateZapAmountDialog(
try {
postViewModel.updateNIP47(nip47uri)
} catch (e: IllegalArgumentException) {
scope.launch {
Toast.makeText(context, e.message, Toast.LENGTH_SHORT)
.show()
if (e.message != null) {
accountViewModel.toast(
context.getString(R.string.error_parsing_nip47_title),
context.getString(R.string.error_parsing_nip47, nip47uri, e.message!!)
)
} else {
accountViewModel.toast(
context.getString(R.string.error_parsing_nip47_title),
context.getString(R.string.error_parsing_nip47_no_error, nip47uri)
)
}
}
}
@@ -288,7 +293,7 @@ fun UpdateZapAmountDialog(
modifier = Modifier.padding(horizontal = 3.dp),
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
),
onClick = {
postViewModel.removeAmount(amountInSats)
@@ -330,7 +335,7 @@ fun UpdateZapAmountDialog(
placeholder = {
Text(
text = "100, 1000, 5000",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true,
@@ -343,7 +348,7 @@ fun UpdateZapAmountDialog(
onClick = { postViewModel.addAmount() },
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = stringResource(R.string.add), color = Color.White)
@@ -418,7 +423,7 @@ fun UpdateZapAmountDialog(
painter = painterResource(R.drawable.ic_qrcode),
null,
modifier = Modifier.size(24.dp),
tint = MaterialTheme.colors.primary
tint = MaterialTheme.colorScheme.primary
)
}
}
@@ -432,7 +437,7 @@ fun UpdateZapAmountDialog(
Text(
stringResource(id = R.string.wallet_connect_service_explainer),
Modifier.weight(1f),
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
fontSize = Font14SP
)
}
@@ -444,9 +449,16 @@ fun UpdateZapAmountDialog(
try {
postViewModel.updateNIP47(it)
} catch (e: IllegalArgumentException) {
scope.launch {
Toast.makeText(context, e.message, Toast.LENGTH_SHORT)
.show()
if (e.message != null) {
accountViewModel.toast(
context.getString(R.string.error_parsing_nip47_title),
context.getString(R.string.error_parsing_nip47, it, e.message!!)
)
} else {
accountViewModel.toast(
context.getString(R.string.error_parsing_nip47_title),
context.getString(R.string.error_parsing_nip47_no_error, it)
)
}
}
}
@@ -471,7 +483,7 @@ fun UpdateZapAmountDialog(
placeholder = {
Text(
text = "npub, hex",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true,
@@ -493,7 +505,7 @@ fun UpdateZapAmountDialog(
placeholder = {
Text(
text = "wss://relay.server.com",
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
},
@@ -505,7 +517,6 @@ fun UpdateZapAmountDialog(
mutableStateOf(false)
}
val scope = rememberCoroutineScope()
val context = LocalContext.current
val keyguardLauncher =
@@ -537,20 +548,23 @@ fun UpdateZapAmountDialog(
placeholder = {
Text(
text = stringResource(R.string.wallet_connect_service_secret_placeholder),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
trailingIcon = {
IconButton(onClick = {
if (!showPassword) {
authenticate(
authTitle,
context,
scope,
keyguardLauncher
) {
showPassword = true
}
title = authTitle,
context = context,
keyguardLauncher = keyguardLauncher,
onApproved = {
showPassword = true
},
onError = { title, message ->
accountViewModel.toast(title, message)
}
)
} else {
showPassword = false
}
@@ -580,9 +594,9 @@ fun UpdateZapAmountDialog(
fun authenticate(
title: String,
context: Context,
scope: CoroutineScope,
keyguardLauncher: ManagedActivityResultLauncher<Intent, ActivityResult>,
onApproved: () -> Unit
onApproved: () -> Unit,
onError: (String, String) -> Unit
) {
val fragmentContext = context.getFragmentActivity()!!
val keyguardManager =
@@ -626,26 +640,19 @@ fun authenticate(
when (errorCode) {
BiometricPrompt.ERROR_NEGATIVE_BUTTON -> keyguardPrompt()
BiometricPrompt.ERROR_LOCKOUT -> keyguardPrompt()
else ->
scope.launch {
Toast.makeText(
context,
"${context.getString(R.string.biometric_error)}: $errString",
Toast.LENGTH_SHORT
).show()
}
else -> onError(
context.getString(R.string.biometric_authentication_failed),
context.getString(R.string.biometric_authentication_failed_explainer_with_error, errString)
)
}
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
scope.launch {
Toast.makeText(
context,
context.getString(R.string.biometric_authentication_failed),
Toast.LENGTH_SHORT
).show()
}
onError(
context.getString(R.string.biometric_authentication_failed),
context.getString(R.string.biometric_authentication_failed_explainer)
)
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
@@ -4,7 +4,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material3.Divider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -10,12 +10,12 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Divider
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Divider
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
@@ -41,9 +41,11 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.ConnectivityType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.ExternalSignerUtils
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -76,6 +78,13 @@ fun NoteAuthorPicture(
onClick: ((User) -> Unit)? = null
) {
val author by baseNote.live().authorChanges.observeAsState(baseNote.author)
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
Crossfade(targetState = author) {
if (it == null) {
@@ -88,7 +97,7 @@ fun NoteAuthorPicture(
@Composable
fun DisplayBlankAuthor(size: Dp, modifier: Modifier = Modifier) {
val backgroundColor = MaterialTheme.colors.background
val backgroundColor = MaterialTheme.colorScheme.background
val nullModifier = remember {
modifier
@@ -315,7 +324,7 @@ fun PictureAndFollowingMark(
modifier: Modifier,
accountViewModel: AccountViewModel
) {
val backgroundColor = MaterialTheme.colors.background
val backgroundColor = MaterialTheme.colorScheme.background
val myImageModifier = remember {
modifier
.size(size)
@@ -323,12 +332,21 @@ fun PictureAndFollowingMark(
.background(backgroundColor)
}
val automaticallyShowProfilePicture = remember {
when (accountViewModel.account.settings.automaticallyShowProfilePictures) {
ConnectivityType.WIFI_ONLY -> !ConnectivityStatus.isOnMobileData.value
ConnectivityType.NEVER -> false
ConnectivityType.ALWAYS -> true
}
}
RobohashAsyncImageProxy(
robot = userHex,
model = userPicture,
contentDescription = stringResource(id = R.string.profile_image),
modifier = myImageModifier,
contentScale = ContentScale.Crop
contentScale = ContentScale.Crop,
loadProfilePicture = automaticallyShowProfilePicture
)
val myIconSize by remember(size) {
@@ -412,70 +430,85 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
if (!state.isFollowingAuthor) {
DropdownMenuItem(
text = {
Text(stringResource(R.string.follow))
},
onClick = {
val author = note.author ?: return@DropdownMenuItem
scope.launch(Dispatchers.IO) {
accountViewModel.follow(author)
onDismiss()
}
accountViewModel.follow(author)
onDismiss()
}
) {
Text(stringResource(R.string.follow))
}
)
Divider()
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.copy_text))
},
onClick = {
scope.launch(Dispatchers.IO) {
clipboardManager.setText(AnnotatedString(accountViewModel.decrypt(note) ?: ""))
onDismiss()
}
}
) {
Text(stringResource(R.string.copy_text))
}
)
DropdownMenuItem(
text = {
Text(stringResource(R.string.copy_user_pubkey))
},
onClick = {
scope.launch(Dispatchers.IO) {
clipboardManager.setText(AnnotatedString("nostr:${note.author?.pubkeyNpub()}"))
onDismiss()
}
}
) {
Text(stringResource(R.string.copy_user_pubkey))
}
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
clipboardManager.setText(AnnotatedString("nostr:" + note.toNEvent()))
)
DropdownMenuItem(
text = {
Text(stringResource(R.string.copy_note_id))
},
onClick = {
scope.launch(Dispatchers.IO) {
clipboardManager.setText(AnnotatedString("nostr:" + note.toNEvent()))
onDismiss()
}
}
)
DropdownMenuItem(
text = {
Text(stringResource(R.string.quick_action_share))
},
onClick = {
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(
Intent.EXTRA_TEXT,
externalLinkForNote(note)
)
putExtra(Intent.EXTRA_TITLE, actContext.getString(R.string.quick_action_share_browser_link))
}
val shareIntent = Intent.createChooser(sendIntent, appContext.getString(R.string.quick_action_share))
ContextCompat.startActivity(actContext, shareIntent, null)
onDismiss()
}
}) {
Text(stringResource(R.string.copy_note_id))
}
DropdownMenuItem(onClick = {
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(
Intent.EXTRA_TEXT,
externalLinkForNote(note)
)
putExtra(Intent.EXTRA_TITLE, actContext.getString(R.string.quick_action_share_browser_link))
}
val shareIntent = Intent.createChooser(sendIntent, appContext.getString(R.string.quick_action_share))
ContextCompat.startActivity(actContext, shareIntent, null)
onDismiss()
}) {
Text(stringResource(R.string.quick_action_share))
}
)
Divider()
DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.broadcast(note); onDismiss() } }) {
Text(stringResource(R.string.broadcast))
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.broadcast))
},
onClick = {
scope.launch(Dispatchers.IO) { accountViewModel.broadcast(note); onDismiss() }
}
)
Divider()
if (state.isPrivateBookmarkNote) {
DropdownMenuItem(
text = {
Text(stringResource(R.string.remove_from_private_bookmarks))
},
onClick = {
scope.launch(Dispatchers.IO) {
if (accountViewModel.loggedInWithExternalSigner()) {
@@ -493,11 +526,12 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
}
}
}
) {
Text(stringResource(R.string.remove_from_private_bookmarks))
}
)
} else {
DropdownMenuItem(
text = {
Text(stringResource(R.string.add_to_private_bookmarks))
},
onClick = {
scope.launch(Dispatchers.IO) {
if (accountViewModel.loggedInWithExternalSigner()) {
@@ -515,12 +549,13 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
}
}
}
) {
Text(stringResource(R.string.add_to_private_bookmarks))
}
)
}
if (state.isPublicBookmarkNote) {
DropdownMenuItem(
text = {
Text(stringResource(R.string.remove_from_public_bookmarks))
},
onClick = {
scope.launch(Dispatchers.IO) {
if (accountViewModel.loggedInWithExternalSigner()) {
@@ -541,11 +576,12 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
}
}
}
) {
Text(stringResource(R.string.remove_from_public_bookmarks))
}
)
} else {
DropdownMenuItem(
text = {
Text(stringResource(R.string.add_to_public_bookmarks))
},
onClick = {
scope.launch(Dispatchers.IO) {
if (accountViewModel.loggedInWithExternalSigner()) {
@@ -566,35 +602,48 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
}
}
}
) {
Text(stringResource(R.string.add_to_public_bookmarks))
}
)
}
Divider()
if (state.showSensitiveContent == null || state.showSensitiveContent == true) {
DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.hideSensitiveContent(); onDismiss() } }) {
Text(stringResource(R.string.content_warning_hide_all_sensitive_content))
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.content_warning_hide_all_sensitive_content))
},
onClick = { scope.launch(Dispatchers.IO) { accountViewModel.hideSensitiveContent(); onDismiss() } }
)
}
if (state.showSensitiveContent == null || state.showSensitiveContent == false) {
DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.disableContentWarnings(); onDismiss() } }) {
Text(stringResource(R.string.content_warning_show_all_sensitive_content))
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.content_warning_show_all_sensitive_content))
},
onClick = { scope.launch(Dispatchers.IO) { accountViewModel.disableContentWarnings(); onDismiss() } }
)
}
if (state.showSensitiveContent != null) {
DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.seeContentWarnings(); onDismiss() } }) {
Text(stringResource(R.string.content_warning_see_warnings))
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.content_warning_see_warnings))
},
onClick = { scope.launch(Dispatchers.IO) { accountViewModel.seeContentWarnings(); onDismiss() } }
)
}
Divider()
if (state.isLoggedUser) {
DropdownMenuItem(onClick = { scope.launch(Dispatchers.IO) { accountViewModel.delete(note); onDismiss() } }) {
Text(stringResource(R.string.request_deletion))
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.request_deletion))
},
onClick = { scope.launch(Dispatchers.IO) { accountViewModel.delete(note); onDismiss() } }
)
} else {
DropdownMenuItem(onClick = { reportDialogShowing = true }) {
Text(stringResource(R.string.block_report))
}
DropdownMenuItem(
text = {
Text(stringResource(R.string.block_report))
},
onClick = { reportDialogShowing = true }
)
}
}
@@ -6,12 +6,12 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.collectAsState
@@ -87,7 +87,7 @@ fun UserReactionsRow(
imageVector = Icons.Default.ExpandMore,
null,
modifier = Size20Modifier,
tint = MaterialTheme.colors.placeholderText
tint = MaterialTheme.colorScheme.placeholderText
)
}
@@ -5,9 +5,9 @@ import android.util.Log
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -52,9 +52,7 @@ fun UsernameDisplay(baseUser: User, weight: Modifier = Modifier, showPlayButton:
}
}
val userMetadata by baseUser.live().metadata.map {
it.user.info
}.observeAsState(baseUser.info)
val userMetadata by baseUser.live().userMetadataInfo.observeAsState(baseUser.info)
Crossfade(targetState = userMetadata, modifier = weight) {
if (it != null) {
@@ -148,7 +146,7 @@ private fun UserAndUsernameDisplay(
CreateTextWithEmoji(
text = remember { "@$bestUserName" },
tags = tags,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@@ -173,7 +171,7 @@ fun DrawPlayName(name: String) {
@Composable
fun DrawPlayNameIcon(onClick: () -> Unit) {
IconButton(onClick = onClick, modifier = StdButtonSizeModifier) {
PlayIcon(modifier = StdButtonSizeModifier, tint = MaterialTheme.colors.placeholderText)
PlayIcon(modifier = StdButtonSizeModifier, tint = MaterialTheme.colorScheme.placeholderText)
}
}
@@ -5,18 +5,19 @@ import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Done
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -53,6 +54,7 @@ import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size16dp
import com.vitorpamplona.amethyst.ui.theme.Size55dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.placeholderText
@@ -89,7 +91,7 @@ class ZapOptionstViewModel : ViewModel() {
@Composable
fun ZapCustomDialog(
onClose: () -> Unit,
onError: (text: String) -> Unit,
onError: (title: String, text: String) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
accountViewModel: AccountViewModel,
@@ -170,7 +172,7 @@ fun ZapCustomDialog(
placeholder = {
Text(
text = "100, 1000, 5000",
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true,
@@ -220,7 +222,7 @@ fun ZapCustomDialog(
placeholder = {
Text(
text = stringResource(id = R.string.custom_zaps_add_a_message_example),
color = MaterialTheme.colors.placeholderText
color = MaterialTheme.colorScheme.placeholderText
)
},
singleLine = true,
@@ -239,10 +241,9 @@ fun ZapButton(isActive: Boolean, onPost: () -> Unit) {
Button(
onClick = { onPost() },
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray
)
colors = ButtonDefaults.buttonColors(
containerColor = if (isActive) MaterialTheme.colorScheme.primary else Color.Gray
)
) {
Text(text = "⚡Zap ", color = Color.White)
}
@@ -253,7 +254,7 @@ fun ErrorMessageDialog(
title: String,
textContent: String,
buttonColors: ButtonColors = ButtonDefaults.buttonColors(),
onClickStartMessage: () -> Unit,
onClickStartMessage: (() -> Unit)? = null,
onDismiss: () -> Unit
) {
AlertDialog(
@@ -262,24 +263,28 @@ fun ErrorMessageDialog(
Text(title)
},
text = {
Text(textContent)
SelectionContainer {
Text(textContent)
}
},
buttons = {
confirmButton = {
Row(
modifier = Modifier
.padding(all = 8.dp)
.padding(vertical = 8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
TextButton(onClick = onClickStartMessage) {
Icon(
painter = painterResource(R.drawable.ic_dm),
contentDescription = null
)
Spacer(Modifier.width(8.dp))
Text(stringResource(R.string.error_dialog_talk_to_user))
onClickStartMessage?.let {
TextButton(onClick = onClickStartMessage) {
Icon(
painter = painterResource(R.drawable.ic_dm),
contentDescription = null
)
Spacer(StdHorzSpacer)
Text(stringResource(R.string.error_dialog_talk_to_user))
}
}
Button(onClick = onDismiss, colors = buttonColors) {
Button(onClick = onDismiss, colors = buttonColors, contentPadding = PaddingValues(horizontal = Size16dp)) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
@@ -287,7 +292,7 @@ fun ErrorMessageDialog(
imageVector = Icons.Outlined.Done,
contentDescription = null
)
Spacer(Modifier.width(8.dp))
Spacer(StdHorzSpacer)
Text(stringResource(R.string.error_dialog_button_ok))
}
}
@@ -307,6 +312,7 @@ fun PayViaIntentDialog(
if (payingInvoices.size == 1) {
payViaIntent(payingInvoices.first().invoice, context, onError)
onClose()
} else {
Dialog(
onDismissRequest = onClose,
@@ -409,10 +415,9 @@ fun PayButton(isActive: Boolean, modifier: Modifier = Modifier, onPost: () -> Un
onPost()
},
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray
),
colors = ButtonDefaults.buttonColors(
containerColor = if (isActive) MaterialTheme.colorScheme.primary else Color.Gray
),
contentPadding = PaddingValues(0.dp)
) {
if (isActive) {
@@ -1,14 +1,13 @@
package com.vitorpamplona.amethyst.ui.note
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@@ -20,7 +19,6 @@ import androidx.compose.runtime.rememberCoroutineScope
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.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -182,9 +180,6 @@ fun ShowFollowingOrUnfollowingButton(
baseAuthor: User,
accountViewModel: AccountViewModel
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
var isFollowing by remember { mutableStateOf(false) }
val accountFollowsState by accountViewModel.account.userProfile().live().follows.observeAsState()
@@ -203,48 +198,30 @@ fun ShowFollowingOrUnfollowingButton(
UnfollowButton {
if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithExternalSigner()) {
scope.launch(Dispatchers.IO) {
accountViewModel.unfollow(baseAuthor)
}
accountViewModel.unfollow(baseAuthor)
} else {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.login_with_a_private_key_to_be_able_to_unfollow),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_unfollow
)
}
} else {
scope.launch(Dispatchers.IO) {
accountViewModel.unfollow(baseAuthor)
}
accountViewModel.unfollow(baseAuthor)
}
}
} else {
FollowButton {
if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithExternalSigner()) {
scope.launch(Dispatchers.IO) {
accountViewModel.account.follow(baseAuthor)
}
accountViewModel.follow(baseAuthor)
} else {
scope.launch {
Toast
.makeText(
context,
context.getString(R.string.login_with_a_private_key_to_be_able_to_follow),
Toast.LENGTH_SHORT
)
.show()
}
accountViewModel.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_follow
)
}
} else {
scope.launch(Dispatchers.IO) {
accountViewModel.follow(baseAuthor)
}
accountViewModel.follow(baseAuthor)
}
}
}
@@ -261,7 +238,7 @@ fun AboutDisplay(baseAuthor: User) {
Text(
userAboutMe,
color = MaterialTheme.colors.placeholderText,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -9,8 +9,8 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -30,21 +30,15 @@ import com.vitorpamplona.amethyst.ui.theme.Size25dp
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
import com.vitorpamplona.amethyst.ui.theme.Size55dp
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun ZapUserSetCompose(zapSetCard: ZapUserSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val defaultBackgroundColor = MaterialTheme.colors.background
val defaultBackgroundColor = MaterialTheme.colorScheme.background
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
val newItemColor = MaterialTheme.colors.newItemBackgroundColor
val newItemColor = MaterialTheme.colorScheme.newItemBackgroundColor
LaunchedEffect(key1 = zapSetCard.createdAt()) {
launch(Dispatchers.IO) {
val isNew = zapSetCard.createdAt > accountViewModel.account.loadLastRead(routeForLastRead)
accountViewModel.account.markAsRead(routeForLastRead, zapSetCard.createdAt)
accountViewModel.loadAndMarkAsRead(routeForLastRead, zapSetCard.createdAt) { isNew ->
val newBackgroundColor = if (isNew) {
newItemColor.compositeOver(defaultBackgroundColor)
} else {
@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -34,13 +34,13 @@ fun QrCodeDrawer(contents: String, modifier: Modifier = Modifier) {
createQrCode(contents = contents)
}
val foregroundColor = MaterialTheme.colors.onSurface
val foregroundColor = MaterialTheme.colorScheme.onSurface
Box(
modifier = modifier
.defaultMinSize(48.dp, 48.dp)
.aspectRatio(1f)
.background(MaterialTheme.colors.background)
.background(MaterialTheme.colorScheme.background)
) {
Canvas(modifier = Modifier.fillMaxSize()) {
// Calculate the height and width of each column/row
@@ -12,11 +12,11 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -41,7 +41,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.quartz.events.toImmutableListOfLists
@Composable
fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
fun ShowQRDialog(user: User, loadProfilePicture: Boolean, onScan: (String) -> Unit, onClose: () -> Unit) {
var presenting by remember { mutableStateOf(true) }
Dialog(
@@ -51,7 +51,7 @@ fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
Surface(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.background(MaterialTheme.colorScheme.background)
.fillMaxSize()
) {
Row(
@@ -81,8 +81,9 @@ fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
.width(100.dp)
.height(100.dp)
.clip(shape = CircleShape)
.border(3.dp, MaterialTheme.colors.background, CircleShape)
.background(MaterialTheme.colors.background)
.border(3.dp, MaterialTheme.colorScheme.background, CircleShape)
.background(MaterialTheme.colorScheme.background),
loadProfilePicture = loadProfilePicture
)
}
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().padding(top = 5.dp)) {
@@ -118,7 +119,7 @@ fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
.height(50.dp),
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = stringResource(R.string.scan_qr))
@@ -13,10 +13,9 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.pullrefresh.PullRefreshIndicator
import androidx.compose.material3.pullrefresh.pullRefresh
import androidx.compose.material3.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
@@ -36,7 +35,6 @@ import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.note.ZapUserSetCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun RefresheableCardView(
viewModel: CardFeedViewModel,
@@ -8,8 +8,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.material3.Divider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -18,7 +18,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.ui.note.ChatroomHeaderCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.events.ChatroomKeyable
import kotlin.time.ExperimentalTime
import kotlin.time.measureTimedValue
@@ -83,24 +82,9 @@ private fun FeedLoaded(
LaunchedEffect(key1 = markAsRead.value) {
if (markAsRead.value) {
for (note in state.feed.value) {
note.event?.let { noteEvent ->
val channelHex = note.channelHex()
val route = if (channelHex != null) {
"Channel/$channelHex"
} else if (note.event is ChatroomKeyable) {
val withKey = (note.event as ChatroomKeyable).chatroomKey(accountViewModel.userProfile().pubkeyHex)
"Room/${withKey.hashCode()}"
} else {
null
}
route?.let {
accountViewModel.account.markAsRead(route, noteEvent.createdAt())
}
}
accountViewModel.markAllAsRead(state.feed.value) {
markAsRead.value = false
}
markAsRead.value = false
}
}
@@ -16,13 +16,12 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Button
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.pullrefresh.PullRefreshIndicator
import androidx.compose.material3.pullrefresh.pullRefresh
import androidx.compose.material3.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -54,7 +53,6 @@ fun RefresheableFeedView(
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun RefresheableView(
viewModel: InvalidatableViewModel,

Some files were not shown because too many files have changed in this diff Show More