feat(app-check): stable debug token via BuildConfig; feat(firestore): indexes for questions + bucket_list
This commit is contained in:
parent
6977db7600
commit
e5c13b6b6d
|
|
@ -57,7 +57,14 @@ android {
|
|||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
// Stable debug token registered in Firebase Console > App Check.
|
||||
// Pre-seeded into SharedPreferences by FirebaseInitializer so all installs
|
||||
// use the same token without manual re-registration.
|
||||
buildConfigField("String", "APP_CHECK_DEBUG_TOKEN", "\"e2dc8256-403f-449b-846e-76614a7297cc\"")
|
||||
}
|
||||
release {
|
||||
buildConfigField("String", "APP_CHECK_DEBUG_TOKEN", "\"\"")
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
package app.closer.core.firebase
|
||||
|
||||
import app.closer.BuildConfig // generated by buildFeatures { buildConfig = true }
|
||||
import android.content.Context
|
||||
import app.closer.BuildConfig
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.appcheck.FirebaseAppCheck
|
||||
import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FirebaseInitializer @Inject constructor() {
|
||||
class FirebaseInitializer @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
fun initialize() {
|
||||
val appCheck = FirebaseAppCheck.getInstance()
|
||||
if (BuildConfig.DEBUG) {
|
||||
// DebugAppCheckProviderFactory is in the debug artifact only;
|
||||
// referenced by name to avoid a compile-time dep in release.
|
||||
// Pre-seed the stable registered debug token so every install (emulator or device)
|
||||
// uses the same token without needing re-registration in Firebase Console.
|
||||
seedDebugToken()
|
||||
try {
|
||||
val cls = Class.forName(
|
||||
"com.google.firebase.appcheck.debug.DebugAppCheckProviderFactory"
|
||||
|
|
@ -32,4 +38,19 @@ class FirebaseInitializer @Inject constructor() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun seedDebugToken() {
|
||||
val token = BuildConfig.APP_CHECK_DEBUG_TOKEN
|
||||
if (token.isBlank()) return
|
||||
try {
|
||||
val persistenceKey = FirebaseApp.getInstance().persistenceKey
|
||||
val prefsFile = "com.google.firebase.appcheck.debug.store.$persistenceKey"
|
||||
context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString("com.google.firebase.appcheck.debug.DEBUG_SECRET", token)
|
||||
.commit()
|
||||
} catch (_: Exception) {
|
||||
// Best effort — debug provider will generate and log a new token if this fails.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,22 @@
|
|||
{ "fieldPath": "status", "order": "ASCENDING" },
|
||||
{ "fieldPath": "unlockAt", "order": "ASCENDING" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionGroup": "questions",
|
||||
"queryScope": "COLLECTION",
|
||||
"fields": [
|
||||
{ "fieldPath": "active", "order": "ASCENDING" },
|
||||
{ "fieldPath": "isPremium", "order": "ASCENDING" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"collectionGroup": "bucket_list",
|
||||
"queryScope": "COLLECTION",
|
||||
"fields": [
|
||||
{ "fieldPath": "category", "order": "ASCENDING" },
|
||||
{ "fieldPath": "addedAt", "order": "DESCENDING" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"fieldOverrides": [
|
||||
|
|
|
|||
|
|
@ -3,32 +3,38 @@ import SwiftUI
|
|||
// MARK: - Theme
|
||||
|
||||
extension Color {
|
||||
private static func adaptive(light: String, dark: String) -> Color {
|
||||
Color(UIColor { traitCollection in
|
||||
UIColor(hex: traitCollection.userInterfaceStyle == .dark ? dark : light)
|
||||
})
|
||||
}
|
||||
|
||||
// Primary palette
|
||||
static let closerPrimary = Color(hex: "B98AF4")
|
||||
static let closerSecondary = Color(hex: "E7A2D1")
|
||||
static let closerBackground = Color(hex: "FFFBFE")
|
||||
static let closerSurface = Color(hex: "F5F0FF")
|
||||
static let closerPrimary = Color.adaptive(light: "B98AF4", dark: "CFA7FF")
|
||||
static let closerSecondary = Color.adaptive(light: "E7A2D1", dark: "FFAFD9")
|
||||
static let closerBackground = Color.adaptive(light: "FFFBFE", dark: "18111E")
|
||||
static let closerSurface = Color.adaptive(light: "F5F0FF", dark: "211729")
|
||||
static let closerOnPrimary = Color.white
|
||||
static let closerText = Color(hex: "1C1B1F")
|
||||
static let closerTextSecondary = Color(hex: "49454F")
|
||||
static let closerDivider = Color(hex: "E6E0E9")
|
||||
static let closerText = Color.adaptive(light: "1C1B1F", dark: "F2E8F6")
|
||||
static let closerTextSecondary = Color.adaptive(light: "49454F", dark: "D9C8E2")
|
||||
static let closerDivider = Color.adaptive(light: "E6E0E9", dark: "5A4666")
|
||||
|
||||
// Semantic
|
||||
static let closerSuccess = Color(hex: "4CAF50")
|
||||
static let closerWarning = Color(hex: "FF9800")
|
||||
static let closerDanger = Color(hex: "F44336")
|
||||
static let closerGold = Color(hex: "FFD700")
|
||||
static let closerSuccess = Color.adaptive(light: "4CAF50", dark: "8DD99B")
|
||||
static let closerWarning = Color.adaptive(light: "FF9800", dark: "FFC46B")
|
||||
static let closerDanger = Color.adaptive(light: "F44336", dark: "FFB3BA")
|
||||
static let closerGold = Color.adaptive(light: "FFD700", dark: "FFE680")
|
||||
|
||||
// Category colors
|
||||
static let categoryCommunication = Color(hex: "B98AF4")
|
||||
static let categoryIntimacy = Color(hex: "E7A2D1")
|
||||
static let categoryFun = Color(hex: "FFB74D")
|
||||
static let categoryGoals = Color(hex: "81C784")
|
||||
static let categoryAdventure = Color(hex: "64B5F6")
|
||||
static let categoryCommunication = Color.adaptive(light: "B98AF4", dark: "CFA7FF")
|
||||
static let categoryIntimacy = Color.adaptive(light: "E7A2D1", dark: "FFAFD9")
|
||||
static let categoryFun = Color.adaptive(light: "FFB74D", dark: "FFD38A")
|
||||
static let categoryGoals = Color.adaptive(light: "81C784", dark: "A6DFA8")
|
||||
static let categoryAdventure = Color.adaptive(light: "64B5F6", dark: "9AD1FF")
|
||||
|
||||
// Streak
|
||||
static let streakActive = Color(hex: "FF6B6B")
|
||||
static let streakInactive = Color(hex: "E0E0E0")
|
||||
static let streakActive = Color.adaptive(light: "FF6B6B", dark: "FF9A9A")
|
||||
static let streakInactive = Color.adaptive(light: "E0E0E0", dark: "5A4666")
|
||||
|
||||
init(hex: String) {
|
||||
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
|
|
@ -53,6 +59,29 @@ extension Color {
|
|||
}
|
||||
}
|
||||
|
||||
private extension UIColor {
|
||||
convenience init(hex: String) {
|
||||
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int: UInt64 = 0
|
||||
Scanner(string: hex).scanHexInt64(&int)
|
||||
let a, r, g, b: UInt64
|
||||
switch hex.count {
|
||||
case 6:
|
||||
(a, r, g, b) = (255, (int >> 16) & 0xFF, (int >> 8) & 0xFF, int & 0xFF)
|
||||
case 8:
|
||||
(a, r, g, b) = ((int >> 24) & 0xFF, (int >> 16) & 0xFF, (int >> 8) & 0xFF, int & 0xFF)
|
||||
default:
|
||||
(a, r, g, b) = (255, 0, 0, 0)
|
||||
}
|
||||
self.init(
|
||||
red: Double(r) / 255,
|
||||
green: Double(g) / 255,
|
||||
blue: Double(b) / 255,
|
||||
alpha: Double(a) / 255
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Typography
|
||||
|
||||
enum CloserFont {
|
||||
|
|
|
|||
Loading…
Reference in New Issue