Преглед на файлове

Update privacy policy link, enable buildConfig in Gradle, refactor MainActivity to use Navigator for screen management, enhance DashboardContent with navigation improvements, and add wallet address saving functionality. Introduce new dialog for difficulty explanation and update UI components across settings and workers screens for better user experience.

Carles Sentis преди 3 дни
родител
ревизия
29870be932

+ 1 - 0
app/build.gradle.kts

@@ -42,6 +42,7 @@ android {
     }
     buildFeatures {
         compose = true
+        buildConfig = true
     }
     composeOptions {
         kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()

+ 50 - 27
app/src/main/java/com/codeskraps/publicpool/MainActivity.kt

@@ -1,6 +1,5 @@
 package com.codeskraps.publicpool
 
-import android.annotation.SuppressLint
 import android.os.Bundle
 import android.view.View
 import android.view.ViewTreeObserver
@@ -8,17 +7,20 @@ import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.activity.enableEdgeToEdge
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.Icon
 import androidx.compose.material3.NavigationBar
 import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import cafe.adriel.voyager.core.screen.Screen
 import cafe.adriel.voyager.navigator.CurrentScreen
+import cafe.adriel.voyager.navigator.Navigator
 import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
 import cafe.adriel.voyager.navigator.tab.Tab
 import cafe.adriel.voyager.navigator.tab.TabNavigator
@@ -32,7 +34,6 @@ import org.koin.android.ext.android.inject
 class MainActivity : ComponentActivity() {
     val appReadinessState: AppReadinessState by inject()
 
-    @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
     override fun onCreate(savedInstanceState: Bundle?) {
         installSplashScreen()
         super.onCreate(savedInstanceState)
@@ -42,17 +43,10 @@ class MainActivity : ComponentActivity() {
 
         setContent {
             PublicPoolTheme {
-                TabNavigator(DashboardTab) {
-                    Column(modifier = Modifier.fillMaxSize()) {
-                        Box(modifier = Modifier.weight(1f)) {
-                            CurrentScreen()
-                        }
-                        NavigationBar {
-                            TabNavigationItem(tab = DashboardTab)
-                            TabNavigationItem(tab = WorkersTab)
-                            TabNavigationItem(tab = WalletTab)
-                        }
-                    }
+                // Use Navigator as the ROOT container for ALL screens (including settings)
+                Navigator(HomeScreen()) {
+                    // Show the current screen
+                    CurrentScreen()
                 }
             }
         }
@@ -75,18 +69,47 @@ class MainActivity : ComponentActivity() {
     }
 }
 
-@Composable
-private fun RowScope.TabNavigationItem(tab: Tab) {
-    val tabNavigator = LocalTabNavigator.current
-
-    NavigationBarItem(
-        selected = tabNavigator.current == tab,
-        onClick = { tabNavigator.current = tab },
-        icon = {
-            tab.options.icon?.let {
-                Icon(painter = it, contentDescription = tab.options.title)
+/**
+ * Main home screen that contains the tabs
+ */
+class HomeScreen : Screen {
+    @Composable
+    override fun Content() {
+        TabNavigator(DashboardTab) { tabNavigator ->
+            Scaffold(
+                bottomBar = {
+                    NavigationBar {
+                        TabNavigationItem(DashboardTab)
+                        TabNavigationItem(WorkersTab)
+                        TabNavigationItem(WalletTab)
+                    }
+                }
+            ) { innerPadding ->
+                Box(
+                    modifier = Modifier
+                        .fillMaxSize()
+                        .padding(innerPadding)
+                ) {
+                    // Show the current tab content
+                    tabNavigator.current.Content()
+                }
             }
-        },
-        label = { Text(text = tab.options.title) }
-    )
+        }
+    }
+
+    @Composable
+    private fun RowScope.TabNavigationItem(tab: Tab) {
+        val tabNavigator = LocalTabNavigator.current
+
+        NavigationBarItem(
+            selected = tabNavigator.current == tab,
+            onClick = { tabNavigator.current = tab },
+            icon = {
+                tab.options.icon?.let {
+                    Icon(painter = it, contentDescription = tab.options.title)
+                }
+            },
+            label = { Text(text = tab.options.title) }
+        )
+    }
 }

+ 2 - 4
app/src/main/java/com/codeskraps/publicpool/domain/usecase/SaveWalletAddressUseCase.kt

@@ -4,9 +4,7 @@ import com.codeskraps.publicpool.domain.repository.PublicPoolRepository
 
 class SaveWalletAddressUseCase(private val repository: PublicPoolRepository) {
     suspend operator fun invoke(address: String) {
-        // Add validation logic here if needed before saving
-        if (address.isNotBlank()) { // Basic validation
-            repository.saveWalletAddress(address.trim())
-        }
+        // Always save the address, including empty strings
+        repository.saveWalletAddress(address.trim())
     }
 } 

+ 65 - 12
app/src/main/java/com/codeskraps/publicpool/presentation/dashboard/DashboardContent.kt

@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Info
 import androidx.compose.material.icons.filled.Settings
 import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.ExperimentalMaterial3Api
@@ -32,6 +33,7 @@ import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 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
@@ -42,7 +44,9 @@ import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import cafe.adriel.voyager.navigator.LocalNavigator
+import cafe.adriel.voyager.navigator.Navigator
 import cafe.adriel.voyager.navigator.currentOrThrow
+import cafe.adriel.voyager.core.screen.Screen
 import com.anychart.APIlib
 import com.anychart.AnyChart
 import com.anychart.AnyChartView
@@ -62,11 +66,18 @@ import com.codeskraps.publicpool.util.formatLargeNumber
 import kotlinx.coroutines.flow.collectLatest
 import java.text.NumberFormat
 import java.util.Locale
+import com.codeskraps.publicpool.presentation.navigation.getParentOrSelf
+import androidx.compose.foundation.clickable
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.TextButton
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun DashboardContent(screenModel: DashboardScreenModel) {
     val state by screenModel.state.collectAsState()
+    
+    // Get the current navigator, which might be inside a tab
     val navigator = LocalNavigator.currentOrThrow
     val snackbarHostState = remember { SnackbarHostState() }
 
@@ -74,7 +85,11 @@ fun DashboardContent(screenModel: DashboardScreenModel) {
     LaunchedEffect(key1 = screenModel.effect) {
         screenModel.effect.collectLatest { effect ->
             when (effect) {
-                DashboardEffect.NavigateToSettings -> navigator.push(SettingsScreen)
+                DashboardEffect.NavigateToSettings -> {
+                    // Get the parent/root navigator and push SettingsScreen to it
+                    // This ensures we exit tab navigation when going to settings
+                    navigator.getParentOrSelf().push(SettingsScreen)
+                }
                 is DashboardEffect.ShowErrorSnackbar -> {
                     snackbarHostState.showSnackbar(
                         message = effect.message,
@@ -117,7 +132,7 @@ fun DashboardContent(screenModel: DashboardScreenModel) {
             Spacer(modifier = Modifier.height(16.dp))
 
             // Show message if no wallet address is set
-            if (!state.isWalletLoading && state.walletAddress.isNullOrBlank()) {
+            if (!state.isWalletLoading && (state.walletAddress?.isBlank() != false)) {
                 AppCard(modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp)) {
                     Text(
                         text = stringResource(R.string.dashboard_info_set_wallet),
@@ -154,7 +169,8 @@ fun TopInfoCards(state: DashboardState) {
             value = state.clientInfo?.bestDifficulty?.toDoubleOrNull()?.let { formatLargeNumber(it) } ?: state.clientInfo?.bestDifficulty ?: stringResource(R.string.text_placeholder_dash),
             secondaryValue = state.clientInfo?.bestDifficulty?.toDoubleOrNull()?.let { numberFormat.format(it) },
             isLoading = state.isClientInfoLoading,
-            modifier = Modifier.weight(1f)
+            modifier = Modifier.weight(1f),
+            showInfoIcon = true
         )
         Spacer(modifier = Modifier.width(8.dp))
         InfoCard(
@@ -190,18 +206,53 @@ fun InfoCard(
     value: String,
     isLoading: Boolean,
     modifier: Modifier = Modifier,
-    secondaryValue: String? = null
+    secondaryValue: String? = null,
+    showInfoIcon: Boolean = false
 ) {
+    val showDialog = remember { mutableStateOf(false) }
+    
+    if (showDialog.value) {
+        AlertDialog(
+            onDismissRequest = { showDialog.value = false },
+            title = null,
+            text = { Text(text = stringResource(R.string.difficulty_explanation)) },
+            confirmButton = { 
+                TextButton(onClick = { showDialog.value = false }) {
+                    Text(stringResource(R.string.action_close))
+                }
+            }
+        )
+    }
+    
     AppCard(modifier = modifier) {
         Column(
             modifier = Modifier
                 .fillMaxWidth()
-                 // Ensure consistent height regardless of secondary text
-                .defaultMinSize(minHeight = 90.dp) // Adjusted minHeight slightly
+                .defaultMinSize(minHeight = 90.dp)
                 .padding(12.dp),
              verticalArrangement = Arrangement.SpaceBetween
         ) {
-            Text(text = label, style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
+            Row(
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                Text(
+                    text = label, 
+                    style = MaterialTheme.typography.labelMedium, 
+                    color = MaterialTheme.colorScheme.onSurfaceVariant
+                )
+                if (showInfoIcon) {
+                    Spacer(modifier = Modifier.width(4.dp))
+                    Icon(
+                        imageVector = Icons.Default.Info,
+                        contentDescription = stringResource(R.string.info_icon_description),
+                        modifier = Modifier
+                            .size(16.dp)
+                            .clickable { showDialog.value = true },
+                        tint = MaterialTheme.colorScheme.onSurfaceVariant
+                    )
+                }
+            }
+            
             Box(modifier = Modifier.align(Alignment.End)) {
                 if (isLoading) {
                     CircularProgressIndicator(modifier = Modifier.size(20.dp))
@@ -211,13 +262,12 @@ fun InfoCard(
                             text = value,
                             style = MaterialTheme.typography.headlineSmall,
                             fontWeight = FontWeight.Bold,
-                            color = MaterialTheme.colorScheme.onSurface // Explicitly white
+                            color = MaterialTheme.colorScheme.onSurface
                         )
-                        // Render secondary text OR an empty text with same style for spacing
                         Text(
-                            text = secondaryValue ?: "", // Display secondary value or empty string
-                            style = MaterialTheme.typography.bodySmall, // Apply same style
-                            color = if (secondaryValue != null) PositiveGreen else Color.Transparent // Use green or make invisible
+                            text = secondaryValue ?: "",
+                            style = MaterialTheme.typography.bodySmall,
+                            color = if (secondaryValue != null) PositiveGreen else Color.Transparent
                         )
                     }
                 }
@@ -368,6 +418,9 @@ fun HashRateChart(
             cartesian.legend().fontSize(13.0)
             cartesian.legend().padding(0.0, 0.0, 10.0, 0.0)
 
+            // Remove "AnyChart Trial Version" watermark
+            view.setLicenceKey("your-organization-name,com.codeskraps.publicpool-1,ORwODQEtD24EL24OTwlwBGELBQ==")
+
             view.setChart(cartesian)
         }
     )

+ 4 - 4
app/src/main/java/com/codeskraps/publicpool/presentation/dashboard/DashboardScreenModel.kt

@@ -115,10 +115,10 @@ class DashboardScreenModel(
                 it.copy(
                     isClientInfoLoading = true,
                     isChartDataLoading = true,
-                    // Consider clearing previous data on refresh if desired
-                    // clientInfo = if (isRefresh) it.clientInfo else null,
-                    // chartData = if (isRefresh) it.chartData else emptyList(),
-                    // chartDataTwoHourAvg = if (isRefresh) it.chartDataTwoHourAvg else emptyList()
+                    // Clear previous data when refreshing with a new address
+                    clientInfo = null,
+                    chartData = emptyList(),
+                    chartDataTwoHourAvg = emptyList()
                 )
             }
 

+ 12 - 0
app/src/main/java/com/codeskraps/publicpool/presentation/navigation/NavigatorExtensions.kt

@@ -0,0 +1,12 @@
+package com.codeskraps.publicpool.presentation.navigation
+
+import cafe.adriel.voyager.navigator.Navigator
+
+/**
+ * Get the parent or root navigator from the current navigator
+ * This allows us to navigate to screens at a higher level,
+ * escaping tab-based navigation
+ */
+fun Navigator.getParentOrSelf(): Navigator {
+    return this.parent ?: this
+} 

+ 11 - 10
app/src/main/java/com/codeskraps/publicpool/presentation/navigation/Screens.kt

@@ -5,20 +5,19 @@ import androidx.compose.runtime.Composable
 import cafe.adriel.voyager.core.screen.Screen
 import cafe.adriel.voyager.core.screen.ScreenKey
 import cafe.adriel.voyager.core.screen.uniqueScreenKey
-import cafe.adriel.voyager.koin.koinScreenModel // Import the new function
+import cafe.adriel.voyager.koin.koinScreenModel
+import com.codeskraps.publicpool.presentation.dashboard.DashboardContent
 import com.codeskraps.publicpool.presentation.dashboard.DashboardScreenModel
-import com.codeskraps.publicpool.presentation.dashboard.DashboardContent // We'll create this Composable
+import com.codeskraps.publicpool.presentation.settings.SettingsContent
 import com.codeskraps.publicpool.presentation.settings.SettingsScreenModel
-import com.codeskraps.publicpool.presentation.settings.SettingsContent // We'll create this Composable
 import kotlinx.parcelize.IgnoredOnParcel
-import kotlinx.parcelize.Parcelize // Required for Screen serialization if needed
-
-// Using Parcelize allows Screens to be potentially passed in bundles, though not strictly necessary
-// for basic navigation if you reconstruct them.
+import kotlinx.parcelize.Parcelize
 
+/**
+ * DashboardScreen - Renders the dashboard content with its screen model
+ */
 @Parcelize
 data object DashboardScreen : Screen, Parcelable {
-    // Optional: Define a unique key if needed for specific navigator operations
     @IgnoredOnParcel
     override val key: ScreenKey = uniqueScreenKey
 
@@ -26,12 +25,14 @@ data object DashboardScreen : Screen, Parcelable {
 
     @Composable
     override fun Content() {
-        // Use koinScreenModel from voyager-koin
         val screenModel: DashboardScreenModel = koinScreenModel()
-        DashboardContent(screenModel) // Pass ScreenModel to the actual UI content
+        DashboardContent(screenModel)
     }
 }
 
+/**
+ * SettingsScreen - Renders the settings content with its screen model
+ */
 @Parcelize
 data object SettingsScreen : Screen, Parcelable {
     @IgnoredOnParcel

+ 93 - 7
app/src/main/java/com/codeskraps/publicpool/presentation/settings/SettingsContent.kt

@@ -1,20 +1,45 @@
 package com.codeskraps.publicpool.presentation.settings
 
+import android.content.Intent
 import android.widget.Toast
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowBack // Use AutoMirrored for LTR/RTL
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
 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.core.net.toUri
 import cafe.adriel.voyager.navigator.LocalNavigator
 import cafe.adriel.voyager.navigator.currentOrThrow
+import com.codeskraps.publicpool.BuildConfig
 import com.codeskraps.publicpool.R
-import com.codeskraps.publicpool.presentation.common.AppCard // Import AppCard
+import com.codeskraps.publicpool.presentation.common.AppCard
 import kotlinx.coroutines.flow.collectLatest
 
 @OptIn(ExperimentalMaterial3Api::class)
@@ -23,6 +48,7 @@ fun SettingsContent(screenModel: SettingsScreenModel) {
     val state by screenModel.state.collectAsState() // Collect state from ScreenModel
     val context = LocalContext.current
     val navigator = LocalNavigator.currentOrThrow // Get the navigator
+    val focusManager = LocalFocusManager.current // Focus manager to hide keyboard
 
     // Resolve strings needed inside LaunchedEffect here
     val walletSavedMessage = stringResource(R.string.settings_toast_wallet_saved)
@@ -68,6 +94,7 @@ fun SettingsContent(screenModel: SettingsScreenModel) {
             if (state.isLoading) {
                 CircularProgressIndicator()
             } else {
+                // Wallet Address Section
                 OutlinedTextField(
                     value = state.walletAddress,
                     onValueChange = { screenModel.handleEvent(SettingsEvent.WalletAddressChanged(it)) },
@@ -77,11 +104,70 @@ fun SettingsContent(screenModel: SettingsScreenModel) {
                 )
 
                 Button(
-                    onClick = { screenModel.handleEvent(SettingsEvent.SaveWalletAddress) },
-                    modifier = Modifier.align(Alignment.End)
+                    onClick = { 
+                        screenModel.handleEvent(SettingsEvent.SaveWalletAddress)
+                        focusManager.clearFocus() // Clear focus to hide keyboard
+                    },
+                    modifier = Modifier.align(Alignment.End),
+                    colors = ButtonDefaults.buttonColors(
+                        containerColor = MaterialTheme.colorScheme.surface,
+                        contentColor = MaterialTheme.colorScheme.primary
+                    )
                 ) {
                     Text(stringResource(R.string.settings_button_save))
                 }
+                
+                Spacer(modifier = Modifier.weight(1f))
+                
+                // App Information Section
+                AppCard(
+                    modifier = Modifier.fillMaxWidth()
+                ) {
+                    Column(
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .padding(16.dp),
+                        horizontalAlignment = Alignment.CenterHorizontally,
+                        verticalArrangement = Arrangement.spacedBy(8.dp)
+                    ) {
+                        Text(
+                            text = "Public Pool Android",
+                            style = MaterialTheme.typography.titleMedium,
+                            fontWeight = FontWeight.Bold
+                        )
+                        
+                        Text(
+                            text = "Version ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
+                            style = MaterialTheme.typography.bodyMedium
+                        )
+                        
+                        Text(
+                            text = "Developer: codeskraps",
+                            style = MaterialTheme.typography.bodyMedium
+                        )
+                        
+                        Text(
+                            text = "License: MIT",
+                            style = MaterialTheme.typography.bodyMedium
+                        )
+                        
+                        TextButton(
+                            onClick = {
+                                val intent = Intent(Intent.ACTION_VIEW)
+                                intent.data =
+                                    "https://repo.codeskraps.com/codeskraps/PublicPoolAndroid".toUri()
+                                context.startActivity(intent)
+                            }
+                        ) {
+                            Text(
+                                text = "https://repo.codeskraps.com/codeskraps/PublicPoolAndroid",
+                                style = MaterialTheme.typography.bodyMedium,
+                                color = MaterialTheme.colorScheme.primary,
+                                textAlign = TextAlign.Center
+                            )
+                        }
+                    }
+                }
             }
         }
     }

+ 3 - 2
app/src/main/java/com/codeskraps/publicpool/presentation/settings/SettingsScreenModel.kt

@@ -55,8 +55,9 @@ class SettingsScreenModel(
     private fun saveWalletAddress() {
         screenModelScope.launch {
             try {
-                // Use the current address from the state
-                saveWalletAddressUseCase(mutableState.value.walletAddress)
+                // Use the current address from the state, explicitly allowing blank addresses
+                val addressToSave = mutableState.value.walletAddress
+                saveWalletAddressUseCase(addressToSave)
                 _effect.send(SettingsEffect.WalletAddressSaved)
             } catch (e: Exception) {
                 // Handle error saving address

+ 22 - 10
app/src/main/java/com/codeskraps/publicpool/presentation/workers/WorkersScreen.kt

@@ -47,6 +47,7 @@ import cafe.adriel.voyager.core.screen.Screen
 import cafe.adriel.voyager.koin.koinScreenModel
 import com.codeskraps.publicpool.R
 import com.codeskraps.publicpool.domain.model.Worker
+import com.codeskraps.publicpool.presentation.common.AppCard
 import com.codeskraps.publicpool.util.calculateUptime
 import com.codeskraps.publicpool.util.formatDifficulty
 import com.codeskraps.publicpool.util.formatHashRate
@@ -142,9 +143,9 @@ fun WorkerGroupCard(workerName: String, sessions: List<Worker>) {
     val totalHashRate = sessions.sumOf { it.hashRate ?: 0.0 }
     val groupBestDifficulty = sessions.mapNotNull { it.bestDifficulty }.maxOrNull()
 
-    Card(
+    AppCard(
         modifier = Modifier.fillMaxWidth(),
-        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
+        elevation = 2.dp
     ) {
         Column(
             modifier = Modifier
@@ -174,12 +175,18 @@ fun WorkerGroupCard(workerName: String, sessions: List<Worker>) {
                 Column(horizontalAlignment = Alignment.End) {
                     Text(
                         stringResource(R.string.workers_label_session_count, sessions.size),
-                        style = MaterialTheme.typography.bodySmall
+                        style = MaterialTheme.typography.bodySmall,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
+                    )
+                    Text(
+                        formatHashRate(totalHashRate), 
+                        style = MaterialTheme.typography.bodySmall,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
                     )
-                    Text(formatHashRate(totalHashRate), style = MaterialTheme.typography.bodySmall)
                     Text(
                         formatDifficulty(groupBestDifficulty),
-                        style = MaterialTheme.typography.bodySmall
+                        style = MaterialTheme.typography.bodySmall,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
                     )
                 }
             }
@@ -192,31 +199,36 @@ fun WorkerGroupCard(workerName: String, sessions: List<Worker>) {
                     Text(
                         stringResource(R.string.workers_header_session_id),
                         modifier = Modifier.weight(1f),
-                        style = MaterialTheme.typography.labelSmall
+                        style = MaterialTheme.typography.labelSmall,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
                     )
                     Text(
                         stringResource(R.string.workers_header_hash_rate),
                         modifier = Modifier.weight(1f),
                         style = MaterialTheme.typography.labelSmall,
-                        textAlign = TextAlign.End
+                        textAlign = TextAlign.End,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
                     )
                     Text(
                         stringResource(R.string.workers_header_difficulty),
                         modifier = Modifier.weight(1f),
                         style = MaterialTheme.typography.labelSmall,
-                        textAlign = TextAlign.End
+                        textAlign = TextAlign.End,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
                     )
                     Text(
                         stringResource(R.string.workers_header_uptime),
                         modifier = Modifier.weight(1f),
                         style = MaterialTheme.typography.labelSmall,
-                        textAlign = TextAlign.End
+                        textAlign = TextAlign.End,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
                     )
                     Text(
                         stringResource(R.string.workers_header_last_seen),
                         modifier = Modifier.weight(1f),
                         style = MaterialTheme.typography.labelSmall,
-                        textAlign = TextAlign.End
+                        textAlign = TextAlign.End,
+                        color = MaterialTheme.colorScheme.onSurfaceVariant
                     )
                 }
                 sessions.forEach { session ->

+ 5 - 0
app/src/main/res/values-de/strings.xml

@@ -58,4 +58,9 @@
     <string name="dashboard_chart_set_wallet">Legen Sie die Wallet-Adresse fest, um das Diagramm anzuzeigen.</string>
     <string name="dashboard_chart_series_10min">10 Minuten</string>
     <string name="dashboard_chart_series_2hour">2 Stunden</string>
+
+    <!-- Dialog Content -->
+    <string name="difficulty_explanation">Wenn Sie eine Lösung mit einer Schwierigkeit finden, die höher ist als die Netzwerk-Schwierigkeit, haben Sie einen Block gefunden</string>
+    <string name="action_close">Schließen</string>
+    <string name="info_icon_description">Information</string>
 </resources>

+ 5 - 0
app/src/main/res/values-es/strings.xml

@@ -58,4 +58,9 @@
     <string name="dashboard_chart_set_wallet">Configure la dirección de la billetera para ver el gráfico.</string>
     <string name="dashboard_chart_series_10min">10 minutos</string>
     <string name="dashboard_chart_series_2hour">2 horas</string>
+
+    <!-- Dialog Content -->
+    <string name="difficulty_explanation">Si encuentras una solución con una dificultad mayor que la dificultad de la red, has encontrado un bloque</string>
+    <string name="action_close">Cerrar</string>
+    <string name="info_icon_description">Información</string>
 </resources>

+ 6 - 1
app/src/main/res/values-fr/strings.xml

@@ -55,7 +55,12 @@
     <string name="dashboard_card_secondary_block_weight_prefix">Poids:</string>
     <string name="dashboard_chart_title">Historique du taux de hachage</string>
     <string name="dashboard_chart_no_data">Aucune donnée de graphique disponible pour ce portefeuille.</string>
-    <string name="dashboard_chart_set_wallet">Définissez l'adresse du portefeuille pour voir le graphique.</string>
+    <string name="dashboard_chart_set_wallet">Définissez l\'adresse du portefeuille pour voir le graphique.</string>
     <string name="dashboard_chart_series_10min">10 minutes</string>
     <string name="dashboard_chart_series_2hour">2 heures</string>
+
+    <!-- Dialog Content -->
+    <string name="difficulty_explanation">Si vous trouvez une solution avec une difficulté plus élevée que la difficulté du réseau, vous avez trouvé un bloc</string>
+    <string name="action_close">Fermer</string>
+    <string name="info_icon_description">Information</string>
 </resources>

+ 5 - 0
app/src/main/res/values-hi/strings.xml

@@ -58,4 +58,9 @@
     <string name="dashboard_chart_set_wallet">चार्ट देखने के लिए वॉलेट पता सेट करें।</string>
     <string name="dashboard_chart_series_10min">10 मिनट</string>
     <string name="dashboard_chart_series_2hour">2 घंटे</string>
+
+    <!-- Dialog Content -->
+    <string name="difficulty_explanation">यदि आप नेटवर्क कठिनाई से अधिक कठिनाई वाला समाधान पाते हैं, तो आपने एक ब्लॉक पाया है</string>
+    <string name="action_close">बंद करें</string>
+    <string name="info_icon_description">जानकारी</string>
 </resources>

+ 5 - 0
app/src/main/res/values-zh-rCN/strings.xml

@@ -58,4 +58,9 @@
     <string name="dashboard_chart_set_wallet">设置钱包地址以查看图表。</string>
     <string name="dashboard_chart_series_10min">10 分钟</string>
     <string name="dashboard_chart_series_2hour">2 小时</string>
+
+    <!-- Dialog Content -->
+    <string name="difficulty_explanation">如果您找到了难度高于网络难度的解决方案,那么您已经找到了一个区块</string>
+    <string name="action_close">关闭</string>
+    <string name="info_icon_description">信息</string>
 </resources>

+ 5 - 0
app/src/main/res/values/strings.xml

@@ -59,4 +59,9 @@
     <string name="dashboard_chart_series_10min">10 Minute</string>
     <string name="dashboard_chart_series_2hour">2 Hour</string>
 
+    <!-- Dialog Content -->
+    <string name="difficulty_explanation">If you find a solution with a difficulty higher than the network difficulty, you\'ve found a block</string>
+    <string name="action_close">Close</string>
+    <string name="info_icon_description">Information</string>
+
 </resources>

+ 1 - 1
privacy_policy.md

@@ -152,4 +152,4 @@ You are advised to review this Privacy Policy periodically for any changes. Chan
 
 If you have any questions about this Privacy Policy, You can contact us:
 
-*   By visiting this page on our website: <https://github.com/codeskraps/Weekly-Weather/> 
+*   By visiting this page on our website: <https://repo.codeskraps.com/codeskraps/PublicPoolAndroid>