mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-05 22:34:38 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c381f64f9 | ||
|
|
5e3703e849 | ||
|
|
acbd41ce61 | ||
|
|
056d00b73b |
+2
-2
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "com.vitorpamplona.amethyst"
|
||||
minSdk 26
|
||||
targetSdk 33
|
||||
versionCode 182
|
||||
versionName "0.52.2"
|
||||
versionCode 183
|
||||
versionName "0.52.3"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
@@ -63,7 +63,6 @@ import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
||||
@@ -78,7 +77,10 @@ import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, account: Account, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val account = remember(accountState) { accountState?.account } ?: return
|
||||
|
||||
val postViewModel: NewPostViewModel = viewModel()
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -17,18 +17,17 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Composable
|
||||
fun NewNoteButton(account: Account, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
fun NewNoteButton(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
var wantsToPost by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
if (wantsToPost) {
|
||||
NewPostView({ wantsToPost = false }, account = account, accountViewModel = accountViewModel, nav = nav)
|
||||
NewPostView({ wantsToPost = false }, accountViewModel = accountViewModel, nav = nav)
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
|
||||
@@ -83,7 +83,6 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Channel
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.NoteState
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.model.AudioTrackEvent
|
||||
import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
|
||||
@@ -151,9 +150,9 @@ fun NoteCompose(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
println("AARender NoteCompose")
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val account = remember(accountState) { accountState?.account } ?: return
|
||||
val loggedIn = remember(accountState) { accountState?.account?.userProfile() } ?: return
|
||||
|
||||
val noteState by baseNote.live().metadata.observeAsState()
|
||||
val note = remember(noteState) { noteState?.note } ?: return
|
||||
@@ -161,13 +160,11 @@ fun NoteCompose(
|
||||
val noteReportsState by baseNote.live().reports.observeAsState()
|
||||
val noteForReports = remember(noteReportsState) { noteReportsState?.note } ?: return
|
||||
|
||||
val noteEvent = remember(noteState) { note.event }
|
||||
val baseChannel = remember(noteState) { note.channel() }
|
||||
val isSensitive = remember(noteState) { note.event?.isSensitive() ?: false }
|
||||
|
||||
var popupExpanded by remember { mutableStateOf(false) }
|
||||
if (note.event == null) {
|
||||
var popupExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
if (noteEvent == null) {
|
||||
BlankNote(
|
||||
remember {
|
||||
modifier.combinedClickable(
|
||||
@@ -178,13 +175,11 @@ fun NoteCompose(
|
||||
isBoostedNote
|
||||
)
|
||||
|
||||
note.let {
|
||||
NoteQuickActionMenu(it, popupExpanded, { popupExpanded = false }, accountViewModel)
|
||||
}
|
||||
NoteQuickActionMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel)
|
||||
} else if (account.isHidden(noteForReports.author!!) || (isSensitive && account.showSensitiveContent == false)) {
|
||||
// Does nothing
|
||||
} else {
|
||||
var showHiddenNote by remember { mutableStateOf(false) }
|
||||
var showReportedNote by remember { mutableStateOf(false) }
|
||||
var isAcceptableAndCanPreview by remember { mutableStateOf(Pair(true, true)) }
|
||||
|
||||
LaunchedEffect(key1 = noteReportsState, key2 = accountState) {
|
||||
@@ -203,192 +198,236 @@ fun NoteCompose(
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAcceptableAndCanPreview.first && !showHiddenNote) {
|
||||
if (!isAcceptableAndCanPreview.first && !showReportedNote) {
|
||||
HiddenNote(
|
||||
account.getRelevantReports(noteForReports),
|
||||
account.userProfile(),
|
||||
modifier,
|
||||
isBoostedNote,
|
||||
nav,
|
||||
onClick = { showHiddenNote = true }
|
||||
onClick = { showReportedNote = true }
|
||||
)
|
||||
} else if ((noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) && baseChannel != null) {
|
||||
ChannelHeader(baseChannel = baseChannel, account = account, nav = nav)
|
||||
} else if (noteEvent is BadgeDefinitionEvent) {
|
||||
BadgeDisplay(baseNote = note)
|
||||
} else if (noteEvent is FileHeaderEvent) {
|
||||
FileHeaderDisplay(note)
|
||||
} else if (noteEvent is FileStorageHeaderEvent) {
|
||||
FileStorageHeaderDisplay(note)
|
||||
} else {
|
||||
var isNew by remember { mutableStateOf<Boolean>(false) }
|
||||
NormalNote(
|
||||
baseNote,
|
||||
routeForLastRead,
|
||||
modifier,
|
||||
isBoostedNote,
|
||||
isQuotedNote,
|
||||
unPackReply,
|
||||
makeItShort,
|
||||
addMarginTop,
|
||||
isAcceptableAndCanPreview.second,
|
||||
parentBackgroundColor,
|
||||
accountViewModel,
|
||||
nav
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun NormalNote(
|
||||
baseNote: Note,
|
||||
routeForLastRead: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
isBoostedNote: Boolean = false,
|
||||
isQuotedNote: Boolean = false,
|
||||
unPackReply: Boolean = true,
|
||||
makeItShort: Boolean = false,
|
||||
addMarginTop: Boolean = true,
|
||||
canPreview: Boolean = true,
|
||||
parentBackgroundColor: Color? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
println("AARender NormalNote")
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val account = remember(accountState) { accountState?.account } ?: return
|
||||
val loggedIn = remember(accountState) { accountState?.account?.userProfile() } ?: return
|
||||
|
||||
LaunchedEffect(key1 = routeForLastRead) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
routeForLastRead?.let {
|
||||
val lastTime = NotificationCache.load(it)
|
||||
val noteEvent = remember { baseNote.event }
|
||||
val channelHex = remember { baseNote.channelHex() }
|
||||
|
||||
val createdAt = note.createdAt()
|
||||
if (createdAt != null) {
|
||||
NotificationCache.markAsRead(it, createdAt)
|
||||
var popupExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
val newIsNew = createdAt > lastTime
|
||||
if (newIsNew != isNew) {
|
||||
isNew = newIsNew
|
||||
}
|
||||
if ((noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) && channelHex != null) {
|
||||
ChannelHeader(channelHex = channelHex, account = account, nav = nav)
|
||||
} else if (noteEvent is BadgeDefinitionEvent) {
|
||||
BadgeDisplay(baseNote = baseNote)
|
||||
} else if (noteEvent is FileHeaderEvent) {
|
||||
FileHeaderDisplay(baseNote)
|
||||
} else if (noteEvent is FileStorageHeaderEvent) {
|
||||
FileStorageHeaderDisplay(baseNote)
|
||||
} else {
|
||||
var isNew by remember { mutableStateOf<Boolean>(false) }
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(key1 = routeForLastRead) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
routeForLastRead?.let {
|
||||
val lastTime = NotificationCache.load(it)
|
||||
|
||||
val createdAt = baseNote.createdAt()
|
||||
if (createdAt != null) {
|
||||
NotificationCache.markAsRead(it, createdAt)
|
||||
|
||||
val newIsNew = createdAt > lastTime
|
||||
if (newIsNew != isNew) {
|
||||
isNew = newIsNew
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val primaryColor = MaterialTheme.colors.primary.copy(0.12f)
|
||||
val defaultBackgroundColor = MaterialTheme.colors.background
|
||||
val primaryColor = MaterialTheme.colors.primary.copy(0.12f)
|
||||
val defaultBackgroundColor = MaterialTheme.colors.background
|
||||
|
||||
val backgroundColor = remember(isNew, parentBackgroundColor) {
|
||||
if (isNew) {
|
||||
if (parentBackgroundColor != null) {
|
||||
primaryColor.compositeOver(parentBackgroundColor)
|
||||
} else {
|
||||
primaryColor.compositeOver(defaultBackgroundColor)
|
||||
}
|
||||
val backgroundColor = remember(isNew, parentBackgroundColor) {
|
||||
if (isNew) {
|
||||
if (parentBackgroundColor != null) {
|
||||
primaryColor.compositeOver(parentBackgroundColor)
|
||||
} else {
|
||||
parentBackgroundColor ?: defaultBackgroundColor
|
||||
primaryColor.compositeOver(defaultBackgroundColor)
|
||||
}
|
||||
} else {
|
||||
parentBackgroundColor ?: defaultBackgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
val columnModifier = remember(backgroundColor) {
|
||||
modifier
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
routeFor(note, loggedIn)?.let {
|
||||
nav(it)
|
||||
}
|
||||
val columnModifier = remember(backgroundColor) {
|
||||
modifier
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
routeFor(baseNote, loggedIn)?.let {
|
||||
nav(it)
|
||||
}
|
||||
},
|
||||
onLongClick = { popupExpanded = true }
|
||||
)
|
||||
.background(backgroundColor)
|
||||
}
|
||||
}
|
||||
},
|
||||
onLongClick = { popupExpanded = true }
|
||||
)
|
||||
.background(backgroundColor)
|
||||
}
|
||||
|
||||
Column(modifier = columnModifier) {
|
||||
Row(
|
||||
Column(modifier = columnModifier) {
|
||||
Row(
|
||||
modifier = remember {
|
||||
Modifier
|
||||
.padding(
|
||||
start = if (!isBoostedNote) 12.dp else 0.dp,
|
||||
end = if (!isBoostedNote) 12.dp else 0.dp,
|
||||
top = if (addMarginTop && !isBoostedNote) 10.dp else 0.dp
|
||||
)
|
||||
}
|
||||
) {
|
||||
if (!isBoostedNote && !isQuotedNote) {
|
||||
DrawAuthorImages(baseNote, loggedIn, nav)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = remember {
|
||||
Modifier
|
||||
.padding(
|
||||
start = if (!isBoostedNote) 12.dp else 0.dp,
|
||||
end = if (!isBoostedNote) 12.dp else 0.dp,
|
||||
top = if (addMarginTop && !isBoostedNote) 10.dp else 0.dp
|
||||
)
|
||||
.padding(start = if (!isBoostedNote && !isQuotedNote) 10.dp else 0.dp)
|
||||
}
|
||||
) {
|
||||
if (!isBoostedNote && !isQuotedNote) {
|
||||
DrawAuthorImages(baseNote, loggedIn, nav)
|
||||
FirstUserInfoRow(
|
||||
baseNote = baseNote,
|
||||
showAuthorPicture = isQuotedNote,
|
||||
account = account,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav
|
||||
)
|
||||
|
||||
if (noteEvent !is RepostEvent && !makeItShort && !isQuotedNote) {
|
||||
SecondUserInfoRow(
|
||||
baseNote,
|
||||
account,
|
||||
nav
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = remember {
|
||||
Modifier
|
||||
.padding(start = if (!isBoostedNote && !isQuotedNote) 10.dp else 0.dp)
|
||||
}
|
||||
) {
|
||||
FirstUserInfoRow(
|
||||
baseNote = baseNote,
|
||||
showAuthorPicture = isQuotedNote,
|
||||
account = account,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
|
||||
if (!makeItShort) {
|
||||
ReplyRow(
|
||||
baseNote,
|
||||
unPackReply,
|
||||
backgroundColor,
|
||||
account,
|
||||
accountViewModel,
|
||||
nav
|
||||
)
|
||||
}
|
||||
|
||||
if (noteEvent !is RepostEvent && !makeItShort && !isQuotedNote) {
|
||||
SecondUserInfoRow(
|
||||
note,
|
||||
account,
|
||||
nav
|
||||
)
|
||||
when (noteEvent) {
|
||||
is ReactionEvent -> {
|
||||
RenderReaction(baseNote, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
is RepostEvent -> {
|
||||
RenderRepost(baseNote, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
if (!makeItShort) {
|
||||
ReplyRow(
|
||||
note,
|
||||
unPackReply,
|
||||
is ReportEvent -> {
|
||||
RenderReport(baseNote)
|
||||
}
|
||||
|
||||
is LongTextNoteEvent -> {
|
||||
RenderLongFormContent(baseNote, loggedIn, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is BadgeAwardEvent -> {
|
||||
RenderBadgeAward(baseNote, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PeopleListEvent -> {
|
||||
RenderPeopleList(baseNote, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is AudioTrackEvent -> {
|
||||
RenderAudioTrack(baseNote, loggedIn, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PinListEvent -> {
|
||||
RenderPinListEvent(baseNote, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PrivateDmEvent -> {
|
||||
RenderPrivateMessage(baseNote, makeItShort, canPreview, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is HighlightEvent -> {
|
||||
RenderHighlight(baseNote, makeItShort, canPreview, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PollNoteEvent -> {
|
||||
RenderPoll(
|
||||
baseNote,
|
||||
makeItShort,
|
||||
canPreview,
|
||||
backgroundColor,
|
||||
account,
|
||||
accountViewModel,
|
||||
nav
|
||||
)
|
||||
}
|
||||
|
||||
when (noteEvent) {
|
||||
is ReactionEvent -> {
|
||||
RenderReaction(note, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is RepostEvent -> {
|
||||
RenderRepost(note, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is ReportEvent -> {
|
||||
RenderReport(note)
|
||||
}
|
||||
|
||||
is LongTextNoteEvent -> {
|
||||
RenderLongFormContent(note, loggedIn, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is BadgeAwardEvent -> {
|
||||
RenderBadgeAward(note, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PeopleListEvent -> {
|
||||
RenderPeopleList(noteState, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is AudioTrackEvent -> {
|
||||
RenderAudioTrack(note, loggedIn, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PinListEvent -> {
|
||||
RenderPinListEvent(noteState, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PrivateDmEvent -> {
|
||||
RenderPrivateMessage(note, makeItShort, isAcceptableAndCanPreview.second, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is HighlightEvent -> {
|
||||
RenderHighlight(note, makeItShort, isAcceptableAndCanPreview.second, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is PollNoteEvent -> {
|
||||
RenderPoll(
|
||||
note,
|
||||
makeItShort,
|
||||
isAcceptableAndCanPreview.second,
|
||||
backgroundColor,
|
||||
accountViewModel,
|
||||
nav
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
RenderTextEvent(
|
||||
note,
|
||||
makeItShort,
|
||||
isAcceptableAndCanPreview.second,
|
||||
backgroundColor,
|
||||
accountViewModel,
|
||||
nav
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
RenderTextEvent(
|
||||
baseNote,
|
||||
makeItShort,
|
||||
canPreview,
|
||||
backgroundColor,
|
||||
accountViewModel,
|
||||
nav
|
||||
)
|
||||
}
|
||||
|
||||
NoteQuickActionMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel)
|
||||
}
|
||||
|
||||
NoteQuickActionMenu(baseNote, popupExpanded, { popupExpanded = false }, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -581,14 +620,15 @@ private fun RenderPrivateMessage(
|
||||
val noteEvent = note.event as? PrivateDmEvent ?: return
|
||||
|
||||
val withMe = remember { noteEvent.with(accountViewModel.userProfile().pubkeyHex) }
|
||||
val tags = remember(note.event?.id()) { note.event?.tags() }
|
||||
val hashtags = remember(note.event?.id()) { note.event?.hashtags() ?: emptyList() }
|
||||
val modifier = remember(note.event?.id()) { Modifier.fillMaxWidth() }
|
||||
val isAuthorTheLoggedUser = remember(note.event?.id()) { accountViewModel.isLoggedUser(note.author) }
|
||||
|
||||
if (withMe) {
|
||||
val eventContent = remember { accountViewModel.decrypt(note) }
|
||||
|
||||
val hashtags = remember(note.event?.id()) { note.event?.hashtags() ?: emptyList() }
|
||||
val modifier = remember(note.event?.id()) { Modifier.fillMaxWidth() }
|
||||
val isAuthorTheLoggedUser = remember(note.event?.id()) { accountViewModel.isLoggedUser(note.author) }
|
||||
val tags = remember(note.event?.id()) { note.event?.tags() }
|
||||
|
||||
if (eventContent != null) {
|
||||
if (makeItShort && isAuthorTheLoggedUser) {
|
||||
Text(
|
||||
@@ -628,7 +668,7 @@ private fun RenderPrivateMessage(
|
||||
),
|
||||
canPreview = !makeItShort,
|
||||
Modifier.fillMaxWidth(),
|
||||
noteEvent.tags(),
|
||||
emptyList(),
|
||||
backgroundColor,
|
||||
accountViewModel,
|
||||
nav
|
||||
@@ -647,16 +687,14 @@ private fun RenderPrivateMessage(
|
||||
|
||||
@Composable
|
||||
fun RenderPeopleList(
|
||||
noteState: NoteState?,
|
||||
baseNote: Note,
|
||||
backgroundColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
DisplayPeopleList(noteState, backgroundColor, accountViewModel, nav)
|
||||
DisplayPeopleList(baseNote, backgroundColor, accountViewModel, nav)
|
||||
|
||||
noteState?.note?.let {
|
||||
ReactionsRow(it, accountViewModel, nav)
|
||||
}
|
||||
ReactionsRow(baseNote, accountViewModel, nav)
|
||||
|
||||
Divider(
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
@@ -666,13 +704,12 @@ fun RenderPeopleList(
|
||||
|
||||
@Composable
|
||||
fun DisplayPeopleList(
|
||||
noteState: NoteState?,
|
||||
baseNote: Note,
|
||||
backgroundColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val note = remember(noteState) { noteState?.note } ?: return
|
||||
val noteEvent = note.event as? PeopleListEvent ?: return
|
||||
val noteEvent = baseNote.event as? PeopleListEvent ?: return
|
||||
|
||||
var members by remember { mutableStateOf<List<User>>(listOf()) }
|
||||
|
||||
@@ -698,7 +735,7 @@ fun DisplayPeopleList(
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
LaunchedEffect(key1 = noteState) {
|
||||
LaunchedEffect(Unit) {
|
||||
withContext(Dispatchers.IO) {
|
||||
members = noteEvent.bookmarkedPeople().mapNotNull { hex ->
|
||||
LocalCache.checkGetOrCreateUser(hex)
|
||||
@@ -875,16 +912,16 @@ private fun RenderRepost(
|
||||
|
||||
@Composable
|
||||
private fun RenderPinListEvent(
|
||||
noteState: NoteState?,
|
||||
baseNote: Note,
|
||||
backgroundColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val noteEvent = noteState?.note?.event as? PinListEvent ?: return
|
||||
val noteEvent = baseNote.event as? PinListEvent ?: return
|
||||
|
||||
PinListHeader(noteState, backgroundColor, accountViewModel, nav)
|
||||
PinListHeader(baseNote, backgroundColor, accountViewModel, nav)
|
||||
|
||||
ReactionsRow(noteState?.note, accountViewModel, nav)
|
||||
ReactionsRow(baseNote, accountViewModel, nav)
|
||||
|
||||
Divider(
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
@@ -894,15 +931,14 @@ private fun RenderPinListEvent(
|
||||
|
||||
@Composable
|
||||
fun PinListHeader(
|
||||
noteState: NoteState?,
|
||||
baseNote: Note,
|
||||
backgroundColor: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val note = remember(noteState) { noteState?.note } ?: return
|
||||
val noteEvent = note.event as? PinListEvent ?: return
|
||||
val noteEvent = baseNote.event as? PinListEvent ?: return
|
||||
|
||||
var pins by remember { mutableStateOf<List<String>>(noteEvent.pins()) }
|
||||
var pins by remember { mutableStateOf(noteEvent.pins()) }
|
||||
|
||||
var expanded by remember {
|
||||
mutableStateOf(false)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.note
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
@@ -20,6 +21,7 @@ import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.ProgressIndicatorDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Bolt
|
||||
@@ -56,6 +58,7 @@ import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
@@ -68,9 +71,6 @@ import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val account = remember(accountState) { accountState?.account } ?: return
|
||||
|
||||
val grayTint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||
|
||||
var wantsToReplyTo by remember {
|
||||
@@ -82,17 +82,17 @@ fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel, nav: (Strin
|
||||
}
|
||||
|
||||
if (wantsToReplyTo != null) {
|
||||
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, account, accountViewModel, nav)
|
||||
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, accountViewModel, nav)
|
||||
}
|
||||
|
||||
if (wantsToQuote != null) {
|
||||
NewPostView({ wantsToQuote = null }, null, wantsToQuote, account, accountViewModel, nav)
|
||||
NewPostView({ wantsToQuote = null }, null, wantsToQuote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(verticalAlignment = CenterVertically) {
|
||||
Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) {
|
||||
Row(verticalAlignment = CenterVertically, modifier = remember { Modifier.weight(1f) }) {
|
||||
ReplyReaction(baseNote, grayTint, accountViewModel) {
|
||||
wantsToReplyTo = baseNote
|
||||
}
|
||||
@@ -123,17 +123,6 @@ fun ReplyReaction(
|
||||
iconSize: Dp = 20.dp,
|
||||
onPress: () -> Unit
|
||||
) {
|
||||
val repliesState by baseNote.live().replies.observeAsState()
|
||||
val replies = remember(repliesState) { repliesState?.note?.replies } ?: emptySet()
|
||||
|
||||
val isWriteable = remember { accountViewModel.isWriteable() }
|
||||
|
||||
val replyCount by remember(repliesState) {
|
||||
derivedStateOf {
|
||||
showCount(replies.size)
|
||||
}
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -148,7 +137,7 @@ fun ReplyReaction(
|
||||
IconButton(
|
||||
modifier = iconButtonModifier,
|
||||
onClick = {
|
||||
if (isWriteable) {
|
||||
if (accountViewModel.isWriteable()) {
|
||||
onPress()
|
||||
} else {
|
||||
scope.launch {
|
||||
@@ -170,14 +159,26 @@ fun ReplyReaction(
|
||||
}
|
||||
|
||||
if (showCounter) {
|
||||
Text(
|
||||
" $replyCount",
|
||||
fontSize = 14.sp,
|
||||
color = grayTint
|
||||
)
|
||||
ReplyCounter(baseNote, grayTint)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReplyCounter(baseNote: Note, textColor: Color) {
|
||||
val repliesState by baseNote.live().replies.observeAsState()
|
||||
val replyCount by remember(repliesState) {
|
||||
derivedStateOf {
|
||||
" " + showCount(repliesState?.note?.replies?.size)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = replyCount,
|
||||
fontSize = 14.sp,
|
||||
color = textColor
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BoostReaction(
|
||||
baseNote: Note,
|
||||
@@ -186,29 +187,6 @@ fun BoostReaction(
|
||||
iconSize: Dp = 20.dp,
|
||||
onQuotePress: () -> Unit
|
||||
) {
|
||||
val boostsState by baseNote.live().boosts.observeAsState()
|
||||
val boostedNote = remember(boostsState) { boostsState?.note } ?: return
|
||||
|
||||
val hasBoosted by remember(boostsState) {
|
||||
derivedStateOf {
|
||||
accountViewModel.hasBoosted(baseNote)
|
||||
}
|
||||
}
|
||||
|
||||
val wasBoostedByLoggedIn by remember(boostsState) {
|
||||
derivedStateOf {
|
||||
boostedNote.isBoostedBy(accountViewModel.userProfile())
|
||||
}
|
||||
}
|
||||
|
||||
val isWriteable = remember { accountViewModel.isWriteable() }
|
||||
|
||||
val boostCount by remember(boostsState) {
|
||||
derivedStateOf {
|
||||
showCount(boostedNote.boosts.size)
|
||||
}
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -218,15 +196,11 @@ fun BoostReaction(
|
||||
Modifier.size(iconSize)
|
||||
}
|
||||
|
||||
val iconModifier = remember {
|
||||
Modifier.size(iconSize)
|
||||
}
|
||||
|
||||
IconButton(
|
||||
modifier = iconButtonModifier,
|
||||
onClick = {
|
||||
if (isWriteable) {
|
||||
if (hasBoosted) {
|
||||
if (accountViewModel.isWriteable()) {
|
||||
if (accountViewModel.hasBoosted(baseNote)) {
|
||||
accountViewModel.deleteBoostsTo(baseNote)
|
||||
} else {
|
||||
wantsToBoost = true
|
||||
@@ -256,16 +230,45 @@ fun BoostReaction(
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_retweeted),
|
||||
null,
|
||||
modifier = iconModifier,
|
||||
tint = if (wasBoostedByLoggedIn) Color.Unspecified else grayTint
|
||||
)
|
||||
BoostIcon(baseNote, iconSize, grayTint, accountViewModel.userProfile())
|
||||
}
|
||||
|
||||
BoostText(baseNote, grayTint)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BoostIcon(baseNote: Note, iconSize: Dp = 20.dp, grayTint: Color, loggedIn: User) {
|
||||
val boostsState by baseNote.live().boosts.observeAsState()
|
||||
|
||||
val iconTint by remember(boostsState) {
|
||||
derivedStateOf {
|
||||
if (boostsState?.note?.isBoostedBy(loggedIn) == true) Color.Unspecified else grayTint
|
||||
}
|
||||
}
|
||||
|
||||
val iconModifier = remember {
|
||||
Modifier.size(iconSize)
|
||||
}
|
||||
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_retweeted),
|
||||
null,
|
||||
modifier = iconModifier,
|
||||
tint = iconTint
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BoostText(baseNote: Note, grayTint: Color) {
|
||||
val boostsState by baseNote.live().boosts.observeAsState()
|
||||
val boostCount by remember(boostsState) {
|
||||
derivedStateOf {
|
||||
" " + showCount(boostsState?.note?.boosts?.size)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
" $boostCount",
|
||||
boostCount,
|
||||
fontSize = 14.sp,
|
||||
color = grayTint
|
||||
)
|
||||
@@ -279,29 +282,6 @@ fun LikeReaction(
|
||||
iconSize: Dp = 20.dp,
|
||||
heartSize: Dp = 16.dp
|
||||
) {
|
||||
val reactionsState by baseNote.live().reactions.observeAsState()
|
||||
val reactedNote = remember(reactionsState) { reactionsState?.note } ?: return
|
||||
|
||||
val hasReacted by remember(reactionsState) {
|
||||
derivedStateOf {
|
||||
accountViewModel.hasReactedTo(baseNote)
|
||||
}
|
||||
}
|
||||
|
||||
val wasReactedByLoggedIn by remember(reactionsState) {
|
||||
derivedStateOf {
|
||||
reactedNote.isReactedBy(accountViewModel.userProfile())
|
||||
}
|
||||
}
|
||||
|
||||
val isWriteable = remember { accountViewModel.isWriteable() }
|
||||
|
||||
val reactionCount by remember(reactionsState) {
|
||||
derivedStateOf {
|
||||
showCount(reactedNote.reactions.size)
|
||||
}
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -309,15 +289,11 @@ fun LikeReaction(
|
||||
Modifier.size(iconSize)
|
||||
}
|
||||
|
||||
val iconModifier = remember {
|
||||
Modifier.size(heartSize)
|
||||
}
|
||||
|
||||
IconButton(
|
||||
modifier = iconButtonModifier,
|
||||
onClick = {
|
||||
if (isWriteable) {
|
||||
if (hasReacted) {
|
||||
if (accountViewModel.isWriteable()) {
|
||||
if (accountViewModel.hasReactedTo(baseNote)) {
|
||||
accountViewModel.deleteReactionTo(baseNote)
|
||||
} else {
|
||||
accountViewModel.reactTo(baseNote)
|
||||
@@ -333,25 +309,55 @@ fun LikeReaction(
|
||||
}
|
||||
}
|
||||
) {
|
||||
if (wasReactedByLoggedIn) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_liked),
|
||||
null,
|
||||
modifier = iconModifier,
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_like),
|
||||
null,
|
||||
modifier = iconModifier,
|
||||
tint = grayTint
|
||||
)
|
||||
LikeIcon(baseNote, heartSize, grayTint, accountViewModel.userProfile())
|
||||
}
|
||||
|
||||
LikeText(baseNote, grayTint)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LikeIcon(baseNote: Note, iconSize: Dp = 20.dp, grayTint: Color, loggedIn: User) {
|
||||
val reactionsState by baseNote.live().reactions.observeAsState()
|
||||
|
||||
val wasReactedByLoggedIn by remember(reactionsState) {
|
||||
derivedStateOf {
|
||||
reactionsState?.note?.isReactedBy(loggedIn) == true
|
||||
}
|
||||
}
|
||||
|
||||
val iconModifier = remember {
|
||||
Modifier.size(iconSize)
|
||||
}
|
||||
|
||||
if (wasReactedByLoggedIn) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_liked),
|
||||
null,
|
||||
modifier = iconModifier,
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_like),
|
||||
null,
|
||||
modifier = iconModifier,
|
||||
tint = grayTint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LikeText(baseNote: Note, grayTint: Color) {
|
||||
val reactionsState by baseNote.live().reactions.observeAsState()
|
||||
|
||||
val reactionsCount by remember(reactionsState) {
|
||||
derivedStateOf {
|
||||
" " + showCount(reactionsState?.note?.reactions?.size)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
" $reactionCount",
|
||||
reactionsCount,
|
||||
fontSize = 14.sp,
|
||||
color = grayTint
|
||||
)
|
||||
@@ -363,16 +369,12 @@ fun ZapReaction(
|
||||
baseNote: Note,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
textModifier: Modifier = Modifier,
|
||||
iconSize: Dp = 20.dp,
|
||||
animationSize: Dp = 14.dp
|
||||
) {
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val account = remember(accountState) { accountState?.account } ?: return
|
||||
|
||||
val zapsState by baseNote.live().zaps.observeAsState()
|
||||
val zappedNote = remember(zapsState) { zapsState?.note } ?: return
|
||||
|
||||
var wantsToZap by remember { mutableStateOf(false) }
|
||||
var wantsToChangeZapAmount by remember { mutableStateOf(false) }
|
||||
var wantsToSetCustomZap by remember { mutableStateOf(false) }
|
||||
@@ -382,32 +384,6 @@ fun ZapReaction(
|
||||
|
||||
var zappingProgress by remember { mutableStateOf(0f) }
|
||||
|
||||
var wasZappedByLoggedInUser by remember { mutableStateOf(false) }
|
||||
var zapAmountTxt by remember { mutableStateOf<String>("") }
|
||||
|
||||
LaunchedEffect(key1 = zapsState) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (!wasZappedByLoggedInUser) {
|
||||
val newWasZapped = accountViewModel.calculateIfNoteWasZappedByAccount(zappedNote)
|
||||
|
||||
if (wasZappedByLoggedInUser != newWasZapped) {
|
||||
wasZappedByLoggedInUser = newWasZapped
|
||||
}
|
||||
}
|
||||
|
||||
val newZapAmount = showAmount(account.calculateZappedAmount(zappedNote))
|
||||
if (newZapAmount != zapAmountTxt) {
|
||||
zapAmountTxt = newZapAmount
|
||||
}
|
||||
|
||||
if (wasZappedByLoggedInUser) {
|
||||
if (abs(zappingProgress - 1) < 0.001) {
|
||||
zappingProgress = 1f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = CenterVertically,
|
||||
modifier = Modifier
|
||||
@@ -498,6 +474,7 @@ fun ZapReaction(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (wantsToChangeZapAmount) {
|
||||
UpdateZapAmountDialog({ wantsToChangeZapAmount = false }, account = account)
|
||||
}
|
||||
@@ -506,28 +483,93 @@ fun ZapReaction(
|
||||
ZapCustomDialog({ wantsToSetCustomZap = false }, account = account, accountViewModel, baseNote)
|
||||
}
|
||||
|
||||
if (wasZappedByLoggedInUser) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Bolt,
|
||||
contentDescription = stringResource(R.string.zaps),
|
||||
modifier = Modifier.size(iconSize),
|
||||
tint = BitcoinOrange
|
||||
if (zappingProgress > 0.00001 && zappingProgress < 0.99999) {
|
||||
Spacer(Modifier.width(3.dp))
|
||||
|
||||
val animatedProgress = animateFloatAsState(
|
||||
targetValue = zappingProgress,
|
||||
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
|
||||
).value
|
||||
|
||||
CircularProgressIndicator(
|
||||
progress = animatedProgress,
|
||||
modifier = Modifier.size(animationSize),
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
} else {
|
||||
if (zappingProgress < 0.1 || zappingProgress > 0.99) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Bolt,
|
||||
contentDescription = stringResource(id = R.string.zaps),
|
||||
modifier = Modifier.size(iconSize),
|
||||
tint = grayTint
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.width(3.dp))
|
||||
CircularProgressIndicator(
|
||||
progress = zappingProgress,
|
||||
modifier = Modifier.size(animationSize),
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
ZapIcon(
|
||||
baseNote,
|
||||
iconSize,
|
||||
grayTint,
|
||||
accountViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ZapAmountText(baseNote, grayTint, accountViewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ZapIcon(
|
||||
baseNote: Note,
|
||||
iconSize: Dp,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel
|
||||
) {
|
||||
var wasZappedByLoggedInUser by remember { mutableStateOf(false) }
|
||||
val zapsState by baseNote.live().zaps.observeAsState()
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(key1 = zapsState) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
zapsState?.note?.let {
|
||||
if (!wasZappedByLoggedInUser) {
|
||||
val newWasZapped = accountViewModel.calculateIfNoteWasZappedByAccount(it)
|
||||
|
||||
if (wasZappedByLoggedInUser != newWasZapped) {
|
||||
wasZappedByLoggedInUser = newWasZapped
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wasZappedByLoggedInUser) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Bolt,
|
||||
contentDescription = stringResource(R.string.zaps),
|
||||
modifier = remember { Modifier.size(iconSize) },
|
||||
tint = BitcoinOrange
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Bolt,
|
||||
contentDescription = stringResource(id = R.string.zaps),
|
||||
modifier = remember { Modifier.size(iconSize) },
|
||||
tint = grayTint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ZapAmountText(
|
||||
baseNote: Note,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel
|
||||
) {
|
||||
val zapsState by baseNote.live().zaps.observeAsState()
|
||||
val zappedNote = remember(zapsState) { zapsState?.note } ?: return
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var zapAmountTxt by remember { mutableStateOf("") }
|
||||
|
||||
LaunchedEffect(key1 = zapsState) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val newZapAmount = showAmount(accountViewModel.calculateZapAmount(zappedNote))
|
||||
if (newZapAmount != zapAmountTxt) {
|
||||
zapAmountTxt = newZapAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,8 +577,7 @@ fun ZapReaction(
|
||||
Text(
|
||||
zapAmountTxt,
|
||||
fontSize = 14.sp,
|
||||
color = grayTint,
|
||||
modifier = textModifier
|
||||
color = grayTint
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -353,11 +353,11 @@ fun NoteMaster(
|
||||
) {
|
||||
Column() {
|
||||
if (noteEvent is PeopleListEvent) {
|
||||
DisplayPeopleList(noteState, MaterialTheme.colors.background, accountViewModel, nav)
|
||||
DisplayPeopleList(baseNote, MaterialTheme.colors.background, accountViewModel, nav)
|
||||
} else if (noteEvent is AudioTrackEvent) {
|
||||
AudioTrackHeader(noteEvent, note, account.userProfile(), nav)
|
||||
} else if (noteEvent is PinListEvent) {
|
||||
PinListHeader(noteState, MaterialTheme.colors.background, accountViewModel, nav)
|
||||
PinListHeader(baseNote, MaterialTheme.colors.background, accountViewModel, nav)
|
||||
} else if (noteEvent is HighlightEvent) {
|
||||
DisplayHighlight(
|
||||
noteEvent.quote(),
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.vitorpamplona.amethyst.service.model.ReportEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import java.util.Locale
|
||||
|
||||
@Immutable
|
||||
@@ -66,6 +67,10 @@ class AccountViewModel(private val account: Account) : ViewModel() {
|
||||
return account.calculateIfNoteWasZappedByAccount(zappedNote)
|
||||
}
|
||||
|
||||
fun calculateZapAmount(zappedNote: Note): BigDecimal {
|
||||
return account.calculateZappedAmount(zappedNote)
|
||||
}
|
||||
|
||||
fun zap(note: Note, amount: Long, pollOption: Int?, message: String, context: Context, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit, zapType: LnZapEvent.ZapType) {
|
||||
val lud16 = note.event?.zapAddress() ?: note.author?.info?.lud16?.trim() ?: note.author?.info?.lud06?.trim()
|
||||
|
||||
@@ -105,7 +110,7 @@ class AccountViewModel(private val account: Account) : ViewModel() {
|
||||
?: "Error parsing error message"
|
||||
)
|
||||
} else {
|
||||
onProgress(0.99f)
|
||||
onProgress(1f)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -40,6 +40,7 @@ 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
|
||||
@@ -63,6 +64,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Channel
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
||||
@@ -77,6 +79,8 @@ import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.ChatroomFeedView
|
||||
import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun ChannelScreen(
|
||||
@@ -158,7 +162,12 @@ fun ChannelScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Row(Modifier.padding(horizontal = 10.dp).animateContentSize(), verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(horizontal = 10.dp)
|
||||
.animateContentSize(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val replyingNote = replyTo.value
|
||||
if (replyingNote != null) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
@@ -182,7 +191,9 @@ fun ChannelScreen(
|
||||
Icon(
|
||||
imageVector = Icons.Default.Cancel,
|
||||
null,
|
||||
modifier = Modifier.padding(end = 5.dp).size(30.dp),
|
||||
modifier = Modifier
|
||||
.padding(end = 5.dp)
|
||||
.size(30.dp),
|
||||
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||
)
|
||||
}
|
||||
@@ -192,7 +203,9 @@ fun ChannelScreen(
|
||||
|
||||
// LAST ROW
|
||||
Row(
|
||||
modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 10.dp, top = 5.dp).fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.padding(start = 10.dp, end = 10.dp, bottom = 10.dp, top = 5.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
@@ -246,10 +259,26 @@ fun ChannelScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChannelHeader(channelHex: String, account: Account, nav: (String) -> Unit) {
|
||||
var baseChannel by remember { mutableStateOf<Channel?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(key1 = channelHex) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
baseChannel = LocalCache.checkGetOrCreateChannel(channelHex)
|
||||
}
|
||||
}
|
||||
|
||||
baseChannel?.let {
|
||||
ChannelHeader(it, account, nav)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChannelHeader(baseChannel: Channel, account: Account, nav: (String) -> Unit) {
|
||||
val channelState by baseChannel.live.observeAsState()
|
||||
val channel = channelState?.channel ?: return
|
||||
val channel = remember(channelState) { channelState?.channel } ?: return
|
||||
|
||||
val context = LocalContext.current.applicationContext
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ fun FloatingButtons(navController: NavHostController, accountViewModel: AccountV
|
||||
}
|
||||
is AccountState.LoggedIn -> {
|
||||
if (currentRoute(navController)?.substringBefore("?") == Route.Home.base) {
|
||||
NewNoteButton(state.account, accountViewModel, nav)
|
||||
NewNoteButton(accountViewModel, nav)
|
||||
}
|
||||
if (currentRoute(navController) == Route.Message.base) {
|
||||
ChannelFabColumn(state.account, nav)
|
||||
|
||||
@@ -325,9 +325,6 @@ private fun RelayBadges(baseNote: Note) {
|
||||
|
||||
@Composable
|
||||
fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||
val account = accountState?.account ?: return
|
||||
|
||||
var wantsToReplyTo by remember {
|
||||
mutableStateOf<Note?>(null)
|
||||
}
|
||||
@@ -337,11 +334,11 @@ fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, nav: (St
|
||||
}
|
||||
|
||||
if (wantsToReplyTo != null) {
|
||||
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, account, accountViewModel, nav)
|
||||
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, accountViewModel, nav)
|
||||
}
|
||||
|
||||
if (wantsToQuote != null) {
|
||||
NewPostView({ wantsToQuote = null }, null, wantsToQuote, account, accountViewModel, nav)
|
||||
NewPostView({ wantsToQuote = null }, null, wantsToQuote, accountViewModel, nav)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Reference in New Issue
Block a user