diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2.kt b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2.kt index baf9a83776..00aba08457 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2.kt @@ -16,6 +16,7 @@ import org.signal.core.util.Result import org.signal.core.util.isAbsent import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.util.Debouncer +import org.thoughtcrime.securesms.util.delegate import org.thoughtcrime.securesms.util.rx.RxStore import java.util.Optional @@ -35,7 +36,8 @@ class LinkPreviewViewModelV2( } private var enabled = SignalStore.settings().isLinkPreviewsEnabled - private val linkPreviewStateStore = RxStore(savedStateHandle[LINK_PREVIEW_STATE] ?: LinkPreviewState.forNoLinks()) + private var savedLinkPreviewState by savedStateHandle.delegate(LINK_PREVIEW_STATE) { LinkPreviewState.forNoLinks() } + private val linkPreviewStateStore = RxStore(savedLinkPreviewState) val linkPreviewState: Flowable = linkPreviewStateStore.stateFlowable.observeOn(AndroidSchedulers.mainThread()) val linkPreviewStateSnapshot: LinkPreviewState = linkPreviewStateStore.state @@ -43,16 +45,8 @@ class LinkPreviewViewModelV2( val hasLinkPreview: Boolean = linkPreviewStateStore.state.linkPreview.isPresent val hasLinkPreviewUi: Boolean = linkPreviewStateStore.state.hasContent() - private var activeUrl: String? - get() = savedStateHandle[ACTIVE_URL] - set(value) { - savedStateHandle[ACTIVE_URL] = value - } - private var userCancelled: Boolean - get() = savedStateHandle[USER_CANCELLED] ?: false - set(value) { - savedStateHandle[USER_CANCELLED] = value - } + private var activeUrl: String? by savedStateHandle.delegate(ACTIVE_URL) + private var userCancelled: Boolean by savedStateHandle.delegate(USER_CANCELLED, false) private var activeRequest: Disposable = Disposable.disposed() private val debouncer: Debouncer = Debouncer(250) @@ -61,7 +55,7 @@ class LinkPreviewViewModelV2( .stateFlowable .observeOn(AndroidSchedulers.mainThread()) .subscribeBy { - savedStateHandle[LINK_PREVIEW_STATE] = it + savedLinkPreviewState = it } override fun onCleared() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SavedStateHandleExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/util/SavedStateHandleExtensions.kt new file mode 100644 index 0000000000..e5d137d98d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SavedStateHandleExtensions.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.util + +import androidx.lifecycle.SavedStateHandle +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/** + * Nullable SavedStateHandle delegate, which does not accept a default. + */ +fun SavedStateHandle.delegate(key: String): ReadWriteProperty { + return NullableSavedStateHandleDelegate(this, key) +} + +/** + * Non-null SavedStateHandle delegate with a default value. Recommended when the default + * value has to be created on-demand. The default is NOT written to the SavedStateHandle. + */ +fun SavedStateHandle.delegate(key: String, default: () -> T): ReadWriteProperty { + return DefaultSavedStateHandleDelegate(this, key, default) +} + +/** + * Convenience function for non-null SavedStateHandle delegate. Recommended when working + * with primitive or pre-constructed objects. The default is NOT written to the SavedStateHandle. + */ +fun SavedStateHandle.delegate(key: String, default: T): ReadWriteProperty { + return DefaultSavedStateHandleDelegate(this, key) { default } +} + +private class NullableSavedStateHandleDelegate( + private val handle: SavedStateHandle, + private val key: String +) : ReadWriteProperty { + override operator fun getValue(thisRef: Any?, property: KProperty<*>): T? { + return handle[key] + } + + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + handle[key] = value + } +} + +private class DefaultSavedStateHandleDelegate( + private val handle: SavedStateHandle, + private val key: String, + default: () -> T +) : ReadWriteProperty { + + private val lazyDefault by lazy { default() } + + override operator fun getValue(thisRef: Any?, property: KProperty<*>): T { + return handle[key] ?: lazyDefault + } + + override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + handle[key] = value + } +}