1+ package xyz.flipchat.app.features.chat.description
2+
3+ import android.os.Parcelable
4+ import androidx.activity.compose.BackHandler
5+ import androidx.compose.foundation.layout.Arrangement
6+ import androidx.compose.foundation.layout.Box
7+ import androidx.compose.foundation.layout.Column
8+ import androidx.compose.foundation.layout.PaddingValues
9+ import androidx.compose.foundation.layout.fillMaxSize
10+ import androidx.compose.foundation.layout.fillMaxWidth
11+ import androidx.compose.foundation.layout.imePadding
12+ import androidx.compose.foundation.layout.navigationBarsPadding
13+ import androidx.compose.foundation.layout.padding
14+ import androidx.compose.foundation.text.KeyboardOptions
15+ import androidx.compose.runtime.Composable
16+ import androidx.compose.runtime.LaunchedEffect
17+ import androidx.compose.runtime.collectAsState
18+ import androidx.compose.runtime.getValue
19+ import androidx.compose.runtime.remember
20+ import androidx.compose.runtime.rememberCoroutineScope
21+ import androidx.compose.ui.Alignment
22+ import androidx.compose.ui.Modifier
23+ import androidx.compose.ui.focus.FocusRequester
24+ import androidx.compose.ui.focus.focusRequester
25+ import androidx.compose.ui.graphics.Color
26+ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
27+ import androidx.compose.ui.res.stringResource
28+ import androidx.compose.ui.text.input.KeyboardCapitalization
29+ import androidx.compose.ui.text.input.KeyboardType
30+ import androidx.compose.ui.unit.dp
31+ import cafe.adriel.voyager.core.screen.Screen
32+ import cafe.adriel.voyager.core.screen.ScreenKey
33+ import cafe.adriel.voyager.core.screen.uniqueScreenKey
34+ import cafe.adriel.voyager.hilt.getViewModel
35+ import com.getcode.model.ID
36+ import com.getcode.navigation.core.LocalCodeNavigator
37+ import com.getcode.theme.CodeTheme
38+ import com.getcode.theme.inputColors
39+ import com.getcode.ui.components.AppBarWithTitle
40+ import com.getcode.ui.components.TextInput
41+ import com.getcode.ui.components.keyboardAsState
42+ import com.getcode.ui.core.rememberAnimationScale
43+ import com.getcode.ui.core.scaled
44+ import com.getcode.ui.theme.ButtonState
45+ import com.getcode.ui.theme.CodeButton
46+ import com.getcode.ui.theme.CodeScaffold
47+ import kotlinx.coroutines.delay
48+ import kotlinx.coroutines.flow.filterIsInstance
49+ import kotlinx.coroutines.flow.launchIn
50+ import kotlinx.coroutines.flow.onEach
51+ import kotlinx.coroutines.launch
52+ import kotlinx.parcelize.IgnoredOnParcel
53+ import kotlinx.parcelize.Parcelize
54+ import xyz.flipchat.app.R
55+ import kotlin.time.Duration.Companion.seconds
56+
57+ @Parcelize
58+ data class RoomDescriptionScreen (val roomId : ID , val existingDescription : String ) : Screen, Parcelable {
59+
60+ @IgnoredOnParcel
61+ override val key: ScreenKey = uniqueScreenKey
62+
63+ @Composable
64+ override fun Content () {
65+ val navigator = LocalCodeNavigator .current
66+ val keyboardIsVisible by keyboardAsState()
67+ val keyboard = LocalSoftwareKeyboardController .current
68+ val scope = rememberCoroutineScope()
69+ val animationScale by rememberAnimationScale()
70+ val goBack = {
71+ scope.launch {
72+ if (keyboardIsVisible) {
73+ keyboard?.hide()
74+ delay(500 .scaled(animationScale))
75+ }
76+ navigator.pop()
77+ }
78+ }
79+ BackHandler {
80+ goBack()
81+ }
82+
83+ Column (
84+ modifier = Modifier
85+ .fillMaxSize()
86+ ) {
87+ AppBarWithTitle (
88+ backButton = true ,
89+ title = stringResource(R .string.action_changeRoomDescription),
90+ onBackIconClicked = { goBack() },
91+ )
92+
93+ val viewModel = getViewModel<RoomDescriptionScreenViewModel >()
94+
95+ LaunchedEffect (viewModel, roomId, existingDescription) {
96+ viewModel.dispatchEvent(
97+ RoomDescriptionScreenViewModel .Event .OnNewRequest (
98+ roomId,
99+ existingDescription
100+ )
101+ )
102+ }
103+
104+ LaunchedEffect (viewModel) {
105+ viewModel.eventFlow
106+ .filterIsInstance<RoomDescriptionScreenViewModel .Event .OnSuccess >()
107+ .onEach {
108+ delay(2 .seconds)
109+ navigator.pop()
110+ }
111+ .launchIn(this )
112+ }
113+
114+ val state by viewModel.stateFlow.collectAsState()
115+ RoomDescriptionScreenContent (
116+ state = state,
117+ dispatch = viewModel::dispatchEvent
118+ )
119+ }
120+ }
121+ }
122+
123+ @Composable
124+ private fun RoomDescriptionScreenContent (
125+ state : RoomDescriptionScreenViewModel .State ,
126+ dispatch : (RoomDescriptionScreenViewModel .Event ) -> Unit ,
127+ ) {
128+ val keyboard = LocalSoftwareKeyboardController .current
129+ val focusRequester = remember { FocusRequester () }
130+
131+ CodeScaffold (
132+ modifier = Modifier
133+ .fillMaxSize()
134+ .imePadding(),
135+ bottomBar = {
136+ Box (
137+ modifier = Modifier
138+ .fillMaxWidth()
139+ .navigationBarsPadding()
140+ .padding(bottom = CodeTheme .dimens.grid.x3),
141+ ) {
142+ CodeButton (
143+ enabled = state.canCheck || state.previousRoomDescription.isNotEmpty(), // allow resets
144+ modifier = Modifier
145+ .fillMaxWidth()
146+ .padding(horizontal = CodeTheme .dimens.inset),
147+ buttonState = ButtonState .Filled ,
148+ text = stringResource(R .string.action_save),
149+ isLoading = state.update.loading,
150+ isSuccess = state.update.success
151+ ) {
152+ keyboard?.hide()
153+ dispatch(RoomDescriptionScreenViewModel .Event .UpdateDescription )
154+ focusRequester.freeFocus()
155+ }
156+ }
157+ }
158+ ) { padding ->
159+ Box (
160+ modifier = Modifier
161+ .fillMaxSize()
162+ .padding(padding)
163+ ) {
164+ Column (
165+ modifier = Modifier
166+ .fillMaxWidth()
167+ .padding(top = CodeTheme .dimens.inset)
168+ .align(Alignment .TopStart )
169+ .padding(horizontal = CodeTheme .dimens.inset),
170+ verticalArrangement = Arrangement .spacedBy(CodeTheme .dimens.inset)
171+ ) {
172+ TextInput (
173+ modifier = Modifier
174+ .fillMaxWidth()
175+ .focusRequester(focusRequester),
176+ state = state.textFieldState,
177+ colors = inputColors(backgroundColor = Color .Transparent ),
178+ keyboardOptions = KeyboardOptions (
179+ keyboardType = KeyboardType .Text ,
180+ capitalization = KeyboardCapitalization .Sentences
181+ ),
182+ contentPadding = PaddingValues (CodeTheme .dimens.grid.x3),
183+ textFieldAlignment = Alignment .TopStart ,
184+ minHeight = 120 .dp,
185+ maxLines = 6 ,
186+ isError = state.textFieldState.text.length > state.maxLimit,
187+ placeholder = stringResource(R .string.subtitle_roomDescription),
188+ )
189+ }
190+ }
191+
192+ LaunchedEffect (focusRequester) {
193+ focusRequester.requestFocus()
194+ }
195+ }
196+ }
0 commit comments