FeedbackRepository.kt

2.6 KB · raw

1package com.peachpasswords.feedback23import com.peachpasswords.BuildConfig4import kotlinx.coroutines.Dispatchers5import kotlinx.coroutines.withContext6import okhttp3.MediaType.Companion.toMediaType7import okhttp3.OkHttpClient8import okhttp3.Request9import okhttp3.RequestBody.Companion.toRequestBody10import org.json.JSONObject11import javax.inject.Inject12import javax.inject.Singleton1314/**15 * Submits user feedback to the Peach feedback endpoint. Payload shape mirrors16 * the browser extension's `SUBMIT_FEEDBACK` handler exactly17 * (`src/background/messages.ts`): a JSON body of18 * `{ category, message, version, browser, source }`, POSTed as19 * `application/json`.20 *21 * MN-7: [FEEDBACK_ENDPOINT] is empty until a real backend exists. The browser22 * returns ok:true (a silent no-op) in this case, but here we deliberately diverge23 * and throw [FeedbackUnavailableException] so the UI tells the user feedback is24 * unavailable rather than falsely reporting it was sent. The `source` discriminator25 * is `"peach-android"` (the browser sends `"peach-extension"`).26 */27class FeedbackUnavailableException : Exception("feedback_endpoint_not_configured")2829@Singleton30class FeedbackRepository @Inject constructor() {3132    private val client: OkHttpClient by lazy { OkHttpClient() }3334    suspend fun submit(type: String, message: String) {35        if (message.isBlank()) throw IllegalArgumentException("message_empty")3637        // No endpoint → be honest, never fake a successful send (MN-7).38        if (FEEDBACK_ENDPOINT.isBlank()) throw FeedbackUnavailableException()3940        withContext(Dispatchers.IO) {41            val payload = JSONObject().apply {42                put("category", type)43                put("message", message)44                put("version", BuildConfig.VERSION_NAME)45                put("browser", "android")46                put("source", "peach-android")47            }48            val body = payload.toString().toRequestBody(JSON)49            val request = Request.Builder()50                .url(FEEDBACK_ENDPOINT)51                .post(body)52                .build()53            client.newCall(request).execute().use { res ->54                if (!res.isSuccessful) throw java.io.IOException("HTTP ${res.code}")55            }56        }57    }5859    companion object {60        // Set to the real Peach feedback endpoint when it exists (the browser source61        // also ships this empty). While empty, submit() throws so the UI reports the62        // feature as unavailable rather than silently pretending success (MN-7).63        const val FEEDBACK_ENDPOINT: String = ""6465        private val JSON = "application/json; charset=utf-8".toMediaType()66    }67}