Merge branch 'frontend/infoscreen' into 'main'

feat: info popup screen implemented + some grammar fixed in other files

Closes #3

See merge request EmmaVandewalle/writand!43
This commit is contained in:
Emma Vandewalle 2024-09-05 19:20:54 +00:00
commit 09ce863d23
10 changed files with 335 additions and 12 deletions

View file

@ -69,7 +69,7 @@ dependencies {
implementation 'com.google.ar:core:1.45.0'
implementation 'androidx.navigation:navigation-runtime-ktx:2.7.7'
implementation 'androidx.navigation:navigation-compose:2.7.7'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.compose.foundation:foundation-layout-android:1.6.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'

View file

@ -1,17 +1,18 @@
package be.re.writand.domain.tos
import be.re.writand.data.local.models.TermsOfService
import be.re.writand.data.repos.tos.ITOSRepository
import javax.inject.Inject
/**
* Use-case for getting back the TOS file in a list.
* Use-case for getting back the Terms of Services.
*/
class GetTOSUseCase @Inject constructor(
private val tosRepository: ITOSRepository
) {
operator fun invoke(): List<String> {
return tosRepository.getTOS().text
operator fun invoke(): TermsOfService {
return tosRepository.getTOS()
}
}

View file

@ -3,9 +3,9 @@ package be.re.writand.screens.editor.editorspace
/**
* Interface to present the 3 differences stages of getting the contents of file:
* - [Loading]: the content is pending.
* - [Success]: the file content is ready, and is given in a String.
* - [Failed]: unable to get the content from the file, give back an error message.
* - [Loading] the content is pending.
* - [Success] the file content is ready, and is given in a String.
* - [Failed] unable to get the content from the file, give back an error message.
*/
sealed interface EditorSpaceUiState {
object Loading : EditorSpaceUiState

View file

@ -0,0 +1,256 @@
package be.re.writand.screens.info
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
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.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import be.re.writand.screens.components.WLoadingIndicator
import be.re.writand.screens.components.WLogoImage
import be.re.writand.screens.components.WText
/**
* Popup screen to display all the information about the app, and provide links for support etc.
* @param[vM] view model corresponding to this screen.
*/
@Composable
fun InfoPopup(
vM: InfoPopupViewModel = hiltViewModel()
) {
val openDialog = remember { mutableStateOf(false) }
val buttonTitle = remember {
mutableStateOf("Show Info")
}
Button(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = {
openDialog.value = !openDialog.value
if (!openDialog.value) {
buttonTitle.value = "Show Info"
}
}
) {
Text(text = buttonTitle.value, modifier = Modifier.padding(3.dp))
}
if (openDialog.value) {
val uiState by vM.uiState.collectAsState()
buttonTitle.value = "Hide Info"
Dialog(
onDismissRequest = { openDialog.value = false },
properties = DialogProperties(usePlatformDefaultWidth = false)
) {
Box(
modifier = Modifier
.height(600.dp)
.width(800.dp),
contentAlignment = Alignment.Center
) {
Scaffold(
modifier = Modifier
.fillMaxSize()
.clip(shape = RoundedCornerShape(10.dp))
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.tertiary,
shape = RoundedCornerShape(10.dp)
),
topBar = {
Row(
modifier = Modifier
.fillMaxWidth()
.background(color = MaterialTheme.colorScheme.tertiary),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
// spacer to divide the row in 3 parts and spread them over the width
Spacer(modifier = Modifier.size(0.dp))
WText(text = "About Writand", fontSize = 25.sp)
IconButton(
onClick = { openDialog.value = false },
modifier = Modifier.padding(start = 10.dp),
) {
Icon(
modifier = Modifier.size(35.dp),
imageVector = Icons.Default.Close,
contentDescription = "Close"
)
}
}
}
) {
Row(
modifier = Modifier.padding(it)
) {
WLogoImage(
modifier = Modifier
.padding(25.dp)
.fillMaxHeight()
.size(100.dp),
alignment = Alignment.CenterStart,
)
Column(
modifier = Modifier.padding(start = 5.dp).verticalScroll(
rememberScrollState()
)
) {
// Writand (short description) + version
Row(
modifier = Modifier.padding(vertical = 15.dp),
verticalAlignment = Alignment.Bottom
) {
WText(
text = "Writand",
color = MaterialTheme.colorScheme.tertiary,
fontWeight = FontWeight.ExtraBold,
fontSize = 30.sp
)
WText(
text = " | version ${vM.version}",
fontSize = 20.sp
)
}
WText(text =
"Writand is a new integrated development environment (IDE) designed specifically for Android devices.\n" +
"Whether you're a seasoned developer or just starting, Writand offers an intuitive and powerful\n" +
"platform to code on your Android tablet.\n"
)
// change log / what's new: version 2 will have this
/*Column {
WText(text = "What's new", fontSize = 25.sp)
WText("upcoming...")
}*/
// TOS
Column {
WText(
modifier = Modifier.padding(vertical = 10.dp),
text = "Terms Of Services",
fontSize = 25.sp
)
Box(
modifier = Modifier
.height(300.dp)
.padding(horizontal = 10.dp)
.border(1.dp, color = MaterialTheme.colorScheme.secondary)
) {
when (val value = uiState) {
InfoPopupUiState.Loading -> {
WLoadingIndicator()
}
is InfoPopupUiState.Success -> {
LazyColumn {
items(value.tosStrings.size) { item ->
Text(text = value.tosStrings[item])
}
}
}
is InfoPopupUiState.Failed -> {
WText(text = value.message)
}
}
}
}
// support + feedback (link site / contact gmail)
Column {
WText(
modifier = Modifier.padding(vertical = 15.dp),
text = "Support & Feedback",
fontSize = 25.sp
)
WText(
modifier = Modifier.padding(bottom = 5.dp),
text = "Were here to assist you with any issues you might encounter, and wed love to hear your thoughts on how we can improve our app. Whether you need support or want to share feedback, we're just a click away."
)
WText(
modifier = Modifier.padding(start = 10.dp),
text = "* Email us at emro-dev@gmail.com"
)
WText(
modifier = Modifier.padding(start = 10.dp),
text = "* More information at emro-dev.com"
)
WText(
modifier = Modifier.padding(top = 5.dp),
text = "Thank you for helping us make the app better!"
)
}
// (developer information +?) support us (patreon link)
Column {
WText(
modifier = Modifier.padding(vertical = 15.dp),
text = "Support Us",
fontSize = 25.sp
)
WText(text = "Support us via patreon and get more features: (link)")
}
// rate the app: for version 2 as well
Column(
modifier = Modifier.padding(bottom = 25.dp)
) {
WText(
modifier = Modifier.padding(vertical = 15.dp),
text = "Rate The App",
fontSize = 25.sp
)
WText(
modifier = Modifier.padding(bottom = 5.dp),
text = "If you love using our app, wed really appreciate it if you could take a moment to rate us! Your feedback helps us improve and reach more users like you."
)
WText(
modifier = Modifier.padding(bottom = 5.dp),
text = "(link)"
)
WText(text = "Thank you for your support!")
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,16 @@
package be.re.writand.screens.info
/**
* Interface to present the 3 differences stages of getting the contents of file:
* - [Loading] the content is pending.
* - [Success] all the info for the popup is ready.
* - [Failed] unable to get the content for the info popup, give back an error message.
*/
sealed interface InfoPopupUiState {
object Loading: InfoPopupUiState
data class Success(
val changeLog: List<String>,
val tosStrings: List<String>
): InfoPopupUiState
data class Failed(val message: String): InfoPopupUiState
}

View file

@ -0,0 +1,45 @@
package be.re.writand.screens.info
import be.re.writand.data.local.models.TermsOfService
import be.re.writand.domain.tos.GetTOSUseCase
import be.re.writand.screens.WViewModel
import be.re.writand.utils.Version
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
/**
* View model to be used by InfoPopup that handles all the needed information to be displayed.
* This view model uses the InfoPopupUiState to enable handling failures.
* @param[getTOSUseCase] use case to get the Terms of Services.
*/
@HiltViewModel
class InfoPopupViewModel @Inject constructor(
getTOSUseCase: GetTOSUseCase
) : WViewModel() {
private val _uiState: MutableStateFlow<InfoPopupUiState> =
MutableStateFlow(InfoPopupUiState.Loading)
val uiState: StateFlow<InfoPopupUiState> = _uiState
var version: String = ""
init {
launchCatching {
val tos: TermsOfService = getTOSUseCase()
version = tos.version.toString()
val tosList: List<String> = tos.text
val changeLog = listOf<String>() // this will be used in version 2
if (tosList.isNotEmpty()) {
_uiState.value =
InfoPopupUiState.Success(changeLog = changeLog, tosStrings = tosList)
} else {
_uiState.value = InfoPopupUiState.Failed("Terms of Services could not be loaded.")
}
}
}
}

View file

@ -52,7 +52,7 @@ import be.re.writand.screens.components.WRadioButtonsSelectorRowWise
import be.re.writand.screens.components.WText
/**
* Popup screen to handle let the user change the settings once the welcome part of the app has
* Popup screen to let the user change the settings once the welcome part of the app has
* gone through. This should involve all the settings possible to be changed.
* @param[vM] view model corresponding to this screen.
*/

View file

@ -2,9 +2,9 @@ package be.re.writand.screens.welcome
/**
* Interface to present the 3 differences stages of getting the TOS file content:
* - [Loading]: the content is pending.
* - [Success]: the TOS file is ready, and given in a list of Strings.
* - [Failed]: unable to get the TOS content from the file, give back an error message.
* - [Loading] the content is pending.
* - [Success] the TOS file is ready, and given in a list of Strings.
* - [Failed] unable to get the TOS content from the file, give back an error message.
*/
sealed interface WelcomeTOSUiState {
object Loading: WelcomeTOSUiState

View file

@ -22,7 +22,7 @@ class WelcomeTOSViewModel @Inject constructor(
init {
launchCatching {
val tosList: List<String> = getTOSUseCase()
val tosList: List<String> = getTOSUseCase().text
if (tosList.isNotEmpty()) {
_uiState.value = WelcomeTOSUiState.Success(tosList)
} else {

View file

@ -3,6 +3,11 @@ package be.re.writand.utils
import android.util.Log
data class Version(var major: Int, var minor: Int, var patch: Int) {
override fun toString(): String {
return "${major}.${minor}.${patch}"
}
companion object {
/**