Skip to content

feat: implement maintenance mode with dedicated activity and UI components#56

Merged
dk-a-dev merged 4 commits intoGDGVIT:v3from
dk-a-dev:v3
Aug 4, 2025
Merged

feat: implement maintenance mode with dedicated activity and UI components#56
dk-a-dev merged 4 commits intoGDGVIT:v3from
dk-a-dev:v3

Conversation

@dk-a-dev
Copy link
Contributor

@dk-a-dev dk-a-dev commented Aug 3, 2025

…nents# Pull Request Type
Please check the type of change your PR introduces:

  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no API changes)
  • Build related changes
  • Documentation content changes
  • Other (please describe):

@dk-a-dev dk-a-dev requested a review from iamyajat as a code owner August 3, 2025 21:02
@dk-a-dev dk-a-dev changed the title chore: update version to 3.0.1 and modify support contact details feat: implement maintenance mode with dedicated activity and UI components Aug 3, 2025
Copy link
Collaborator

@JothishKamal JothishKamal left a comment

Choose a reason for hiding this comment

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

Pls make the requested changes @dk-a-dev

LaunchedEffect(Unit) {
if (MaintenanceChecker.isNetworkAvailable(context)) {
MaintenanceChecker.checkMaintenanceStatusAsync(context) { isUnderMaintenance ->
android.util.Log.d("MaintenanceCheck", "Dialog check result: isUnderMaintenance=$isUnderMaintenance")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use Timber for logs pls

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lgtm

lastMaintenanceCheck = System.currentTimeMillis()
}
} else {
android.util.Log.d("MaintenanceCheck", "No network available")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use Timber for logs pls

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lgtm

Comment on lines +1 to +270
package com.dscvit.vitty.ui.maintenance

import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
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 com.dscvit.vitty.R
import com.dscvit.vitty.theme.*

@Composable
fun MaintenanceBannerDialog(
isVisible: Boolean,
onDismiss: () -> Unit,
onRetryClick: () -> Unit
) {
if (isVisible) {
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true
)
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
shape = RoundedCornerShape(20.dp),
colors = CardDefaults.cardColors(
containerColor = Background
),
elevation = CardDefaults.cardElevation(
defaultElevation = 8.dp
)
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// Header with close button
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
IconButton(
onClick = onDismiss,
modifier = Modifier.size(24.dp)
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Close",
tint = Accent.copy(alpha = 0.7f),
modifier = Modifier.size(20.dp)
)
}
}

Spacer(modifier = Modifier.height(16.dp))

// Maintenance icon with gradient background
Box(
modifier = Modifier
.size(80.dp)
.clip(RoundedCornerShape(16.dp))
.background(
brush = Brush.radialGradient(
colors = listOf(
Accent.copy(alpha = 0.2f),
Accent.copy(alpha = 0.1f)
)
)
),
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(id = R.drawable.ic_maintenance),
contentDescription = "Maintenance",
modifier = Modifier.size(40.dp),
tint = Accent
)
}

Spacer(modifier = Modifier.height(16.dp))

// Title
Text(
text = "Server Maintenance",
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = TextColor,
textAlign = TextAlign.Center
)

Spacer(modifier = Modifier.height(8.dp))

// Message
Text(
text = "The server is currently under maintenance. Some features may be temporarily unavailable.",
fontSize = 14.sp,
color = Accent.copy(alpha = 0.8f),
textAlign = TextAlign.Center,
lineHeight = 18.sp
)

Spacer(modifier = Modifier.height(20.dp))

// Action buttons
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// Retry button
Button(
onClick = onRetryClick,
modifier = Modifier.weight(1f),
colors = ButtonDefaults.buttonColors(
containerColor = Accent,
contentColor = Background
),
shape = RoundedCornerShape(12.dp)
) {
Text(
text = "Retry",
fontSize = 14.sp,
fontWeight = FontWeight.Medium
)
}

// Dismiss button
OutlinedButton(
onClick = onDismiss,
modifier = Modifier.weight(1f),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = Accent.copy(alpha = 0.8f)
),
shape = RoundedCornerShape(12.dp)
) {
Text(
text = "Continue",
fontSize = 14.sp,
fontWeight = FontWeight.Medium
)
}
}

Spacer(modifier = Modifier.height(8.dp))

// Footer
Text(
text = "Thank you for your patience",
fontSize = 12.sp,
color = Accent.copy(alpha = 0.6f),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Medium
)
}
}
}
}
}

@Composable
fun MaintenanceBanner(
isVisible: Boolean,
onDismiss: () -> Unit,
onRetryClick: () -> Unit
) {
AnimatedVisibility(
visible = isVisible,
enter = slideInVertically(
initialOffsetY = { -it }
) + fadeIn(),
exit = slideOutVertically(
targetOffsetY = { -it }
) + fadeOut()
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = Secondary.copy(alpha = 0.95f)
),
elevation = CardDefaults.cardElevation(
defaultElevation = 6.dp
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Icon
Icon(
painter = painterResource(id = R.drawable.ic_maintenance),
contentDescription = "Maintenance",
modifier = Modifier.size(24.dp),
tint = Accent
)

Spacer(modifier = Modifier.width(12.dp))

// Text content
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Server Maintenance",
fontSize = 14.sp,
fontWeight = FontWeight.SemiBold,
color = Accent
)
Text(
text = "Some features may be limited",
fontSize = 12.sp,
color = Accent.copy(alpha = 0.7f)
)
}

// Action buttons
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TextButton(
onClick = onRetryClick,
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp)
) {
Text(
text = "Retry",
fontSize = 12.sp,
color = Accent,
fontWeight = FontWeight.Medium
)
}

IconButton(
onClick = onDismiss,
modifier = Modifier.size(24.dp)
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Dismiss",
tint = Accent.copy(alpha = 0.7f),
modifier = Modifier.size(16.dp)
)
}
}
}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pls attach screenshot of this composable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lgtm

Comment on lines +1 to +115
package com.dscvit.vitty.ui.maintenance

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.dscvit.vitty.R
import com.dscvit.vitty.theme.*

@Composable
fun MaintenanceScreen(
onRetryClick: () -> Unit,
onExitClick: () -> Unit
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(32.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
painter = painterResource(id = R.drawable.ic_maintenance),
contentDescription = stringResource(R.string.maintenance_icon_desc),
modifier = Modifier.size(120.dp),
colorFilter = ColorFilter.tint(Accent)
)

Spacer(modifier = Modifier.height(32.dp))

Text(
text = stringResource(R.string.maintenance_title),
fontSize = 28.sp,
fontWeight = FontWeight.SemiBold,
color = TextColor,
textAlign = TextAlign.Center,
lineHeight = 36.sp
)

Spacer(modifier = Modifier.height(16.dp))

Text(
text = stringResource(R.string.maintenance_message),
fontSize = 16.sp,
color = Accent,
textAlign = TextAlign.Center,
lineHeight = 20.sp
)

Spacer(modifier = Modifier.height(24.dp))

Text(
text = stringResource(R.string.maintenance_description),
fontSize = 14.sp,
color = Accent.copy(alpha = 0.8f),
textAlign = TextAlign.Center,
lineHeight = 16.sp
)

Spacer(modifier = Modifier.height(48.dp))

Button(
onClick = onRetryClick,
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Accent,
contentColor = Background
),
shape = RoundedCornerShape(12.dp)
) {
Text(
text = stringResource(R.string.retry_connection),
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}

Spacer(modifier = Modifier.height(16.dp))

TextButton(
onClick = onExitClick
) {
Text(
text = stringResource(R.string.exit_app),
fontSize = 14.sp,
color = Accent.copy(alpha = 0.7f)
)
}

Spacer(modifier = Modifier.height(32.dp))

Text(
text = stringResource(R.string.maintenance_footer),
fontSize = 12.sp,
color = Accent.copy(alpha = 0.6f),
textAlign = TextAlign.Center
)
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pls attach screenshot of this composable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lgtm

Comment on lines +27 to +66
val isUnderMaintenance = try {
val client = OkHttpClient.Builder()
.connectTimeout(MAINTENANCE_CHECK_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(MAINTENANCE_CHECK_TIMEOUT, TimeUnit.SECONDS)
.callTimeout(MAINTENANCE_CHECK_TIMEOUT, TimeUnit.SECONDS)
.build()

val request = Request.Builder()
.url(COMMUNITY_BASE_URL)
.build()

val response = client.newCall(request).execute()
val responseBody = response.body?.string()

val result = when {
!response.isSuccessful -> {
Timber.d("Server returned error code: ${response.code}")
true // Server error = maintenance
}
responseBody == null -> {
Timber.d("No response body received")
true // No response = maintenance
}
responseBody.contains(API_OPERATIONAL_MESSAGE) -> {
Timber.d("API operational message found")
false // API working
}
else -> {
Timber.d("Different response received: $responseBody")
true // Different response = maintenance
}
}

Timber.d("Maintenance check: isUnderMaintenance=$result, response=$responseBody")
result

} catch (e: Exception) {
Timber.e(e, "Maintenance check failed")
false // Network error = don't assume maintenance, just fail silently
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pls use existing ApiCommunityRestClient if possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated check

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lgtm

Comment on lines +75 to +77
fun isNetworkAvailable(context: Context): Boolean {
return UtilFunctions.isNetworkAvailable(context)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems like this function is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep removed

Copy link
Collaborator

Choose a reason for hiding this comment

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

Lgtm

@dk-a-dev dk-a-dev requested a review from JothishKamal August 4, 2025 06:52
@dk-a-dev dk-a-dev merged commit d6465a7 into GDGVIT:v3 Aug 4, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants