From 1d69082a31bb3a1dd171bd0d946ae40382547a94 Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 12:00:11 -0400 Subject: [PATCH 01/17] Create .editorconfig --- .editorconfig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..a8c52379 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +[*.{kt,kts}] +end_of_line = lf +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_imports_layout = * +ij_kotlin_indent_before_arrow_on_new_line = false +ij_kotlin_line_break_after_multiline_when_entry = true +ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.** +indent_size = 4 +indent_style = space +insert_final_newline = true +ktlint_argument_list_wrapping_ignore_when_parameter_count_greater_or_equal_than = 8 +ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = 4 +ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset +ktlint_code_style = android_studio +ktlint_enum_entry_name_casing = upper_or_camel_cases +ktlint_function_naming_ignore_when_annotated_with = [unset] +ktlint_function_signature_body_expression_wrapping = default +ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset +ktlint_ignore_back_ticked_identifier = false +max_line_length = 100 From b97f85e14959118bb36a82eba0dfea7cee498dda Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 12:04:04 -0400 Subject: [PATCH 02/17] formatting --- android/build.gradle.kts | 5 ++++- .../co/touchlab/droidcon/android/MainActivity.kt | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 24540f66..a3c5aaeb 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -47,7 +47,10 @@ android { if (releaseEnabled) { getByName("release") { isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) signingConfig = signingConfigs.getByName("release") } } diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt index 86e57adf..07d5cc68 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt @@ -154,8 +154,16 @@ class MainActivity : ComponentActivity(), KoinComponent { private fun askNotificationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { - } else if (false && shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { + if ( + ContextCompat.checkSelfPermission( + this, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + ) { + } else if ( + false && + shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) + ) { // TODO: display an educational UI explaining to the user the features that will be enabled // by them granting the POST_NOTIFICATION permission. This UI should provide the user // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission. From 7ab4ad1e223da0a707ac08e63f1de90070c4032c Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 12:21:08 -0400 Subject: [PATCH 03/17] formatting --- .../co/touchlab/droidcon/android/MainApp.kt | 5 +- .../impl/DefaultFirebaseMessagingService.kt | 10 ++- .../NotificationLocalizedStringFactory.kt | 4 +- .../NotificationLocalizedStringFactory.kt | 26 ++++++- .../ios/util/formatter/IOSDateFormatter.kt | 27 +++++-- .../ui/settings/PlatformSpecificSettings.kt | 5 +- .../co/touchlab/droidcon/ui/util/Dialog.kt | 8 +- .../droidcon/ui/util/LocalImage.jvm.kt | 21 ++++- .../ui/BottomNavigationView.kt | 8 +- .../ui/settings/SettingsView.kt | 6 +- .../ui/util/LocalImage.kt | 12 ++- .../util/NavigationController.kt | 63 +++++++++++---- .../viewmodel/session/AgendaViewModel.kt | 8 +- .../session/BaseSessionListViewModel.kt | 9 ++- .../viewmodel/session/ScheduleViewModel.kt | 8 +- .../session/SessionBlockViewModel.kt | 8 +- .../SessionDetailScrollStateStorage.kt | 11 ++- .../session/SessionDetailViewModel.kt | 14 +++- .../viewmodel/settings/AboutViewModel.kt | 5 +- .../sponsor/SponsorDetailViewModel.kt | 8 +- .../viewmodel/sponsor/SponsorListViewModel.kt | 8 +- .../touchlab/droidcon/ui/util/LocalImage.kt | 22 +++++- .../ui/util/NavigationBackPressWrapper.kt | 5 +- .../touchlab/droidcon/ui/util/ToSkiaImage.kt | 23 ++++-- .../co/touchlab/droidcon/Koin.android.kt | 10 ++- .../service/AndroidNotificationService.kt | 38 ++++++--- .../droidcon/service/NotificationPublisher.kt | 9 ++- .../util/formatter/AndroidDateFormatter.kt | 21 +++-- .../service/NotificationService.kt | 8 +- .../DefaultNotificationSchedulingService.kt | 32 ++++++-- .../impl/SqlDelightSessionRepository.kt | 18 +++-- .../impl/SqlDelightSponsorGroupRepository.kt | 12 ++- .../impl/SqlDelightSponsorRepository.kt | 9 ++- .../domain/service/impl/DefaultSyncService.kt | 77 +++++++++++++------ .../domain/service/impl/dto/SpeakersDto.kt | 5 +- .../impl/json/AboutJsonResourceDataSource.kt | 5 +- .../kotlin/co/touchlab/droidcon/Koin.ios.kt | 34 ++++++-- .../kotlin/co/touchlab/droidcon/MainScope.kt | 5 +- .../service/IOSNotificationService.kt | 67 ++++++++++++---- .../co/touchlab/droidcon/util/AppInit.kt | 7 +- .../droidcon/util/BundleResourceReader.kt | 22 +++++- .../droidcon/util/wrapMultiThreadCallback.kt | 13 ++-- 42 files changed, 553 insertions(+), 163 deletions(-) diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt index b96e87dc..a2acb4b6 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt @@ -31,7 +31,10 @@ class MainApp : Application() { single { this@MainApp } single> { MainActivity::class.java } single { - get().getSharedPreferences("DROIDCON_SETTINGS_2023", Context.MODE_PRIVATE) + get().getSharedPreferences( + "DROIDCON_SETTINGS_2023", + Context.MODE_PRIVATE + ) } single { SharedPreferencesSettings(delegate = get()) } diff --git a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt index 2fce49a7..277ab102 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt @@ -22,7 +22,10 @@ class DefaultFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) - if (message.data.isNotEmpty() && message.data[Notification.Keys.notificationType] == Notification.Values.refreshDataType) { + if ( + message.data.isNotEmpty() && + message.data[Notification.Keys.notificationType] == Notification.Values.refreshDataType + ) { MainScope().launch { notificationService.handleNotification( Notification.Remote.RefreshData @@ -32,7 +35,10 @@ class DefaultFirebaseMessagingService : FirebaseMessagingService() { // If we have notification, we're running in foreground and should show it ourselves. val originalNotification = message.notification ?: return - val notification = NotificationCompat.Builder(this, message.notification?.channelId ?: "") + val notification = NotificationCompat.Builder( + this, + message.notification?.channelId ?: "" + ) .setContentTitle(originalNotification.title) .setContentText(originalNotification.body) .apply { diff --git a/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt b/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt index 96f07e38..15b9286a 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt @@ -9,7 +9,9 @@ class NotificationLocalizedStringFactory( ) : NotificationSchedulingService.LocalizedStringFactory { override fun reminderTitle(roomName: String?): String { - val ending = roomName?.let { context.getString(R.string.notification_reminder_title_in_room, it) } ?: "" + val ending = roomName?.let { + context.getString(R.string.notification_reminder_title_in_room, it) + } ?: "" return context.getString(R.string.notification_reminder_title_base, ending) } diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt index 783791d1..83d0cf36 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt @@ -12,12 +12,32 @@ class NotificationLocalizedStringFactory( ) : NotificationSchedulingService.LocalizedStringFactory { override fun reminderTitle(roomName: String?): String { - val ending = roomName?.let { NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null).convertParametersForPrintf(), it.cstr) } ?: "" - return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.Base", null, null).convertParametersForPrintf(), ending.cstr) + val ending = roomName?.let { + NSString.stringWithFormat( + bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null) + .convertParametersForPrintf(), + it.cstr + ) + } ?: "" + return NSString.stringWithFormat( + bundle.localizedStringForKey( + "Notification.Reminder.Title.Base", + null, + null + ).convertParametersForPrintf(), + ending.cstr + ) } override fun reminderBody(sessionTitle: String): String { - return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Body", null, null).convertParametersForPrintf(), sessionTitle.cstr) + return NSString.stringWithFormat( + bundle.localizedStringForKey( + "Notification.Reminder.Body", + null, + null + ).convertParametersForPrintf(), + sessionTitle.cstr + ) } override fun feedbackTitle(): String { diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt index 0e1385c7..6b732bad 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt @@ -16,7 +16,11 @@ class IOSDateFormatter : DateFormatter { private val monthWithDay: NSDateFormatter by lazy { NSDateFormatter().also { val dateTemplate = "MMM d" - it.dateFormat = NSDateFormatter.dateFormatFromTemplate(dateTemplate, 0.toULong(), NSLocale.currentLocale)!! + it.dateFormat = NSDateFormatter.dateFormatFromTemplate( + dateTemplate, + 0.toULong(), + NSLocale.currentLocale + )!! } } @@ -30,7 +34,11 @@ class IOSDateFormatter : DateFormatter { private val timeOnlyNoPeriod: NSDateFormatter by lazy { NSDateFormatter().also { val dateTemplate = "hh:mm" - it.dateFormat = NSDateFormatter.dateFormatFromTemplate(dateTemplate, 0.toULong(), NSLocale.currentLocale)!! + it.dateFormat = NSDateFormatter.dateFormatFromTemplate( + dateTemplate, + 0.toULong(), + NSLocale.currentLocale + )!! } } @@ -40,15 +48,18 @@ class IOSDateFormatter : DateFormatter { override fun timeOnly(dateTime: LocalDateTime) = dateTime.date()?.let { timeOnly.stringFromDate(it) } - override fun timeOnlyInterval(fromDateTime: LocalDateTime, toDateTime: LocalDateTime) = interval( - fromDateTime.date()?.let { timeOnlyNoPeriod.stringFromDate(it) }, - toDateTime.date()?.let { timeOnly.stringFromDate(it) } - ) + override fun timeOnlyInterval(fromDateTime: LocalDateTime, toDateTime: LocalDateTime) = + interval( + fromDateTime.date()?.let { timeOnlyNoPeriod.stringFromDate(it) }, + toDateTime.date()?.let { timeOnly.stringFromDate(it) } + ) - private fun LocalDate.date() = NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) + private fun LocalDate.date() = + NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) private fun LocalDateTime.date() = - NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating + // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating + NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) private fun interval(from: String?, to: String?) = listOfNotNull(from, to).joinToString(" – ") } diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt index 629b1226..2bc66830 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt @@ -19,7 +19,10 @@ internal actual fun PlatformSwitchApp() { val context = LocalContext.current Button( onClick = { - val intent = context.packageManager.getLaunchIntentForPackage(Constants.SisterApp.androidPackageName) + val intent = + context + .packageManager + .getLaunchIntentForPackage(Constants.SisterApp.androidPackageName) if (intent != null) { context.startActivity(intent) } else { diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt index 1a040985..538f05ea 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt @@ -2,15 +2,19 @@ package co.touchlab.droidcon.ui.util import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.Dialog as AndroidXComposeDialog +import androidx.compose.ui.window.DialogProperties @OptIn(ExperimentalComposeUiApi::class) @Composable internal actual fun Dialog(dismiss: () -> Unit, content: @Composable () -> Unit) { AndroidXComposeDialog( onDismissRequest = dismiss, - properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false, usePlatformDefaultWidth = false), + properties = DialogProperties( + dismissOnBackPress = false, + dismissOnClickOutside = false, + usePlatformDefaultWidth = false + ), ) { content() } diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt index 7ab27810..4acc58b1 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt @@ -24,9 +24,15 @@ import co.touchlab.droidcon.ui.theme.Dimensions // platforms and on each platform we need to get the drawable according to provided name. @SuppressLint("ComposableNaming", "DiscouragedApi") @Composable -internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, contentDescription: String?) { +internal actual fun __LocalImage( + imageResourceName: String, + modifier: Modifier, + contentDescription: String? +) { val context = LocalContext.current - val imageRes = context.resources.getIdentifier(imageResourceName, "drawable", context.packageName).takeIf { it != 0 } + val imageRes = + context.resources.getIdentifier(imageResourceName, "drawable", context.packageName) + .takeIf { it != 0 } if (imageRes != null) { androidx.compose.foundation.Image( modifier = modifier, @@ -36,7 +42,10 @@ internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, ) } else { Row( - modifier = modifier.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(Dimensions.Padding.half)), + modifier = modifier.background( + MaterialTheme.colorScheme.primary, + RoundedCornerShape(Dimensions.Padding.half) + ), verticalAlignment = Alignment.CenterVertically ) { Spacer(modifier = Modifier.weight(1f)) @@ -46,7 +55,11 @@ internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, modifier = Modifier.padding(Dimensions.Padding.half), tint = Color.White ) - Text("Image not supported", modifier = Modifier.padding(Dimensions.Padding.default), color = Color.White) + Text( + "Image not supported", + modifier = Modifier.padding(Dimensions.Padding.default), + color = Color.White + ) Spacer(modifier = Modifier.weight(1f)) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt index ed98270f..fc77d37c 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt @@ -35,11 +35,13 @@ internal fun BottomNavigationView(viewModel: ApplicationViewModel, modifier: Mod NavigationBar { viewModel.tabs.forEach { tab -> val (title, icon) = when (tab) { - ApplicationViewModel.Tab.Schedule -> "Schedule" to Icons.Filled.CalendarMonth + ApplicationViewModel.Tab.Schedule -> + "Schedule" to Icons.Filled.CalendarMonth // FIXME: Was originally "My agenda" but then it doesn't seem to fit. ApplicationViewModel.Tab.MyAgenda -> "Agenda" to Icons.Filled.Schedule ApplicationViewModel.Tab.Venue -> "Venue" to Icons.Filled.Map - ApplicationViewModel.Tab.Sponsors -> "Sponsors" to Icons.Filled.LocalFireDepartment + ApplicationViewModel.Tab.Sponsors -> + "Sponsors" to Icons.Filled.LocalFireDepartment ApplicationViewModel.Tab.Settings -> "Settings" to Icons.Filled.Settings } NavigationBarItem( @@ -66,11 +68,13 @@ internal fun BottomNavigationView(viewModel: ApplicationViewModel, modifier: Mod title = "Droidcon NYC 2024", emptyText = "Sessions could not be loaded.", ) + ApplicationViewModel.Tab.MyAgenda -> SessionListView( viewModel = viewModel.agenda, title = "Agenda", emptyText = "Add sessions to your agenda from session detail in schedule.", ) + ApplicationViewModel.Tab.Venue -> VenueView() ApplicationViewModel.Tab.Sponsors -> SponsorsView(viewModel.sponsors) ApplicationViewModel.Tab.Settings -> SettingsView(viewModel.settings) diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt index dab5b8bb..c161768d 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt @@ -79,7 +79,11 @@ internal fun SettingsView(viewModel: SettingsViewModel) { } @Composable -internal fun IconTextSwitchRow(text: String, image: ImageVector, checked: MutableObservableProperty) { +internal fun IconTextSwitchRow( + text: String, + image: ImageVector, + checked: MutableObservableProperty +) { val isChecked by checked.observeAsState() Row( modifier = Modifier diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt index b5680f41..93656900 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt @@ -4,9 +4,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @Composable -internal expect fun __LocalImage(imageResourceName: String, modifier: Modifier, contentDescription: String?) +internal expect fun __LocalImage( + imageResourceName: String, + modifier: Modifier, + contentDescription: String? +) @Composable -internal fun LocalImage(imageResourceName: String, modifier: Modifier = Modifier, contentDescription: String? = null) { +internal fun LocalImage( + imageResourceName: String, + modifier: Modifier = Modifier, + contentDescription: String? = null +) { __LocalImage(imageResourceName, modifier, contentDescription) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt index 76f94f92..957f4a21 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt @@ -36,7 +36,10 @@ private val LocalNavigationViewDimensions = staticCompositionLocalOf() - private var stackTracking: MutableList by published(stack, equalityPolicy = neverEqualPolicy()) + private var stackTracking: MutableList by published( + stack, + equalityPolicy = neverEqualPolicy() + ) private val observeStack by observe(::stackTracking) private var activeChild: NavigationController? = null @@ -46,17 +49,23 @@ class NavigationController : BaseViewModel() { } internal sealed class NavigationStackItem { - class BackPressHandler(val onBackPressed: BackPressHandlerScope.() -> Unit) : NavigationStackItem() { + class BackPressHandler(val onBackPressed: BackPressHandlerScope.() -> Unit) : + NavigationStackItem() { override fun toString(): String { return "BackPress@${hashCode().toUInt().toString(16)}" } } - class Push(val item: MutableObservableProperty, val content: @Composable (T) -> Unit) : NavigationStackItem() { + class Push( + val item: MutableObservableProperty, + val content: @Composable (T) -> Unit + ) : NavigationStackItem() { override fun toString(): String { - return "Push(${item.value}@${item.hashCode().toUInt().toString(16)})@${hashCode().toUInt().toString(16)}" + return "Push(${item.value}@${ + item.hashCode().toUInt().toString(16) + })@${hashCode().toUInt().toString(16)}" } } } @@ -130,7 +139,10 @@ class NavigationController : BaseViewModel() { } @Composable - private fun PushedStackItem(item: NavigationStackItem.Push, itemModifier: Modifier) { + private fun PushedStackItem( + item: NavigationStackItem.Push, + itemModifier: Modifier + ) { println("$item") val itemValue by item.item.observeAsState() @@ -142,7 +154,10 @@ class NavigationController : BaseViewModel() { } @Composable - internal fun Pushed(item: MutableObservableProperty, content: @Composable (T) -> Unit) { + internal fun Pushed( + item: MutableObservableProperty, + content: @Composable (T) -> Unit + ) { remember { val stackItem = NavigationStackItem.Push(item, content).also { notifyingStackChange { @@ -206,14 +221,19 @@ private class ReferenceTracking(private val onDispose: () -> Unit) : RememberObs } @Composable -internal fun BackPressHandler(onBackPressed: NavigationController.BackPressHandlerScope.() -> Unit) { +internal fun BackPressHandler( + onBackPressed: NavigationController.BackPressHandlerScope.() -> Unit +) { val navigationController = LocalNavigationController.current navigationController.HandleBackPressEffect(onBackPressed) } internal interface NavigationStackScope { - fun NavigationLink(item: MutableObservableProperty, content: @Composable (T) -> Unit) + fun NavigationLink( + item: MutableObservableProperty, + content: @Composable (T) -> Unit + ) } internal class NavigationLinkWrapper( @@ -236,7 +256,8 @@ internal class NavigationLinkWrapper( } override fun equals(other: Any?): Boolean { - return (other as? NavigationLinkWrapper<*>)?.let { it.index == index && it.value == value } ?: false + return (other as? NavigationLinkWrapper<*>)?.let { it.index == index && it.value == value } + ?: false } override fun hashCode(): Int { @@ -246,7 +267,11 @@ internal class NavigationLinkWrapper( @OptIn(ExperimentalAnimationApi::class) @Composable -internal fun NavigationStack(key: Any?, links: NavigationStackScope.() -> Unit, content: @Composable () -> Unit) { +internal fun NavigationStack( + key: Any?, + links: NavigationStackScope.() -> Unit, + content: @Composable () -> Unit +) { val activeLinkComposables by remember(key) { val constructedLinks = mutableListOf>>() val scope = object : NavigationStackScope { @@ -256,7 +281,12 @@ internal fun NavigationStack(key: Any?, links: NavigationStackScope.() -> Unit, ) { constructedLinks.add( item.map { - NavigationLinkWrapper(index = constructedLinks.size, value = it, reset = { item.value = null }, content) + NavigationLinkWrapper( + index = constructedLinks.size, + value = it, + reset = { item.value = null }, + content + ) } ) } @@ -269,10 +299,15 @@ internal fun NavigationStack(key: Any?, links: NavigationStackScope.() -> Unit, AnimatedContent( targetState = activeLinkComposables, transitionSpec = { - if (initialState.indexOfLast { it.body != null } < targetState.indexOfLast { it.body != null }) { - slideInHorizontally(initialOffsetX = { it }) with slideOutHorizontally(targetOffsetX = { -it }) + if ( + initialState.indexOfLast { it.body != null } < + targetState.indexOfLast { it.body != null } + ) { + slideInHorizontally(initialOffsetX = { it }) with + slideOutHorizontally(targetOffsetX = { -it }) } else { - slideInHorizontally(initialOffsetX = { -it }) with slideOutHorizontally(targetOffsetX = { it }) + slideInHorizontally(initialOffsetX = { -it }) with + slideOutHorizontally(targetOffsetX = { it }) } }, contentAlignment = Alignment.BottomCenter diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt index 7c2d89a3..a77c9aab 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt @@ -26,6 +26,12 @@ class AgendaViewModel( ) { fun create() = - AgendaViewModel(sessionGateway, sessionDayFactory, sessionDetailFactory, sessionDetailScrollStateStorage, dateTimeService) + AgendaViewModel( + sessionGateway, + sessionDayFactory, + sessionDetailFactory, + sessionDetailScrollStateStorage, + dateTimeService + ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt index b1bfef29..987dd2ad 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt @@ -18,7 +18,11 @@ abstract class BaseSessionListViewModel( private set val observeDays by observe(::days) - var selectedDay: SessionDayViewModel? by managed(days?.firstOrNull { it.date == sessionDetailScrollStateStorage.selectedDay }) + var selectedDay: SessionDayViewModel? by managed( + days?.firstOrNull { + it.date == sessionDetailScrollStateStorage.selectedDay + } + ) val observeSelectedDay by observe(::selectedDay) var presentedSessionDetail: SessionDetailViewModel? by managed(null) @@ -45,7 +49,8 @@ abstract class BaseSessionListViewModel( } .also { newDays -> days = newDays - selectedDay = newDays.firstOrNull { it.day == selectedDay?.day } ?: newDays.firstOrNull() + selectedDay = newDays.firstOrNull { it.day == selectedDay?.day } + ?: newDays.firstOrNull() } } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt index ed46896a..90dc4639 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt @@ -35,6 +35,12 @@ class ScheduleViewModel( ) { fun create() = - ScheduleViewModel(sessionGateway, sessionDayFactory, sessionDetailFactory, sessionDetailScrollStateStorage, dateTimeService) + ScheduleViewModel( + sessionGateway, + sessionDayFactory, + sessionDetailFactory, + sessionDetailScrollStateStorage, + dateTimeService + ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt index f97da9c5..47ba21b1 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt @@ -32,6 +32,12 @@ class SessionBlockViewModel( startsAt: LocalDateTime, items: List, onScheduleItemSelected: (ScheduleItem) -> Unit, - ) = SessionBlockViewModel(sessionListItemFactory, dateFormatter, startsAt, items, onScheduleItemSelected) + ) = SessionBlockViewModel( + sessionListItemFactory, + dateFormatter, + startsAt, + items, + onScheduleItemSelected + ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt index ba1ce1ca..a7da1ec4 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt @@ -13,9 +13,16 @@ class SessionDetailScrollStateStorage { agendaScrollStates[day] } else { scrollStates[day] - } ?: SessionDayViewModel.ScrollState(firstVisibleItemIndex = 0, firstVisibleItemScrollOffset = 0) + } ?: SessionDayViewModel.ScrollState( + firstVisibleItemIndex = 0, + firstVisibleItemScrollOffset = 0 + ) - fun setScrollState(day: LocalDate, agenda: Boolean, scrollState: SessionDayViewModel.ScrollState) { + fun setScrollState( + day: LocalDate, + agenda: Boolean, + scrollState: SessionDayViewModel.ScrollState + ) { if (agenda) { agendaScrollStates[day] = scrollState } else { diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt index 6d954abb..0e091769 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt @@ -36,7 +36,11 @@ class SessionDetailViewModel( initialItem: ScheduleItem, ) : BaseViewModel() { - private val item by collected(initialItem, sessionGateway.observeScheduleItem(initialItem.session.id), identityEqualityPolicy()) + private val item by collected( + initialItem, + sessionGateway.observeScheduleItem(initialItem.session.id), + identityEqualityPolicy() + ) private val observeItem by observe(::item) private val time: Instant by collected( @@ -78,7 +82,11 @@ class SessionDetailViewModel( val observeState by observe(::state) val abstract by observeItem.map { it.session.description } val observeAbstract by observe(::abstract) - val abstractLinks: List by observeItem.map { it.session.description?.let(parseUrlViewService::parse) ?: emptyList() } + val abstractLinks: List by observeItem.map { + it.session.description?.let( + parseUrlViewService::parse + ) ?: emptyList() + } val observeAbstractLinks by observe(::abstractLinks) val speakers: List by managedList( @@ -141,7 +149,7 @@ class SessionDetailViewModel( private fun parseUrl(text: String): List { val urlRegex = - "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)".toRegex() + "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)".toRegex() // ktlint-disable max-line-length return urlRegex.findAll(text).map { WebLink(it.range, it.value) }.toList() } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt index 14c092e0..4deb4c1b 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt @@ -24,7 +24,10 @@ class AboutViewModel( } } - class Factory(private val aboutRepository: AboutRepository, private val parseUrlViewService: ParseUrlViewService) { + class Factory( + private val aboutRepository: AboutRepository, + private val parseUrlViewService: ParseUrlViewService + ) { fun create() = AboutViewModel(aboutRepository, parseUrlViewService) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt index b313f94c..b841691b 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt @@ -44,6 +44,12 @@ class SponsorDetailViewModel( ) { fun create(sponsor: Sponsor, groupName: String) = - SponsorDetailViewModel(sponsorGateway, speakerListItemFactory, speakerDetailFactory, sponsor, groupName) + SponsorDetailViewModel( + sponsorGateway, + speakerListItemFactory, + speakerDetailFactory, + sponsor, + groupName + ) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt index 067eda99..d07058d6 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt @@ -21,7 +21,10 @@ class SponsorListViewModel( sponsorGroup, onSponsorSelected = { sponsor -> if (sponsor.hasDetail) { - presentedSponsorDetail = sponsorDetailFactory.create(sponsor, sponsorGroup.group.name) + presentedSponsorDetail = sponsorDetailFactory.create( + sponsor, + sponsorGroup.group.name + ) } else { // UIApplication.sharedApplication.openURL(NSURL(string = sponsor.url.string)) presentedUrl = sponsor.url @@ -45,6 +48,7 @@ class SponsorListViewModel( private val sponsorDetailFactory: SponsorDetailViewModel.Factory, ) { - fun create() = SponsorListViewModel(sponsorGateway, sponsorGroupFactory, sponsorDetailFactory) + fun create() = + SponsorListViewModel(sponsorGateway, sponsorGroupFactory, sponsorDetailFactory) } } diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt index 75e2aad5..b5acce9d 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt @@ -23,8 +23,15 @@ import platform.UIKit.UIImage @OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) @Composable -internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, contentDescription: String?) { - val painter = remember { UIImage.imageNamed(imageResourceName)?.toSkiaImage()?.toComposeImageBitmap()?.let(::BitmapPainter) } +internal actual fun __LocalImage( + imageResourceName: String, + modifier: Modifier, + contentDescription: String? +) { + val painter = remember { + UIImage.imageNamed(imageResourceName)?.toSkiaImage()?.toComposeImageBitmap() + ?.let(::BitmapPainter) + } if (painter != null) { androidx.compose.foundation.Image( modifier = modifier, @@ -34,7 +41,10 @@ internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, ) } else { Row( - modifier = modifier.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(Dimensions.Padding.half)), + modifier = modifier.background( + MaterialTheme.colorScheme.primary, + RoundedCornerShape(Dimensions.Padding.half) + ), verticalAlignment = Alignment.CenterVertically, ) { Spacer(modifier = Modifier.weight(1f)) @@ -44,7 +54,11 @@ internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, modifier = Modifier.padding(Dimensions.Padding.half), tint = Color.White, ) - Text("Image not supported", modifier = Modifier.padding(Dimensions.Padding.default), color = Color.White) + Text( + "Image not supported", + modifier = Modifier.padding(Dimensions.Padding.default), + color = Color.White + ) Spacer(modifier = Modifier.weight(1f)) } } diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt index 0c6649d7..171b18e6 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt @@ -55,7 +55,10 @@ internal actual fun NavigationBackPressWrapper(content: @Composable () -> Unit) AnimatedVisibility( visible = dragDistance > triggerBackPressDragDistance, - enter = slideInHorizontally(initialOffsetX = { -it }, animationSpec = tween(durationMillis = 100)), + enter = slideInHorizontally( + initialOffsetX = { -it }, + animationSpec = tween(durationMillis = 100) + ), exit = slideOutHorizontally(targetOffsetX = { -it }) ) { Icon( diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt index 8aa2044f..a868d785 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt @@ -23,7 +23,8 @@ import platform.UIKit.UIImage // TODO: Add support for remaining color spaces when the Skia library supports them. @ExperimentalForeignApi internal fun UIImage.toSkiaImage(): Image? { - val imageRef = CGImageCreateCopyWithColorSpace(this.CGImage, CGColorSpaceCreateDeviceRGB()) ?: return null + val imageRef = + CGImageCreateCopyWithColorSpace(this.CGImage, CGColorSpaceCreateDeviceRGB()) ?: return null val width = CGImageGetWidth(imageRef).toInt() val height = CGImageGetHeight(imageRef).toInt() @@ -35,9 +36,16 @@ internal fun UIImage.toSkiaImage(): Image? { val alphaInfo = CGImageGetAlphaInfo(imageRef) val alphaType = when (alphaInfo) { - CGImageAlphaInfo.kCGImageAlphaPremultipliedFirst, CGImageAlphaInfo.kCGImageAlphaPremultipliedLast -> ColorAlphaType.PREMUL - CGImageAlphaInfo.kCGImageAlphaFirst, CGImageAlphaInfo.kCGImageAlphaLast -> ColorAlphaType.UNPREMUL - CGImageAlphaInfo.kCGImageAlphaNone, CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, CGImageAlphaInfo.kCGImageAlphaNoneSkipLast -> ColorAlphaType.OPAQUE + CGImageAlphaInfo.kCGImageAlphaPremultipliedFirst, + CGImageAlphaInfo.kCGImageAlphaPremultipliedLast -> ColorAlphaType.PREMUL + + CGImageAlphaInfo.kCGImageAlphaFirst, + CGImageAlphaInfo.kCGImageAlphaLast -> ColorAlphaType.UNPREMUL + + CGImageAlphaInfo.kCGImageAlphaNone, + CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, + CGImageAlphaInfo.kCGImageAlphaNoneSkipLast -> ColorAlphaType.OPAQUE + else -> ColorAlphaType.UNKNOWN } @@ -48,7 +56,12 @@ internal fun UIImage.toSkiaImage(): Image? { CFRelease(imageRef) return Image.makeRaster( - imageInfo = ImageInfo(width = width, height = height, colorType = ColorType.RGBA_8888, alphaType = alphaType), + imageInfo = ImageInfo( + width = width, + height = height, + colorType = ColorType.RGBA_8888, + alphaType = alphaType + ), bytes = byteArray, rowBytes = bytesPerRow.toInt(), ) diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt index 196c7054..59fff784 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt @@ -45,6 +45,14 @@ actual val platformModule: Module = module { AndroidDateFormatter(dateTimeService = get()) } - val baseKermit = Logger(config = StaticConfig(logWriterList = listOf(LogcatWriter(), CrashlyticsLogWriter())), tag = "Droidcon") + val baseKermit = Logger( + config = StaticConfig( + logWriterList = listOf( + LogcatWriter(), + CrashlyticsLogWriter() + ) + ), + tag = "Droidcon" + ) factory { (tag: String?) -> if (tag != null) baseKermit.withTag(tag) else baseKermit } } diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt index 78b26813..e20203ae 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt @@ -39,9 +39,10 @@ class AndroidNotificationService( settings[NOTIFICATION_ID_COUNTER_KEY] = value } - private val registeredNotifications: MutableMap> = settings.getStringOrNull(NOTIFICATION_ID_MAP_KEY)?.let { - json.decodeFromString(it) - } ?: mutableMapOf() + private val registeredNotifications: MutableMap> = + settings.getStringOrNull(NOTIFICATION_ID_MAP_KEY)?.let { + json.decodeFromString(it) + } ?: mutableMapOf() // TODO: Not called on Android. private var notificationHandler: DeepLinkNotificationHandler? = null @@ -72,7 +73,13 @@ class AndroidNotificationService( return true } - override suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) { + override suspend fun schedule( + notification: Notification.Local, + title: String, + body: String, + delivery: Instant, + dismiss: Instant? + ) { log.v { "Scheduling local notification at $delivery." } val deliveryTime = delivery.toEpochMilliseconds() @@ -134,14 +141,20 @@ class AndroidNotificationService( putExtra(NOTIFICATION_PAYLOAD_ID, intentId) putExtra(NOTIFICATION_PAYLOAD_TYPE, NOTIFICATION_TYPE_DISMISS) } - alarmManager.set(AlarmManager.RTC_WAKEUP, dismiss.toEpochMilliseconds(), dismissPendingIntent) + alarmManager.set( + AlarmManager.RTC_WAKEUP, + dismiss.toEpochMilliseconds(), + dismissPendingIntent + ) saveRegisteredNotificationId(sessionId, dismissIntentId) } } override suspend fun cancel(sessionIds: List) { - if (sessionIds.isEmpty()) { return } + if (sessionIds.isEmpty()) { + return + } log.v { "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" } @@ -178,6 +191,7 @@ class AndroidNotificationService( log.w { "notificationHandler not registered when received $notification" } } } + Notification.Remote.RefreshData -> syncService.forceSynchronize() } } @@ -217,8 +231,13 @@ class AndroidNotificationService( ) } - private fun createPendingIntent(id: Int, intentTransform: Intent.() -> Unit = {}): PendingIntent { - val intent = IdentifiableIntent("$id", context, NotificationPublisher::class.java).apply(intentTransform) + private fun createPendingIntent( + id: Int, + intentTransform: Intent.() -> Unit = {} + ): PendingIntent { + val intent = IdentifiableIntent("$id", context, NotificationPublisher::class.java).apply( + intentTransform + ) return PendingIntent.getBroadcast( context, id, @@ -238,7 +257,8 @@ class AndroidNotificationService( } private fun saveRegisteredNotificationId(sessionId: Session.Id, notificationId: Int) { - val currentNotificationIds = (registeredNotifications[sessionId.value] ?: emptyList()).toMutableList() + val currentNotificationIds = + (registeredNotifications[sessionId.value] ?: emptyList()).toMutableList() currentNotificationIds.add(notificationId) registeredNotifications[sessionId.value] = currentNotificationIds saveRegisteredNotifications() diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt index ec3a8ce2..76498001 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt @@ -9,9 +9,12 @@ import android.content.Intent class NotificationPublisher : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - val notificationId = intent.getIntExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_ID, 0) - val notificationType = intent.getStringExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_TYPE) - val notification = intent.getParcelableExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_NOTIFICATION) + val notificationId = + intent.getIntExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_ID, 0) + val notificationType = + intent.getStringExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_TYPE) + val notification = + intent.getParcelableExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_NOTIFICATION) with(context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) { if (notificationType == AndroidNotificationService.NOTIFICATION_TYPE_DISMISS) { diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt index 2c095524..680a73f6 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt @@ -2,25 +2,32 @@ package co.touchlab.droidcon.util.formatter import co.touchlab.droidcon.Constants.conferenceTimeZone import co.touchlab.droidcon.domain.service.DateTimeService -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.atTime import java.text.DateFormat import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.atTime class AndroidDateFormatter(private val dateTimeService: DateTimeService) : DateFormatter { // TODOKPG - May not need to set timezone. Java date has no TZ private val shortDateFormat = - SimpleDateFormat("MMM d", Locale.getDefault()).apply { timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) } - private val minuteHourTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) - .apply { timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) } + SimpleDateFormat("MMM d", Locale.getDefault()).apply { + timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) + } + private val minuteHourTimeFormat = + DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) + .apply { timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) } override fun monthWithDay(date: LocalDate): String { return shortDateFormat.format( - Date(with(dateTimeService) { date.atTime(0, 0).fromConferenceDateTime() }.toEpochMilliseconds()) + Date( + with(dateTimeService) { + date.atTime(0, 0).fromConferenceDateTime() + }.toEpochMilliseconds() + ) ).uppercase() } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt index 6c9a1f54..002bd7a3 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt @@ -7,7 +7,13 @@ import kotlinx.datetime.Instant interface NotificationService { suspend fun initialize(): Boolean - suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) + suspend fun schedule( + notification: Notification.Local, + title: String, + body: String, + delivery: Instant, + dismiss: Instant? + ) suspend fun cancel(sessionIds: List) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt index 2363e53b..cb5a2c4d 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt @@ -65,11 +65,20 @@ class DefaultNotificationSchedulingService( ) } - private suspend fun scheduleNotifications(sessionFlow: Flow>, settingsFlow: Flow) { + private suspend fun scheduleNotifications( + sessionFlow: Flow>, + settingsFlow: Flow + ) { sessionFlow .combine( settingsFlow, - transform = { agenda, settings -> Triple(agenda, settings.isRemindersEnabled, settings.isFeedbackEnabled) } + transform = { agenda, settings -> + Triple( + agenda, + settings.isRemindersEnabled, + settings.isFeedbackEnabled + ) + } ) .distinctUntilChanged() .collect { (agenda, isRemindersEnabled, isFeedbackEnabled) -> @@ -80,7 +89,8 @@ class DefaultNotificationSchedulingService( val oldSessionIds = scheduledSessionIds.filterNot { sessionId -> agenda.map { it.id.value }.contains(sessionId.value) } - scheduledNotifications = scheduledNotifications.filterNot { oldSessionIds.contains(it) } + scheduledNotifications = + scheduledNotifications.filterNot { oldSessionIds.contains(it) } notificationService.cancel(oldSessionIds) // Schedule new upcoming sessions. @@ -89,7 +99,10 @@ class DefaultNotificationSchedulingService( if (isRemindersEnabled) { val roomName = session.room?.let { roomRepository.get(it).name } val reminderDelivery = - session.startsAt.plus(NotificationSchedulingService.REMINDER_DELIVERY_START_OFFSET, DateTimeUnit.MINUTE) + session.startsAt.plus( + NotificationSchedulingService.REMINDER_DELIVERY_START_OFFSET, + DateTimeUnit.MINUTE + ) if (session.endsAt >= dateTimeService.now()) { notificationService.schedule( notification = Notification.Local.Reminder( @@ -108,8 +121,15 @@ class DefaultNotificationSchedulingService( if (isFeedbackEnabled) { val feedbackDelivery = - session.endsAt.plus(NotificationSchedulingService.FEEDBACK_DISMISS_END_OFFSET, DateTimeUnit.MINUTE) - if (feedbackDelivery.plus(24, DateTimeUnit.HOUR) >= dateTimeService.now() && session.feedback == null) { + session.endsAt.plus( + NotificationSchedulingService.FEEDBACK_DISMISS_END_OFFSET, + DateTimeUnit.MINUTE + ) + if (feedbackDelivery.plus( + 24, + DateTimeUnit.HOUR + ) >= dateTimeService.now() && session.feedback == null + ) { notificationService.schedule( notification = Notification.Local.Feedback( sessionId = session.id, diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt index 42a25888..7eee9908 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt @@ -19,17 +19,21 @@ class SqlDelightSessionRepository( private val sessionQueries: SessionQueries, ) : BaseRepository(), SessionRepository { override fun observe(id: Session.Id): Flow { - return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOne(Dispatchers.Main) + return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow() + .mapToOne(Dispatchers.Main) } - fun sessionById(id: Session.Id): Session? = sessionQueries.sessionById(id.value, ::sessionFactory).executeAsOneOrNull() + fun sessionById(id: Session.Id): Session? = + sessionQueries.sessionById(id.value, ::sessionFactory).executeAsOneOrNull() override fun observeOrNull(id: Session.Id): Flow { - return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOneOrNull(Dispatchers.Main) + return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow() + .mapToOneOrNull(Dispatchers.Main) } override fun observeAllAttending(): Flow> { - return sessionQueries.attendingSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) + return sessionQueries.attendingSessions(::sessionFactory).asFlow() + .mapToList(Dispatchers.Main) } override suspend fun allAttending(): List { @@ -52,9 +56,11 @@ class SqlDelightSessionRepository( sessionQueries.updateFeedBackSent(if (isSent) 1 else 0, sessionId.value) } - override fun allSync(): List = sessionQueries.allSessions(::sessionFactory).executeAsList() + override fun allSync(): List = + sessionQueries.allSessions(::sessionFactory).executeAsList() - override fun findSync(id: Session.Id): Session? = sessionQueries.sessionById(id.value, mapper = ::sessionFactory).executeAsOneOrNull() + override fun findSync(id: Session.Id): Session? = + sessionQueries.sessionById(id.value, mapper = ::sessionFactory).executeAsOneOrNull() override fun observeAll(): Flow> { return sessionQueries.allSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt index 6b310860..1c127190 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt @@ -14,18 +14,22 @@ class SqlDelightSponsorGroupRepository( private val sponsorGroupQueries: SponsorGroupQueries, ) : BaseRepository(), SponsorGroupRepository { - override fun allSync(): List = sponsorGroupQueries.selectAll(::sponsorGroupFactory).executeAsList() + override fun allSync(): List = + sponsorGroupQueries.selectAll(::sponsorGroupFactory).executeAsList() override fun observe(id: SponsorGroup.Id): Flow { - return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOne(Dispatchers.Main) + return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow() + .mapToOne(Dispatchers.Main) } override fun observeOrNull(id: SponsorGroup.Id): Flow { - return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOneOrNull(Dispatchers.Main) + return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow() + .mapToOneOrNull(Dispatchers.Main) } override fun observeAll(): Flow> { - return sponsorGroupQueries.selectAll(::sponsorGroupFactory).asFlow().mapToList(Dispatchers.Main) + return sponsorGroupQueries.selectAll(::sponsorGroupFactory).asFlow() + .mapToList(Dispatchers.Main) } override fun contains(id: SponsorGroup.Id): Boolean { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt index 5f798697..90139b6a 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt @@ -16,11 +16,13 @@ class SqlDelightSponsorRepository( ) : BaseRepository(), SponsorRepository { override fun observe(id: Sponsor.Id): Flow { - return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOne(Dispatchers.Main) + return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow() + .mapToOne(Dispatchers.Main) } override fun observeOrNull(id: Sponsor.Id): Flow { - return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOneOrNull(Dispatchers.Main) + return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow() + .mapToOneOrNull(Dispatchers.Main) } override fun observeAll(): Flow> { @@ -35,7 +37,8 @@ class SqlDelightSponsorRepository( return sponsorQueries.sponsorsByGroup(group, ::sponsorFactory).executeAsList() } - override fun allSync(): List = sponsorQueries.selectAll(::sponsorFactory).executeAsList() + override fun allSync(): List = + sponsorQueries.selectAll(::sponsorFactory).executeAsList() override fun doUpsert(entity: Sponsor) { sponsorQueries.upsert( diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt index 4e72b7f3..8f8c634e 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt @@ -60,8 +60,10 @@ class DefaultSyncService( private const val SESSIONIZE_SYNC_SINCE_LAST_MINUTES = 15 private const val SESSIONIZE_SYNC_NEXT_DELAY: Long = 1L * 60L * 60L * 1000L + // 5 minutes private const val RSVP_SYNC_DELAY: Long = 5L * 60L * 1000L + // 5 minutes private const val FEEDBACK_SYNC_DELAY: Long = 5L * 60L * 1000L } @@ -73,7 +75,8 @@ class DefaultSyncService( } private var lastSessionizeSync: Instant? - get() = settings.getLongOrNull(LAST_SESSIONIZE_SYNC_KEY)?.let { Instant.fromEpochMilliseconds(it) } + get() = settings.getLongOrNull(LAST_SESSIONIZE_SYNC_KEY) + ?.let { Instant.fromEpochMilliseconds(it) } set(value) { settings[LAST_SESSIONIZE_SYNC_KEY] = value?.toEpochMilliseconds() } @@ -86,7 +89,9 @@ class DefaultSyncService( while (isActive) { val lastSessionizeSync = lastSessionizeSync // If this is the first Sessionize sync or if the last sync occurred more than 2 hours ago. - if (lastSessionizeSync == null || lastSessionizeSync <= dateTimeService.now().minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE)) { + if (lastSessionizeSync == null || lastSessionizeSync <= dateTimeService.now() + .minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE) + ) { try { runApiDataSourcesSynchronization() } catch (e: Exception) { @@ -94,10 +99,16 @@ class DefaultSyncService( delay(SESSIONIZE_SYNC_POLL_DELAY) continue } - log.d { "Sync successful, waiting for next sync in $SESSIONIZE_SYNC_NEXT_DELAY ms." } + log.d { + "Sync successful, " + + "waiting for next sync in $SESSIONIZE_SYNC_NEXT_DELAY ms." + } delay(SESSIONIZE_SYNC_NEXT_DELAY) } else { - log.d { "The sync didn't happen, so we'll try again in a short while ($SESSIONIZE_SYNC_POLL_DELAY ms)." } + log.d { + "The sync didn't happen, so we'll try again " + + "in a short while ($SESSIONIZE_SYNC_POLL_DELAY ms)." + } delay(SESSIONIZE_SYNC_POLL_DELAY) } } @@ -147,9 +158,16 @@ class DefaultSyncService( .forEach { (sessionId, feedback) -> while (isActive) { try { - val isFeedbackSent = serverApi.setFeedback(sessionId, feedback.rating, feedback.comment) + val isFeedbackSent = serverApi.setFeedback( + sessionId, + feedback.rating, + feedback.comment + ) if (isFeedbackSent) { - sessionRepository.setFeedbackSent(sessionId, isFeedbackSent) + sessionRepository.setFeedbackSent( + sessionId, + isFeedbackSent + ) } break } catch (e: Exception) { @@ -238,8 +256,10 @@ class DefaultSyncService( id = Session.Id(dto.id), title = dto.title, description = dto.description, - startsAt = LocalDateTime.parse(dto.startsAt).fromConferenceDateTime(dateTimeService), - endsAt = LocalDateTime.parse(dto.endsAt).fromConferenceDateTime(dateTimeService), + startsAt = LocalDateTime.parse(dto.startsAt) + .fromConferenceDateTime(dateTimeService), + endsAt = LocalDateTime.parse(dto.endsAt) + .fromConferenceDateTime(dateTimeService), isServiceSession = dto.isServiceSession, room = Room.Id(dto.roomID), rsvp = Session.RSVP( @@ -280,12 +300,17 @@ class DefaultSyncService( } } - private fun updateSponsorsFromDataSource(sponsorSessionsGroups: List, sponsors: SponsorsDto.SponsorCollectionDto) { + private fun updateSponsorsFromDataSource( + sponsorSessionsGroups: List, + sponsors: SponsorsDto.SponsorCollectionDto + ) { val sponsorSessions = sponsorSessionsGroups.flatMap { it.sessions }.associateBy { it.id } val sponsorGroupsToSponsorDtos = sponsors.groups.map { group -> val groupName = (group.name.split('/').lastOrNull() ?: group.name) .split(' ').joinToString(" ") { - it.replaceFirstChar { s -> if (s.isLowerCase()) s.titlecase() else s.toString() } + it.replaceFirstChar { s -> + if (s.isLowerCase()) s.titlecase() else s.toString() + } } SponsorGroup( @@ -295,20 +320,23 @@ class DefaultSyncService( ) to group.fields.sponsors.arrayValue.values.map { it.mapValue.fields } } - val sponsorsAndRepresentativeIds = sponsorGroupsToSponsorDtos.flatMap { (group, sponsorDtos) -> - sponsorDtos.map { sponsorDto -> - val sponsorSession = sponsorDto.sponsorId?.stringValue?.let(sponsorSessions::get) - val representativeIds = sponsorSession?.speakers?.map { Profile.Id(it.id) } ?: emptyList() - - Sponsor( - id = Sponsor.Id(sponsorDto.name.stringValue, group.name), - hasDetail = sponsorSession != null, - description = sponsorSession?.description, - icon = Url(sponsorDto.icon.stringValue), - url = Url(sponsorDto.url.stringValue), - ) to representativeIds + val sponsorsAndRepresentativeIds = + sponsorGroupsToSponsorDtos.flatMap { (group, sponsorDtos) -> + sponsorDtos.map { sponsorDto -> + val sponsorSession = + sponsorDto.sponsorId?.stringValue?.let(sponsorSessions::get) + val representativeIds = + sponsorSession?.speakers?.map { Profile.Id(it.id) } ?: emptyList() + + Sponsor( + id = Sponsor.Id(sponsorDto.name.stringValue, group.name), + hasDetail = sponsorSession != null, + description = sponsorSession?.description, + icon = Url(sponsorDto.icon.stringValue), + url = Url(sponsorDto.url.stringValue), + ) to representativeIds + } } - } sponsorRepository.allSync().map { it.id } .subtract(sponsorsAndRepresentativeIds.map { it.first.id }.toSet()) @@ -340,7 +368,8 @@ class DefaultSyncService( twitter = groupedLinks[LinkType.Twitter]?.firstOrNull()?.url?.let(::Url), linkedIn = groupedLinks[LinkType.LinkedIn]?.firstOrNull()?.url?.let(::Url), website = ( - groupedLinks[LinkType.CompanyWebsite] ?: groupedLinks[LinkType.Blog] ?: groupedLinks[LinkType.Other] + groupedLinks[LinkType.CompanyWebsite] ?: groupedLinks[LinkType.Blog] + ?: groupedLinks[LinkType.Other] )?.firstOrNull()?.url?.let(::Url), ) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt index 3aa16801..6176ad09 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt @@ -53,7 +53,10 @@ object SpeakersDto { override val descriptor: SerialDescriptor get() { - return PrimitiveSerialDescriptor("co.touchlab.droidcon.domain.service.impl.dto.LinkType", PrimitiveKind.STRING) + return PrimitiveSerialDescriptor( + "co.touchlab.droidcon.domain.service.impl.dto.LinkType", + PrimitiveKind.STRING + ) } override fun deserialize(decoder: Decoder): LinkType = decoder.decodeString().let { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt index 7d9e7419..38184940 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt @@ -6,6 +6,9 @@ import kotlinx.serialization.builtins.ListSerializer class AboutJsonResourceDataSource(private val jsonResourceReader: JsonResourceReader) { fun getAboutItems(): List { - return jsonResourceReader.readAndDecodeResource("about.json", ListSerializer(AboutDto.AboutItemDto.serializer())) + return jsonResourceReader.readAndDecodeResource( + "about.json", + ListSerializer(AboutDto.AboutItemDto.serializer()) + ) } } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt index 6a75154e..66751bd6 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt @@ -38,7 +38,8 @@ actual val platformModule = module { get() } - val baseKermit = Logger(config = StaticConfig(logWriterList = listOf(NSLogWriter())), tag = "Droidcon") + val baseKermit = + Logger(config = StaticConfig(logWriterList = listOf(NSLogWriter())), tag = "Droidcon") factory { (tag: String?) -> if (tag != null) baseKermit.withTag(tag) else baseKermit } single { AppChecker } @@ -46,35 +47,54 @@ actual val platformModule = module { @BetaInteropApi fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?, parameter: Any): Any { - val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } + val kClazz = + requireNotNull(getOriginalKotlinClass(objCClass)) { + "Could not get original kotlin class for $objCClass." + } return get(kClazz, qualifier) { parametersOf(parameter) } } @BetaInteropApi fun Koin.get(objCClass: ObjCClass, parameter: Any): Any { - val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } + val kClazz = + requireNotNull(getOriginalKotlinClass(objCClass)) { + "Could not get original kotlin class for $objCClass." + } return get(kClazz, null) { parametersOf(parameter) } } @BetaInteropApi fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?): Any { - val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } + val kClazz = + requireNotNull(getOriginalKotlinClass(objCClass)) { + "Could not get original kotlin class for $objCClass." + } return get(kClazz, qualifier, null) } @BetaInteropApi fun Koin.get(objCClass: ObjCClass): Any { - val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } + val kClazz = + requireNotNull(getOriginalKotlinClass(objCClass)) { + "Could not get original kotlin class for $objCClass." + } return get(kClazz, null) } @BetaInteropApi fun Koin.get(objCProtocol: ObjCProtocol, qualifier: Qualifier?): Any { - val kClazz = requireNotNull(getOriginalKotlinClass(objCProtocol)) { "Could not get original kotlin class for $objCProtocol." } + val kClazz = + requireNotNull(getOriginalKotlinClass(objCProtocol)) { + "Could not get original kotlin class for $objCProtocol." + } return get(kClazz, qualifier, null) } -fun Koin.getAny(objCObject: ObjCObject, qualifier: Qualifier?, parameters: ParametersDefinition?): Any { +fun Koin.getAny( + objCObject: ObjCObject, + qualifier: Qualifier?, + parameters: ParametersDefinition? +): Any { val kclass = when (objCObject) { is ObjCClass -> getOriginalKotlinClass(objCObject) is ObjCProtocol -> getOriginalKotlinClass(objCObject) diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt index e36ec876..31d25606 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt @@ -2,12 +2,13 @@ package co.touchlab.droidcon import co.touchlab.droidcon.util.printThrowable import co.touchlab.kermit.Logger +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob -import kotlin.coroutines.CoroutineContext -class MainScope(private val mainContext: CoroutineContext, private val log: Logger) : CoroutineScope { +class MainScope(private val mainContext: CoroutineContext, private val log: Logger) : + CoroutineScope { override val coroutineContext: CoroutineContext get() = mainContext + job + exceptionHandler diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt index f47f9a2f..5f4e7bce 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt @@ -42,7 +42,9 @@ class IOSNotificationService( override suspend fun initialize(): Boolean { log.d { "Initializing." } - val notificationSettings = wrapMultiThreadCallback(notificationCenter::getNotificationSettingsWithCompletionHandler) + val notificationSettings = wrapMultiThreadCallback( + notificationCenter::getNotificationSettingsWithCompletionHandler + ) if (notificationSettings == null) { log.i { "Failed to get current notification authorization." } return false @@ -50,25 +52,39 @@ class IOSNotificationService( when (notificationSettings.authorizationStatus) { UNAuthorizationStatusNotDetermined -> { val requestOptions = UNAuthorizationOptionAlert or UNAuthorizationOptionSound - val (isAuthorized, error) = wrapMultiThreadCallback { notificationCenter.requestAuthorizationWithOptions(requestOptions, it) } + val (isAuthorized, error) = wrapMultiThreadCallback { + notificationCenter.requestAuthorizationWithOptions( + requestOptions, + it + ) + } if (error != null) { log.i { "Notifications authorization request failed with '$error'." } } return isAuthorized } + UNAuthorizationStatusDenied -> { log.i { "Notifications not authorized." } return false } + UNAuthorizationStatusAuthorized -> { log.i { "Notifications authorized." } return true } + else -> return false } } - override suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) { + override suspend fun schedule( + notification: Notification.Local, + title: String, + body: String, + delivery: Instant, + dismiss: Instant? + ) { log.v { "Scheduling local notification at ${delivery.toNSDate().description}." } val deliveryDate = delivery.toNSDate() val allUnits = NSCalendarUnitSecond or @@ -80,15 +96,21 @@ class IOSNotificationService( NSCalendarUnitTimeZone val dateComponents = NSCalendar.currentCalendar.components(allUnits, deliveryDate) - val trigger = UNCalendarNotificationTrigger.triggerWithDateMatchingComponents(dateComponents, repeats = false) + val trigger = UNCalendarNotificationTrigger.triggerWithDateMatchingComponents( + dateComponents, + repeats = false + ) val content = UNMutableNotificationContent() content.setTitle(title) content.setBody(body) content.setSound(UNNotificationSound.defaultSound) val (typeValue, sessionId) = when (notification) { - is Notification.Local.Feedback -> Notification.Values.feedbackType to notification.sessionId - is Notification.Local.Reminder -> Notification.Values.reminderType to notification.sessionId + is Notification.Local.Feedback -> + Notification.Values.feedbackType to notification.sessionId + + is Notification.Local.Reminder -> + Notification.Values.reminderType to notification.sessionId } content.setUserInfo( mapOf( @@ -97,9 +119,18 @@ class IOSNotificationService( ) ) - val request = UNNotificationRequest.requestWithIdentifier("${sessionId.value}-$typeValue", content, trigger) + val request = UNNotificationRequest.requestWithIdentifier( + "${sessionId.value}-$typeValue", + content, + trigger + ) - val error = wrapMultiThreadCallback { notificationCenter.addNotificationRequest(request, it) } + val error = wrapMultiThreadCallback { + notificationCenter.addNotificationRequest( + request, + it + ) + } if (error == null) { log.v { "Scheduling notification complete." } } else { @@ -108,9 +139,14 @@ class IOSNotificationService( } override suspend fun cancel(sessionIds: List) { - if (sessionIds.isEmpty()) { return } - log.v { "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" } - notificationCenter.removePendingNotificationRequestsWithIdentifiers(sessionIds.map { it.value }) + if (sessionIds.isEmpty()) { + return + } + log.v { + "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" + } + notificationCenter + .removePendingNotificationRequestsWithIdentifiers(sessionIds.map { it.value }) } @Suppress("unused") @@ -138,6 +174,7 @@ class IOSNotificationService( log.w { "notificationHandler not registered when received $notification" } } } + Notification.Remote.RefreshData -> syncService.forceSynchronize() } } @@ -156,7 +193,9 @@ class IOSNotificationService( private fun Map.parseReminderNotification(): Notification.Local.Reminder? { val sessionId = this[Notification.Keys.sessionId] as? String ?: run { - log.e { "Couldn't parse reminder notification. Session ID doesn't exist or isn't String." } + log.e { + "Couldn't parse reminder notification. Session ID doesn't exist or isn't String." + } return null } @@ -167,7 +206,9 @@ class IOSNotificationService( private fun Map.parseFeedbackNotification(): Notification.Local.Feedback? { val sessionId = this[Notification.Keys.sessionId] as? String ?: run { - log.e { "Couldn't parse feedback notification. Session ID doesn't exist or isn't String." } + log.e { + "Couldn't parse feedback notification. Session ID doesn't exist or isn't String." + } return null } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt index 677fa2cf..f8ca9e85 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt @@ -9,7 +9,12 @@ import co.touchlab.kermit.crashlytics.CrashlyticsLogWriter @OptIn(ExperimentalKermitApi::class) fun setupKermit() { - Logger.addLogWriter(CrashlyticsLogWriter(minSeverity = Severity.Info, minCrashSeverity = Severity.Warn)) + Logger.addLogWriter( + CrashlyticsLogWriter( + minSeverity = Severity.Info, + minCrashSeverity = Severity.Warn + ) + ) enableCrashlytics() setCrashlyticsUnhandledExceptionHook() } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt index bbff7c72..d536cd4d 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt @@ -26,21 +26,37 @@ class BundleResourceReader( 0 -> { null to name.drop(1) } + in 1..Int.MAX_VALUE -> { name.take(lastPeriodIndex) to name.drop(lastPeriodIndex + 1) } + else -> { name to null } } - val path = bundle.pathForResource(filename, type) ?: error("Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type).joinToString(".")})") + val path = bundle.pathForResource(filename, type) ?: error( + "Couldn't get path of $name (parsed as: ${ + listOfNotNull( + filename, + type + ).joinToString(".") + })" + ) return memScoped { val errorPtr = alloc>() - NSString.stringWithContentsOfFile(path, encoding = NSUTF8StringEncoding, error = errorPtr.ptr) ?: run { + NSString.stringWithContentsOfFile( + path, + encoding = NSUTF8StringEncoding, + error = errorPtr.ptr + ) ?: run { // TODO: Check the NSError and throw common exception. - error("Couldn't load resource: $name. Error: ${errorPtr.value?.localizedDescription} - ${errorPtr.value}") + error( + "Couldn't load resource: $name. " + + "Error: ${errorPtr.value?.localizedDescription} - ${errorPtr.value}" + ) } } } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt index a313cf03..d778ee5a 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt @@ -10,9 +10,10 @@ suspend fun wrapMultiThreadCallback(call: (callback: (T) -> Unit) -> Unit): return completable.await() } -suspend fun wrapMultiThreadCallback(call: (callback: (T1, T2) -> Unit) -> Unit): Pair { - val completable = CompletableDeferred>() - val closure: (T1, T2) -> Unit = { t1, t2 -> completable.complete(t1 to t2) } - call(closure) - return completable.await() -} +suspend fun wrapMultiThreadCallback(call: (callback: (T1, T2) -> Unit) -> Unit): + Pair { + val completable = CompletableDeferred>() + val closure: (T1, T2) -> Unit = { t1, t2 -> completable.complete(t1 to t2) } + call(closure) + return completable.await() + } From f62e26db8ae68115f6144c23534e773010913db2 Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 13:58:59 -0400 Subject: [PATCH 04/17] Update .editorconfig --- .editorconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index a8c52379..e4043085 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ [*.{kt,kts}] end_of_line = lf -ij_kotlin_allow_trailing_comma = false -ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true ij_kotlin_imports_layout = * ij_kotlin_indent_before_arrow_on_new_line = false ij_kotlin_line_break_after_multiline_when_entry = true @@ -18,4 +18,4 @@ ktlint_function_naming_ignore_when_annotated_with = [unset] ktlint_function_signature_body_expression_wrapping = default ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset ktlint_ignore_back_ticked_identifier = false -max_line_length = 100 +max_line_length = 140 From 9c3395ae3bfc290f9b1fc98a1e77113229c129c7 Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 13:59:06 -0400 Subject: [PATCH 05/17] Revert "formatting" This reverts commit 7ab4ad1e223da0a707ac08e63f1de90070c4032c. --- .../co/touchlab/droidcon/android/MainApp.kt | 5 +- .../impl/DefaultFirebaseMessagingService.kt | 10 +-- .../NotificationLocalizedStringFactory.kt | 4 +- .../NotificationLocalizedStringFactory.kt | 26 +------ .../ios/util/formatter/IOSDateFormatter.kt | 27 ++----- .../ui/settings/PlatformSpecificSettings.kt | 5 +- .../co/touchlab/droidcon/ui/util/Dialog.kt | 8 +- .../droidcon/ui/util/LocalImage.jvm.kt | 21 +---- .../ui/BottomNavigationView.kt | 8 +- .../ui/settings/SettingsView.kt | 6 +- .../ui/util/LocalImage.kt | 12 +-- .../util/NavigationController.kt | 63 ++++----------- .../viewmodel/session/AgendaViewModel.kt | 8 +- .../session/BaseSessionListViewModel.kt | 9 +-- .../viewmodel/session/ScheduleViewModel.kt | 8 +- .../session/SessionBlockViewModel.kt | 8 +- .../SessionDetailScrollStateStorage.kt | 11 +-- .../session/SessionDetailViewModel.kt | 14 +--- .../viewmodel/settings/AboutViewModel.kt | 5 +- .../sponsor/SponsorDetailViewModel.kt | 8 +- .../viewmodel/sponsor/SponsorListViewModel.kt | 8 +- .../touchlab/droidcon/ui/util/LocalImage.kt | 22 +----- .../ui/util/NavigationBackPressWrapper.kt | 5 +- .../touchlab/droidcon/ui/util/ToSkiaImage.kt | 23 ++---- .../co/touchlab/droidcon/Koin.android.kt | 10 +-- .../service/AndroidNotificationService.kt | 38 +++------ .../droidcon/service/NotificationPublisher.kt | 9 +-- .../util/formatter/AndroidDateFormatter.kt | 21 ++--- .../service/NotificationService.kt | 8 +- .../DefaultNotificationSchedulingService.kt | 32 ++------ .../impl/SqlDelightSessionRepository.kt | 18 ++--- .../impl/SqlDelightSponsorGroupRepository.kt | 12 +-- .../impl/SqlDelightSponsorRepository.kt | 9 +-- .../domain/service/impl/DefaultSyncService.kt | 77 ++++++------------- .../domain/service/impl/dto/SpeakersDto.kt | 5 +- .../impl/json/AboutJsonResourceDataSource.kt | 5 +- .../kotlin/co/touchlab/droidcon/Koin.ios.kt | 34 ++------ .../kotlin/co/touchlab/droidcon/MainScope.kt | 5 +- .../service/IOSNotificationService.kt | 67 ++++------------ .../co/touchlab/droidcon/util/AppInit.kt | 7 +- .../droidcon/util/BundleResourceReader.kt | 22 +----- .../droidcon/util/wrapMultiThreadCallback.kt | 13 ++-- 42 files changed, 163 insertions(+), 553 deletions(-) diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt index a2acb4b6..b96e87dc 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt @@ -31,10 +31,7 @@ class MainApp : Application() { single { this@MainApp } single> { MainActivity::class.java } single { - get().getSharedPreferences( - "DROIDCON_SETTINGS_2023", - Context.MODE_PRIVATE - ) + get().getSharedPreferences("DROIDCON_SETTINGS_2023", Context.MODE_PRIVATE) } single { SharedPreferencesSettings(delegate = get()) } diff --git a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt index 277ab102..2fce49a7 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt @@ -22,10 +22,7 @@ class DefaultFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) - if ( - message.data.isNotEmpty() && - message.data[Notification.Keys.notificationType] == Notification.Values.refreshDataType - ) { + if (message.data.isNotEmpty() && message.data[Notification.Keys.notificationType] == Notification.Values.refreshDataType) { MainScope().launch { notificationService.handleNotification( Notification.Remote.RefreshData @@ -35,10 +32,7 @@ class DefaultFirebaseMessagingService : FirebaseMessagingService() { // If we have notification, we're running in foreground and should show it ourselves. val originalNotification = message.notification ?: return - val notification = NotificationCompat.Builder( - this, - message.notification?.channelId ?: "" - ) + val notification = NotificationCompat.Builder(this, message.notification?.channelId ?: "") .setContentTitle(originalNotification.title) .setContentText(originalNotification.body) .apply { diff --git a/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt b/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt index 15b9286a..96f07e38 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/util/NotificationLocalizedStringFactory.kt @@ -9,9 +9,7 @@ class NotificationLocalizedStringFactory( ) : NotificationSchedulingService.LocalizedStringFactory { override fun reminderTitle(roomName: String?): String { - val ending = roomName?.let { - context.getString(R.string.notification_reminder_title_in_room, it) - } ?: "" + val ending = roomName?.let { context.getString(R.string.notification_reminder_title_in_room, it) } ?: "" return context.getString(R.string.notification_reminder_title_base, ending) } diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt index 83d0cf36..783791d1 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt @@ -12,32 +12,12 @@ class NotificationLocalizedStringFactory( ) : NotificationSchedulingService.LocalizedStringFactory { override fun reminderTitle(roomName: String?): String { - val ending = roomName?.let { - NSString.stringWithFormat( - bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null) - .convertParametersForPrintf(), - it.cstr - ) - } ?: "" - return NSString.stringWithFormat( - bundle.localizedStringForKey( - "Notification.Reminder.Title.Base", - null, - null - ).convertParametersForPrintf(), - ending.cstr - ) + val ending = roomName?.let { NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null).convertParametersForPrintf(), it.cstr) } ?: "" + return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.Base", null, null).convertParametersForPrintf(), ending.cstr) } override fun reminderBody(sessionTitle: String): String { - return NSString.stringWithFormat( - bundle.localizedStringForKey( - "Notification.Reminder.Body", - null, - null - ).convertParametersForPrintf(), - sessionTitle.cstr - ) + return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Body", null, null).convertParametersForPrintf(), sessionTitle.cstr) } override fun feedbackTitle(): String { diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt index 6b732bad..0e1385c7 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt @@ -16,11 +16,7 @@ class IOSDateFormatter : DateFormatter { private val monthWithDay: NSDateFormatter by lazy { NSDateFormatter().also { val dateTemplate = "MMM d" - it.dateFormat = NSDateFormatter.dateFormatFromTemplate( - dateTemplate, - 0.toULong(), - NSLocale.currentLocale - )!! + it.dateFormat = NSDateFormatter.dateFormatFromTemplate(dateTemplate, 0.toULong(), NSLocale.currentLocale)!! } } @@ -34,11 +30,7 @@ class IOSDateFormatter : DateFormatter { private val timeOnlyNoPeriod: NSDateFormatter by lazy { NSDateFormatter().also { val dateTemplate = "hh:mm" - it.dateFormat = NSDateFormatter.dateFormatFromTemplate( - dateTemplate, - 0.toULong(), - NSLocale.currentLocale - )!! + it.dateFormat = NSDateFormatter.dateFormatFromTemplate(dateTemplate, 0.toULong(), NSLocale.currentLocale)!! } } @@ -48,18 +40,15 @@ class IOSDateFormatter : DateFormatter { override fun timeOnly(dateTime: LocalDateTime) = dateTime.date()?.let { timeOnly.stringFromDate(it) } - override fun timeOnlyInterval(fromDateTime: LocalDateTime, toDateTime: LocalDateTime) = - interval( - fromDateTime.date()?.let { timeOnlyNoPeriod.stringFromDate(it) }, - toDateTime.date()?.let { timeOnly.stringFromDate(it) } - ) + override fun timeOnlyInterval(fromDateTime: LocalDateTime, toDateTime: LocalDateTime) = interval( + fromDateTime.date()?.let { timeOnlyNoPeriod.stringFromDate(it) }, + toDateTime.date()?.let { timeOnly.stringFromDate(it) } + ) - private fun LocalDate.date() = - NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) + private fun LocalDate.date() = NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) private fun LocalDateTime.date() = - // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating - NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) + NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating private fun interval(from: String?, to: String?) = listOfNotNull(from, to).joinToString(" – ") } diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt index 2bc66830..629b1226 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/settings/PlatformSpecificSettings.kt @@ -19,10 +19,7 @@ internal actual fun PlatformSwitchApp() { val context = LocalContext.current Button( onClick = { - val intent = - context - .packageManager - .getLaunchIntentForPackage(Constants.SisterApp.androidPackageName) + val intent = context.packageManager.getLaunchIntentForPackage(Constants.SisterApp.androidPackageName) if (intent != null) { context.startActivity(intent) } else { diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt index 538f05ea..1a040985 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt @@ -2,19 +2,15 @@ package co.touchlab.droidcon.ui.util import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.Dialog as AndroidXComposeDialog import androidx.compose.ui.window.DialogProperties +import androidx.compose.ui.window.Dialog as AndroidXComposeDialog @OptIn(ExperimentalComposeUiApi::class) @Composable internal actual fun Dialog(dismiss: () -> Unit, content: @Composable () -> Unit) { AndroidXComposeDialog( onDismissRequest = dismiss, - properties = DialogProperties( - dismissOnBackPress = false, - dismissOnClickOutside = false, - usePlatformDefaultWidth = false - ), + properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false, usePlatformDefaultWidth = false), ) { content() } diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt index 4acc58b1..7ab27810 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.jvm.kt @@ -24,15 +24,9 @@ import co.touchlab.droidcon.ui.theme.Dimensions // platforms and on each platform we need to get the drawable according to provided name. @SuppressLint("ComposableNaming", "DiscouragedApi") @Composable -internal actual fun __LocalImage( - imageResourceName: String, - modifier: Modifier, - contentDescription: String? -) { +internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, contentDescription: String?) { val context = LocalContext.current - val imageRes = - context.resources.getIdentifier(imageResourceName, "drawable", context.packageName) - .takeIf { it != 0 } + val imageRes = context.resources.getIdentifier(imageResourceName, "drawable", context.packageName).takeIf { it != 0 } if (imageRes != null) { androidx.compose.foundation.Image( modifier = modifier, @@ -42,10 +36,7 @@ internal actual fun __LocalImage( ) } else { Row( - modifier = modifier.background( - MaterialTheme.colorScheme.primary, - RoundedCornerShape(Dimensions.Padding.half) - ), + modifier = modifier.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(Dimensions.Padding.half)), verticalAlignment = Alignment.CenterVertically ) { Spacer(modifier = Modifier.weight(1f)) @@ -55,11 +46,7 @@ internal actual fun __LocalImage( modifier = Modifier.padding(Dimensions.Padding.half), tint = Color.White ) - Text( - "Image not supported", - modifier = Modifier.padding(Dimensions.Padding.default), - color = Color.White - ) + Text("Image not supported", modifier = Modifier.padding(Dimensions.Padding.default), color = Color.White) Spacer(modifier = Modifier.weight(1f)) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt index fc77d37c..ed98270f 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/BottomNavigationView.kt @@ -35,13 +35,11 @@ internal fun BottomNavigationView(viewModel: ApplicationViewModel, modifier: Mod NavigationBar { viewModel.tabs.forEach { tab -> val (title, icon) = when (tab) { - ApplicationViewModel.Tab.Schedule -> - "Schedule" to Icons.Filled.CalendarMonth + ApplicationViewModel.Tab.Schedule -> "Schedule" to Icons.Filled.CalendarMonth // FIXME: Was originally "My agenda" but then it doesn't seem to fit. ApplicationViewModel.Tab.MyAgenda -> "Agenda" to Icons.Filled.Schedule ApplicationViewModel.Tab.Venue -> "Venue" to Icons.Filled.Map - ApplicationViewModel.Tab.Sponsors -> - "Sponsors" to Icons.Filled.LocalFireDepartment + ApplicationViewModel.Tab.Sponsors -> "Sponsors" to Icons.Filled.LocalFireDepartment ApplicationViewModel.Tab.Settings -> "Settings" to Icons.Filled.Settings } NavigationBarItem( @@ -68,13 +66,11 @@ internal fun BottomNavigationView(viewModel: ApplicationViewModel, modifier: Mod title = "Droidcon NYC 2024", emptyText = "Sessions could not be loaded.", ) - ApplicationViewModel.Tab.MyAgenda -> SessionListView( viewModel = viewModel.agenda, title = "Agenda", emptyText = "Add sessions to your agenda from session detail in schedule.", ) - ApplicationViewModel.Tab.Venue -> VenueView() ApplicationViewModel.Tab.Sponsors -> SponsorsView(viewModel.sponsors) ApplicationViewModel.Tab.Settings -> SettingsView(viewModel.settings) diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt index c161768d..dab5b8bb 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/settings/SettingsView.kt @@ -79,11 +79,7 @@ internal fun SettingsView(viewModel: SettingsViewModel) { } @Composable -internal fun IconTextSwitchRow( - text: String, - image: ImageVector, - checked: MutableObservableProperty -) { +internal fun IconTextSwitchRow(text: String, image: ImageVector, checked: MutableObservableProperty) { val isChecked by checked.observeAsState() Row( modifier = Modifier diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt index 93656900..b5680f41 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/util/LocalImage.kt @@ -4,17 +4,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @Composable -internal expect fun __LocalImage( - imageResourceName: String, - modifier: Modifier, - contentDescription: String? -) +internal expect fun __LocalImage(imageResourceName: String, modifier: Modifier, contentDescription: String?) @Composable -internal fun LocalImage( - imageResourceName: String, - modifier: Modifier = Modifier, - contentDescription: String? = null -) { +internal fun LocalImage(imageResourceName: String, modifier: Modifier = Modifier, contentDescription: String? = null) { __LocalImage(imageResourceName, modifier, contentDescription) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt index 957f4a21..76f94f92 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/util/NavigationController.kt @@ -36,10 +36,7 @@ private val LocalNavigationViewDimensions = staticCompositionLocalOf() - private var stackTracking: MutableList by published( - stack, - equalityPolicy = neverEqualPolicy() - ) + private var stackTracking: MutableList by published(stack, equalityPolicy = neverEqualPolicy()) private val observeStack by observe(::stackTracking) private var activeChild: NavigationController? = null @@ -49,23 +46,17 @@ class NavigationController : BaseViewModel() { } internal sealed class NavigationStackItem { - class BackPressHandler(val onBackPressed: BackPressHandlerScope.() -> Unit) : - NavigationStackItem() { + class BackPressHandler(val onBackPressed: BackPressHandlerScope.() -> Unit) : NavigationStackItem() { override fun toString(): String { return "BackPress@${hashCode().toUInt().toString(16)}" } } - class Push( - val item: MutableObservableProperty, - val content: @Composable (T) -> Unit - ) : NavigationStackItem() { + class Push(val item: MutableObservableProperty, val content: @Composable (T) -> Unit) : NavigationStackItem() { override fun toString(): String { - return "Push(${item.value}@${ - item.hashCode().toUInt().toString(16) - })@${hashCode().toUInt().toString(16)}" + return "Push(${item.value}@${item.hashCode().toUInt().toString(16)})@${hashCode().toUInt().toString(16)}" } } } @@ -139,10 +130,7 @@ class NavigationController : BaseViewModel() { } @Composable - private fun PushedStackItem( - item: NavigationStackItem.Push, - itemModifier: Modifier - ) { + private fun PushedStackItem(item: NavigationStackItem.Push, itemModifier: Modifier) { println("$item") val itemValue by item.item.observeAsState() @@ -154,10 +142,7 @@ class NavigationController : BaseViewModel() { } @Composable - internal fun Pushed( - item: MutableObservableProperty, - content: @Composable (T) -> Unit - ) { + internal fun Pushed(item: MutableObservableProperty, content: @Composable (T) -> Unit) { remember { val stackItem = NavigationStackItem.Push(item, content).also { notifyingStackChange { @@ -221,19 +206,14 @@ private class ReferenceTracking(private val onDispose: () -> Unit) : RememberObs } @Composable -internal fun BackPressHandler( - onBackPressed: NavigationController.BackPressHandlerScope.() -> Unit -) { +internal fun BackPressHandler(onBackPressed: NavigationController.BackPressHandlerScope.() -> Unit) { val navigationController = LocalNavigationController.current navigationController.HandleBackPressEffect(onBackPressed) } internal interface NavigationStackScope { - fun NavigationLink( - item: MutableObservableProperty, - content: @Composable (T) -> Unit - ) + fun NavigationLink(item: MutableObservableProperty, content: @Composable (T) -> Unit) } internal class NavigationLinkWrapper( @@ -256,8 +236,7 @@ internal class NavigationLinkWrapper( } override fun equals(other: Any?): Boolean { - return (other as? NavigationLinkWrapper<*>)?.let { it.index == index && it.value == value } - ?: false + return (other as? NavigationLinkWrapper<*>)?.let { it.index == index && it.value == value } ?: false } override fun hashCode(): Int { @@ -267,11 +246,7 @@ internal class NavigationLinkWrapper( @OptIn(ExperimentalAnimationApi::class) @Composable -internal fun NavigationStack( - key: Any?, - links: NavigationStackScope.() -> Unit, - content: @Composable () -> Unit -) { +internal fun NavigationStack(key: Any?, links: NavigationStackScope.() -> Unit, content: @Composable () -> Unit) { val activeLinkComposables by remember(key) { val constructedLinks = mutableListOf>>() val scope = object : NavigationStackScope { @@ -281,12 +256,7 @@ internal fun NavigationStack( ) { constructedLinks.add( item.map { - NavigationLinkWrapper( - index = constructedLinks.size, - value = it, - reset = { item.value = null }, - content - ) + NavigationLinkWrapper(index = constructedLinks.size, value = it, reset = { item.value = null }, content) } ) } @@ -299,15 +269,10 @@ internal fun NavigationStack( AnimatedContent( targetState = activeLinkComposables, transitionSpec = { - if ( - initialState.indexOfLast { it.body != null } < - targetState.indexOfLast { it.body != null } - ) { - slideInHorizontally(initialOffsetX = { it }) with - slideOutHorizontally(targetOffsetX = { -it }) + if (initialState.indexOfLast { it.body != null } < targetState.indexOfLast { it.body != null }) { + slideInHorizontally(initialOffsetX = { it }) with slideOutHorizontally(targetOffsetX = { -it }) } else { - slideInHorizontally(initialOffsetX = { -it }) with - slideOutHorizontally(targetOffsetX = { it }) + slideInHorizontally(initialOffsetX = { -it }) with slideOutHorizontally(targetOffsetX = { it }) } }, contentAlignment = Alignment.BottomCenter diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt index a77c9aab..7c2d89a3 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/AgendaViewModel.kt @@ -26,12 +26,6 @@ class AgendaViewModel( ) { fun create() = - AgendaViewModel( - sessionGateway, - sessionDayFactory, - sessionDetailFactory, - sessionDetailScrollStateStorage, - dateTimeService - ) + AgendaViewModel(sessionGateway, sessionDayFactory, sessionDetailFactory, sessionDetailScrollStateStorage, dateTimeService) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt index 987dd2ad..b1bfef29 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/BaseSessionListViewModel.kt @@ -18,11 +18,7 @@ abstract class BaseSessionListViewModel( private set val observeDays by observe(::days) - var selectedDay: SessionDayViewModel? by managed( - days?.firstOrNull { - it.date == sessionDetailScrollStateStorage.selectedDay - } - ) + var selectedDay: SessionDayViewModel? by managed(days?.firstOrNull { it.date == sessionDetailScrollStateStorage.selectedDay }) val observeSelectedDay by observe(::selectedDay) var presentedSessionDetail: SessionDetailViewModel? by managed(null) @@ -49,8 +45,7 @@ abstract class BaseSessionListViewModel( } .also { newDays -> days = newDays - selectedDay = newDays.firstOrNull { it.day == selectedDay?.day } - ?: newDays.firstOrNull() + selectedDay = newDays.firstOrNull { it.day == selectedDay?.day } ?: newDays.firstOrNull() } } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt index 90dc4639..ed46896a 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/ScheduleViewModel.kt @@ -35,12 +35,6 @@ class ScheduleViewModel( ) { fun create() = - ScheduleViewModel( - sessionGateway, - sessionDayFactory, - sessionDetailFactory, - sessionDetailScrollStateStorage, - dateTimeService - ) + ScheduleViewModel(sessionGateway, sessionDayFactory, sessionDetailFactory, sessionDetailScrollStateStorage, dateTimeService) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt index 47ba21b1..f97da9c5 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionBlockViewModel.kt @@ -32,12 +32,6 @@ class SessionBlockViewModel( startsAt: LocalDateTime, items: List, onScheduleItemSelected: (ScheduleItem) -> Unit, - ) = SessionBlockViewModel( - sessionListItemFactory, - dateFormatter, - startsAt, - items, - onScheduleItemSelected - ) + ) = SessionBlockViewModel(sessionListItemFactory, dateFormatter, startsAt, items, onScheduleItemSelected) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt index a7da1ec4..ba1ce1ca 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailScrollStateStorage.kt @@ -13,16 +13,9 @@ class SessionDetailScrollStateStorage { agendaScrollStates[day] } else { scrollStates[day] - } ?: SessionDayViewModel.ScrollState( - firstVisibleItemIndex = 0, - firstVisibleItemScrollOffset = 0 - ) + } ?: SessionDayViewModel.ScrollState(firstVisibleItemIndex = 0, firstVisibleItemScrollOffset = 0) - fun setScrollState( - day: LocalDate, - agenda: Boolean, - scrollState: SessionDayViewModel.ScrollState - ) { + fun setScrollState(day: LocalDate, agenda: Boolean, scrollState: SessionDayViewModel.ScrollState) { if (agenda) { agendaScrollStates[day] = scrollState } else { diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt index 0e091769..6d954abb 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/session/SessionDetailViewModel.kt @@ -36,11 +36,7 @@ class SessionDetailViewModel( initialItem: ScheduleItem, ) : BaseViewModel() { - private val item by collected( - initialItem, - sessionGateway.observeScheduleItem(initialItem.session.id), - identityEqualityPolicy() - ) + private val item by collected(initialItem, sessionGateway.observeScheduleItem(initialItem.session.id), identityEqualityPolicy()) private val observeItem by observe(::item) private val time: Instant by collected( @@ -82,11 +78,7 @@ class SessionDetailViewModel( val observeState by observe(::state) val abstract by observeItem.map { it.session.description } val observeAbstract by observe(::abstract) - val abstractLinks: List by observeItem.map { - it.session.description?.let( - parseUrlViewService::parse - ) ?: emptyList() - } + val abstractLinks: List by observeItem.map { it.session.description?.let(parseUrlViewService::parse) ?: emptyList() } val observeAbstractLinks by observe(::abstractLinks) val speakers: List by managedList( @@ -149,7 +141,7 @@ class SessionDetailViewModel( private fun parseUrl(text: String): List { val urlRegex = - "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)".toRegex() // ktlint-disable max-line-length + "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)".toRegex() return urlRegex.findAll(text).map { WebLink(it.range, it.value) }.toList() } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt index 4deb4c1b..14c092e0 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/settings/AboutViewModel.kt @@ -24,10 +24,7 @@ class AboutViewModel( } } - class Factory( - private val aboutRepository: AboutRepository, - private val parseUrlViewService: ParseUrlViewService - ) { + class Factory(private val aboutRepository: AboutRepository, private val parseUrlViewService: ParseUrlViewService) { fun create() = AboutViewModel(aboutRepository, parseUrlViewService) } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt index b841691b..b313f94c 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorDetailViewModel.kt @@ -44,12 +44,6 @@ class SponsorDetailViewModel( ) { fun create(sponsor: Sponsor, groupName: String) = - SponsorDetailViewModel( - sponsorGateway, - speakerListItemFactory, - speakerDetailFactory, - sponsor, - groupName - ) + SponsorDetailViewModel(sponsorGateway, speakerListItemFactory, speakerDetailFactory, sponsor, groupName) } } diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt index d07058d6..067eda99 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/viewmodel/sponsor/SponsorListViewModel.kt @@ -21,10 +21,7 @@ class SponsorListViewModel( sponsorGroup, onSponsorSelected = { sponsor -> if (sponsor.hasDetail) { - presentedSponsorDetail = sponsorDetailFactory.create( - sponsor, - sponsorGroup.group.name - ) + presentedSponsorDetail = sponsorDetailFactory.create(sponsor, sponsorGroup.group.name) } else { // UIApplication.sharedApplication.openURL(NSURL(string = sponsor.url.string)) presentedUrl = sponsor.url @@ -48,7 +45,6 @@ class SponsorListViewModel( private val sponsorDetailFactory: SponsorDetailViewModel.Factory, ) { - fun create() = - SponsorListViewModel(sponsorGateway, sponsorGroupFactory, sponsorDetailFactory) + fun create() = SponsorListViewModel(sponsorGateway, sponsorGroupFactory, sponsorDetailFactory) } } diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt index b5acce9d..75e2aad5 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/LocalImage.kt @@ -23,15 +23,8 @@ import platform.UIKit.UIImage @OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) @Composable -internal actual fun __LocalImage( - imageResourceName: String, - modifier: Modifier, - contentDescription: String? -) { - val painter = remember { - UIImage.imageNamed(imageResourceName)?.toSkiaImage()?.toComposeImageBitmap() - ?.let(::BitmapPainter) - } +internal actual fun __LocalImage(imageResourceName: String, modifier: Modifier, contentDescription: String?) { + val painter = remember { UIImage.imageNamed(imageResourceName)?.toSkiaImage()?.toComposeImageBitmap()?.let(::BitmapPainter) } if (painter != null) { androidx.compose.foundation.Image( modifier = modifier, @@ -41,10 +34,7 @@ internal actual fun __LocalImage( ) } else { Row( - modifier = modifier.background( - MaterialTheme.colorScheme.primary, - RoundedCornerShape(Dimensions.Padding.half) - ), + modifier = modifier.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(Dimensions.Padding.half)), verticalAlignment = Alignment.CenterVertically, ) { Spacer(modifier = Modifier.weight(1f)) @@ -54,11 +44,7 @@ internal actual fun __LocalImage( modifier = Modifier.padding(Dimensions.Padding.half), tint = Color.White, ) - Text( - "Image not supported", - modifier = Modifier.padding(Dimensions.Padding.default), - color = Color.White - ) + Text("Image not supported", modifier = Modifier.padding(Dimensions.Padding.default), color = Color.White) Spacer(modifier = Modifier.weight(1f)) } } diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt index 171b18e6..0c6649d7 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/NavigationBackPressWrapper.kt @@ -55,10 +55,7 @@ internal actual fun NavigationBackPressWrapper(content: @Composable () -> Unit) AnimatedVisibility( visible = dragDistance > triggerBackPressDragDistance, - enter = slideInHorizontally( - initialOffsetX = { -it }, - animationSpec = tween(durationMillis = 100) - ), + enter = slideInHorizontally(initialOffsetX = { -it }, animationSpec = tween(durationMillis = 100)), exit = slideOutHorizontally(targetOffsetX = { -it }) ) { Icon( diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt index a868d785..8aa2044f 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt @@ -23,8 +23,7 @@ import platform.UIKit.UIImage // TODO: Add support for remaining color spaces when the Skia library supports them. @ExperimentalForeignApi internal fun UIImage.toSkiaImage(): Image? { - val imageRef = - CGImageCreateCopyWithColorSpace(this.CGImage, CGColorSpaceCreateDeviceRGB()) ?: return null + val imageRef = CGImageCreateCopyWithColorSpace(this.CGImage, CGColorSpaceCreateDeviceRGB()) ?: return null val width = CGImageGetWidth(imageRef).toInt() val height = CGImageGetHeight(imageRef).toInt() @@ -36,16 +35,9 @@ internal fun UIImage.toSkiaImage(): Image? { val alphaInfo = CGImageGetAlphaInfo(imageRef) val alphaType = when (alphaInfo) { - CGImageAlphaInfo.kCGImageAlphaPremultipliedFirst, - CGImageAlphaInfo.kCGImageAlphaPremultipliedLast -> ColorAlphaType.PREMUL - - CGImageAlphaInfo.kCGImageAlphaFirst, - CGImageAlphaInfo.kCGImageAlphaLast -> ColorAlphaType.UNPREMUL - - CGImageAlphaInfo.kCGImageAlphaNone, - CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, - CGImageAlphaInfo.kCGImageAlphaNoneSkipLast -> ColorAlphaType.OPAQUE - + CGImageAlphaInfo.kCGImageAlphaPremultipliedFirst, CGImageAlphaInfo.kCGImageAlphaPremultipliedLast -> ColorAlphaType.PREMUL + CGImageAlphaInfo.kCGImageAlphaFirst, CGImageAlphaInfo.kCGImageAlphaLast -> ColorAlphaType.UNPREMUL + CGImageAlphaInfo.kCGImageAlphaNone, CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, CGImageAlphaInfo.kCGImageAlphaNoneSkipLast -> ColorAlphaType.OPAQUE else -> ColorAlphaType.UNKNOWN } @@ -56,12 +48,7 @@ internal fun UIImage.toSkiaImage(): Image? { CFRelease(imageRef) return Image.makeRaster( - imageInfo = ImageInfo( - width = width, - height = height, - colorType = ColorType.RGBA_8888, - alphaType = alphaType - ), + imageInfo = ImageInfo(width = width, height = height, colorType = ColorType.RGBA_8888, alphaType = alphaType), bytes = byteArray, rowBytes = bytesPerRow.toInt(), ) diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt index 59fff784..196c7054 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/Koin.android.kt @@ -45,14 +45,6 @@ actual val platformModule: Module = module { AndroidDateFormatter(dateTimeService = get()) } - val baseKermit = Logger( - config = StaticConfig( - logWriterList = listOf( - LogcatWriter(), - CrashlyticsLogWriter() - ) - ), - tag = "Droidcon" - ) + val baseKermit = Logger(config = StaticConfig(logWriterList = listOf(LogcatWriter(), CrashlyticsLogWriter())), tag = "Droidcon") factory { (tag: String?) -> if (tag != null) baseKermit.withTag(tag) else baseKermit } } diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt index e20203ae..78b26813 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/AndroidNotificationService.kt @@ -39,10 +39,9 @@ class AndroidNotificationService( settings[NOTIFICATION_ID_COUNTER_KEY] = value } - private val registeredNotifications: MutableMap> = - settings.getStringOrNull(NOTIFICATION_ID_MAP_KEY)?.let { - json.decodeFromString(it) - } ?: mutableMapOf() + private val registeredNotifications: MutableMap> = settings.getStringOrNull(NOTIFICATION_ID_MAP_KEY)?.let { + json.decodeFromString(it) + } ?: mutableMapOf() // TODO: Not called on Android. private var notificationHandler: DeepLinkNotificationHandler? = null @@ -73,13 +72,7 @@ class AndroidNotificationService( return true } - override suspend fun schedule( - notification: Notification.Local, - title: String, - body: String, - delivery: Instant, - dismiss: Instant? - ) { + override suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) { log.v { "Scheduling local notification at $delivery." } val deliveryTime = delivery.toEpochMilliseconds() @@ -141,20 +134,14 @@ class AndroidNotificationService( putExtra(NOTIFICATION_PAYLOAD_ID, intentId) putExtra(NOTIFICATION_PAYLOAD_TYPE, NOTIFICATION_TYPE_DISMISS) } - alarmManager.set( - AlarmManager.RTC_WAKEUP, - dismiss.toEpochMilliseconds(), - dismissPendingIntent - ) + alarmManager.set(AlarmManager.RTC_WAKEUP, dismiss.toEpochMilliseconds(), dismissPendingIntent) saveRegisteredNotificationId(sessionId, dismissIntentId) } } override suspend fun cancel(sessionIds: List) { - if (sessionIds.isEmpty()) { - return - } + if (sessionIds.isEmpty()) { return } log.v { "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" } @@ -191,7 +178,6 @@ class AndroidNotificationService( log.w { "notificationHandler not registered when received $notification" } } } - Notification.Remote.RefreshData -> syncService.forceSynchronize() } } @@ -231,13 +217,8 @@ class AndroidNotificationService( ) } - private fun createPendingIntent( - id: Int, - intentTransform: Intent.() -> Unit = {} - ): PendingIntent { - val intent = IdentifiableIntent("$id", context, NotificationPublisher::class.java).apply( - intentTransform - ) + private fun createPendingIntent(id: Int, intentTransform: Intent.() -> Unit = {}): PendingIntent { + val intent = IdentifiableIntent("$id", context, NotificationPublisher::class.java).apply(intentTransform) return PendingIntent.getBroadcast( context, id, @@ -257,8 +238,7 @@ class AndroidNotificationService( } private fun saveRegisteredNotificationId(sessionId: Session.Id, notificationId: Int) { - val currentNotificationIds = - (registeredNotifications[sessionId.value] ?: emptyList()).toMutableList() + val currentNotificationIds = (registeredNotifications[sessionId.value] ?: emptyList()).toMutableList() currentNotificationIds.add(notificationId) registeredNotifications[sessionId.value] = currentNotificationIds saveRegisteredNotifications() diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt index 76498001..ec3a8ce2 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/service/NotificationPublisher.kt @@ -9,12 +9,9 @@ import android.content.Intent class NotificationPublisher : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - val notificationId = - intent.getIntExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_ID, 0) - val notificationType = - intent.getStringExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_TYPE) - val notification = - intent.getParcelableExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_NOTIFICATION) + val notificationId = intent.getIntExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_ID, 0) + val notificationType = intent.getStringExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_TYPE) + val notification = intent.getParcelableExtra(AndroidNotificationService.NOTIFICATION_PAYLOAD_NOTIFICATION) with(context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager) { if (notificationType == AndroidNotificationService.NOTIFICATION_TYPE_DISMISS) { diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt index 680a73f6..2c095524 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt @@ -2,32 +2,25 @@ package co.touchlab.droidcon.util.formatter import co.touchlab.droidcon.Constants.conferenceTimeZone import co.touchlab.droidcon.domain.service.DateTimeService +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.atTime import java.text.DateFormat import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.atTime class AndroidDateFormatter(private val dateTimeService: DateTimeService) : DateFormatter { // TODOKPG - May not need to set timezone. Java date has no TZ private val shortDateFormat = - SimpleDateFormat("MMM d", Locale.getDefault()).apply { - timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) - } - private val minuteHourTimeFormat = - DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) - .apply { timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) } + SimpleDateFormat("MMM d", Locale.getDefault()).apply { timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) } + private val minuteHourTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()) + .apply { timeZone = java.util.TimeZone.getTimeZone(conferenceTimeZone.id) } override fun monthWithDay(date: LocalDate): String { return shortDateFormat.format( - Date( - with(dateTimeService) { - date.atTime(0, 0).fromConferenceDateTime() - }.toEpochMilliseconds() - ) + Date(with(dateTimeService) { date.atTime(0, 0).fromConferenceDateTime() }.toEpochMilliseconds()) ).uppercase() } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt index 002bd7a3..6c9a1f54 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/NotificationService.kt @@ -7,13 +7,7 @@ import kotlinx.datetime.Instant interface NotificationService { suspend fun initialize(): Boolean - suspend fun schedule( - notification: Notification.Local, - title: String, - body: String, - delivery: Instant, - dismiss: Instant? - ) + suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) suspend fun cancel(sessionIds: List) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt index cb5a2c4d..2363e53b 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/application/service/impl/DefaultNotificationSchedulingService.kt @@ -65,20 +65,11 @@ class DefaultNotificationSchedulingService( ) } - private suspend fun scheduleNotifications( - sessionFlow: Flow>, - settingsFlow: Flow - ) { + private suspend fun scheduleNotifications(sessionFlow: Flow>, settingsFlow: Flow) { sessionFlow .combine( settingsFlow, - transform = { agenda, settings -> - Triple( - agenda, - settings.isRemindersEnabled, - settings.isFeedbackEnabled - ) - } + transform = { agenda, settings -> Triple(agenda, settings.isRemindersEnabled, settings.isFeedbackEnabled) } ) .distinctUntilChanged() .collect { (agenda, isRemindersEnabled, isFeedbackEnabled) -> @@ -89,8 +80,7 @@ class DefaultNotificationSchedulingService( val oldSessionIds = scheduledSessionIds.filterNot { sessionId -> agenda.map { it.id.value }.contains(sessionId.value) } - scheduledNotifications = - scheduledNotifications.filterNot { oldSessionIds.contains(it) } + scheduledNotifications = scheduledNotifications.filterNot { oldSessionIds.contains(it) } notificationService.cancel(oldSessionIds) // Schedule new upcoming sessions. @@ -99,10 +89,7 @@ class DefaultNotificationSchedulingService( if (isRemindersEnabled) { val roomName = session.room?.let { roomRepository.get(it).name } val reminderDelivery = - session.startsAt.plus( - NotificationSchedulingService.REMINDER_DELIVERY_START_OFFSET, - DateTimeUnit.MINUTE - ) + session.startsAt.plus(NotificationSchedulingService.REMINDER_DELIVERY_START_OFFSET, DateTimeUnit.MINUTE) if (session.endsAt >= dateTimeService.now()) { notificationService.schedule( notification = Notification.Local.Reminder( @@ -121,15 +108,8 @@ class DefaultNotificationSchedulingService( if (isFeedbackEnabled) { val feedbackDelivery = - session.endsAt.plus( - NotificationSchedulingService.FEEDBACK_DISMISS_END_OFFSET, - DateTimeUnit.MINUTE - ) - if (feedbackDelivery.plus( - 24, - DateTimeUnit.HOUR - ) >= dateTimeService.now() && session.feedback == null - ) { + session.endsAt.plus(NotificationSchedulingService.FEEDBACK_DISMISS_END_OFFSET, DateTimeUnit.MINUTE) + if (feedbackDelivery.plus(24, DateTimeUnit.HOUR) >= dateTimeService.now() && session.feedback == null) { notificationService.schedule( notification = Notification.Local.Feedback( sessionId = session.id, diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt index 7eee9908..42a25888 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSessionRepository.kt @@ -19,21 +19,17 @@ class SqlDelightSessionRepository( private val sessionQueries: SessionQueries, ) : BaseRepository(), SessionRepository { override fun observe(id: Session.Id): Flow { - return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow() - .mapToOne(Dispatchers.Main) + return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOne(Dispatchers.Main) } - fun sessionById(id: Session.Id): Session? = - sessionQueries.sessionById(id.value, ::sessionFactory).executeAsOneOrNull() + fun sessionById(id: Session.Id): Session? = sessionQueries.sessionById(id.value, ::sessionFactory).executeAsOneOrNull() override fun observeOrNull(id: Session.Id): Flow { - return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow() - .mapToOneOrNull(Dispatchers.Main) + return sessionQueries.sessionById(id.value, ::sessionFactory).asFlow().mapToOneOrNull(Dispatchers.Main) } override fun observeAllAttending(): Flow> { - return sessionQueries.attendingSessions(::sessionFactory).asFlow() - .mapToList(Dispatchers.Main) + return sessionQueries.attendingSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) } override suspend fun allAttending(): List { @@ -56,11 +52,9 @@ class SqlDelightSessionRepository( sessionQueries.updateFeedBackSent(if (isSent) 1 else 0, sessionId.value) } - override fun allSync(): List = - sessionQueries.allSessions(::sessionFactory).executeAsList() + override fun allSync(): List = sessionQueries.allSessions(::sessionFactory).executeAsList() - override fun findSync(id: Session.Id): Session? = - sessionQueries.sessionById(id.value, mapper = ::sessionFactory).executeAsOneOrNull() + override fun findSync(id: Session.Id): Session? = sessionQueries.sessionById(id.value, mapper = ::sessionFactory).executeAsOneOrNull() override fun observeAll(): Flow> { return sessionQueries.allSessions(::sessionFactory).asFlow().mapToList(Dispatchers.Main) diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt index 1c127190..6b310860 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorGroupRepository.kt @@ -14,22 +14,18 @@ class SqlDelightSponsorGroupRepository( private val sponsorGroupQueries: SponsorGroupQueries, ) : BaseRepository(), SponsorGroupRepository { - override fun allSync(): List = - sponsorGroupQueries.selectAll(::sponsorGroupFactory).executeAsList() + override fun allSync(): List = sponsorGroupQueries.selectAll(::sponsorGroupFactory).executeAsList() override fun observe(id: SponsorGroup.Id): Flow { - return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow() - .mapToOne(Dispatchers.Main) + return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOne(Dispatchers.Main) } override fun observeOrNull(id: SponsorGroup.Id): Flow { - return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow() - .mapToOneOrNull(Dispatchers.Main) + return sponsorGroupQueries.sponsorGroupByName(id.value, ::sponsorGroupFactory).asFlow().mapToOneOrNull(Dispatchers.Main) } override fun observeAll(): Flow> { - return sponsorGroupQueries.selectAll(::sponsorGroupFactory).asFlow() - .mapToList(Dispatchers.Main) + return sponsorGroupQueries.selectAll(::sponsorGroupFactory).asFlow().mapToList(Dispatchers.Main) } override fun contains(id: SponsorGroup.Id): Boolean { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt index 90139b6a..5f798697 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/repository/impl/SqlDelightSponsorRepository.kt @@ -16,13 +16,11 @@ class SqlDelightSponsorRepository( ) : BaseRepository(), SponsorRepository { override fun observe(id: Sponsor.Id): Flow { - return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow() - .mapToOne(Dispatchers.Main) + return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOne(Dispatchers.Main) } override fun observeOrNull(id: Sponsor.Id): Flow { - return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow() - .mapToOneOrNull(Dispatchers.Main) + return sponsorQueries.sponsorById(id.name, id.group, ::sponsorFactory).asFlow().mapToOneOrNull(Dispatchers.Main) } override fun observeAll(): Flow> { @@ -37,8 +35,7 @@ class SqlDelightSponsorRepository( return sponsorQueries.sponsorsByGroup(group, ::sponsorFactory).executeAsList() } - override fun allSync(): List = - sponsorQueries.selectAll(::sponsorFactory).executeAsList() + override fun allSync(): List = sponsorQueries.selectAll(::sponsorFactory).executeAsList() override fun doUpsert(entity: Sponsor) { sponsorQueries.upsert( diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt index 8f8c634e..4e72b7f3 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt @@ -60,10 +60,8 @@ class DefaultSyncService( private const val SESSIONIZE_SYNC_SINCE_LAST_MINUTES = 15 private const val SESSIONIZE_SYNC_NEXT_DELAY: Long = 1L * 60L * 60L * 1000L - // 5 minutes private const val RSVP_SYNC_DELAY: Long = 5L * 60L * 1000L - // 5 minutes private const val FEEDBACK_SYNC_DELAY: Long = 5L * 60L * 1000L } @@ -75,8 +73,7 @@ class DefaultSyncService( } private var lastSessionizeSync: Instant? - get() = settings.getLongOrNull(LAST_SESSIONIZE_SYNC_KEY) - ?.let { Instant.fromEpochMilliseconds(it) } + get() = settings.getLongOrNull(LAST_SESSIONIZE_SYNC_KEY)?.let { Instant.fromEpochMilliseconds(it) } set(value) { settings[LAST_SESSIONIZE_SYNC_KEY] = value?.toEpochMilliseconds() } @@ -89,9 +86,7 @@ class DefaultSyncService( while (isActive) { val lastSessionizeSync = lastSessionizeSync // If this is the first Sessionize sync or if the last sync occurred more than 2 hours ago. - if (lastSessionizeSync == null || lastSessionizeSync <= dateTimeService.now() - .minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE) - ) { + if (lastSessionizeSync == null || lastSessionizeSync <= dateTimeService.now().minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE)) { try { runApiDataSourcesSynchronization() } catch (e: Exception) { @@ -99,16 +94,10 @@ class DefaultSyncService( delay(SESSIONIZE_SYNC_POLL_DELAY) continue } - log.d { - "Sync successful, " + - "waiting for next sync in $SESSIONIZE_SYNC_NEXT_DELAY ms." - } + log.d { "Sync successful, waiting for next sync in $SESSIONIZE_SYNC_NEXT_DELAY ms." } delay(SESSIONIZE_SYNC_NEXT_DELAY) } else { - log.d { - "The sync didn't happen, so we'll try again " + - "in a short while ($SESSIONIZE_SYNC_POLL_DELAY ms)." - } + log.d { "The sync didn't happen, so we'll try again in a short while ($SESSIONIZE_SYNC_POLL_DELAY ms)." } delay(SESSIONIZE_SYNC_POLL_DELAY) } } @@ -158,16 +147,9 @@ class DefaultSyncService( .forEach { (sessionId, feedback) -> while (isActive) { try { - val isFeedbackSent = serverApi.setFeedback( - sessionId, - feedback.rating, - feedback.comment - ) + val isFeedbackSent = serverApi.setFeedback(sessionId, feedback.rating, feedback.comment) if (isFeedbackSent) { - sessionRepository.setFeedbackSent( - sessionId, - isFeedbackSent - ) + sessionRepository.setFeedbackSent(sessionId, isFeedbackSent) } break } catch (e: Exception) { @@ -256,10 +238,8 @@ class DefaultSyncService( id = Session.Id(dto.id), title = dto.title, description = dto.description, - startsAt = LocalDateTime.parse(dto.startsAt) - .fromConferenceDateTime(dateTimeService), - endsAt = LocalDateTime.parse(dto.endsAt) - .fromConferenceDateTime(dateTimeService), + startsAt = LocalDateTime.parse(dto.startsAt).fromConferenceDateTime(dateTimeService), + endsAt = LocalDateTime.parse(dto.endsAt).fromConferenceDateTime(dateTimeService), isServiceSession = dto.isServiceSession, room = Room.Id(dto.roomID), rsvp = Session.RSVP( @@ -300,17 +280,12 @@ class DefaultSyncService( } } - private fun updateSponsorsFromDataSource( - sponsorSessionsGroups: List, - sponsors: SponsorsDto.SponsorCollectionDto - ) { + private fun updateSponsorsFromDataSource(sponsorSessionsGroups: List, sponsors: SponsorsDto.SponsorCollectionDto) { val sponsorSessions = sponsorSessionsGroups.flatMap { it.sessions }.associateBy { it.id } val sponsorGroupsToSponsorDtos = sponsors.groups.map { group -> val groupName = (group.name.split('/').lastOrNull() ?: group.name) .split(' ').joinToString(" ") { - it.replaceFirstChar { s -> - if (s.isLowerCase()) s.titlecase() else s.toString() - } + it.replaceFirstChar { s -> if (s.isLowerCase()) s.titlecase() else s.toString() } } SponsorGroup( @@ -320,23 +295,20 @@ class DefaultSyncService( ) to group.fields.sponsors.arrayValue.values.map { it.mapValue.fields } } - val sponsorsAndRepresentativeIds = - sponsorGroupsToSponsorDtos.flatMap { (group, sponsorDtos) -> - sponsorDtos.map { sponsorDto -> - val sponsorSession = - sponsorDto.sponsorId?.stringValue?.let(sponsorSessions::get) - val representativeIds = - sponsorSession?.speakers?.map { Profile.Id(it.id) } ?: emptyList() - - Sponsor( - id = Sponsor.Id(sponsorDto.name.stringValue, group.name), - hasDetail = sponsorSession != null, - description = sponsorSession?.description, - icon = Url(sponsorDto.icon.stringValue), - url = Url(sponsorDto.url.stringValue), - ) to representativeIds - } + val sponsorsAndRepresentativeIds = sponsorGroupsToSponsorDtos.flatMap { (group, sponsorDtos) -> + sponsorDtos.map { sponsorDto -> + val sponsorSession = sponsorDto.sponsorId?.stringValue?.let(sponsorSessions::get) + val representativeIds = sponsorSession?.speakers?.map { Profile.Id(it.id) } ?: emptyList() + + Sponsor( + id = Sponsor.Id(sponsorDto.name.stringValue, group.name), + hasDetail = sponsorSession != null, + description = sponsorSession?.description, + icon = Url(sponsorDto.icon.stringValue), + url = Url(sponsorDto.url.stringValue), + ) to representativeIds } + } sponsorRepository.allSync().map { it.id } .subtract(sponsorsAndRepresentativeIds.map { it.first.id }.toSet()) @@ -368,8 +340,7 @@ class DefaultSyncService( twitter = groupedLinks[LinkType.Twitter]?.firstOrNull()?.url?.let(::Url), linkedIn = groupedLinks[LinkType.LinkedIn]?.firstOrNull()?.url?.let(::Url), website = ( - groupedLinks[LinkType.CompanyWebsite] ?: groupedLinks[LinkType.Blog] - ?: groupedLinks[LinkType.Other] + groupedLinks[LinkType.CompanyWebsite] ?: groupedLinks[LinkType.Blog] ?: groupedLinks[LinkType.Other] )?.firstOrNull()?.url?.let(::Url), ) } diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt index 6176ad09..3aa16801 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/dto/SpeakersDto.kt @@ -53,10 +53,7 @@ object SpeakersDto { override val descriptor: SerialDescriptor get() { - return PrimitiveSerialDescriptor( - "co.touchlab.droidcon.domain.service.impl.dto.LinkType", - PrimitiveKind.STRING - ) + return PrimitiveSerialDescriptor("co.touchlab.droidcon.domain.service.impl.dto.LinkType", PrimitiveKind.STRING) } override fun deserialize(decoder: Decoder): LinkType = decoder.decodeString().let { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt index 38184940..7d9e7419 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/json/AboutJsonResourceDataSource.kt @@ -6,9 +6,6 @@ import kotlinx.serialization.builtins.ListSerializer class AboutJsonResourceDataSource(private val jsonResourceReader: JsonResourceReader) { fun getAboutItems(): List { - return jsonResourceReader.readAndDecodeResource( - "about.json", - ListSerializer(AboutDto.AboutItemDto.serializer()) - ) + return jsonResourceReader.readAndDecodeResource("about.json", ListSerializer(AboutDto.AboutItemDto.serializer())) } } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt index 66751bd6..6a75154e 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/Koin.ios.kt @@ -38,8 +38,7 @@ actual val platformModule = module { get() } - val baseKermit = - Logger(config = StaticConfig(logWriterList = listOf(NSLogWriter())), tag = "Droidcon") + val baseKermit = Logger(config = StaticConfig(logWriterList = listOf(NSLogWriter())), tag = "Droidcon") factory { (tag: String?) -> if (tag != null) baseKermit.withTag(tag) else baseKermit } single { AppChecker } @@ -47,54 +46,35 @@ actual val platformModule = module { @BetaInteropApi fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?, parameter: Any): Any { - val kClazz = - requireNotNull(getOriginalKotlinClass(objCClass)) { - "Could not get original kotlin class for $objCClass." - } + val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } return get(kClazz, qualifier) { parametersOf(parameter) } } @BetaInteropApi fun Koin.get(objCClass: ObjCClass, parameter: Any): Any { - val kClazz = - requireNotNull(getOriginalKotlinClass(objCClass)) { - "Could not get original kotlin class for $objCClass." - } + val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } return get(kClazz, null) { parametersOf(parameter) } } @BetaInteropApi fun Koin.get(objCClass: ObjCClass, qualifier: Qualifier?): Any { - val kClazz = - requireNotNull(getOriginalKotlinClass(objCClass)) { - "Could not get original kotlin class for $objCClass." - } + val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } return get(kClazz, qualifier, null) } @BetaInteropApi fun Koin.get(objCClass: ObjCClass): Any { - val kClazz = - requireNotNull(getOriginalKotlinClass(objCClass)) { - "Could not get original kotlin class for $objCClass." - } + val kClazz = requireNotNull(getOriginalKotlinClass(objCClass)) { "Could not get original kotlin class for $objCClass." } return get(kClazz, null) } @BetaInteropApi fun Koin.get(objCProtocol: ObjCProtocol, qualifier: Qualifier?): Any { - val kClazz = - requireNotNull(getOriginalKotlinClass(objCProtocol)) { - "Could not get original kotlin class for $objCProtocol." - } + val kClazz = requireNotNull(getOriginalKotlinClass(objCProtocol)) { "Could not get original kotlin class for $objCProtocol." } return get(kClazz, qualifier, null) } -fun Koin.getAny( - objCObject: ObjCObject, - qualifier: Qualifier?, - parameters: ParametersDefinition? -): Any { +fun Koin.getAny(objCObject: ObjCObject, qualifier: Qualifier?, parameters: ParametersDefinition?): Any { val kclass = when (objCObject) { is ObjCClass -> getOriginalKotlinClass(objCObject) is ObjCProtocol -> getOriginalKotlinClass(objCObject) diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt index 31d25606..e36ec876 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt @@ -2,13 +2,12 @@ package co.touchlab.droidcon import co.touchlab.droidcon.util.printThrowable import co.touchlab.kermit.Logger -import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob +import kotlin.coroutines.CoroutineContext -class MainScope(private val mainContext: CoroutineContext, private val log: Logger) : - CoroutineScope { +class MainScope(private val mainContext: CoroutineContext, private val log: Logger) : CoroutineScope { override val coroutineContext: CoroutineContext get() = mainContext + job + exceptionHandler diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt index 5f4e7bce..f47f9a2f 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt @@ -42,9 +42,7 @@ class IOSNotificationService( override suspend fun initialize(): Boolean { log.d { "Initializing." } - val notificationSettings = wrapMultiThreadCallback( - notificationCenter::getNotificationSettingsWithCompletionHandler - ) + val notificationSettings = wrapMultiThreadCallback(notificationCenter::getNotificationSettingsWithCompletionHandler) if (notificationSettings == null) { log.i { "Failed to get current notification authorization." } return false @@ -52,39 +50,25 @@ class IOSNotificationService( when (notificationSettings.authorizationStatus) { UNAuthorizationStatusNotDetermined -> { val requestOptions = UNAuthorizationOptionAlert or UNAuthorizationOptionSound - val (isAuthorized, error) = wrapMultiThreadCallback { - notificationCenter.requestAuthorizationWithOptions( - requestOptions, - it - ) - } + val (isAuthorized, error) = wrapMultiThreadCallback { notificationCenter.requestAuthorizationWithOptions(requestOptions, it) } if (error != null) { log.i { "Notifications authorization request failed with '$error'." } } return isAuthorized } - UNAuthorizationStatusDenied -> { log.i { "Notifications not authorized." } return false } - UNAuthorizationStatusAuthorized -> { log.i { "Notifications authorized." } return true } - else -> return false } } - override suspend fun schedule( - notification: Notification.Local, - title: String, - body: String, - delivery: Instant, - dismiss: Instant? - ) { + override suspend fun schedule(notification: Notification.Local, title: String, body: String, delivery: Instant, dismiss: Instant?) { log.v { "Scheduling local notification at ${delivery.toNSDate().description}." } val deliveryDate = delivery.toNSDate() val allUnits = NSCalendarUnitSecond or @@ -96,21 +80,15 @@ class IOSNotificationService( NSCalendarUnitTimeZone val dateComponents = NSCalendar.currentCalendar.components(allUnits, deliveryDate) - val trigger = UNCalendarNotificationTrigger.triggerWithDateMatchingComponents( - dateComponents, - repeats = false - ) + val trigger = UNCalendarNotificationTrigger.triggerWithDateMatchingComponents(dateComponents, repeats = false) val content = UNMutableNotificationContent() content.setTitle(title) content.setBody(body) content.setSound(UNNotificationSound.defaultSound) val (typeValue, sessionId) = when (notification) { - is Notification.Local.Feedback -> - Notification.Values.feedbackType to notification.sessionId - - is Notification.Local.Reminder -> - Notification.Values.reminderType to notification.sessionId + is Notification.Local.Feedback -> Notification.Values.feedbackType to notification.sessionId + is Notification.Local.Reminder -> Notification.Values.reminderType to notification.sessionId } content.setUserInfo( mapOf( @@ -119,18 +97,9 @@ class IOSNotificationService( ) ) - val request = UNNotificationRequest.requestWithIdentifier( - "${sessionId.value}-$typeValue", - content, - trigger - ) + val request = UNNotificationRequest.requestWithIdentifier("${sessionId.value}-$typeValue", content, trigger) - val error = wrapMultiThreadCallback { - notificationCenter.addNotificationRequest( - request, - it - ) - } + val error = wrapMultiThreadCallback { notificationCenter.addNotificationRequest(request, it) } if (error == null) { log.v { "Scheduling notification complete." } } else { @@ -139,14 +108,9 @@ class IOSNotificationService( } override suspend fun cancel(sessionIds: List) { - if (sessionIds.isEmpty()) { - return - } - log.v { - "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" - } - notificationCenter - .removePendingNotificationRequestsWithIdentifiers(sessionIds.map { it.value }) + if (sessionIds.isEmpty()) { return } + log.v { "Cancelling scheduled notifications with IDs: [${sessionIds.joinToString { it.value }}]" } + notificationCenter.removePendingNotificationRequestsWithIdentifiers(sessionIds.map { it.value }) } @Suppress("unused") @@ -174,7 +138,6 @@ class IOSNotificationService( log.w { "notificationHandler not registered when received $notification" } } } - Notification.Remote.RefreshData -> syncService.forceSynchronize() } } @@ -193,9 +156,7 @@ class IOSNotificationService( private fun Map.parseReminderNotification(): Notification.Local.Reminder? { val sessionId = this[Notification.Keys.sessionId] as? String ?: run { - log.e { - "Couldn't parse reminder notification. Session ID doesn't exist or isn't String." - } + log.e { "Couldn't parse reminder notification. Session ID doesn't exist or isn't String." } return null } @@ -206,9 +167,7 @@ class IOSNotificationService( private fun Map.parseFeedbackNotification(): Notification.Local.Feedback? { val sessionId = this[Notification.Keys.sessionId] as? String ?: run { - log.e { - "Couldn't parse feedback notification. Session ID doesn't exist or isn't String." - } + log.e { "Couldn't parse feedback notification. Session ID doesn't exist or isn't String." } return null } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt index f8ca9e85..677fa2cf 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/AppInit.kt @@ -9,12 +9,7 @@ import co.touchlab.kermit.crashlytics.CrashlyticsLogWriter @OptIn(ExperimentalKermitApi::class) fun setupKermit() { - Logger.addLogWriter( - CrashlyticsLogWriter( - minSeverity = Severity.Info, - minCrashSeverity = Severity.Warn - ) - ) + Logger.addLogWriter(CrashlyticsLogWriter(minSeverity = Severity.Info, minCrashSeverity = Severity.Warn)) enableCrashlytics() setCrashlyticsUnhandledExceptionHook() } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt index d536cd4d..bbff7c72 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt @@ -26,37 +26,21 @@ class BundleResourceReader( 0 -> { null to name.drop(1) } - in 1..Int.MAX_VALUE -> { name.take(lastPeriodIndex) to name.drop(lastPeriodIndex + 1) } - else -> { name to null } } - val path = bundle.pathForResource(filename, type) ?: error( - "Couldn't get path of $name (parsed as: ${ - listOfNotNull( - filename, - type - ).joinToString(".") - })" - ) + val path = bundle.pathForResource(filename, type) ?: error("Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type).joinToString(".")})") return memScoped { val errorPtr = alloc>() - NSString.stringWithContentsOfFile( - path, - encoding = NSUTF8StringEncoding, - error = errorPtr.ptr - ) ?: run { + NSString.stringWithContentsOfFile(path, encoding = NSUTF8StringEncoding, error = errorPtr.ptr) ?: run { // TODO: Check the NSError and throw common exception. - error( - "Couldn't load resource: $name. " + - "Error: ${errorPtr.value?.localizedDescription} - ${errorPtr.value}" - ) + error("Couldn't load resource: $name. Error: ${errorPtr.value?.localizedDescription} - ${errorPtr.value}") } } } diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt index d778ee5a..a313cf03 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/wrapMultiThreadCallback.kt @@ -10,10 +10,9 @@ suspend fun wrapMultiThreadCallback(call: (callback: (T) -> Unit) -> Unit): return completable.await() } -suspend fun wrapMultiThreadCallback(call: (callback: (T1, T2) -> Unit) -> Unit): - Pair { - val completable = CompletableDeferred>() - val closure: (T1, T2) -> Unit = { t1, t2 -> completable.complete(t1 to t2) } - call(closure) - return completable.await() - } +suspend fun wrapMultiThreadCallback(call: (callback: (T1, T2) -> Unit) -> Unit): Pair { + val completable = CompletableDeferred>() + val closure: (T1, T2) -> Unit = { t1, t2 -> completable.complete(t1 to t2) } + call(closure) + return completable.await() +} From ceb24caf671913703106a6217d996b09b44d461c Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 13:59:14 -0400 Subject: [PATCH 06/17] Revert "formatting" This reverts commit b97f85e14959118bb36a82eba0dfea7cee498dda. --- android/build.gradle.kts | 5 +---- .../co/touchlab/droidcon/android/MainActivity.kt | 12 ++---------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index a3c5aaeb..24540f66 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -47,10 +47,7 @@ android { if (releaseEnabled) { getByName("release") { isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") signingConfig = signingConfigs.getByName("release") } } diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt index 07d5cc68..86e57adf 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt @@ -154,16 +154,8 @@ class MainActivity : ComponentActivity(), KoinComponent { private fun askNotificationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if ( - ContextCompat.checkSelfPermission( - this, - Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED - ) { - } else if ( - false && - shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) - ) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + } else if (false && shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { // TODO: display an educational UI explaining to the user the features that will be enabled // by them granting the POST_NOTIFICATION permission. This UI should provide the user // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission. From c5592a003d5e27a2c0e0e85589e6d241613b4425 Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 14:07:34 -0400 Subject: [PATCH 07/17] running formatting --- .../NotificationLocalizedStringFactory.kt | 23 ++++++++++++++++--- .../ios/util/formatter/IOSDateFormatter.kt | 3 ++- .../co/touchlab/droidcon/ui/util/Dialog.kt | 2 +- .../ui/session/SessionListView.kt | 5 +++- .../touchlab/droidcon/ui/util/ToSkiaImage.kt | 3 ++- .../util/formatter/AndroidDateFormatter.kt | 6 ++--- .../domain/service/impl/DefaultSyncService.kt | 10 ++++++-- .../kotlin/co/touchlab/droidcon/MainScope.kt | 2 +- .../droidcon/util/BundleResourceReader.kt | 6 ++++- 9 files changed, 46 insertions(+), 14 deletions(-) diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt index 783791d1..8ff23978 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/NotificationLocalizedStringFactory.kt @@ -12,12 +12,29 @@ class NotificationLocalizedStringFactory( ) : NotificationSchedulingService.LocalizedStringFactory { override fun reminderTitle(roomName: String?): String { - val ending = roomName?.let { NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null).convertParametersForPrintf(), it.cstr) } ?: "" - return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Title.Base", null, null).convertParametersForPrintf(), ending.cstr) + val ending = roomName?.let { + NSString + .stringWithFormat( + bundle.localizedStringForKey("Notification.Reminder.Title.InRoom", null, null) + .convertParametersForPrintf(), + it.cstr + ) + } ?: "" + return NSString + .stringWithFormat( + bundle.localizedStringForKey("Notification.Reminder.Title.Base", null, null) + .convertParametersForPrintf(), + ending.cstr + ) } override fun reminderBody(sessionTitle: String): String { - return NSString.stringWithFormat(bundle.localizedStringForKey("Notification.Reminder.Body", null, null).convertParametersForPrintf(), sessionTitle.cstr) + return NSString + .stringWithFormat( + bundle.localizedStringForKey("Notification.Reminder.Body", null, null) + .convertParametersForPrintf(), + sessionTitle.cstr + ) } override fun feedbackTitle(): String { diff --git a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt index 0e1385c7..a2140f47 100644 --- a/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt +++ b/ios/src/iosMain/kotlin/co/touchlab/droidcon/ios/util/formatter/IOSDateFormatter.kt @@ -48,7 +48,8 @@ class IOSDateFormatter : DateFormatter { private fun LocalDate.date() = NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) private fun LocalDateTime.date() = - NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating + // TODOKPG - Pretty sure this is device time zone, might be OK. Just for local formating + NSCalendar.currentCalendar.dateFromComponents(toNSDateComponents()) private fun interval(from: String?, to: String?) = listOfNotNull(from, to).joinToString(" – ") } diff --git a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt index 1a040985..82eb170e 100644 --- a/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt +++ b/shared-ui/src/androidMain/kotlin/co/touchlab/droidcon/ui/util/Dialog.kt @@ -2,8 +2,8 @@ package co.touchlab.droidcon.ui.util import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.Dialog as AndroidXComposeDialog +import androidx.compose.ui.window.DialogProperties @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt index d3b42cf5..e593e5d5 100644 --- a/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt +++ b/shared-ui/src/commonMain/kotlin/co.touchlab.droidcon/ui/session/SessionListView.kt @@ -112,7 +112,10 @@ internal fun SessionListView( Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]) ) } else { - Logger.w("SessionList TabRow requested an indicator for selectedTabIndex: ${pagerState.currentPage}, but only got ${tabPositions.count()} tabs.") + Logger.w( + "SessionList TabRow requested an indicator for selectedTabIndex: " + + "${pagerState.currentPage}, but only got ${tabPositions.count()} tabs." + ) TabRowDefaults.SecondaryIndicator() } } diff --git a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt index 8aa2044f..3173b42e 100644 --- a/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt +++ b/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt @@ -37,7 +37,8 @@ internal fun UIImage.toSkiaImage(): Image? { val alphaType = when (alphaInfo) { CGImageAlphaInfo.kCGImageAlphaPremultipliedFirst, CGImageAlphaInfo.kCGImageAlphaPremultipliedLast -> ColorAlphaType.PREMUL CGImageAlphaInfo.kCGImageAlphaFirst, CGImageAlphaInfo.kCGImageAlphaLast -> ColorAlphaType.UNPREMUL - CGImageAlphaInfo.kCGImageAlphaNone, CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, CGImageAlphaInfo.kCGImageAlphaNoneSkipLast -> ColorAlphaType.OPAQUE + CGImageAlphaInfo.kCGImageAlphaNone, CGImageAlphaInfo.kCGImageAlphaNoneSkipFirst, CGImageAlphaInfo.kCGImageAlphaNoneSkipLast + -> ColorAlphaType.OPAQUE else -> ColorAlphaType.UNKNOWN } diff --git a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt index 2c095524..28cb36ca 100644 --- a/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt +++ b/shared/src/androidMain/kotlin/co/touchlab/droidcon/util/formatter/AndroidDateFormatter.kt @@ -2,13 +2,13 @@ package co.touchlab.droidcon.util.formatter import co.touchlab.droidcon.Constants.conferenceTimeZone import co.touchlab.droidcon.domain.service.DateTimeService -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.atTime import java.text.DateFormat import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.atTime class AndroidDateFormatter(private val dateTimeService: DateTimeService) : DateFormatter { diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt index 4e72b7f3..c8abbc62 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt @@ -86,7 +86,10 @@ class DefaultSyncService( while (isActive) { val lastSessionizeSync = lastSessionizeSync // If this is the first Sessionize sync or if the last sync occurred more than 2 hours ago. - if (lastSessionizeSync == null || lastSessionizeSync <= dateTimeService.now().minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE)) { + if ( + lastSessionizeSync == null || + lastSessionizeSync <= dateTimeService.now().minus(SESSIONIZE_SYNC_SINCE_LAST_MINUTES, DateTimeUnit.MINUTE) + ) { try { runApiDataSourcesSynchronization() } catch (e: Exception) { @@ -280,7 +283,10 @@ class DefaultSyncService( } } - private fun updateSponsorsFromDataSource(sponsorSessionsGroups: List, sponsors: SponsorsDto.SponsorCollectionDto) { + private fun updateSponsorsFromDataSource( + sponsorSessionsGroups: List, + sponsors: SponsorsDto.SponsorCollectionDto + ) { val sponsorSessions = sponsorSessionsGroups.flatMap { it.sessions }.associateBy { it.id } val sponsorGroupsToSponsorDtos = sponsors.groups.map { group -> val groupName = (group.name.split('/').lastOrNull() ?: group.name) diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt index e36ec876..782d5ef1 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/MainScope.kt @@ -2,10 +2,10 @@ package co.touchlab.droidcon import co.touchlab.droidcon.util.printThrowable import co.touchlab.kermit.Logger +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob -import kotlin.coroutines.CoroutineContext class MainScope(private val mainContext: CoroutineContext, private val log: Logger) : CoroutineScope { diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt index bbff7c72..13e1d3e5 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt @@ -33,7 +33,11 @@ class BundleResourceReader( name to null } } - val path = bundle.pathForResource(filename, type) ?: error("Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type).joinToString(".")})") + val path = bundle + .pathForResource(filename, type) ?: error( + "Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type) + .joinToString(".")})" + ) return memScoped { val errorPtr = alloc>() From c9bc9aa0d6e3203a8d05e5f56e6f7da80589089d Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 14:22:57 -0400 Subject: [PATCH 08/17] adding missing line --- .editorconfig | 1 + .../droidcon/domain/service/impl/DefaultSyncService.kt | 3 ++- .../co/touchlab/droidcon/service/IOSNotificationService.kt | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index e4043085..93c16cf1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,7 @@ ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthet indent_size = 4 indent_style = space insert_final_newline = true +parameter-list-wrapping = true ktlint_argument_list_wrapping_ignore_when_parameter_count_greater_or_equal_than = 8 ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = 4 ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset diff --git a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt index c8abbc62..01b75b7f 100644 --- a/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt +++ b/shared/src/commonMain/kotlin/co/touchlab/droidcon/domain/service/impl/DefaultSyncService.kt @@ -286,7 +286,7 @@ class DefaultSyncService( private fun updateSponsorsFromDataSource( sponsorSessionsGroups: List, sponsors: SponsorsDto.SponsorCollectionDto - ) { + ): String { val sponsorSessions = sponsorSessionsGroups.flatMap { it.sessions }.associateBy { it.id } val sponsorGroupsToSponsorDtos = sponsors.groups.map { group -> val groupName = (group.name.split('/').lastOrNull() ?: group.name) @@ -333,6 +333,7 @@ class DefaultSyncService( profileRepository.setSponsorRepresentatives(sponsor, representativeIds) } + return "" } private fun profileFactory(speakerDto: SpeakersDto.SpeakerDto): Profile { diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt index f47f9a2f..da7940b2 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/service/IOSNotificationService.kt @@ -50,7 +50,9 @@ class IOSNotificationService( when (notificationSettings.authorizationStatus) { UNAuthorizationStatusNotDetermined -> { val requestOptions = UNAuthorizationOptionAlert or UNAuthorizationOptionSound - val (isAuthorized, error) = wrapMultiThreadCallback { notificationCenter.requestAuthorizationWithOptions(requestOptions, it) } + val (isAuthorized, error) = wrapMultiThreadCallback { + notificationCenter.requestAuthorizationWithOptions(requestOptions, it) + } if (error != null) { log.i { "Notifications authorization request failed with '$error'." } } From c3816b4bab86a578c2de025775489fcb47b90cd2 Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 14:36:12 -0400 Subject: [PATCH 09/17] Update shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt Co-authored-by: Tadeas Kriz --- .../co/touchlab/droidcon/util/BundleResourceReader.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt index 13e1d3e5..db16b882 100644 --- a/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt +++ b/shared/src/iosMain/kotlin/co/touchlab/droidcon/util/BundleResourceReader.kt @@ -33,10 +33,8 @@ class BundleResourceReader( name to null } } - val path = bundle - .pathForResource(filename, type) ?: error( - "Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type) - .joinToString(".")})" + val path = bundle.pathForResource(filename, type) ?: error( + "Couldn't get path of $name (parsed as: ${listOfNotNull(filename, type).joinToString(".")})" ) return memScoped { From ca4a2707f852f1c02d36ab9113d6bf3548e28a9d Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 15:00:49 -0400 Subject: [PATCH 10/17] Adding idea files --- .gitignore | 26 +++++++-------- .idea/codeStyles/Project.xml | 32 +++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 +++ .idea/inspectionProfiles/ktlint.xml | 7 ++++ .../inspectionProfiles/profiles_settings.xml | 6 ++++ 5 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/inspectionProfiles/ktlint.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml diff --git a/.gitignore b/.gitignore index 86fdc539..1f139b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,17 +38,7 @@ captures/ *.iml .gradle /local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -/.idea/encodings.xml -.idea/saveactions_settings.xml -.idea/deploymentTargetDropDown.xml -.idea/gradle.xml -.idea/misc.xml + .DS_Store /build /captures @@ -87,8 +77,14 @@ GoogleService-Info.plist GoogleService-Info.plist Pods/ *.xcworkspace -.idea/inspectionProfiles/profiles_settings.xml -.idea/inspectionProfiles/Project_Default.xml -.idea/dictionaries/matyas.xml -/.idea/ .kotlin +.idea/.name +.idea/compiler.xml +.idea/deploymentTargetSelector.xml +.idea/gradle.xml +.idea/kotlinc.xml +.idea/misc.xml +.idea/runConfigurations.xml +.idea/vcs.xml +.idea/workspace.xml +.idea/caches/deviceStreaming.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..cbe742a2 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..0f7bc519 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + diff --git a/.idea/inspectionProfiles/ktlint.xml b/.idea/inspectionProfiles/ktlint.xml new file mode 100644 index 00000000..7d04a74b --- /dev/null +++ b/.idea/inspectionProfiles/ktlint.xml @@ -0,0 +1,7 @@ + + + + diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..64580d14 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + From 35e031d4b19b05e2a85ada1aa31d4635e5676c61 Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 15:14:27 -0400 Subject: [PATCH 11/17] Update Project.xml --- .idea/codeStyles/Project.xml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index cbe742a2..6c0b9196 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,11 +3,9 @@ - @@ -17,16 +15,15 @@ - + \ No newline at end of file From a083acb285092112936d8dcf47593c98ded2bc57 Mon Sep 17 00:00:00 2001 From: Kevin Schildhorn Date: Thu, 24 Oct 2024 15:27:05 -0400 Subject: [PATCH 12/17] running formatting --- .editorconfig | 2 +- android/src/main/AndroidManifest.xml | 20 +- .../touchlab/droidcon/android/MainActivity.kt | 6 +- .../co/touchlab/droidcon/android/MainApp.kt | 2 +- .../impl/DefaultFirebaseMessagingService.kt | 3 +- .../res/drawable-night/about_droidcon.xml | 70 +- .../main/res/drawable-night/about_kotlin.xml | 77 +- .../src/main/res/drawable/about_droidcon.xml | 54 +- .../src/main/res/drawable/about_kotlin.xml | 77 +- .../ic_baseline_insert_invitation_24.xml | 10 +- .../res/drawable/ic_launcher_foreground.xml | 16 +- .../main/res/drawable/ic_splash_screen.xml | 728 +++++++++--------- android/src/main/res/drawable/linkedin.xml | 10 +- android/src/main/res/drawable/twitter.xml | 10 +- .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 +- android/src/main/res/values/themes.xml | 3 +- .../ui/session/SessionDetailView.kt | 12 +- shared/build.gradle.kts | 2 +- .../service/AndroidNotificationService.kt | 7 +- .../droidcon/util/AssetResourceReader.kt | 2 +- .../util/formatter/AndroidDateFormatter.kt | 4 +- .../ic_baseline_insert_invitation_24.xml | 10 +- .../kotlin/co/touchlab/droidcon/Koin.kt | 14 +- .../impl/DefaultSettingsRepository.kt | 2 +- .../application/service/Notification.kt | 1 + .../DefaultNotificationSchedulingService.kt | 4 +- .../gateway/impl/DefaultSponsorGateway.kt | 2 +- .../impl/SqlDelightProfileRepository.kt | 2 +- .../impl/SqlDelightSessionRepository.kt | 2 +- .../domain/service/impl/DefaultServerApi.kt | 2 +- .../domain/service/impl/DefaultSyncService.kt | 6 +- .../domain/service/impl/dto/SpeakersDto.kt | 4 +- .../service/impl/dto/SponsorSessionsDto.kt | 2 +- .../impl/json/JsonSeedResourceDataSource.kt | 4 +- .../service/IOSNotificationService.kt | 10 +- .../droidcon/util/BundleResourceReader.kt | 6 +- 36 files changed, 611 insertions(+), 581 deletions(-) diff --git a/.editorconfig b/.editorconfig index 93c16cf1..e498c932 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,11 +1,11 @@ [*.{kt,kts}] end_of_line = lf +ij_kotlin_packages_to_use_import_on_demand = true ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true ij_kotlin_imports_layout = * ij_kotlin_indent_before_arrow_on_new_line = false ij_kotlin_line_break_after_multiline_when_entry = true -ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.** indent_size = 4 indent_style = space insert_final_newline = true diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index a4d91700..8881129e 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,9 +1,13 @@ - - - + - + + + + @@ -36,11 +40,13 @@ - + + android:name="co.touchlab.droidcon.android.service.impl.DefaultFirebaseMessagingService" + android:exported="false"> diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt index 86e57adf..2fb3dab9 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainActivity.kt @@ -56,7 +56,7 @@ class MainActivity : ComponentActivity(), KoinComponent { private val root = LifecycleGraph.Root(this) private val requestPermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestPermission() + ActivityResultContracts.RequestPermission(), ) { isGranted: Boolean -> } @@ -103,12 +103,12 @@ class MainActivity : ComponentActivity(), KoinComponent { modifier = Modifier .background(Colors.primary) .fillMaxSize(), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Image( painter = painterResource(id = R.drawable.ic_splash_screen), contentDescription = getString(R.string.droidcon_title), - modifier = Modifier.padding(32.dp) + modifier = Modifier.padding(32.dp), ) } } diff --git a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt index b96e87dc..6452a9d4 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/MainApp.kt @@ -50,7 +50,7 @@ class MainApp : Application() { single { AndroidAnalyticsService(firebaseAnalytics = Firebase.analytics) } - } + uiModule + } + uiModule, ) } } diff --git a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt index 2fce49a7..7fa34636 100644 --- a/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt +++ b/android/src/main/java/co/touchlab/droidcon/android/service/impl/DefaultFirebaseMessagingService.kt @@ -10,7 +10,6 @@ import com.google.firebase.messaging.RemoteMessage import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import org.koin.android.ext.android.inject -import org.koin.core.component.inject class DefaultFirebaseMessagingService : FirebaseMessagingService() { private val notificationService: AndroidNotificationService by inject() @@ -25,7 +24,7 @@ class DefaultFirebaseMessagingService : FirebaseMessagingService() { if (message.data.isNotEmpty() && message.data[Notification.Keys.notificationType] == Notification.Values.refreshDataType) { MainScope().launch { notificationService.handleNotification( - Notification.Remote.RefreshData + Notification.Remote.RefreshData, ) } } diff --git a/android/src/main/res/drawable-night/about_droidcon.xml b/android/src/main/res/drawable-night/about_droidcon.xml index b3518dc5..efda5998 100644 --- a/android/src/main/res/drawable-night/about_droidcon.xml +++ b/android/src/main/res/drawable-night/about_droidcon.xml @@ -3,39 +3,39 @@ android:height="98dp" android:viewportWidth="518" android:viewportHeight="98"> - - - - - - - - - + + + + + + + + + diff --git a/android/src/main/res/drawable-night/about_kotlin.xml b/android/src/main/res/drawable-night/about_kotlin.xml index 40181fdf..0d03ca44 100644 --- a/android/src/main/res/drawable-night/about_kotlin.xml +++ b/android/src/main/res/drawable-night/about_kotlin.xml @@ -4,40 +4,45 @@ android:height="528dp" android:viewportWidth="2435" android:viewportHeight="528"> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/drawable/about_droidcon.xml b/android/src/main/res/drawable/about_droidcon.xml index fbdb5044..7540c6e3 100644 --- a/android/src/main/res/drawable/about_droidcon.xml +++ b/android/src/main/res/drawable/about_droidcon.xml @@ -3,31 +3,31 @@ android:height="127.2dp" android:viewportWidth="357.6" android:viewportHeight="127.2"> - - - - - - - - - + + + + + + + + + diff --git a/android/src/main/res/drawable/about_kotlin.xml b/android/src/main/res/drawable/about_kotlin.xml index dbe8bd54..18bed16f 100644 --- a/android/src/main/res/drawable/about_kotlin.xml +++ b/android/src/main/res/drawable/about_kotlin.xml @@ -4,40 +4,45 @@ android:height="528dp" android:viewportWidth="2435" android:viewportHeight="528"> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml b/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml index 98cf2e2a..9087f64a 100755 --- a/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml +++ b/android/src/main/res/drawable/ic_baseline_insert_invitation_24.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,12h-5v5h5v-5zM16,1v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2h-1L18,1h-2zM19,19L5,19L5,8h14v11z" /> diff --git a/android/src/main/res/drawable/ic_launcher_foreground.xml b/android/src/main/res/drawable/ic_launcher_foreground.xml index a3a5dee1..3016c10b 100644 --- a/android/src/main/res/drawable/ic_launcher_foreground.xml +++ b/android/src/main/res/drawable/ic_launcher_foreground.xml @@ -3,12 +3,12 @@ android:height="108dp" android:viewportWidth="1024" android:viewportHeight="1024"> - - + + diff --git a/android/src/main/res/drawable/ic_splash_screen.xml b/android/src/main/res/drawable/ic_splash_screen.xml index f72ad625..a2674f51 100644 --- a/android/src/main/res/drawable/ic_splash_screen.xml +++ b/android/src/main/res/drawable/ic_splash_screen.xml @@ -3,368 +3,368 @@ android:height="1920dp" android:viewportWidth="1280" android:viewportHeight="1920"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/drawable/linkedin.xml b/android/src/main/res/drawable/linkedin.xml index b58dac32..b9ed4164 100644 --- a/android/src/main/res/drawable/linkedin.xml +++ b/android/src/main/res/drawable/linkedin.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="72" + android:viewportHeight="72"> +V31.4h6.8v3.1h0.1c0.9-1.8,3.3-3.7,6.7-3.7c7.2,0,8.5,4.7,8.5,10.9V54.4z" /> diff --git a/android/src/main/res/drawable/twitter.xml b/android/src/main/res/drawable/twitter.xml index edd5d300..3241156c 100644 --- a/android/src/main/res/drawable/twitter.xml +++ b/android/src/main/res/drawable/twitter.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportHeight="72" + android:viewportWidth="72"> +c0-0.4,0-0.9,0-1.3C57,24.8,58.7,23.1,60,21.1Z" /> diff --git a/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index e974f9b0..20211a2c 100644 --- a/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,7 +1,9 @@ - + - + diff --git a/android/src/main/res/values/themes.xml b/android/src/main/res/values/themes.xml index 27cd6517..6ab3e289 100644 --- a/android/src/main/res/values/themes.xml +++ b/android/src/main/res/values/themes.xml @@ -12,8 +12,7 @@ @style/Theme.Droidcon - +