From c8c9f508066c0c8165420dee180b7d10cafbfbb6 Mon Sep 17 00:00:00 2001 From: jorgemucientesfayos Date: Sun, 29 Dec 2024 21:49:57 +0100 Subject: [PATCH] Adds new endpoint to update notification settings --- .../src/main/resources/wp-com-endpoints.txt | 2 + .../notifications/NotificationRestClient.kt | 277 +++++++++++------- .../android/fluxc/store/NotificationStore.kt | 175 +++++++++-- 3 files changed, 326 insertions(+), 128 deletions(-) diff --git a/fluxc-processor/src/main/resources/wp-com-endpoints.txt b/fluxc-processor/src/main/resources/wp-com-endpoints.txt index 80abb6cc3d..95a446444d 100644 --- a/fluxc-processor/src/main/resources/wp-com-endpoints.txt +++ b/fluxc-processor/src/main/resources/wp-com-endpoints.txt @@ -32,6 +32,8 @@ /me/account/close +/me/notifications/settings/ + /notifications/ /notifications/$note_id /notifications/seen/ diff --git a/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/notifications/NotificationRestClient.kt b/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/notifications/NotificationRestClient.kt index fc201890b9..96d5f05ada 100644 --- a/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/notifications/NotificationRestClient.kt +++ b/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/notifications/NotificationRestClient.kt @@ -3,10 +3,7 @@ package org.wordpress.android.fluxc.network.rest.wpcom.notifications import android.content.Context import android.os.Build import com.android.volley.RequestQueue -import java.util.Date -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Singleton +import com.google.gson.annotations.SerializedName import org.wordpress.android.fluxc.Dispatcher import org.wordpress.android.fluxc.generated.NotificationActionBuilder import org.wordpress.android.fluxc.generated.endpoint.WPCOMREST @@ -17,6 +14,7 @@ import org.wordpress.android.fluxc.network.rest.wpcom.BaseWPComRestClient import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequest import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequest.WPComGsonNetworkError import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder +import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder.Response import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder.Response.Error import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder.Response.Success import org.wordpress.android.fluxc.network.rest.wpcom.auth.AccessToken @@ -38,6 +36,10 @@ import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T import org.wordpress.android.util.DeviceUtils import org.wordpress.android.util.PackageUtils +import java.util.Date +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton @Singleton class NotificationRestClient @Inject constructor( @@ -63,33 +65,34 @@ class NotificationRestClient @Inject constructor( val deviceName = DeviceUtils.getInstance().getDeviceName(appContext) val request = wpComGsonRequestBuilder.syncPostRequest( - this, - WPCOMREST.devices.new_.urlV1, - mapOf( - "device_token" to fcmToken, - "device_family" to "android", - "app_secret_key" to appKey.value, - "device_name" to deviceName, - "device_model" to "${Build.MANUFACTURER} ${Build.MODEL}", - "app_version" to PackageUtils.getVersionName(appContext), - "version_code" to PackageUtils.getVersionCode(appContext).toString(), - "os_version" to Build.VERSION.RELEASE, - "device_uuid" to uuid - ), - body = null, - RegisterDeviceRestResponse::class.java + this, + WPCOMREST.devices.new_.urlV1, + mapOf( + "device_token" to fcmToken, + "device_family" to "android", + "app_secret_key" to appKey.value, + "device_name" to deviceName, + "device_model" to "${Build.MANUFACTURER} ${Build.MODEL}", + "app_version" to PackageUtils.getVersionName(appContext), + "version_code" to PackageUtils.getVersionCode(appContext).toString(), + "os_version" to Build.VERSION.RELEASE, + "device_uuid" to uuid + ), + body = null, + RegisterDeviceRestResponse::class.java ) return when (request) { is Success -> { val id = request.data.id if (id.isNullOrEmpty()) { RegisterDeviceResponsePayload( - DeviceRegistrationError(DeviceRegistrationErrorType.MISSING_DEVICE_ID) + DeviceRegistrationError(DeviceRegistrationErrorType.MISSING_DEVICE_ID) ) } else { RegisterDeviceResponsePayload(id) } } + is Error -> { RegisterDeviceResponsePayload(networkErrorToRegistrationError(request.error)) } @@ -97,7 +100,10 @@ class NotificationRestClient @Inject constructor( } // region Device Registration - @Deprecated(message = "EventBus is deprecated.", ReplaceWith("registerDevice(fcmToken, appKey, uuid)")) + @Deprecated( + message = "EventBus is deprecated.", + ReplaceWith("registerDevice(fcmToken, appKey, uuid)") + ) fun registerDeviceForPushNotifications( gcmToken: String, appKey: NotificationAppKey, @@ -106,63 +112,72 @@ class NotificationRestClient @Inject constructor( ) { val deviceName = DeviceUtils.getInstance().getDeviceName(appContext) val params = listOfNotNull( - "device_token" to gcmToken, - "device_family" to "android", - "app_secret_key" to appKey.value, - "device_name" to deviceName, - "device_model" to "${Build.MANUFACTURER} ${Build.MODEL}", - "app_version" to PackageUtils.getVersionName(appContext), - "version_code" to PackageUtils.getVersionCode(appContext).toString(), - "os_version" to Build.VERSION.RELEASE, - "device_uuid" to uuid, - ("selected_blog_id" to site?.siteId.toString()).takeIf { site != null } + "device_token" to gcmToken, + "device_family" to "android", + "app_secret_key" to appKey.value, + "device_name" to deviceName, + "device_model" to "${Build.MANUFACTURER} ${Build.MODEL}", + "app_version" to PackageUtils.getVersionName(appContext), + "version_code" to PackageUtils.getVersionCode(appContext).toString(), + "os_version" to Build.VERSION.RELEASE, + "device_uuid" to uuid, + ("selected_blog_id" to site?.siteId.toString()).takeIf { site != null } ).toMap() val url = WPCOMREST.devices.new_.urlV1 val request = WPComGsonRequest.buildPostRequest( - url, params, RegisterDeviceRestResponse::class.java, - { response: RegisterDeviceRestResponse? -> - response?.let { - if (!it.id.isNullOrEmpty()) { - val payload = RegisterDeviceResponsePayload(it.id) - dispatcher.dispatch(NotificationActionBuilder.newRegisteredDeviceAction(payload)) - } else { - val registrationError = - DeviceRegistrationError(DeviceRegistrationErrorType.MISSING_DEVICE_ID) - val payload = RegisterDeviceResponsePayload(registrationError) - dispatcher.dispatch(NotificationActionBuilder.newRegisteredDeviceAction(payload)) - } - } ?: run { - AppLog.e(T.API, "Response for url $url with param $params is null: $response") - val registrationError = DeviceRegistrationError(DeviceRegistrationErrorType.INVALID_RESPONSE) + url, params, RegisterDeviceRestResponse::class.java, + { response: RegisterDeviceRestResponse? -> + response?.let { + if (!it.id.isNullOrEmpty()) { + val payload = RegisterDeviceResponsePayload(it.id) + dispatcher.dispatch( + NotificationActionBuilder.newRegisteredDeviceAction( + payload + ) + ) + } else { + val registrationError = + DeviceRegistrationError(DeviceRegistrationErrorType.MISSING_DEVICE_ID) val payload = RegisterDeviceResponsePayload(registrationError) - dispatcher.dispatch(NotificationActionBuilder.newRegisteredDeviceAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newRegisteredDeviceAction( + payload + ) + ) } - }, - { wpComError -> - val registrationError = networkErrorToRegistrationError(wpComError) + } ?: run { + AppLog.e(T.API, "Response for url $url with param $params is null: $response") + val registrationError = + DeviceRegistrationError(DeviceRegistrationErrorType.INVALID_RESPONSE) val payload = RegisterDeviceResponsePayload(registrationError) dispatcher.dispatch(NotificationActionBuilder.newRegisteredDeviceAction(payload)) - }) + } + }, + { wpComError -> + val registrationError = networkErrorToRegistrationError(wpComError) + val payload = RegisterDeviceResponsePayload(registrationError) + dispatcher.dispatch(NotificationActionBuilder.newRegisteredDeviceAction(payload)) + }) add(request) } fun unregisterDeviceForPushNotifications(deviceId: String) { val url = WPCOMREST.devices.deviceId(deviceId).delete.urlV1 val request = WPComGsonRequest.buildPostRequest( - url, null, Any::class.java, - { - val payload = UnregisterDeviceResponsePayload() - dispatcher.dispatch(NotificationActionBuilder.newUnregisteredDeviceAction(payload)) - }, - { wpComError -> - val payload = UnregisterDeviceResponsePayload( - DeviceUnregistrationError( - DeviceUnregistrationErrorType.GENERIC_ERROR, wpComError.message - ) + url, null, Any::class.java, + { + val payload = UnregisterDeviceResponsePayload() + dispatcher.dispatch(NotificationActionBuilder.newUnregisteredDeviceAction(payload)) + }, + { wpComError -> + val payload = UnregisterDeviceResponsePayload( + DeviceUnregistrationError( + DeviceUnregistrationErrorType.GENERIC_ERROR, wpComError.message ) - dispatcher.dispatch(NotificationActionBuilder.newUnregisteredDeviceAction(payload)) - }) + ) + dispatcher.dispatch(NotificationActionBuilder.newUnregisteredDeviceAction(payload)) + }) add(request) } // endregion @@ -176,26 +191,35 @@ class NotificationRestClient @Inject constructor( fun fetchNotificationHashes() { val url = WPCOMREST.notifications.urlV1_1 val params = mapOf( - "number" to NOTIFICATION_DEFAULT_NUMBER.toString(), - "num_note_items" to NOTIFICATION_DEFAULT_NUM_NOTE_ITEMS.toString(), - "fields" to NOTIFICATION_SYNC_FIELDS + "number" to NOTIFICATION_DEFAULT_NUMBER.toString(), + "num_note_items" to NOTIFICATION_DEFAULT_NUM_NOTE_ITEMS.toString(), + "fields" to NOTIFICATION_SYNC_FIELDS ) - val request = WPComGsonRequest.buildGetRequest(url, params, NotificationHashesApiResponse::class.java, + val request = + WPComGsonRequest.buildGetRequest(url, params, NotificationHashesApiResponse::class.java, { response: NotificationHashesApiResponse? -> // Create a map of remote id to note_hash val hashesMap: Map = - response?.notes?.map { it.id to it.note_hash }?.toMap() ?: emptyMap() + response?.notes?.map { it.id to it.note_hash }?.toMap() ?: emptyMap() val payload = FetchNotificationHashesResponsePayload(hashesMap) - dispatcher.dispatch(NotificationActionBuilder.newFetchedNotificationHashesAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newFetchedNotificationHashesAction( + payload + ) + ) }, { networkError -> val payload = FetchNotificationHashesResponsePayload().apply { error = NotificationError( - NotificationErrorType.fromString(networkError.apiError), - networkError.message + NotificationErrorType.fromString(networkError.apiError), + networkError.message ) } - dispatcher.dispatch(NotificationActionBuilder.newFetchedNotificationHashesAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newFetchedNotificationHashesAction( + payload + ) + ) }) add(request) } @@ -210,14 +234,15 @@ class NotificationRestClient @Inject constructor( fun fetchNotifications(remoteNoteIds: List? = null) { val url = WPCOMREST.notifications.urlV1_1 val params = mutableMapOf( - "number" to NOTIFICATION_DEFAULT_NUMBER.toString(), - "num_note_items" to NOTIFICATION_DEFAULT_NUM_NOTE_ITEMS.toString(), - "fields" to NOTIFICATION_DEFAULT_FIELDS + "number" to NOTIFICATION_DEFAULT_NUMBER.toString(), + "num_note_items" to NOTIFICATION_DEFAULT_NUM_NOTE_ITEMS.toString(), + "fields" to NOTIFICATION_DEFAULT_FIELDS ) remoteNoteIds?.let { if (it.isNotEmpty()) params["ids"] = it.joinToString() } - val request = WPComGsonRequest.buildGetRequest(url, params, NotificationsApiResponse::class.java, + val request = + WPComGsonRequest.buildGetRequest(url, params, NotificationsApiResponse::class.java, { response: NotificationsApiResponse? -> val lastSeenTime = response?.last_seen_time?.let { Date(it) @@ -226,16 +251,24 @@ class NotificationRestClient @Inject constructor( NotificationApiResponse.notificationResponseToNotificationModel(it) } ?: listOf() val payload = FetchNotificationsResponsePayload(notifications, lastSeenTime) - dispatcher.dispatch(NotificationActionBuilder.newFetchedNotificationsAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newFetchedNotificationsAction( + payload + ) + ) }, { networkError -> val payload = FetchNotificationsResponsePayload().apply { error = NotificationError( - NotificationErrorType.fromString(networkError.apiError), - networkError.message + NotificationErrorType.fromString(networkError.apiError), + networkError.message ) } - dispatcher.dispatch(NotificationActionBuilder.newFetchedNotificationsAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newFetchedNotificationsAction( + payload + ) + ) }) add(request) } @@ -248,24 +281,33 @@ class NotificationRestClient @Inject constructor( fun fetchNotification(remoteNoteId: Long) { val url = WPCOMREST.notifications.note(remoteNoteId).urlV1_1 val params = mapOf( - "fields" to NOTIFICATION_DEFAULT_FIELDS + "fields" to NOTIFICATION_DEFAULT_FIELDS ) - val request = WPComGsonRequest.buildGetRequest(url, params, NotificationsApiResponse::class.java, + val request = + WPComGsonRequest.buildGetRequest(url, params, NotificationsApiResponse::class.java, { response -> val notification = response?.notes?.firstOrNull()?.let { NotificationApiResponse.notificationResponseToNotificationModel(it) } val payload = FetchNotificationResponsePayload(notification) - dispatcher.dispatch(NotificationActionBuilder.newFetchedNotificationAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newFetchedNotificationAction( + payload + ) + ) }, { networkError -> val payload = FetchNotificationResponsePayload().apply { error = NotificationError( - NotificationErrorType.fromString(networkError.apiError), - networkError.message + NotificationErrorType.fromString(networkError.apiError), + networkError.message ) } - dispatcher.dispatch(NotificationActionBuilder.newFetchedNotificationAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newFetchedNotificationAction( + payload + ) + ) }) add(request) } @@ -279,19 +321,31 @@ class NotificationRestClient @Inject constructor( fun markNotificationsSeen(timestamp: Long) { val url = WPCOMREST.notifications.seen.urlV1_1 val params = mapOf("time" to timestamp.toString()) - val request = WPComGsonRequest.buildPostRequest(url, params, NotificationSeenApiResponse::class.java, + val request = + WPComGsonRequest.buildPostRequest(url, params, NotificationSeenApiResponse::class.java, { response -> - val payload = MarkNotificationSeenResponsePayload(response.success, response.last_seen_time) - dispatcher.dispatch(NotificationActionBuilder.newMarkedNotificationsSeenAction(payload)) + val payload = MarkNotificationSeenResponsePayload( + response.success, + response.last_seen_time + ) + dispatcher.dispatch( + NotificationActionBuilder.newMarkedNotificationsSeenAction( + payload + ) + ) }, { networkError -> val payload = MarkNotificationSeenResponsePayload().apply { error = NotificationError( - NotificationErrorType.fromString(networkError.apiError), - networkError.message + NotificationErrorType.fromString(networkError.apiError), + networkError.message ) } - dispatcher.dispatch(NotificationActionBuilder.newMarkedNotificationsSeenAction(payload)) + dispatcher.dispatch( + NotificationActionBuilder.newMarkedNotificationsSeenAction( + payload + ) + ) }) add(request) } @@ -310,21 +364,22 @@ class NotificationRestClient @Inject constructor( notifications.iterator().forEach { params["counts[${it.remoteNoteId}]"] = "9999" } val response = wpComGsonRequestBuilder.syncPostRequest( - this, - url, - params, - null, - NotificationReadApiResponse::class.java + this, + url, + params, + null, + NotificationReadApiResponse::class.java ) return when (response) { is Success -> { MarkNotificationsReadResponsePayload(notifications, response.data.success) } + is Error -> { MarkNotificationsReadResponsePayload().apply { error = NotificationError( - NotificationErrorType.fromString(response.error.apiError), - response.error.message + NotificationErrorType.fromString(response.error.apiError), + response.error.message ) } } @@ -335,4 +390,28 @@ class NotificationRestClient @Inject constructor( val orderErrorType = DeviceRegistrationErrorType.fromString(wpComError.apiError) return DeviceRegistrationError(orderErrorType, wpComError.message) } + + suspend fun disableNotificationsFor(notificationSetting: List): Response { + val url = WPCOMREST.me.notifications.settings.urlV1_1 + val body = mutableMapOf() + body["blogs"] = notificationSetting + return wpComGsonRequestBuilder.syncPostRequest( + restClient = this, + url = url, + params = null, + body = body, + Unit::class.java + ) + } + + data class SiteNotificationSettingDto( + @SerializedName("blog_id") val siteId: Long, + @SerializedName("devices") val devices: List, + ) + + data class DevicesDto( + @SerializedName("device_id") val deviceId: String, + @SerializedName("new_comment") val newComment: Boolean, + @SerializedName("store_order") val storeOrder: Boolean, + ) } diff --git a/fluxc/src/main/java/org/wordpress/android/fluxc/store/NotificationStore.kt b/fluxc/src/main/java/org/wordpress/android/fluxc/store/NotificationStore.kt index b80f4cda65..755ae0fb98 100644 --- a/fluxc/src/main/java/org/wordpress/android/fluxc/store/NotificationStore.kt +++ b/fluxc/src/main/java/org/wordpress/android/fluxc/store/NotificationStore.kt @@ -14,12 +14,18 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.notification.NoteIdSet import org.wordpress.android.fluxc.model.notification.NotificationModel import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError +import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder.Response +import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder.Response.Success import org.wordpress.android.fluxc.network.rest.wpcom.notifications.NotificationRestClient +import org.wordpress.android.fluxc.network.rest.wpcom.notifications.NotificationRestClient.DevicesDto +import org.wordpress.android.fluxc.network.rest.wpcom.notifications.NotificationRestClient.SiteNotificationSettingDto import org.wordpress.android.fluxc.persistence.NotificationSqlUtils +import org.wordpress.android.fluxc.store.NotificationStore.NotificationSettingErrorType.UnregisteredDevice import org.wordpress.android.fluxc.tools.CoroutineEngine import org.wordpress.android.fluxc.utils.PreferenceUtils import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T +import org.wordpress.android.util.AppLog.T.NOTIFS import java.util.Date import java.util.Locale import java.util.UUID @@ -55,11 +61,15 @@ class NotificationStore @Inject constructor( class RegisterDeviceResponsePayload( val deviceId: String? = null ) : Payload() { - constructor(error: DeviceRegistrationError, deviceId: String? = null) : this(deviceId) { this.error = error } + constructor(error: DeviceRegistrationError, deviceId: String? = null) : this(deviceId) { + this.error = error + } } class UnregisterDeviceResponsePayload() : Payload() { - constructor(error: DeviceUnregistrationError) : this() { this.error = error } + constructor(error: DeviceUnregistrationError) : this() { + this.error = error + } } class DeviceRegistrationError( @@ -92,7 +102,9 @@ class NotificationStore @Inject constructor( val notifs: List = emptyList(), val lastSeen: Date? = null ) : Payload() { - constructor(error: NotificationError) : this() { this.error = error } + constructor(error: NotificationError) : this() { + this.error = error + } } class FetchNotificationPayload( @@ -103,14 +115,18 @@ class NotificationStore @Inject constructor( val notification: NotificationModel? = null ) : Payload() { @Suppress("unused") - constructor(error: NotificationError) : this() { this.error = error } + constructor(error: NotificationError) : this() { + this.error = error + } } class FetchNotificationHashesResponsePayload( val hashesMap: Map = emptyMap() ) : Payload() { @Suppress("unused") - constructor(error: NotificationError) : this() { this.error = error } + constructor(error: NotificationError) : this() { + this.error = error + } } class MarkNotificationsSeenPayload( @@ -122,7 +138,9 @@ class NotificationStore @Inject constructor( val lastSeenTime: Long? = null ) : Payload() { @Suppress("unused") - constructor(error: NotificationError) : this() { this.error = error } + constructor(error: NotificationError) : this() { + this.error = error + } } class MarkNotificationsReadPayload( @@ -134,10 +152,13 @@ class NotificationStore @Inject constructor( val success: Boolean = false ) : Payload() { @Suppress("unused") - constructor(error: NotificationError) : this() { this.error = error } + constructor(error: NotificationError) : this() { + this.error = error + } } - class NotificationError(val type: NotificationErrorType, val message: String = "") : OnChangedError + class NotificationError(val type: NotificationErrorType, val message: String = "") : + OnChangedError enum class NotificationErrorType { BAD_REQUEST, @@ -178,14 +199,19 @@ class NotificationStore @Inject constructor( // remote responses NotificationAction.REGISTERED_DEVICE -> handleRegisteredDevice(action.payload as RegisterDeviceResponsePayload) + NotificationAction.UNREGISTERED_DEVICE -> handleUnregisteredDevice(action.payload as UnregisterDeviceResponsePayload) + NotificationAction.FETCHED_NOTIFICATIONS -> handleFetchNotificationsCompleted(action.payload as FetchNotificationsResponsePayload) + NotificationAction.FETCHED_NOTIFICATION_HASHES -> handleFetchNotificationHashesCompleted(action.payload as FetchNotificationHashesResponsePayload) + NotificationAction.FETCHED_NOTIFICATION -> handleFetchNotificationCompleted(action.payload as FetchNotificationResponsePayload) + NotificationAction.MARKED_NOTIFICATIONS_SEEN -> handleMarkedNotificationSeen(action.payload as MarkNotificationSeenResponsePayload) // local actions @@ -211,7 +237,7 @@ class NotificationStore @Inject constructor( filterByType: List? = null, filterBySubtype: List? = null ): List = - notificationSqlUtils.getNotifications(ORDER_DESCENDING, filterByType, filterBySubtype) + notificationSqlUtils.getNotifications(ORDER_DESCENDING, filterByType, filterBySubtype) /** * Fetch all notifications for the given site. @@ -229,14 +255,24 @@ class NotificationStore @Inject constructor( filterByType: List? = null, filterBySubtype: List? = null ): List = - notificationSqlUtils.getNotificationsForSite(site, ORDER_DESCENDING, filterByType, filterBySubtype) + notificationSqlUtils.getNotificationsForSite( + site, + ORDER_DESCENDING, + filterByType, + filterBySubtype + ) fun observeNotificationsForSite( site: SiteModel, filterByType: List? = null, filterBySubtype: List? = null ): Flow> = - notificationSqlUtils.observeNotificationsForSite(site, ORDER_DESCENDING, filterByType, filterBySubtype) + notificationSqlUtils.observeNotificationsForSite( + site, + ORDER_DESCENDING, + filterByType, + filterBySubtype + ) /** * Returns true if the given site has unread notifications @@ -250,7 +286,7 @@ class NotificationStore @Inject constructor( filterByType: List? = null, filterBySubtype: List? = null ): Boolean = - notificationSqlUtils.hasUnreadNotificationsForSite(site, filterByType, filterBySubtype) + notificationSqlUtils.hasUnreadNotificationsForSite(site, filterByType, filterBySubtype) /** * Fetch the first notification matching the parameters specified in [NoteIdSet]. @@ -258,40 +294,52 @@ class NotificationStore @Inject constructor( * @param idSet A [NoteIdSet] containing the localSiteId, remoteNoteId, and localNoteId */ @Suppress("unused") - fun getNotificationByIdSet(idSet: NoteIdSet) = notificationSqlUtils.getNotificationByIdSet(idSet) + fun getNotificationByIdSet(idSet: NoteIdSet) = + notificationSqlUtils.getNotificationByIdSet(idSet) /** * Fetch a notification from the database by the remote notification ID. */ @Suppress("unused") fun getNotificationByRemoteId(remoteNoteId: Long) = - notificationSqlUtils.getNotificationByRemoteId(remoteNoteId) + notificationSqlUtils.getNotificationByRemoteId(remoteNoteId) /** * Fetch a notification from the database by it's local notification id. */ fun getNotificationByLocalId(noteId: Int) = - notificationSqlUtils.getNotificationByIdSet(NoteIdSet(noteId, 0, 0)) + notificationSqlUtils.getNotificationByIdSet(NoteIdSet(noteId, 0, 0)) - suspend fun registerDevice(token: String, appKey: NotificationAppKey): RegisterDeviceResponsePayload { + suspend fun registerDevice( + token: String, + appKey: NotificationAppKey + ): RegisterDeviceResponsePayload { return coroutineEngine.withDefaultContext(T.API, this, "registerDevice") { val uuid = preferences.getString(WPCOM_PUSH_DEVICE_UUID, null) ?: generateAndStoreUUID() notificationRestClient.registerDevice( - fcmToken = token, - appKey = appKey, - uuid = uuid + fcmToken = token, + appKey = appKey, + uuid = uuid ).apply { if (isError || deviceId.isNullOrEmpty()) { when (error.type) { DeviceRegistrationErrorType.MISSING_DEVICE_ID -> - AppLog.e(T.NOTIFS, "Server response missing device_id - registration skipped!") + AppLog.e( + T.NOTIFS, + "Server response missing device_id - registration skipped!" + ) + DeviceRegistrationErrorType.GENERIC_ERROR -> - AppLog.e(T.NOTIFS, "Error trying to register device: ${error.type} - ${error.message}") + AppLog.e( + T.NOTIFS, + "Error trying to register device: ${error.type} - ${error.message}" + ) + DeviceRegistrationErrorType.INVALID_RESPONSE -> AppLog.e( - T.NOTIFS, - "Server response missing response object: ${error.type} - ${error.message}" + T.NOTIFS, + "Server response missing response object: ${error.type} - ${error.message}" ) } } else { @@ -325,11 +373,22 @@ class NotificationStore @Inject constructor( if (isError || deviceId.isNullOrEmpty()) { when (error.type) { DeviceRegistrationErrorType.MISSING_DEVICE_ID -> - AppLog.e(T.NOTIFS, "Server response missing device_id - registration skipped!") + AppLog.e( + T.NOTIFS, + "Server response missing device_id - registration skipped!" + ) + DeviceRegistrationErrorType.GENERIC_ERROR -> - AppLog.e(T.NOTIFS, "Error trying to register device: ${error.type} - ${error.message}") + AppLog.e( + T.NOTIFS, + "Error trying to register device: ${error.type} - ${error.message}" + ) + DeviceRegistrationErrorType.INVALID_RESPONSE -> - AppLog.e(T.NOTIFS, "Server response missing response object: ${error.type} - ${error.message}") + AppLog.e( + T.NOTIFS, + "Server response missing response object: ${error.type} - ${error.message}" + ) } onDeviceRegistered.error = payload.error } else { @@ -411,7 +470,7 @@ class NotificationStore @Inject constructor( // Pull cached notifications from the database and build a map of remoteNoteId to noteHash val existingNotifsByRemoteIdMap = notificationSqlUtils - .getNotifications().associateBy { it.remoteNoteId }.toMap() + .getNotifications().associateBy { it.remoteNoteId }.toMap() // Scrub the newly fetched list against the cached db records. Remove any entries for records that // do not require an update from the remote API @@ -424,7 +483,8 @@ class NotificationStore @Inject constructor( // list of notifs to fetch notifsToFetch.remove(cached.key) } - } ?: notificationSqlUtils.deleteNotificationByRemoteId(cached.key) // Delete notification from the db + } + ?: notificationSqlUtils.deleteNotificationByRemoteId(cached.key) // Delete notification from the db } // Fetch new and updated notifications from the remote api @@ -437,7 +497,8 @@ class NotificationStore @Inject constructor( OnNotificationChanged(0).also { it.error = payload.error } } else { // Save notifications to the database - val rowsAffected = payload.notifs.sumBy { notificationSqlUtils.insertOrUpdateNotification(it) } + val rowsAffected = + payload.notifs.sumBy { notificationSqlUtils.insertOrUpdateNotification(it) } OnNotificationChanged(rowsAffected) }.apply { @@ -536,4 +597,60 @@ class NotificationStore @Inject constructor( } emitChange(onNotificationChanged) } + + suspend fun updateNotificationSettingsFor( + siteNotificationsEnabled: List + ): Result = coroutineEngine.withDefaultContext( + T.API, + this, + "Update notification settings for sites: ${siteNotificationsEnabled.joinToString(",")}}" + ) { + val deviceId = preferences.getString(WPCOM_PUSH_DEVICE_SERVER_ID, null) + ?: return@withDefaultContext Result.failure( + NotificationSettingsUpdateError(type = UnregisteredDevice) + ) + + val payload = siteNotificationsEnabled.map { + SiteNotificationSettingDto( + siteId = it.siteId, + devices = listOf( + DevicesDto( + deviceId = deviceId, + newComment = it.newCommentEnabled, + storeOrder = it.storeOrderEnabled + ) + ) + ) + } + when (val result = notificationRestClient.disableNotificationsFor(payload)) { + is Success -> { + AppLog.i(NOTIFS, "Server response OK. Notifications disabled for device: $deviceId") + Result.success(Unit) + } + + is Response.Error -> { + AppLog.e(NOTIFS, "Error updating notification settings: ${result.error} - ${result.error.message}") + Result.failure( + NotificationSettingsUpdateError( + type = NotificationSettingErrorType.ApiError(result.error.message) + ) + ) + } + } + } + + data class SiteNotificationSetting( + val siteId: Long, + val newCommentEnabled: Boolean, + val storeOrderEnabled: Boolean + ) + + data class NotificationSettingsUpdateError( + val type: NotificationSettingErrorType + ) : Exception() + + sealed interface NotificationSettingErrorType { + object UnregisteredDevice : NotificationSettingErrorType + data class ApiError(val message: String) : NotificationSettingErrorType + } }