Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ jobs:
- name: Build with Gradle
env:
BUILD_NUMBER: ${{github.run_number}}
BUILD_VERSION: ${{github.ref_name}}
BUILD_VERSION: "1.6.0-beta01"
run: ./gradlew :app:assembleRelease :app:bundleRelease :app:check :app:lint --stacktrace

- name: Publish to Google Play
if: startsWith(github.ref, 'refs/tags/')
# if: startsWith(github.ref, 'refs/tags/')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore tag gate for Play publish

Because this workflow also runs for pushes to master/release/develop and for pull requests, commenting out this if makes the Google Play upload step run after every Android CI build instead of only tag builds. In PRs where SERVICE_ACCOUNT_JSON is unavailable this will fail the job, and on branch pushes with secrets it can upload non-tagged run-number builds to the internal Play track.

Useful? React with 👍 / 👎.

uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
Expand All @@ -42,6 +45,8 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
Expand All @@ -59,6 +64,7 @@ import dev.dimension.flare.ui.component.FlareDividerDefaults
import dev.dimension.flare.ui.component.FlareScaffold
import dev.dimension.flare.ui.component.LocalBottomBarHeight
import dev.dimension.flare.ui.theme.screenHorizontalPadding
import kotlinx.coroutines.flow.distinctUntilChanged

@OptIn(ExperimentalLayoutApi::class)
@Composable
Expand Down Expand Up @@ -134,6 +140,20 @@ internal fun <Message : Any> AgentChatScaffold(
leadingContentItemCount: Int = 0,
leadingContent: LazyListScope.() -> Unit = {},
) {
val textState = rememberTextFieldState(input)
val currentOnInputChange by rememberUpdatedState(onInputChange)

LaunchedEffect(input) {
if (textState.text.toString() != input) {
textState.setTextAndPlaceCursorAtEnd(input)
}
}
LaunchedEffect(textState) {
snapshotFlow { textState.text.toString() }
.distinctUntilChanged()
.collect(currentOnInputChange)
}

val bottomBarHeight =
if (reserveBottomBarHeight) {
LocalBottomBarHeight.current
Expand All @@ -156,12 +176,11 @@ internal fun <Message : Any> AgentChatScaffold(
thickness = FlareDividerDefaults.thickness,
)
AgentChatInput(
value = input,
state = textState,
enabled = !isRunning,
canSend = canSend,
placeholder = inputPlaceholder,
sendContentDescription = sendContentDescription,
onValueChange = onInputChange,
onSend = onSend,
modifier =
Modifier
Expand Down Expand Up @@ -337,34 +356,28 @@ internal fun AgentChatMessageBubble(

@Composable
internal fun AgentChatInput(
value: String,
state: TextFieldState,
enabled: Boolean,
canSend: Boolean,
placeholder: String,
sendContentDescription: String,
onValueChange: (String) -> Unit,
onSend: () -> Unit,
modifier: Modifier = Modifier,
) {
OutlinedTextField(
value = value,
onValueChange = onValueChange,
state = state,
modifier = modifier.fillMaxWidth(),
enabled = enabled,
minLines = 1,
maxLines = 4,
lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 4),
placeholder = {
Text(text = placeholder)
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send),
keyboardActions =
KeyboardActions(
onSend = {
if (canSend) {
onSend()
}
},
),
onKeyboardAction = {
if (canSend) {
onSend()
}
},
trailingIcon = {
IconButton(
onClick = onSend,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,7 @@ internal fun EntryProviderScope<NavKey>.statusEntryBuilder(
}

entry<Route.Status.Insight>(
metadata = BottomSheetSceneStrategy.bottomSheet(
expandFully = true,
)
metadata = BottomSheetSceneStrategy.bottomSheet()
) { args ->
StatusInsightSheet(
accountType = args.accountType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
package dev.dimension.flare.ui.screen.status.action

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imeNestedScroll
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
Expand All @@ -23,16 +34,19 @@ import dev.dimension.flare.feature.agent.common.AgentTrace
import dev.dimension.flare.feature.agent.presenter.status.StatusInsightPresenter
import dev.dimension.flare.model.AccountType
import dev.dimension.flare.model.MicroBlogKey
import dev.dimension.flare.ui.component.FlareTopAppBar
import dev.dimension.flare.ui.component.LocalTimelineAppearance
import dev.dimension.flare.ui.component.agent.AgentChatScaffold
import dev.dimension.flare.ui.component.agent.AgentChatCurrentTrace
import dev.dimension.flare.ui.component.agent.AgentChatError
import dev.dimension.flare.ui.component.agent.AgentChatInput
import dev.dimension.flare.ui.component.agent.AgentChatMessageBubble
import dev.dimension.flare.ui.component.status.CommonStatusComponent
import dev.dimension.flare.ui.model.UiTimelineV2
import dev.dimension.flare.ui.presenter.invoke
import dev.dimension.flare.ui.theme.screenHorizontalPadding
import kotlinx.coroutines.flow.distinctUntilChanged
import moe.tlaster.precompose.molecule.producePresenter

@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun StatusInsightSheet(
accountType: AccountType,
Expand All @@ -48,40 +62,92 @@ internal fun StatusInsightSheet(
}.invoke()
}

val title = stringResource(id = R.string.status_insight_title)

AgentChatScaffold(
messages = state.messages,
input = state.input,
isRunning = state.isRunning,
canSend = state.canSend,
error = state.error,
runningTrace = state.currentTrace?.label() ?: stringResource(id = R.string.status_insight_analyzing),
inputPlaceholder = stringResource(id = R.string.status_insight_input_placeholder),
sendContentDescription = stringResource(id = R.string.status_insight_send),
messageText = StatusInsightPresenter.Message::text,
isUserMessage = { it is StatusInsightPresenter.Message.User },
onInputChange = state::setInput,
onSend = state::sendMessage,
modifier = modifier,
topBar = {
FlareTopAppBar(
title = {
Text(text = title)
},
windowInsets = WindowInsets(0),
)
},
reserveBottomBarHeight = false,
leadingContentItemCount = if (state.post != null) 1 else 0,
leadingContent = {
val textState = rememberTextFieldState(state.input)
val currentOnInputChange by rememberUpdatedState(state::setInput)

LaunchedEffect(state.input) {
if (textState.text.toString() != state.input) {
textState.setTextAndPlaceCursorAtEnd(state.input)
}
}
LaunchedEffect(textState) {
snapshotFlow { textState.text.toString() }
.distinctUntilChanged()
.collect(currentOnInputChange)
}

val listState = rememberLazyListState()
val itemCount =
state.messages.size +
(if (state.post != null) 1 else 0) +
(if (state.isRunning) 1 else 0) +
(if (state.error != null) 1 else 0)

LaunchedEffect(itemCount) {
if (itemCount > 0) {
listState.animateScrollToItem(itemCount - 1)
}
}

Column(
modifier = modifier.fillMaxWidth(),
) {
LazyColumn(
modifier =
Modifier
.weight(1f, fill = false)
.fillMaxWidth()
.imeNestedScroll()
.padding(horizontal = screenHorizontalPadding),
state = listState,
contentPadding = PaddingValues(vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
state.post?.let { post ->
item {
StatusInsightPostPreview(post = post)
}
}
},
)

items(state.messages) { message ->
AgentChatMessageBubble(
text = message.text,
isUser = message is StatusInsightPresenter.Message.User,
)
}

if (state.isRunning) {
item {
AgentChatCurrentTrace(
trace = state.currentTrace?.label() ?: stringResource(id = R.string.status_insight_analyzing),
)
}
}

state.error?.let { throwable ->
item {
AgentChatError(
text = throwable.message ?: stringResource(id = R.string.status_insight_error),
)
}
}
}
AgentChatInput(
state = textState,
enabled = !state.isRunning,
canSend = state.canSend,
placeholder = stringResource(id = R.string.status_insight_input_placeholder),
sendContentDescription = stringResource(id = R.string.status_insight_send),
onSend = state::sendMessage,
modifier =
Modifier
.imePadding()
.padding(
horizontal = screenHorizontalPadding,
vertical = 8.dp,
),
)
}
}

@Composable
Expand Down
4 changes: 2 additions & 2 deletions fdroid.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
versionName=1.5.1
versionCode=1510
versionName=1.6.0
versionCode=1600
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx16g -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=true
Expand Down
Loading
Loading