Skip to content

Commit

Permalink
Merge pull request #53 from splendo/avdyushin/feature/alerts-dsl-builder
Browse files Browse the repository at this point in the history
New Alert builder system
  • Loading branch information
thoutbeckers authored Nov 12, 2019
2 parents dae33eb + 97c3a32 commit a048ff8
Show file tree
Hide file tree
Showing 24 changed files with 534 additions and 180 deletions.
1 change: 1 addition & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Components/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ kotlin {
getByName("commonMain") {
dependencies {
implementation(project(":logging", ""))
implementation(project(":alerts", ""))
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions Components/src/androidLibMain/kotlin/dispatcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.splendo.kaluga

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

/*
Copyright 2019 Splendo Consulting B.V. The Netherlands
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

actual val MainQueueDispatcher: CoroutineDispatcher = Dispatchers.Main
23 changes: 23 additions & 0 deletions Components/src/commonMain/kotlin/dispatcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.splendo.kaluga

import kotlinx.coroutines.CoroutineDispatcher

/*
Copyright 2019 Splendo Consulting B.V. The Netherlands
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

expect val MainQueueDispatcher: CoroutineDispatcher
43 changes: 43 additions & 0 deletions Components/src/iosMain/kotlin/dispatcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.splendo.kaluga

import kotlinx.coroutines.*
import platform.darwin.*
import kotlin.coroutines.CoroutineContext

/*
Copyright 2019 Splendo Consulting B.V. The Netherlands
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

@UseExperimental(InternalCoroutinesApi::class)
internal class NsQueueDispatcher(private val dispatchQueue: dispatch_queue_t) : CoroutineDispatcher(), Delay {

// Dispatch block on given queue
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatch_async(dispatchQueue) { block.run() }
}

// Support Delay
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatchQueue) {
with(continuation) {
resumeUndispatched(Unit)
}
}
}
}

actual val MainQueueDispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())
24 changes: 24 additions & 0 deletions Components/src/jsMain/kotlin/dispatcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.splendo.kaluga

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

/*
Copyright 2019 Splendo Consulting B.V. The Netherlands
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

actual val MainQueueDispatcher: CoroutineDispatcher = Dispatchers.Main
24 changes: 24 additions & 0 deletions Components/src/jvmMain/kotlin/dispatcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.splendo.kaluga

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

/*
Copyright 2019 Splendo Consulting B.V. The Netherlands
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

actual val MainQueueDispatcher: CoroutineDispatcher = Dispatchers.Main
90 changes: 72 additions & 18 deletions alerts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,31 @@ It shows `AlertDialog` on Android and `UIAlertController` on iOS.
### Usage
The `BaseAlertBuilder` abstract class has implementations on the Android as `AlertBuilder` and iOS as `AlertsAlertBuilder`.
It has methods:
- `suspend alert(initialize: BaseAlertBuilder.() -> Unit): AlertInterface` — builder to create `AlertInterface`, thread-safe
- `buildUnsafe(initialize: BaseAlertBuilder.() -> Unit): AlertInterface` — build `AlertInterface`, not thread-safe
- `setTitle(title: String?)` — sets optional title for the alert
- `setStyle(style: Alert.Style)` - sets the type of alert to display (An alert or an action sheet/list)
- `setMessage(message: String?)` — sets an optional message for the alert
- `setPositiveButton(title: String, handler: AlertActionHandler)` — sets a positive button for the alert
- `setNegativeButton(title: String, handler: AlertActionHandler)` — sets a negative button for the alert
- `setNeutralButton(title: String, handler: AlertActionHandler)` — sets a neutral button for the alert
- `addActions(actions: List<Alert.Action>)` — adds a list of actions for the alert
- `create(): AlertInterface` — returns created `AlertInterface`
- `addActions(actions: List<Alert.Action>)` or `addActions(vararg actions: Alert.Action)` — adds a list of actions for the alert

#### Alert styles

There are two different Alert styles:
- `Alert.Style.ALERT` — a standard alert type
- `Alert.Style.ACTION_LIST` — an action sheet type

Default alert requires a title or message to be set,
but for action sheet it is not necessary.

#### Action styles

On Android actions can be: `Positive`, `Negative` and `Neutral`.
On iOS actions can be: `Default`, `Cancel` and `Destructive`.

#### Builder

On Android this builder needs a `Context` object:

Expand All @@ -27,30 +45,66 @@ let builder = AlertsAlertBuilder(viewController)
```

The `AlertInterface` has methods to show and dismiss alert:
- `show(animated: Boolean = true, completion: () -> Unit = {})`
- `showAsync(animated: Boolean = true, completion: () -> Unit = {})`
- `suspend show(animated: Boolean = true): Alert.Action?`
- `dismiss(animated: Boolean = true)`

### Example
### Examples

#### Using Kotlin coroutines

Create an Alert:
```kotlin
val alert = AlertBuilder(context).alert {
setTitle("Hello, Kaluga")
setPositiveButton("OK") { println("OK pressed") }
setNegativeButton("Cancel") { println("Cancel pressed") }
setNeutralButton("Details") { println("Details pressed") }
}
```

Building and displaying alert on Android:
In order to show alert use `alert.show()` call. Alert will be dismissed after the user pressed a button.
Dialog is cancellable, so the user also can tap outside of the alert or press back button to dismiss.
You also can dismiss it in code with `alert.dismiss()` call.

Before use on iOS platform you have to wrap `suspend` call
inside `MainScope().launch` with `MainQueueDispatcher`:

```kotlin
AlertBuilder(context)
.setTitle("Hello, Kaluga")
.setPositiveButton("OK") { println("OK pressed") }
.setNegativeButton("Cancel") { println("Cancel pressed") }
.setNeutralButton("Details") { println("Details pressed") }
.create()
.show()
fun showAlert(builder: AlertBuilder, title: String) = MainScope().launch(MainQueueDispatcher) {
// Create OK action
val okAction = Alert.Action("OK") // POSITIVE/DEFAULT style
// Create Cancel action
val cancelAction = Alert.Action("Cancel", Alert.Action.Style.NEGATIVE)
// Create an Alert with title, message and actions
val alert = builder.alert {
setTitle(title)
setMessage("This is sample message")
addActions(okAction, cancelAction)
}
// Show and handle actions
when (alert.show()) {
okAction -> println("OK pressed")
cancelAction -> println("Cancel pressed")
}
}
```

On iOS:
Then call with platform-specific builder:

```swift
AlertsAlertBuilder(viewController: viewController)
.setTitle(title: "Hello, Kaluga")
.setPositiveButton(title: "OK", handler: { debugPrint("OK pressed") } )
.create()
.show(animated: true) { debugPrint("Presented") }
let builder = AlertBuilder(viewController: viewController)
SharedKt.showAlert(builder, "Hello from iOS")
```

#### Without Kotlin coroutines

If you use shared builder object this call is not thread-safe:

```kotlin
val alert = builder.buildUnsafe {
setTitle("Hello")
setStyle(Alert.Style.ACTION_SHEET)
setActions(action1, action2)
}.showAsync()
```
Loading

0 comments on commit a048ff8

Please sign in to comment.