Skip to content

Commit

Permalink
fix(viewmodel): avoid crash with firebase instance when config changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
GerardPaligot committed Nov 1, 2023
1 parent dbe65c5 commit 09b9acf
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 51 deletions.
28 changes: 21 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ An Android client for Open-Feeedback https://github.com/HugoGresse/open-feedback

The Composable `OpenFeedback` is the entry point to vote on a session. It'll make calls
between the Firebase which host your OpenFeedback instance and your mobile application. It is
mandatory to pass the `OpenFeedbackState` to give the Firebase configuration and your open-feedback
configuration which is common for all sessions of your event.
mandatory to pass the `OpenFeedbackFirebaseConfig` to give the Firebase instance which is common
for all sessions of your event.

Note that it is mandatory to keep this instance unique in your application because it creates the
`FirebaseApp` instance which is the active connection between your mobile application and the
OpenFeedback Firebase host. Consider to save this configuration in your custom `Application` class
or in singleton in your dependency injection.

```kotlin
// In your Application class
val firebaseConfig = FirebaseConfig(
val openfeedbackFirebaseConfig = OpenFeedbackFirebaseConfig(
context = applicationContext,
projectId = "<your-firebase-open-feedback-project-id>",
applicationId = "<your-firebase-open-feedback-app-id>",
apiKey = "<your-firebase-open-feedback-api-key>",
Expand All @@ -23,7 +29,7 @@ val firebaseConfig = FirebaseConfig(

// In your Compose screen
OpenFeedback(
config = MyApp.firebaseConfig,
config = (application as MyApp).openfeedbackFirebaseConfig,
projectId = "<your-open-feedback-project-id>",
sessionId = "<your-open-feedback-session-id>",
language = "<language-code>"
Expand All @@ -32,7 +38,12 @@ OpenFeedback(

That's all!

If you want to see an example, please check the [sample-app](sample-app/src/main/java/io/openfeedback/android/sample/MainActivity.kt).
See the [sample-app](sample-app/src/main/java/io/openfeedback/android/sample/MainActivity.kt) app
module if you want to see this implementation in action.

If you are interested to create your own UI, you can use the component `OpenFeedbackLayout`. This
`Composable` takes OpenFeedback Model UI in input and you can use `OpenFeedbackViewModel` in the
viewmodel artifact to get the data from the Firebase host.

## Installation

Expand All @@ -43,11 +54,14 @@ repositories {
mavenCentral()
}

val openfeedbackVersion = "0.0.6"
dependencies {
// Material 2
implementation("io.openfeedback:feedback-android-sdk-m2:0.0.6")
implementation("io.openfeedback:feedback-android-sdk-m2:$openfeedbackVersion")
// Material 3
implementation("io.openfeedback:feedback-android-sdk-m3:0.0.6")
implementation("io.openfeedback:feedback-android-sdk-m3:$openfeedbackVersion")
// ViewModel
implementation("io.openfeedback:feedback-android-sdk-viewmodel:$openfeedbackVersion")
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,24 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import io.openfeedback.android.FirebaseConfig
import io.openfeedback.android.viewmodels.OpenFeedbackFirebaseConfig
import io.openfeedback.android.viewmodels.OpenFeedbackUiState
import io.openfeedback.android.viewmodels.OpenFeedbackViewModel
import io.openfeedback.android.viewmodels.models.UISessionFeedback
import io.openfeedback.android.viewmodels.models.UIVoteItem

@Composable
fun OpenFeedback(
config: FirebaseConfig,
config: OpenFeedbackFirebaseConfig,
projectId: String,
sessionId: String,
language: String,
modifier: Modifier = Modifier,
loading: @Composable () -> Unit = { Loading(modifier = modifier) }
) {
val context = LocalContext.current
val viewModel: OpenFeedbackViewModel = viewModel(
factory = OpenFeedbackViewModel.Factory.create(
context = context,
firebaseConfig = config,
firebaseApp = config.firebaseApp,
projectId = projectId,
sessionId = sessionId,
language = language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import io.openfeedback.android.FirebaseConfig
import io.openfeedback.android.viewmodels.OpenFeedbackFirebaseConfig
import io.openfeedback.android.viewmodels.OpenFeedbackUiState
import io.openfeedback.android.viewmodels.OpenFeedbackViewModel
import io.openfeedback.android.viewmodels.models.UIDot
Expand All @@ -29,19 +28,17 @@ import io.openfeedback.android.viewmodels.models.UIVoteItem
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun OpenFeedback(
config: FirebaseConfig,
config: OpenFeedbackFirebaseConfig,
projectId: String,
sessionId: String,
language: String,
modifier: Modifier = Modifier,
loading: @Composable () -> Unit = { Loading(modifier = modifier) }
) {
val configuration = LocalConfiguration.current
val context = LocalContext.current
val viewModel: OpenFeedbackViewModel = viewModel(
factory = OpenFeedbackViewModel.Factory.create(
context = context,
firebaseConfig = config,
firebaseApp = config.firebaseApp,
projectId = projectId,
sessionId = sessionId,
language = language
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package io.openfeedback.android.sources
package io.openfeedback.android.viewmodels

import android.content.Context
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import io.openfeedback.android.FirebaseConfig

object FirebaseFactory {
internal object FirebaseFactory {
fun create(
context: Context,
config: FirebaseConfig,
config: OpenFeedbackFirebaseConfig,
appName: String = "openfeedback"
): FirebaseApp {
val options = FirebaseOptions.Builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.openfeedback.android.viewmodels

import android.content.Context
import androidx.compose.runtime.Immutable

@Immutable
data class OpenFeedbackFirebaseConfig(
val context: Context,
val projectId: String,
val applicationId: String,
val apiKey: String,
val databaseUrl: String,
val appName: String = "openfeedback"
) {
val firebaseApp = FirebaseFactory.create(context, this, appName)
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package io.openfeedback.android.viewmodels

import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.google.firebase.FirebaseApp
import io.openfeedback.android.FirebaseConfig
import io.openfeedback.android.OpenFeedbackRepository
import io.openfeedback.android.caches.OptimisticVoteCaching
import io.openfeedback.android.model.VoteStatus
import io.openfeedback.android.sources.FirebaseFactory
import io.openfeedback.android.sources.OpenFeedbackAuth
import io.openfeedback.android.sources.OpenFeedbackFirestore
import io.openfeedback.android.viewmodels.mappers.convertToUiSessionFeedback
Expand All @@ -27,14 +24,14 @@ sealed class OpenFeedbackUiState {
}

class OpenFeedbackViewModel(
private val firebase: FirebaseApp,
private val firebaseApp: FirebaseApp,
private val projectId: String,
private val sessionId: String,
private val language: String
) : ViewModel() {
private val repository = OpenFeedbackRepository(
auth = OpenFeedbackAuth.Factory.create(firebase),
firestore = OpenFeedbackFirestore.Factory.create(firebase),
auth = OpenFeedbackAuth.Factory.create(firebaseApp),
firestore = OpenFeedbackFirestore.Factory.create(firebaseApp),
optimisticVoteCaching = OptimisticVoteCaching()
)

Expand Down Expand Up @@ -75,14 +72,13 @@ class OpenFeedbackViewModel(

object Factory {
fun create(
context: Context,
firebaseConfig: FirebaseConfig,
firebaseApp: FirebaseApp,
projectId: String,
sessionId: String,
language: String
) = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T = OpenFeedbackViewModel(
firebase = FirebaseFactory.create(context, firebaseConfig),
firebaseApp = firebaseApp,
projectId = projectId,
sessionId = sessionId,
language = language
Expand Down

This file was deleted.

7 changes: 4 additions & 3 deletions sample-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
Expand All @@ -10,8 +11,8 @@
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="@style/AppTheme.NoActionBar"
android:exported="true">
android:exported="true"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand All @@ -20,4 +21,4 @@
</activity>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.openfeedback.android.FirebaseConfig
import io.openfeedback.android.viewmodels.OpenFeedbackFirebaseConfig
import io.openfeedback.android.m2.OpenFeedback
import io.openfeedback.android.sample.theme.DesignSystem
import io.openfeedback.android.sample.theme.OpenFeedbackTheme
Expand All @@ -31,12 +31,7 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val projectId = "mMHR63ARZQpPidFQISyc"
val firebaseConfig = FirebaseConfig(
projectId = "openfeedback-b7ab9",
applicationId = "1:765209934800:android:a6bb09f3deabc2277297d5",
apiKey = "AIzaSyC_cfbh8xKwF8UPxCeasGcsHyK4s5yZFeA",
databaseUrl = "https://openfeedback-b7ab9.firebaseio.com"
)
val openFeedbackFirebaseConfig = (application as MainApplication).openFeedbackFirebaseConfig
setContent {
var designSystem by rememberSaveable { mutableStateOf(DesignSystem.M2) }
val isDark = isSystemInDarkTheme()
Expand All @@ -55,7 +50,7 @@ class MainActivity : AppCompatActivity() {
when (designSystem) {
DesignSystem.M2 -> Scaffold {
OpenFeedback(
config = firebaseConfig,
config = openFeedbackFirebaseConfig,
projectId = projectId,
sessionId = "173222",
language = "en",
Expand All @@ -66,7 +61,7 @@ class MainActivity : AppCompatActivity() {
}
DesignSystem.M3 -> androidx.compose.material3.Scaffold {
io.openfeedback.android.m3.OpenFeedback(
config = firebaseConfig,
config = openFeedbackFirebaseConfig,
projectId = projectId,
sessionId = "173222",
language = "en",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.openfeedback.android.sample

import android.app.Application
import io.openfeedback.android.viewmodels.OpenFeedbackFirebaseConfig

class MainApplication: Application() {
lateinit var openFeedbackFirebaseConfig: OpenFeedbackFirebaseConfig

override fun onCreate() {
super.onCreate()
openFeedbackFirebaseConfig = OpenFeedbackFirebaseConfig(
context = this,
projectId = "openfeedback-b7ab9",
applicationId = "1:765209934800:android:a6bb09f3deabc2277297d5",
apiKey = "AIzaSyC_cfbh8xKwF8UPxCeasGcsHyK4s5yZFeA",
databaseUrl = "https://openfeedback-b7ab9.firebaseio.com"
)
}
}

0 comments on commit 09b9acf

Please sign in to comment.