@momo-kits/native-kits 0.113.3-webview.2 → 0.114.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compose/build.gradle.kts +1 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +28 -30
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +29 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +3 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/dateTimePicker/DateTimePicker.kt +178 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/dateTimePicker/DateTimePickerExample.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/dateTimePicker/DateTimePickerTypes.kt +15 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/dateTimePicker/DateTimePickerUtils.kt +226 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/dateTimePicker/WheelPicker.kt +201 -0
- package/ios/Application/ApplicationEnvironment.swift +17 -9
- package/ios/Application/HeaderRight.swift +29 -30
- package/ios/Swipeable/SwipeCell.swift +39 -31
- package/ios/Template/TrustBanner/TrustBanner.swift +75 -144
- package/local.properties +2 -2
- package/package.json +1 -1
package/compose/src/commonMain/kotlin/vn/momo/kits/components/dateTimePicker/DateTimePickerUtils.kt
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
package vn.momo.kits.components.dateTimePicker
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.compose.runtime.remember
|
|
5
|
+
import kotlinx.datetime.Clock
|
|
6
|
+
import kotlinx.datetime.DatePeriod
|
|
7
|
+
import kotlinx.datetime.LocalDateTime
|
|
8
|
+
import kotlinx.datetime.TimeZone
|
|
9
|
+
import kotlinx.datetime.minus
|
|
10
|
+
import kotlinx.datetime.number
|
|
11
|
+
import kotlinx.datetime.plus
|
|
12
|
+
import kotlinx.datetime.toLocalDateTime
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Format a LocalDateTime object into a string
|
|
16
|
+
*/
|
|
17
|
+
fun formatLocalDateTime(dateTime: LocalDateTime, format: String = "yyyy-MM-dd HH:mm"): String {
|
|
18
|
+
return when (format) {
|
|
19
|
+
"yyyy-MM-dd HH:mm" -> "${dateTime.year}-${paddingNum(dateTime.month.number)}-${paddingNum(dateTime.dayOfMonth)} ${paddingNum(dateTime.hour)}:${paddingNum(dateTime.minute)}"
|
|
20
|
+
"dd-MM-yyyy" -> "${paddingNum(dateTime.dayOfMonth)}-${paddingNum(dateTime.month.number)}-${dateTime.year}"
|
|
21
|
+
"HH:mm" -> "${paddingNum(dateTime.hour)}:${paddingNum(dateTime.minute)}"
|
|
22
|
+
else -> "${dateTime.year}-${paddingNum(dateTime.month.number)}-${paddingNum(dateTime.dayOfMonth)} ${paddingNum(dateTime.hour)}:${paddingNum(dateTime.minute)}"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Pad a number with a leading zero if it's less than 10
|
|
28
|
+
*/
|
|
29
|
+
fun paddingNum(num: Int): String {
|
|
30
|
+
return if (num > 9) num.toString() else "0$num"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get a list of days in a month
|
|
35
|
+
*/
|
|
36
|
+
fun getDaysInMonth(
|
|
37
|
+
year: Int,
|
|
38
|
+
month: Int,
|
|
39
|
+
minDate: LocalDateTime,
|
|
40
|
+
maxDate: LocalDateTime
|
|
41
|
+
): List<String> {
|
|
42
|
+
val daysInMonth = when (month) {
|
|
43
|
+
1, 3, 5, 7, 8, 10, 12 -> 31
|
|
44
|
+
4, 6, 9, 11 -> 30
|
|
45
|
+
2 -> if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) 29 else 28
|
|
46
|
+
else -> throw IllegalArgumentException("Invalid month: $month")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
var startDay = 1
|
|
50
|
+
var endDay = daysInMonth
|
|
51
|
+
|
|
52
|
+
if (year == minDate.year && month == minDate.month.number) {
|
|
53
|
+
startDay = minDate.dayOfMonth
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (year == maxDate.year && month == maxDate.month.number) {
|
|
57
|
+
endDay = maxDate.dayOfMonth
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
val days = mutableListOf<String>()
|
|
61
|
+
for (day in startDay..endDay) {
|
|
62
|
+
days.add(paddingNum(day))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return days
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get a list of months
|
|
70
|
+
*/
|
|
71
|
+
fun getMonths(
|
|
72
|
+
minDate: LocalDateTime,
|
|
73
|
+
maxDate: LocalDateTime,
|
|
74
|
+
currentYear: Int
|
|
75
|
+
): List<String> {
|
|
76
|
+
var startMonth = 1
|
|
77
|
+
var endMonth = 12
|
|
78
|
+
|
|
79
|
+
if (currentYear == minDate.year) {
|
|
80
|
+
startMonth = minDate.month.number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (currentYear == maxDate.year) {
|
|
84
|
+
endMonth = maxDate.month.number
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
val months = mutableListOf<String>()
|
|
88
|
+
for (month in startMonth..endMonth) {
|
|
89
|
+
months.add(paddingNum(month))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return months
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get a list of years
|
|
97
|
+
*/
|
|
98
|
+
fun getYears(minDate: LocalDateTime, maxDate: LocalDateTime): List<String> {
|
|
99
|
+
val startYear = minDate.year
|
|
100
|
+
val endYear = maxDate.year
|
|
101
|
+
val years = mutableListOf<String>()
|
|
102
|
+
|
|
103
|
+
for (year in startYear..endYear) {
|
|
104
|
+
years.add(year.toString())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return years
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get a list of hours
|
|
112
|
+
*/
|
|
113
|
+
fun getHours(hourMode: Int): List<String> {
|
|
114
|
+
val hours = mutableListOf<String>()
|
|
115
|
+
for (i in 0 until hourMode) {
|
|
116
|
+
val hour = if (hourMode == 12) i + 1 else i
|
|
117
|
+
hours.add(paddingNum(hour))
|
|
118
|
+
}
|
|
119
|
+
return hours
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get a list of minutes
|
|
124
|
+
*/
|
|
125
|
+
fun getMinutes(interval: Int): List<String> {
|
|
126
|
+
val minutes = mutableListOf<String>()
|
|
127
|
+
var min = 0
|
|
128
|
+
while (min < 60) {
|
|
129
|
+
minutes.add(paddingNum(min))
|
|
130
|
+
min += interval
|
|
131
|
+
}
|
|
132
|
+
return minutes
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Time mode for 12-hour clock
|
|
137
|
+
*/
|
|
138
|
+
val timeMode = listOf("AM", "PM")
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get today's date
|
|
142
|
+
*/
|
|
143
|
+
fun getCurrentDateTime(): LocalDateTime {
|
|
144
|
+
return Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a date that's a certain number of years before or after the current date
|
|
149
|
+
*/
|
|
150
|
+
fun createRelativeDate(years: Int): LocalDateTime {
|
|
151
|
+
val currentDateTime = getCurrentDateTime()
|
|
152
|
+
val period = DatePeriod(years = if (years < 0) -years else years)
|
|
153
|
+
|
|
154
|
+
val adjustedDate = if (years < 0) {
|
|
155
|
+
currentDateTime.date.minus(period)
|
|
156
|
+
} else {
|
|
157
|
+
currentDateTime.date.plus(period)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return LocalDateTime(
|
|
161
|
+
adjustedDate.year,
|
|
162
|
+
adjustedDate.monthNumber,
|
|
163
|
+
adjustedDate.dayOfMonth,
|
|
164
|
+
currentDateTime.hour,
|
|
165
|
+
currentDateTime.minute
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@Composable
|
|
170
|
+
internal fun getDateComponents(
|
|
171
|
+
format: String,
|
|
172
|
+
currentDate: PickerData,
|
|
173
|
+
effectiveMinDate: LocalDateTime,
|
|
174
|
+
effectiveMaxDate: LocalDateTime,
|
|
175
|
+
minuteInterval: Int,
|
|
176
|
+
): List<DateComponent> {
|
|
177
|
+
val formatParts = remember(format) {
|
|
178
|
+
format.split(Regex("[^A-Za-z]+"))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
val isOnlyHour = remember(formatParts) {
|
|
182
|
+
formatParts.size == 1 && formatParts[0] == "HH"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
val dayData = remember(currentDate.year, currentDate.month, effectiveMinDate, effectiveMaxDate) {
|
|
186
|
+
getDaysInMonth(currentDate.year, currentDate.month, effectiveMinDate, effectiveMaxDate)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
val monthData = remember(currentDate.year, effectiveMinDate, effectiveMaxDate) {
|
|
190
|
+
getMonths(effectiveMinDate, effectiveMaxDate, currentDate.year)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
val yearData = remember(effectiveMinDate, effectiveMaxDate) {
|
|
194
|
+
getYears(effectiveMinDate, effectiveMaxDate)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
val hourData = remember(isOnlyHour) {
|
|
198
|
+
getHours(if (isOnlyHour) 12 else 24)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
val minData = remember(minuteInterval) {
|
|
202
|
+
getMinutes(minuteInterval)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
val dateComponents = remember(formatParts, dayData, monthData, yearData, hourData, minData, isOnlyHour) {
|
|
206
|
+
val components = mutableListOf<DateComponent>()
|
|
207
|
+
|
|
208
|
+
formatParts.forEach { part ->
|
|
209
|
+
when (part) {
|
|
210
|
+
"DD" -> components.add(DateComponent("day", dayData))
|
|
211
|
+
"MM" -> components.add(DateComponent("month", monthData))
|
|
212
|
+
"YYYY" -> components.add(DateComponent("year", yearData))
|
|
213
|
+
"HH" -> components.add(DateComponent("hour", hourData))
|
|
214
|
+
"mm" -> components.add(DateComponent("minute", minData))
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (isOnlyHour) {
|
|
219
|
+
components.add(DateComponent("timeMode", timeMode))
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
components
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return dateComponents
|
|
226
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
package vn.momo.kits.components.dateTimePicker
|
|
2
|
+
|
|
3
|
+
import androidx.compose.animation.core.tween
|
|
4
|
+
import androidx.compose.foundation.background
|
|
5
|
+
import androidx.compose.foundation.border
|
|
6
|
+
import androidx.compose.foundation.gestures.Orientation
|
|
7
|
+
import androidx.compose.foundation.gestures.animateScrollBy
|
|
8
|
+
import androidx.compose.foundation.gestures.draggable
|
|
9
|
+
import androidx.compose.foundation.gestures.rememberDraggableState
|
|
10
|
+
import androidx.compose.foundation.gestures.scrollBy
|
|
11
|
+
import androidx.compose.foundation.layout.Box
|
|
12
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
13
|
+
import androidx.compose.foundation.layout.height
|
|
14
|
+
import androidx.compose.foundation.lazy.LazyColumn
|
|
15
|
+
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
16
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
17
|
+
import androidx.compose.runtime.Composable
|
|
18
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
19
|
+
import androidx.compose.runtime.derivedStateOf
|
|
20
|
+
import androidx.compose.runtime.getValue
|
|
21
|
+
import androidx.compose.runtime.remember
|
|
22
|
+
import androidx.compose.runtime.rememberCoroutineScope
|
|
23
|
+
import androidx.compose.ui.Alignment
|
|
24
|
+
import androidx.compose.ui.Modifier
|
|
25
|
+
import androidx.compose.ui.draw.alpha
|
|
26
|
+
import androidx.compose.ui.draw.scale
|
|
27
|
+
import androidx.compose.ui.platform.LocalDensity
|
|
28
|
+
import androidx.compose.ui.unit.Dp
|
|
29
|
+
import androidx.compose.ui.unit.dp
|
|
30
|
+
import androidx.compose.ui.zIndex
|
|
31
|
+
import kotlinx.coroutines.CoroutineScope
|
|
32
|
+
import kotlinx.coroutines.launch
|
|
33
|
+
import vn.momo.kits.components.Text
|
|
34
|
+
import vn.momo.kits.const.AppTheme
|
|
35
|
+
import vn.momo.kits.const.Colors
|
|
36
|
+
import vn.momo.kits.const.Radius
|
|
37
|
+
import vn.momo.kits.const.Typography
|
|
38
|
+
import kotlin.math.abs
|
|
39
|
+
import kotlin.math.roundToInt
|
|
40
|
+
|
|
41
|
+
private val wheelItemHeight = 42.dp
|
|
42
|
+
private val wheelPickerBorderWidth = 1.dp
|
|
43
|
+
|
|
44
|
+
private fun onDragStopped(
|
|
45
|
+
listState: androidx.compose.foundation.lazy.LazyListState,
|
|
46
|
+
itemHeight: Float,
|
|
47
|
+
coroutineScope: CoroutineScope
|
|
48
|
+
) {
|
|
49
|
+
|
|
50
|
+
coroutineScope.launch {
|
|
51
|
+
val targetPosition = listState.firstVisibleItemScrollOffset / itemHeight
|
|
52
|
+
val targetOffset = if (targetPosition.roundToInt() == targetPosition.toInt()) {
|
|
53
|
+
0f
|
|
54
|
+
} else {
|
|
55
|
+
if (targetPosition - targetPosition.toInt() > 0.5f) {
|
|
56
|
+
(1 - (targetPosition - targetPosition.toInt())) * itemHeight
|
|
57
|
+
} else {
|
|
58
|
+
-(targetPosition - targetPosition.toInt()) * itemHeight
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
listState.animateScrollBy(targetOffset, tween(150))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Composable
|
|
67
|
+
fun WheelPicker(
|
|
68
|
+
name: String,
|
|
69
|
+
data: List<String>,
|
|
70
|
+
selectedData: String,
|
|
71
|
+
onChange: (String, String) -> Unit,
|
|
72
|
+
modifier: Modifier = Modifier
|
|
73
|
+
) {
|
|
74
|
+
val coroutineScope = rememberCoroutineScope()
|
|
75
|
+
val listState = rememberLazyListState()
|
|
76
|
+
val itemHeightPx = with(LocalDensity.current) { wheelItemHeight.toPx() }
|
|
77
|
+
|
|
78
|
+
val paddedData = remember(data) {
|
|
79
|
+
listOf("", "") + data + listOf("", "")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
val initialIndex = remember(selectedData, paddedData) {
|
|
83
|
+
paddedData.indexOf(selectedData).coerceAtLeast(2)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
LaunchedEffect(initialIndex) {
|
|
87
|
+
listState.scrollToItem(initialIndex - 2)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
val selectedIndex by remember {
|
|
91
|
+
derivedStateOf {
|
|
92
|
+
(listState.firstVisibleItemIndex + 2).coerceIn(2, paddedData.size - 3)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
LaunchedEffect(listState.isScrollInProgress) {
|
|
97
|
+
if (!listState.isScrollInProgress) {
|
|
98
|
+
val centeredIndex = selectedIndex
|
|
99
|
+
if (centeredIndex in 2 until paddedData.size - 2) {
|
|
100
|
+
onChange(name, paddedData[centeredIndex])
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Box(
|
|
106
|
+
modifier = modifier
|
|
107
|
+
.border(
|
|
108
|
+
width = wheelPickerBorderWidth,
|
|
109
|
+
color = AppTheme.current.colors.border.default,
|
|
110
|
+
shape = RoundedCornerShape(Radius.S)
|
|
111
|
+
)
|
|
112
|
+
.background(
|
|
113
|
+
color = AppTheme.current.colors.background.surface,
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
LazyColumn(
|
|
117
|
+
state = listState,
|
|
118
|
+
modifier = Modifier
|
|
119
|
+
.fillMaxWidth()
|
|
120
|
+
.draggable(
|
|
121
|
+
orientation = Orientation.Vertical,
|
|
122
|
+
state = rememberDraggableState { delta ->
|
|
123
|
+
coroutineScope.launch {
|
|
124
|
+
listState.scrollBy(-delta)
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
onDragStopped = {
|
|
128
|
+
onDragStopped(
|
|
129
|
+
listState = listState,
|
|
130
|
+
itemHeight = itemHeightPx,
|
|
131
|
+
coroutineScope = coroutineScope
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
) {
|
|
136
|
+
items(paddedData.size) { index ->
|
|
137
|
+
WheelPickerItem(
|
|
138
|
+
text = paddedData[index],
|
|
139
|
+
isSelected = index == selectedIndex,
|
|
140
|
+
index = index,
|
|
141
|
+
listState = listState,
|
|
142
|
+
itemHeight = wheelItemHeight
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Box(
|
|
148
|
+
modifier = Modifier
|
|
149
|
+
.fillMaxWidth()
|
|
150
|
+
.height(wheelItemHeight)
|
|
151
|
+
.background(Colors.blue_10)
|
|
152
|
+
.align(Alignment.Center)
|
|
153
|
+
.alpha(0.2f)
|
|
154
|
+
.zIndex(-2f)
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@Composable
|
|
160
|
+
fun WheelPickerItem(
|
|
161
|
+
text: String,
|
|
162
|
+
isSelected: Boolean,
|
|
163
|
+
index: Int,
|
|
164
|
+
listState: androidx.compose.foundation.lazy.LazyListState,
|
|
165
|
+
itemHeight: Dp
|
|
166
|
+
) {
|
|
167
|
+
val firstVisibleIndex = listState.firstVisibleItemIndex
|
|
168
|
+
val visibleItemOffset = listState.firstVisibleItemScrollOffset
|
|
169
|
+
|
|
170
|
+
val viewportHeight = 5 * itemHeight.value
|
|
171
|
+
val itemHeightValue = itemHeight.value
|
|
172
|
+
|
|
173
|
+
val absoluteOffset =
|
|
174
|
+
(index - firstVisibleIndex) * itemHeightValue - (visibleItemOffset / itemHeightValue)
|
|
175
|
+
val relativePosition = absoluteOffset / viewportHeight
|
|
176
|
+
|
|
177
|
+
val distance = abs(2 - relativePosition)
|
|
178
|
+
|
|
179
|
+
val opacity = when {
|
|
180
|
+
distance <= 1 -> 1f
|
|
181
|
+
distance <= 2 -> 0.8f
|
|
182
|
+
distance <= 3 -> 0.4f
|
|
183
|
+
else -> 0.4f
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
val scale = if (isSelected) 1f else 0.87f
|
|
187
|
+
|
|
188
|
+
Box(
|
|
189
|
+
modifier = Modifier
|
|
190
|
+
.fillMaxWidth()
|
|
191
|
+
.height(itemHeight)
|
|
192
|
+
.alpha(opacity),
|
|
193
|
+
contentAlignment = Alignment.Center
|
|
194
|
+
) {
|
|
195
|
+
Text(
|
|
196
|
+
text = text,
|
|
197
|
+
style = Typography.actionS,
|
|
198
|
+
modifier = Modifier.scale(scale)
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -13,12 +13,12 @@ public class MiniAppContext {
|
|
|
13
13
|
public var appName: Any? = nil
|
|
14
14
|
public var appIcon: String = ""
|
|
15
15
|
public var description: Any? = nil
|
|
16
|
-
public var support: Any
|
|
17
|
-
public var toolkitConfig: Any
|
|
18
|
-
public var providerId: String = ""
|
|
19
|
-
public var permissions: Any
|
|
20
|
-
|
|
21
|
-
public init(appId: String, appCode: String, appName: Any? = nil, appIcon: String, description: Any? = nil, support: Any
|
|
16
|
+
public var support: [String: Any] = [:]
|
|
17
|
+
public var toolkitConfig: [String: Any] = [:]
|
|
18
|
+
public var providerId: String = "momo"
|
|
19
|
+
public var permissions: [[String: Any]] = []
|
|
20
|
+
|
|
21
|
+
public init(appId: String, appCode: String, appName: Any? = nil, appIcon: String, description: Any? = nil, support: [String: Any] = [:], toolkitConfig: [String: Any] = [:], providerId: String = "momo", permissions: [[String: Any]] = []) {
|
|
22
22
|
self.appId = appId
|
|
23
23
|
self.appCode = appCode
|
|
24
24
|
self.appName = appName
|
|
@@ -31,12 +31,20 @@ public class MiniAppContext {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
public class KitConfig {
|
|
35
|
+
public var trustBanner: TrustBannerData? = nil
|
|
36
|
+
public var headerBar: String? = nil
|
|
37
|
+
public var headerGradient: String? = nil
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
public class ApplicationEnvironment: ObservableObject {
|
|
35
41
|
let applicationContext: MiniAppContext?
|
|
36
|
-
let composeApi: KitComposeApi
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
let composeApi: KitComposeApi?
|
|
43
|
+
let config: KitConfig?
|
|
44
|
+
|
|
45
|
+
public init(applicationContext: MiniAppContext? = nil, composeApi: KitComposeApi? = nil, config: KitConfig? = nil) {
|
|
39
46
|
self.applicationContext = applicationContext
|
|
40
47
|
self.composeApi = composeApi
|
|
48
|
+
self.config = config
|
|
41
49
|
}
|
|
42
50
|
}
|
|
@@ -6,7 +6,7 @@ public struct HeaderRightData {
|
|
|
6
6
|
var useMore: Bool
|
|
7
7
|
var tools: [ToolGroup]
|
|
8
8
|
var toolCallback: ((String) -> Void)?
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
public init(
|
|
11
11
|
useShortcut: Bool = false,
|
|
12
12
|
useMore: Bool = false,
|
|
@@ -25,7 +25,7 @@ public struct Tool {
|
|
|
25
25
|
var icon: String
|
|
26
26
|
var showBadge: Bool
|
|
27
27
|
var name: [String: String]
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
public init(
|
|
30
30
|
key: String,
|
|
31
31
|
icon: String,
|
|
@@ -37,7 +37,7 @@ public struct Tool {
|
|
|
37
37
|
self.showBadge = showBadge
|
|
38
38
|
self.name = name
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
func toMap() -> [String: Any] {
|
|
42
42
|
return [
|
|
43
43
|
"key": key,
|
|
@@ -51,7 +51,7 @@ public struct Tool {
|
|
|
51
51
|
public struct ToolGroup {
|
|
52
52
|
var title: [String: String]
|
|
53
53
|
var items: [Tool]
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
public init(
|
|
56
56
|
title: [String: String] = [:],
|
|
57
57
|
items: [Tool]
|
|
@@ -59,7 +59,7 @@ public struct ToolGroup {
|
|
|
59
59
|
self.title = title
|
|
60
60
|
self.items = items
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
func toMap() -> [String: Any] {
|
|
64
64
|
return [
|
|
65
65
|
"title": title,
|
|
@@ -79,7 +79,7 @@ public struct HeaderRight: View {
|
|
|
79
79
|
self.headerRight = headerRight
|
|
80
80
|
}
|
|
81
81
|
var headerRight: HeaderRightData?
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
public var body: some View {
|
|
84
84
|
HStack(alignment: .center) {
|
|
85
85
|
ToolkitHeaderRight(headerRight: headerRight)
|
|
@@ -92,17 +92,17 @@ struct ToolkitHeaderRight: View {
|
|
|
92
92
|
@State private var isFavorite: Bool = false
|
|
93
93
|
@State private var isLoading: Bool = false
|
|
94
94
|
@EnvironmentObject private var environment: ApplicationEnvironment
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
// MARK: - Actions
|
|
97
97
|
private func onPressShortcut() {
|
|
98
|
-
environment.composeApi
|
|
98
|
+
environment.composeApi?.request(
|
|
99
99
|
funcName: "onToolAction",
|
|
100
100
|
params: ["item": ["key": "onFavorite"]]
|
|
101
101
|
) { _ in
|
|
102
102
|
isFavorite.toggle()
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
private func onPressHelpCenter() {
|
|
107
107
|
let context = environment.applicationContext
|
|
108
108
|
let paramMap: [String: Any] = [
|
|
@@ -112,20 +112,19 @@ struct ToolkitHeaderRight: View {
|
|
|
112
112
|
"icon": context?.appIcon,
|
|
113
113
|
"description": context?.description
|
|
114
114
|
]
|
|
115
|
-
environment.composeApi
|
|
115
|
+
environment.composeApi?.request(
|
|
116
116
|
funcName: "showHelpCenter",
|
|
117
117
|
params: paramMap
|
|
118
118
|
) { _ in }
|
|
119
119
|
}
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
private func onPressClose() {
|
|
122
|
-
environment.composeApi
|
|
122
|
+
environment.composeApi?.request(
|
|
123
123
|
funcName: "dismiss",
|
|
124
124
|
params: ""
|
|
125
125
|
) { _ in }
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
|
|
129
128
|
private func onPressMore() {
|
|
130
129
|
let context = environment.applicationContext
|
|
131
130
|
let params: [String: Any] = [
|
|
@@ -136,13 +135,13 @@ struct ToolkitHeaderRight: View {
|
|
|
136
135
|
"name": context?.appName,
|
|
137
136
|
"icon": context?.appIcon,
|
|
138
137
|
"description": context?.description,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
"support": context?.support,
|
|
139
|
+
"toolkitConfig": context?.toolkitConfig,
|
|
140
|
+
"providerId": context?.providerId,
|
|
141
|
+
"permissions": context?.permissions
|
|
143
142
|
]
|
|
144
143
|
]
|
|
145
|
-
environment.composeApi
|
|
144
|
+
environment.composeApi?.request(
|
|
146
145
|
funcName: "showTools",
|
|
147
146
|
params: params
|
|
148
147
|
) { response in
|
|
@@ -157,12 +156,12 @@ struct ToolkitHeaderRight: View {
|
|
|
157
156
|
}
|
|
158
157
|
}
|
|
159
158
|
}
|
|
160
|
-
|
|
159
|
+
|
|
161
160
|
private func getNavigationButtonConfig() -> NavigationButtonConfig {
|
|
162
161
|
let totalTools = headerRight?.tools.reduce(0) { $0 + $1.items.count } ?? 0
|
|
163
162
|
var icon = isFavorite ? "pin_star_checked" : "pin_star"
|
|
164
163
|
var onClickHandler: () -> Void = onPressShortcut
|
|
165
|
-
|
|
164
|
+
|
|
166
165
|
if totalTools > 1 || headerRight?.useMore == true {
|
|
167
166
|
icon = "navigation_more_icon"
|
|
168
167
|
onClickHandler = onPressMore
|
|
@@ -172,16 +171,16 @@ struct ToolkitHeaderRight: View {
|
|
|
172
171
|
headerRight?.toolCallback?(singleTool.key)
|
|
173
172
|
}
|
|
174
173
|
}
|
|
175
|
-
|
|
174
|
+
|
|
176
175
|
return NavigationButtonConfig(icon: icon, onPress: onClickHandler)
|
|
177
176
|
}
|
|
178
|
-
|
|
177
|
+
|
|
179
178
|
var body: some View {
|
|
180
179
|
let navButtonConfig = getNavigationButtonConfig()
|
|
181
180
|
let showBadge = headerRight?.tools.contains { group in
|
|
182
181
|
group.items.contains { $0.showBadge }
|
|
183
182
|
} ?? false
|
|
184
|
-
|
|
183
|
+
|
|
185
184
|
HStack(alignment: .center) {
|
|
186
185
|
if headerRight?.useShortcut == true {
|
|
187
186
|
NavigationButton(
|
|
@@ -191,18 +190,18 @@ struct ToolkitHeaderRight: View {
|
|
|
191
190
|
onClick: navButtonConfig.onPress
|
|
192
191
|
)
|
|
193
192
|
}
|
|
194
|
-
|
|
193
|
+
|
|
195
194
|
HStack(alignment: .center, spacing: 0) {
|
|
196
195
|
Icon(source: "help_center", size: 20)
|
|
197
196
|
.padding(4)
|
|
198
197
|
.onTapGesture {
|
|
199
198
|
onPressHelpCenter()
|
|
200
199
|
}
|
|
201
|
-
|
|
200
|
+
|
|
202
201
|
Rectangle()
|
|
203
202
|
.fill(Colors.black20)
|
|
204
203
|
.frame(width: 0.5, height: 12)
|
|
205
|
-
|
|
204
|
+
|
|
206
205
|
Icon(source: "16_navigation_close_circle", size: 20, color: Colors.black17)
|
|
207
206
|
.padding(4)
|
|
208
207
|
.onTapGesture {
|
|
@@ -226,7 +225,7 @@ struct NavigationButton: View {
|
|
|
226
225
|
var icon: String
|
|
227
226
|
var showBadge: Bool
|
|
228
227
|
var onClick: () -> Void
|
|
229
|
-
|
|
228
|
+
|
|
230
229
|
var body: some View {
|
|
231
230
|
ZStack {
|
|
232
231
|
Circle()
|
|
@@ -235,9 +234,9 @@ struct NavigationButton: View {
|
|
|
235
234
|
Circle()
|
|
236
235
|
.stroke(Color.black.opacity(0.2), lineWidth: 0.2)
|
|
237
236
|
)
|
|
238
|
-
|
|
237
|
+
|
|
239
238
|
Icon(source: icon, size: 16)
|
|
240
|
-
|
|
239
|
+
|
|
241
240
|
if showBadge {
|
|
242
241
|
BadgeDot(size: .small)
|
|
243
242
|
.offset(x: -2, y: -2)
|