forked from Writand/writand
feat: info popup screen implemented + some grammar fixed in other files
This commit is contained in:
parent
aab1891835
commit
0c98426f98
10 changed files with 335 additions and 12 deletions
|
@ -69,7 +69,7 @@ dependencies {
|
||||||
implementation 'com.google.ar:core:1.45.0'
|
implementation 'com.google.ar:core:1.45.0'
|
||||||
implementation 'androidx.navigation:navigation-runtime-ktx:2.7.7'
|
implementation 'androidx.navigation:navigation-runtime-ktx:2.7.7'
|
||||||
implementation 'androidx.navigation:navigation-compose: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'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
package be.re.writand.domain.tos
|
package be.re.writand.domain.tos
|
||||||
|
|
||||||
|
import be.re.writand.data.local.models.TermsOfService
|
||||||
import be.re.writand.data.repos.tos.ITOSRepository
|
import be.re.writand.data.repos.tos.ITOSRepository
|
||||||
import javax.inject.Inject
|
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(
|
class GetTOSUseCase @Inject constructor(
|
||||||
private val tosRepository: ITOSRepository
|
private val tosRepository: ITOSRepository
|
||||||
) {
|
) {
|
||||||
|
|
||||||
operator fun invoke(): List<String> {
|
operator fun invoke(): TermsOfService {
|
||||||
return tosRepository.getTOS().text
|
return tosRepository.getTOS()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,9 +3,9 @@ package be.re.writand.screens.editor.editorspace
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to present the 3 differences stages of getting the contents of file:
|
* Interface to present the 3 differences stages of getting the contents of file:
|
||||||
* - [Loading]: the content is pending.
|
* - [Loading] the content is pending.
|
||||||
* - [Success]: the file content is ready, and is given in a String.
|
* - [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.
|
* - [Failed] unable to get the content from the file, give back an error message.
|
||||||
*/
|
*/
|
||||||
sealed interface EditorSpaceUiState {
|
sealed interface EditorSpaceUiState {
|
||||||
object Loading : EditorSpaceUiState
|
object Loading : EditorSpaceUiState
|
||||||
|
|
256
app/src/main/java/be/re/writand/screens/info/InfoPopup.kt
Normal file
256
app/src/main/java/be/re/writand/screens/info/InfoPopup.kt
Normal 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 = "We’re here to assist you with any issues you might encounter, and we’d 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, we’d 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!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ import be.re.writand.screens.components.WRadioButtonsSelectorRowWise
|
||||||
import be.re.writand.screens.components.WText
|
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.
|
* gone through. This should involve all the settings possible to be changed.
|
||||||
* @param[vM] view model corresponding to this screen.
|
* @param[vM] view model corresponding to this screen.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,9 +2,9 @@ package be.re.writand.screens.welcome
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to present the 3 differences stages of getting the TOS file content:
|
* Interface to present the 3 differences stages of getting the TOS file content:
|
||||||
* - [Loading]: the content is pending.
|
* - [Loading] the content is pending.
|
||||||
* - [Success]: the TOS file is ready, and given in a list of Strings.
|
* - [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.
|
* - [Failed] unable to get the TOS content from the file, give back an error message.
|
||||||
*/
|
*/
|
||||||
sealed interface WelcomeTOSUiState {
|
sealed interface WelcomeTOSUiState {
|
||||||
object Loading: WelcomeTOSUiState
|
object Loading: WelcomeTOSUiState
|
||||||
|
|
|
@ -22,7 +22,7 @@ class WelcomeTOSViewModel @Inject constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
launchCatching {
|
launchCatching {
|
||||||
val tosList: List<String> = getTOSUseCase()
|
val tosList: List<String> = getTOSUseCase().text
|
||||||
if (tosList.isNotEmpty()) {
|
if (tosList.isNotEmpty()) {
|
||||||
_uiState.value = WelcomeTOSUiState.Success(tosList)
|
_uiState.value = WelcomeTOSUiState.Success(tosList)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,6 +3,11 @@ package be.re.writand.utils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
data class Version(var major: Int, var minor: Int, var patch: Int) {
|
data class Version(var major: Int, var minor: Int, var patch: Int) {
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "${major}.${minor}.${patch}"
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue