CustomExt.kt 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package org.mjdev.desktop.extensions
  2. import androidx.compose.runtime.Composable
  3. import androidx.compose.runtime.collectAsState
  4. import kotlinx.coroutines.delay
  5. import kotlinx.coroutines.flow.channelFlow
  6. import kotlinx.coroutines.launch
  7. import kotlinx.datetime.Instant
  8. import kotlinx.datetime.TimeZone.Companion.currentSystemDefault
  9. import kotlinx.datetime.toLocalDateTime
  10. import org.mjdev.desktop.extensions.System.currentTime
  11. import org.mjdev.desktop.helpers.generic.JsonHelper.fromJson
  12. import org.mjdev.desktop.helpers.generic.JsonHelper.toJson
  13. import org.mjdev.desktop.log.Log
  14. import kotlin.coroutines.suspendCoroutine
  15. import kotlin.time.ExperimentalTime
  16. @OptIn(ExperimentalTime::class)
  17. @Suppress("unused")
  18. object CustomExt {
  19. // fun runSafe(
  20. // shouldLog: Boolean = true,
  21. // block: () -> Unit
  22. // ) = runCatching {
  23. // block()
  24. // }.onFailure { err ->
  25. // if (shouldLog) Log.e(err)
  26. // }
  27. // todo
  28. private fun Instant.formatDate(): String {
  29. val localDateTime = toLocalDateTime(currentSystemDefault())
  30. val day =
  31. localDateTime.date.dayOfMonth
  32. .toString()
  33. .padStart(2, '0')
  34. val month =
  35. localDateTime.date.monthNumber
  36. .toString()
  37. .padStart(2, '0')
  38. val year = localDateTime.date.year.toString()
  39. return "$day.$month.$year"
  40. }
  41. // todo
  42. private fun Instant.formatTime(): String {
  43. val localDateTime = toLocalDateTime(currentSystemDefault())
  44. val hours = localDateTime.hour.toString().padStart(2, '0')
  45. val minutes = localDateTime.minute.toString().padStart(2, '0')
  46. val seconds = localDateTime.second.toString().padStart(2, '0')
  47. return "$hours:$minutes:$seconds"
  48. }
  49. val dateFlow
  50. @Composable
  51. get() =
  52. channelFlow {
  53. launch {
  54. var date = "1.1.1970"
  55. do {
  56. // todo
  57. currentTime.formatDate().also { t ->
  58. if (date != t) {
  59. date = t
  60. send(date)
  61. }
  62. }
  63. delay(5000L)
  64. } while (true)
  65. }
  66. }.collectAsState(initial = "")
  67. val timeFlow
  68. @Composable
  69. get() =
  70. channelFlow {
  71. launch {
  72. var time = "00:00:00"
  73. do {
  74. currentTime.formatTime().also { t ->
  75. if (time != t) {
  76. time = t
  77. send(time)
  78. }
  79. }
  80. // sleep to just past the next whole second (~1 wake/s) instead of polling 5×/s
  81. delay((1000L - (currentTime.toEpochMilliseconds() % 1000)).coerceAtLeast(1L))
  82. } while (true)
  83. }
  84. }.collectAsState(initial = "")
  85. // inline fun <T, K> distinctList(
  86. // vararg lists: List<T>,
  87. // selector: (T) -> K
  88. // ): List<T> = mutableListOf<T>().apply {
  89. // lists.forEach { l -> addAll(l) }
  90. // }.distinctBy(selector)
  91. // fun String.notStartsWith(
  92. // prefix: String,
  93. // ignoreCase: Boolean = false
  94. // ) = !startsWith(prefix, ignoreCase)
  95. // fun String.trimIsEmpty() =
  96. // trim().isEmpty()
  97. // fun String.trimIsNotEmpty() =
  98. // trim().isNotEmpty()
  99. // fun State<String>.trimIsEmpty() =
  100. // value.trim().isEmpty()
  101. // fun String.trimStartsWith(
  102. // prefix: String,
  103. // ignoreCase: Boolean = false
  104. // ) = trim().startsWith(prefix, ignoreCase)
  105. // fun String.trimNotStartsWith(
  106. // prefix: String,
  107. // ignoreCase: Boolean = false
  108. // ) = !trim().startsWith(prefix, ignoreCase)
  109. suspend fun <T : Any> getAsync(block: () -> T): T =
  110. suspendCoroutine { continuation ->
  111. runCatching {
  112. block()
  113. }.onSuccess { result ->
  114. continuation.resumeWith(Result.success(result))
  115. }.onFailure { e ->
  116. continuation.resumeWith(Result.failure(e))
  117. }
  118. }
  119. inline fun <reified T> String.jsonToList(): List<T> =
  120. runCatching {
  121. fromJson<List<T>>(this)
  122. }.onFailure { err ->
  123. Log.e(err)
  124. }.getOrNull() ?: emptyList()
  125. inline fun <reified T> String.to(): T? =
  126. runCatching {
  127. fromJson<T>(this)
  128. }.onFailure { err ->
  129. Log.e(err)
  130. }.getOrNull()
  131. inline fun <reified T> T.asJson(): String =
  132. runCatching {
  133. toJson()
  134. }.onFailure { err ->
  135. Log.e(err)
  136. }.getOrDefault("")
  137. // @ExperimentalSerializationApi
  138. // class DynamicLookupSerializer : KSerializer<Any> {
  139. // override val descriptor: SerialDescriptor = ContextualSerializer(
  140. // Any::class,
  141. // null,
  142. // emptyArray()
  143. // ).descriptor
  144. //
  145. // @Suppress("UNCHECKED_CAST")
  146. // @OptIn(InternalSerializationApi::class)
  147. // override fun serialize(
  148. // encoder: Encoder,
  149. // value: Any
  150. // ) {
  151. // if (value is List<*>) {
  152. // encoder.encodeSerializableValue(
  153. // ListSerializer(
  154. // DynamicLookupSerializer()
  155. // ),
  156. // value as List<Any>
  157. // )
  158. // return
  159. // }
  160. // encoder.encodeSerializableValue(
  161. // encoder.serializersModule.getContextual(
  162. // value::class
  163. // ) ?: value::class.serializer(),
  164. // value
  165. // )
  166. // }
  167. //
  168. // override fun deserialize(decoder: Decoder): Any {
  169. // error("Unsupported")
  170. // }
  171. // }
  172. }