Skip to content

Commit 8b69f84

Browse files
committed
add write tag view
1 parent 36903ef commit 8b69f84

17 files changed

+2950
-0
lines changed

app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/NatNavHost.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,20 @@ import com.nativeapptemplate.nativeapptemplatefree.ui.scan.navigation.scanView
3838
import com.nativeapptemplate.nativeapptemplatefree.ui.settings.navigation.settingBaseView
3939
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_detail.navigation.navigateToShopDetail
4040
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_detail.navigation.shopDetailView
41+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.itemTagCreateView
42+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.itemTagDetailView
43+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.itemTagEditView
44+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.itemTagListView
45+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.itemTagWriteView
46+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToItemTagCreate
47+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToItemTagDetail
48+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToItemTagEdit
49+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToItemTagList
50+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToItemTagWrite
51+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToNumberTagsWebpageList
4152
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToShopBasicSettings
4253
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToShopSettings
54+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.numberTagsWebpageListView
4355
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.shopBasicSettingsView
4456
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.shopSettingsView
4557
import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.ShopBaseRoute
@@ -153,13 +165,45 @@ fun NatNavHost(
153165
)
154166
shopSettingsView(
155167
onShowBasicSettingsClick = { shopId -> navController.navigateToShopBasicSettings(shopId) },
168+
onShowItemTagListClick = { shopId -> navController.navigateToItemTagList(shopId) },
169+
onShowNumberTagsWebpageListClick = { shopId -> navController.navigateToNumberTagsWebpageList(shopId) },
170+
156171
onShowSnackbar = onShowSnackbar,
157172
onBackClick = navController::popBackStack,
158173
)
159174
shopBasicSettingsView(
160175
onShowSnackbar = onShowSnackbar,
161176
onBackClick = navController::popBackStack,
162177
)
178+
179+
numberTagsWebpageListView(
180+
onShowSnackbar = onShowSnackbar,
181+
onBackClick = navController::popBackStack,
182+
)
183+
184+
itemTagListView(
185+
onItemClick = { itemTagId -> navController.navigateToItemTagDetail(itemTagId) },
186+
onAddItemTagClick = { shopId -> navController.navigateToItemTagCreate(shopId) },
187+
onShowSnackbar = onShowSnackbar,
188+
onBackClick =navController::popBackStack,
189+
)
190+
itemTagCreateView(
191+
onShowSnackbar = onShowSnackbar,
192+
onBackClick = navController::popBackStack,
193+
)
194+
itemTagDetailView(
195+
onShowItemTagEditClick = { itemTagId -> navController.navigateToItemTagEdit(itemTagId) },
196+
onShowItemTagWriteClick = { itemTagId, isLock, itemTagType -> navController.navigateToItemTagWrite(itemTagId, isLock, itemTagType) },
197+
onShowSnackbar = onShowSnackbar,
198+
onBackClick = navController::popBackStack,
199+
)
200+
itemTagEditView(
201+
onShowSnackbar = onShowSnackbar,
202+
onBackClick = navController::popBackStack,
203+
)
204+
itemTagWriteView(
205+
onBackClick = navController::popBackStack,
206+
)
163207
}
164208

165209
scanBaseView {
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings
2+
3+
import androidx.compose.foundation.clickable
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.fillMaxHeight
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.foundation.lazy.LazyColumn
10+
import androidx.compose.material.icons.Icons
11+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
12+
import androidx.compose.material.icons.outlined.Web
13+
import androidx.compose.material3.CenterAlignedTopAppBar
14+
import androidx.compose.material3.ExperimentalMaterial3Api
15+
import androidx.compose.material3.Icon
16+
import androidx.compose.material3.IconButton
17+
import androidx.compose.material3.ListItem
18+
import androidx.compose.material3.MaterialTheme
19+
import androidx.compose.material3.Scaffold
20+
import androidx.compose.material3.SnackbarDuration
21+
import androidx.compose.material3.Text
22+
import androidx.compose.material3.TopAppBarDefaults
23+
import androidx.compose.runtime.Composable
24+
import androidx.compose.runtime.LaunchedEffect
25+
import androidx.compose.runtime.getValue
26+
import androidx.compose.ui.Alignment
27+
import androidx.compose.ui.Modifier
28+
import androidx.compose.ui.platform.LocalClipboardManager
29+
import androidx.compose.ui.res.stringResource
30+
import androidx.compose.ui.text.AnnotatedString
31+
import androidx.compose.ui.text.style.TextAlign
32+
import androidx.compose.ui.unit.dp
33+
import androidx.hilt.navigation.compose.hiltViewModel
34+
import androidx.lifecycle.Lifecycle
35+
import androidx.lifecycle.compose.LifecycleEventEffect
36+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
37+
import com.nativeapptemplate.nativeapptemplatefree.NatConstants
38+
import com.nativeapptemplate.nativeapptemplatefree.R
39+
import com.nativeapptemplate.nativeapptemplatefree.ui.common.ErrorView
40+
import com.nativeapptemplate.nativeapptemplatefree.ui.common.LoadingView
41+
42+
@Composable
43+
internal fun NumberTagsWebpageListView(
44+
viewModel: NumberTagsWebpageListViewModel = hiltViewModel(),
45+
onShowSnackbar: suspend (String, String?, SnackbarDuration?) -> Boolean,
46+
onBackClick: () -> Unit,
47+
) {
48+
val uiState: NumberTagsWebpageListUiState by viewModel.uiState.collectAsStateWithLifecycle()
49+
50+
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
51+
viewModel.reload()
52+
}
53+
54+
LaunchedEffect(uiState.message) {
55+
if (uiState.message.isNotBlank()) {
56+
onShowSnackbar(uiState.message, "dismiss", SnackbarDuration.Indefinite)
57+
viewModel.snackbarMessageShown()
58+
}
59+
}
60+
61+
NumberTagsWebpageListView(
62+
viewModel = viewModel,
63+
uiState = uiState,
64+
onBackClick = onBackClick,
65+
)
66+
}
67+
68+
@Composable
69+
fun NumberTagsWebpageListView(
70+
viewModel: NumberTagsWebpageListViewModel,
71+
uiState: NumberTagsWebpageListUiState,
72+
onBackClick: () -> Unit,
73+
) {
74+
ContentView(
75+
viewModel = viewModel,
76+
uiState = uiState,
77+
onBackClick = onBackClick,
78+
)
79+
}
80+
81+
@Composable
82+
private fun ContentView(
83+
viewModel: NumberTagsWebpageListViewModel,
84+
uiState: NumberTagsWebpageListUiState,
85+
onBackClick: () -> Unit,
86+
) {
87+
if (uiState.isLoading) {
88+
NumberTagsWebpageListLoadingView(onBackClick)
89+
} else if (uiState.success) {
90+
NumberTagsWebpageListContentView(
91+
uiState = uiState,
92+
onBackClick = onBackClick,
93+
)
94+
} else {
95+
NumberTagsWebpageListErrorView(viewModel, onBackClick)
96+
}
97+
}
98+
99+
@Composable
100+
private fun NumberTagsWebpageListContentView(
101+
uiState: NumberTagsWebpageListUiState,
102+
onBackClick: () -> Unit,
103+
) {
104+
val localClipboardManager = LocalClipboardManager.current
105+
106+
Scaffold(
107+
topBar = {
108+
TopAppBar(
109+
onBackClick,
110+
)
111+
},
112+
modifier = Modifier.fillMaxSize(),
113+
) { padding ->
114+
Box(
115+
modifier = Modifier
116+
.fillMaxWidth()
117+
.fillMaxHeight()
118+
.padding(padding)
119+
) {
120+
LazyColumn(
121+
Modifier.padding(24.dp)
122+
) {
123+
item {
124+
Text(
125+
uiState.shop.getName(),
126+
style = MaterialTheme.typography.titleLarge,
127+
textAlign = TextAlign.Center,
128+
modifier = Modifier
129+
.fillMaxWidth(),
130+
)
131+
}
132+
133+
item {
134+
ListItem(
135+
headlineContent = {
136+
Text(
137+
stringResource(R.string.server_number_tags_webpage),
138+
color = MaterialTheme.colorScheme.primary,
139+
style = MaterialTheme.typography.titleMedium,
140+
)
141+
},
142+
leadingContent = {
143+
Icon(
144+
Icons.Outlined.Web,
145+
contentDescription = stringResource(R.string.label_shop_settings_number_tags_webpage),
146+
tint = MaterialTheme.colorScheme.primary,
147+
)
148+
},
149+
modifier = Modifier
150+
.clickable {
151+
localClipboardManager.setText(AnnotatedString(uiState.shop.displayShopServerUrlString(NatConstants.baseUrlString())))
152+
},
153+
)
154+
}
155+
}
156+
}
157+
}
158+
}
159+
160+
@OptIn(ExperimentalMaterial3Api::class)
161+
@Composable
162+
private fun TopAppBar(
163+
onBackClick: () -> Unit,
164+
) {
165+
CenterAlignedTopAppBar(
166+
colors = TopAppBarDefaults.topAppBarColors(
167+
containerColor = MaterialTheme.colorScheme.primaryContainer,
168+
titleContentColor = MaterialTheme.colorScheme.primary,
169+
),
170+
title = { Text(stringResource(R.string.label_shop_settings_number_tags_webpage)) },
171+
navigationIcon = {
172+
IconButton(onClick = {
173+
onBackClick()
174+
}) {
175+
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back")
176+
}
177+
},
178+
modifier = Modifier.fillMaxWidth(),
179+
)
180+
}
181+
182+
@Composable
183+
private fun NumberTagsWebpageListErrorView(
184+
viewModel: NumberTagsWebpageListViewModel,
185+
onBackClick: () -> Unit,
186+
) {
187+
Scaffold(
188+
topBar = {
189+
TopAppBar(
190+
onBackClick
191+
)
192+
},
193+
modifier = Modifier.fillMaxSize(),
194+
) { padding ->
195+
Box(
196+
modifier = Modifier
197+
.fillMaxWidth()
198+
.fillMaxHeight()
199+
.padding(padding),
200+
contentAlignment = Alignment.Center
201+
) {
202+
ErrorView(onClick = viewModel::reload)
203+
}
204+
}
205+
}
206+
207+
@Composable
208+
private fun NumberTagsWebpageListLoadingView(
209+
onBackClick: () -> Unit,
210+
) {
211+
Scaffold(
212+
topBar = {
213+
TopAppBar(
214+
onBackClick,
215+
)
216+
},
217+
modifier = Modifier.fillMaxSize(),
218+
) { padding ->
219+
Box(
220+
modifier = Modifier
221+
.fillMaxWidth()
222+
.fillMaxHeight()
223+
.padding(padding),
224+
contentAlignment = Alignment.Center
225+
) {
226+
LoadingView()
227+
}
228+
}
229+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings
2+
3+
import androidx.lifecycle.SavedStateHandle
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.viewModelScope
6+
import androidx.navigation.toRoute
7+
import com.nativeapptemplate.nativeapptemplatefree.data.shop.ShopRepository
8+
import com.nativeapptemplate.nativeapptemplatefree.model.Shop
9+
import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.NumberTagsWebpageListRoute
10+
import dagger.hilt.android.lifecycle.HiltViewModel
11+
import kotlinx.coroutines.flow.Flow
12+
import kotlinx.coroutines.flow.MutableStateFlow
13+
import kotlinx.coroutines.flow.StateFlow
14+
import kotlinx.coroutines.flow.asStateFlow
15+
import kotlinx.coroutines.flow.catch
16+
import kotlinx.coroutines.flow.update
17+
import kotlinx.coroutines.launch
18+
import javax.inject.Inject
19+
20+
data class NumberTagsWebpageListUiState(
21+
val isLoading: Boolean = true,
22+
val success: Boolean = false,
23+
val message: String = "",
24+
val shop: Shop = Shop(),
25+
)
26+
27+
/**
28+
* ViewModel for library view
29+
*/
30+
@HiltViewModel
31+
class NumberTagsWebpageListViewModel @Inject constructor(
32+
savedStateHandle: SavedStateHandle,
33+
private val shopRepository: ShopRepository,
34+
35+
) : ViewModel() {
36+
private val shopId = savedStateHandle.toRoute<NumberTagsWebpageListRoute>().id
37+
38+
private val _uiState = MutableStateFlow(NumberTagsWebpageListUiState())
39+
val uiState: StateFlow<NumberTagsWebpageListUiState> = _uiState.asStateFlow()
40+
41+
fun reload() {
42+
fetchData(shopId)
43+
}
44+
45+
private fun fetchData(shopId: String) {
46+
_uiState.update {
47+
it.copy(
48+
isLoading = true,
49+
success = false,
50+
)
51+
}
52+
53+
viewModelScope.launch {
54+
val shopFlow: Flow<Shop> = shopRepository.getShop(shopId)
55+
56+
shopFlow
57+
.catch { exception ->
58+
val message = exception.message
59+
_uiState.update {
60+
it.copy(
61+
message = message ?: "Unknown Error",
62+
isLoading = false,
63+
)
64+
}
65+
}
66+
.collect { shop ->
67+
_uiState.update {
68+
it.copy(
69+
shop = shop,
70+
success = true,
71+
isLoading = false,
72+
)
73+
}
74+
}
75+
}
76+
}
77+
78+
fun snackbarMessageShown() {
79+
_uiState.update { it.copy(message = "") }
80+
}
81+
}
82+

0 commit comments

Comments
 (0)