Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Library to Maven Central
name: Publish Artifacts to Maven Central

on:
push:
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ plugins {
}

kick {
enabled = KickEnabled.Auto
modules(KickModule.FileExplorer)
enabledAuto() // or enabled() / disabled()
modules {
fileExplorer()
}
}
```

Expand All @@ -79,7 +81,7 @@ Kick.init(context) {
enableKick(false)
```

`-Pkick.enabled=true|false` has highest priority and overrides both `enableKick(...)` and `kick { enabled = ... }`.
`-Pkick.enabled=true|false` has highest priority and overrides both `enableKick(...)` and `kick { enabledAuto() / enabled() / disabled() }`.

### Wizard

Expand Down
38 changes: 36 additions & 2 deletions content/docs/Advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,21 @@ plugins {
}

kick {
enabled = KickEnabled.Auto
modules(KickModule.FileExplorer, KickModule.Ktor3)
enabledAuto() // or enabled() / disabled()
modules {
controlPanel()
fileExplorer()
firebaseAnalytics()
firebaseCloudMessaging()
ktor3()
layout()
logging()
multiplatformSettings()
overlay()
room()
runner()
sqldelight()
}
}
// Optional: enableKick(false) or -Pkick.enabled=true|false for override
```
Expand Down Expand Up @@ -275,6 +288,27 @@ final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCent
}
```

**iOS (Token/FID):** the module does not link Firebase SDK on iOS.
Pass values from your existing Firebase integration (CocoaPods, SPM, manual).

```swift
import FirebaseMessaging
import FirebaseInstallations
import shared

// FCM token
Messaging.messaging().token { token, _ in
KickCompanion.shared.firebaseCloudMessaging.setFcmToken(token: token)
}

// Firebase Installation ID
Installations.installations().installationID { id, _ in
KickCompanion.shared.firebaseCloudMessaging.setFirebaseInstallationId(id: id)
}
```

If the token or installation ID changes, call the same setters again — Kick updates the UI automatically.

### Firebase Analytics

Capture analytics calls made by your app and inspect them inside Kick (events, user id, user properties).
Expand Down
40 changes: 33 additions & 7 deletions content/wizard/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,7 @@ function getGlueGuideText(item) {
function buildKtor3ExampleSnippet() {
return [
"val httpClient = HttpClient {",
" install(KickKtor3Plugin) {",
" maxBodySizeBytes = 1024 * 1024L",
" }",
" install(KickKtor3Plugin)",
"}",
].join("\n");
}
Expand All @@ -542,12 +540,32 @@ function buildFirebaseCloudMessagingExampleSnippet(item) {
if (item.includeIos) {
parts.push(
[
"// Shared Kotlin bridge for iOS push callbacks",
"object IosPushBridge {",
" fun onApnsPayload(userInfo: Map<Any?, *>) {",
" Kick.firebaseCloudMessaging.handleApnsPayload(userInfo)",
"// iOS (Swift)",
"import UIKit",
"import FirebaseMessaging",
"import FirebaseInstallations",
"import shared",
"",
"class AppDelegate: UIResponder, UIApplicationDelegate {",
" func application(",
" _ application: UIApplication,",
" didReceiveRemoteNotification userInfo: [AnyHashable: Any],",
" fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void",
" ) {",
" // your app logic...",
" KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo)",
" completionHandler(.noData)",
" }",
"}",
"",
"// After FirebaseApp.configure() (or when values become available)",
"Messaging.messaging().token { token, _ in",
" KickCompanion.shared.firebaseCloudMessaging.setFcmToken(token: token)",
"}",
"",
"Installations.installations().installationID { id, _ in",
" KickCompanion.shared.firebaseCloudMessaging.setFirebaseInstallationId(id: id)",
"}",
].join("\n")
);
}
Expand Down Expand Up @@ -593,6 +611,14 @@ function buildControlPanelExampleSnippet() {
"if (Kick.controlPanel.getBoolean(\"enableSomeRequest\")) {",
" makeRequest(Kick.controlPanel.getString(\"someRequestUrl\"))",
"}",
"",
"appScope.launch {",
" Kick.controlPanel.events.collect { event ->",
" if (event is ControlPanelEvent.ButtonClicked && event.id == \"refresh_cache\") {",
" refreshCache()",
" }",
" }",
"}",
].join("\n");
}

Expand Down
39 changes: 16 additions & 23 deletions content/wizard/app/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,45 +165,42 @@ export function getUnsupportedPlatforms(moduleDescription, selectedPlatforms) {
return selectedPlatforms.filter((platform) => !moduleDescription.supportedPlatforms.includes(platform));
}

function collectKickModuleEnums(selectedModules) {
const enums = [];
function collectKickModuleMethods(selectedModules) {
const methods = [];
selectedModules.forEach((module) => {
if (module.kickModuleEnum) {
enums.push(module.kickModuleEnum);
if (module.kickModuleMethod) {
methods.push(module.kickModuleMethod);
}
if (Array.isArray(module.extraKickModuleEnums)) {
module.extraKickModuleEnums.forEach((entry) => {
if (Array.isArray(module.extraKickModuleMethods)) {
module.extraKickModuleMethods.forEach((entry) => {
if (entry) {
enums.push(entry);
methods.push(entry);
}
});
}
});
return unique(enums);
return unique(methods);
}

function buildGradleSnippet(selectedModules, kickVersion) {
const enums = collectKickModuleEnums(selectedModules);
const methods = collectKickModuleMethods(selectedModules);

const lines = [];
lines.push("import ru.bartwell.kick.gradle.KickEnabled");
lines.push("import ru.bartwell.kick.gradle.KickModule");
lines.push("");
lines.push("plugins {");
lines.push(` id(\"ru.bartwell.kick\") version \"${kickVersion}\"`);
lines.push("}");
lines.push("");
lines.push("kick {");
lines.push(" enabled = KickEnabled.Auto");
lines.push(" modules(");
if (enums.length > 0) {
enums.forEach((entry) => {
lines.push(` ${entry},`);
lines.push(" enabledAuto() // or enabled() / disabled()");
lines.push(" modules {");
if (methods.length > 0) {
methods.forEach((method) => {
lines.push(` ${method}()`);
});
} else {
lines.push(" // Select at least one module supported by KickModule enum.");
lines.push(" // Select at least one module, e.g. fileExplorer(), ktor3()");
}
lines.push(" )");
lines.push(" }");
lines.push("}");
lines.push("");
lines.push("// Enable/disable strategy:");
Expand Down Expand Up @@ -277,10 +274,6 @@ function buildCommonSnippet(state, selectedModules, hasPlatformBridge) {
if (module.id === "ktor3") {
imports.add("ru.bartwell.kick.module.ktor3.Ktor3Module");
moduleLines.push(" module(Ktor3Module(context))");
moduleLines.push(" // Ktor client integration (outside Kick.init):");
moduleLines.push(" // install(KickKtor3Plugin) {");
moduleLines.push(" // maxBodySizeBytes = 1024 * 1024L");
moduleLines.push(" // }");
return;
}

Expand Down
48 changes: 24 additions & 24 deletions content/wizard/data/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"id": "sqldelight",
"titleKey": "module.sqldelight.title",
"descriptionKey": "module.sqldelight.description",
"kickModuleEnum": "KickModule.SqliteRuntime",
"extraKickModuleEnums": ["KickModule.SqliteSqlDelightAdapter"],
"kickModuleMethod": "sqldelight",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "medium",
Expand All @@ -19,8 +19,8 @@
"id": "room",
"titleKey": "module.room.title",
"descriptionKey": "module.room.description",
"kickModuleEnum": "KickModule.SqliteRuntime",
"extraKickModuleEnums": ["KickModule.SqliteRoomAdapter"],
"kickModuleMethod": "room",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm"],
"needsPlatformGlue": false,
"configComplexity": "medium",
Expand All @@ -35,8 +35,8 @@
"id": "logging",
"titleKey": "module.logging.title",
"descriptionKey": "module.logging.description",
"kickModuleEnum": "KickModule.Logging",
"extraKickModuleEnums": [],
"kickModuleMethod": "logging",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "low",
Expand All @@ -51,8 +51,8 @@
"id": "ktor3",
"titleKey": "module.ktor3.title",
"descriptionKey": "module.ktor3.description",
"kickModuleEnum": "KickModule.Ktor3",
"extraKickModuleEnums": [],
"kickModuleMethod": "ktor3",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "low",
Expand All @@ -67,8 +67,8 @@
"id": "control_panel",
"titleKey": "module.control_panel.title",
"descriptionKey": "module.control_panel.description",
"kickModuleEnum": null,
"extraKickModuleEnums": [],
"kickModuleMethod": "controlPanel",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "high",
Expand All @@ -83,8 +83,8 @@
"id": "multiplatform_settings",
"titleKey": "module.multiplatform_settings.title",
"descriptionKey": "module.multiplatform_settings.description",
"kickModuleEnum": "KickModule.MultiplatformSettings",
"extraKickModuleEnums": [],
"kickModuleMethod": "multiplatformSettings",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "low",
Expand All @@ -99,8 +99,8 @@
"id": "file_explorer",
"titleKey": "module.file_explorer.title",
"descriptionKey": "module.file_explorer.description",
"kickModuleEnum": "KickModule.FileExplorer",
"extraKickModuleEnums": [],
"kickModuleMethod": "fileExplorer",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "low",
Expand All @@ -115,8 +115,8 @@
"id": "layout",
"titleKey": "module.layout.title",
"descriptionKey": "module.layout.description",
"kickModuleEnum": "KickModule.Layout",
"extraKickModuleEnums": [],
"kickModuleMethod": "layout",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm"],
"needsPlatformGlue": false,
"configComplexity": "medium",
Expand All @@ -131,8 +131,8 @@
"id": "overlay",
"titleKey": "module.overlay.title",
"descriptionKey": "module.overlay.description",
"kickModuleEnum": null,
"extraKickModuleEnums": [],
"kickModuleMethod": "overlay",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "medium",
Expand All @@ -147,8 +147,8 @@
"id": "firebase_cloud_messaging",
"titleKey": "module.firebase_cloud_messaging.title",
"descriptionKey": "module.firebase_cloud_messaging.description",
"kickModuleEnum": "KickModule.FirebaseCloudMessaging",
"extraKickModuleEnums": [],
"kickModuleMethod": "firebaseCloudMessaging",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios"],
"needsPlatformGlue": true,
"configComplexity": "medium",
Expand All @@ -163,8 +163,8 @@
"id": "firebase_analytics",
"titleKey": "module.firebase_analytics.title",
"descriptionKey": "module.firebase_analytics.description",
"kickModuleEnum": null,
"extraKickModuleEnums": [],
"kickModuleMethod": "firebaseAnalytics",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios"],
"needsPlatformGlue": true,
"configComplexity": "medium",
Expand All @@ -179,8 +179,8 @@
"id": "runner",
"titleKey": "module.runner.title",
"descriptionKey": "module.runner.description",
"kickModuleEnum": null,
"extraKickModuleEnums": [],
"kickModuleMethod": "runner",
"extraKickModuleMethods": [],
"supportedPlatforms": ["android", "ios", "jvm", "wasm"],
"needsPlatformGlue": false,
"configComplexity": "low",
Expand Down
8 changes: 4 additions & 4 deletions content/wizard/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
},
"hook": {
"firebaseCloudMessagingAndroid": "In FirebaseMessagingService.onMessageReceived(...) call Kick.firebaseCloudMessaging.handleFcm(message).",
"firebaseCloudMessagingIos": "When APNS notification is received, call one of Kick.firebaseCloudMessaging.handleApns... handlers from shared Kotlin API bridge.",
"firebaseCloudMessagingIos": "When APNS notification is received, call one of Kick.firebaseCloudMessaging.handleApns... handlers from shared Kotlin API bridge. Also pass FCM token and Firebase Installation ID via KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) and setFirebaseInstallationId(...).",
"firebaseAnalytics": "Mirror analytics wrapper calls to Kick.firebaseAnalytics.logEvent / setUserId / setUserProperty."
},
"unsupportedModule": "{module}: unsupported on this platform.",
Expand All @@ -192,10 +192,10 @@
"logging": "To send logs to Logging module, call Kick.log(level, message) in the places you want to log.",
"loggingWithNapier": "To send logs to Logging module, call Kick.log(level, message) where needed, or route all Napier logs with installNapierBridge() from KickBootstrap.",
"ktor3": "To log Ktor3 network activity, add install(KickKtor3Plugin) when you create HttpClient.",
"firebaseCloudMessagingBoth": "For Firebase Cloud Messaging: on Android call Kick.firebaseCloudMessaging.handleFcm(message) inside FirebaseMessagingService.onMessageReceived(...). On iOS call KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) in AppDelegate when APNS payload is received.",
"firebaseCloudMessagingBoth": "For Firebase Cloud Messaging: on Android call Kick.firebaseCloudMessaging.handleFcm(message) inside FirebaseMessagingService.onMessageReceived(...). On iOS call KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) in AppDelegate when APNS payload is received. Also on iOS pass FCM token and Firebase Installation ID via KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) and setFirebaseInstallationId(...).",
"firebaseCloudMessagingAndroid": "For Firebase Cloud Messaging on Android: call Kick.firebaseCloudMessaging.handleFcm(message) inside FirebaseMessagingService.onMessageReceived(...).",
"firebaseCloudMessagingIos": "For Firebase Cloud Messaging on iOS: call KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) in AppDelegate when APNS payload is received.",
"firebaseCloudMessagingGeneric": "For Firebase Cloud Messaging, forward push payloads to Kick handlers in your platform lifecycle code.",
"firebaseCloudMessagingIos": "For Firebase Cloud Messaging on iOS: call KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) in AppDelegate when APNS payload is received. Also pass FCM token and Firebase Installation ID via KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) and setFirebaseInstallationId(...).",
"firebaseCloudMessagingGeneric": "For Firebase Cloud Messaging, forward push payloads to Kick handlers in your platform lifecycle code. On iOS also pass FCM token and Firebase Installation ID via KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) and setFirebaseInstallationId(...).",
"firebaseAnalytics": "For Firebase Analytics, add Kick.firebaseAnalytics.logEvent(...), Kick.firebaseAnalytics.setUserId(...), and Kick.firebaseAnalytics.setUserProperty(...) in matching places in your analytics wrapper.",
"controlPanel": "Read ControlPanel values in your code with Kick.controlPanel.getBoolean(...), Kick.controlPanel.getString(...), etc. If you added buttons, listen for clicks with Kick.controlPanel.events.collect { event -> ... }.",
"overlay": "Show required runtime values in Overlay floating window with Kick.overlay.set(\"key\", value).",
Expand Down
8 changes: 4 additions & 4 deletions content/wizard/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
},
"hook": {
"firebaseCloudMessagingAndroid": "En FirebaseMessagingService.onMessageReceived(...) llama a Kick.firebaseCloudMessaging.handleFcm(message).",
"firebaseCloudMessagingIos": "Cuando se reciba una notificación APNS, llama a uno de los handlers Kick.firebaseCloudMessaging.handleApns... desde el bridge de Kotlin compartido.",
"firebaseCloudMessagingIos": "Cuando se reciba una notificación APNS, llama a uno de los handlers Kick.firebaseCloudMessaging.handleApns... desde el bridge de Kotlin compartido. Además, pasa el token FCM y el Firebase Installation ID mediante KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) y setFirebaseInstallationId(...).",
"firebaseAnalytics": "Duplica las llamadas del wrapper de analytics en Kick.firebaseAnalytics.logEvent / setUserId / setUserProperty."
},
"unsupportedModule": "{module}: no compatible en esta plataforma.",
Expand All @@ -192,10 +192,10 @@
"logging": "Para enviar logs al módulo Logging, llama a Kick.log(level, message) en los lugares que quieras registrar.",
"loggingWithNapier": "Para enviar logs al módulo Logging, llama a Kick.log(level, message) donde haga falta, o redirige todos los logs de Napier con installNapierBridge() desde KickBootstrap.",
"ktor3": "Para registrar actividad de red de Ktor3, agrega install(KickKtor3Plugin) al crear HttpClient.",
"firebaseCloudMessagingBoth": "Para Firebase Cloud Messaging: en Android llama a Kick.firebaseCloudMessaging.handleFcm(message) dentro de FirebaseMessagingService.onMessageReceived(...). En iOS llama a KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) en AppDelegate cuando recibas payload APNS.",
"firebaseCloudMessagingBoth": "Para Firebase Cloud Messaging: en Android llama a Kick.firebaseCloudMessaging.handleFcm(message) dentro de FirebaseMessagingService.onMessageReceived(...). En iOS llama a KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) en AppDelegate cuando recibas payload APNS. Además en iOS pasa el token FCM y el Firebase Installation ID mediante KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) y setFirebaseInstallationId(...).",
"firebaseCloudMessagingAndroid": "Para Firebase Cloud Messaging en Android: llama a Kick.firebaseCloudMessaging.handleFcm(message) dentro de FirebaseMessagingService.onMessageReceived(...).",
"firebaseCloudMessagingIos": "Para Firebase Cloud Messaging en iOS: llama a KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) en AppDelegate cuando recibas payload APNS.",
"firebaseCloudMessagingGeneric": "Para Firebase Cloud Messaging, reenvía payloads push a los handlers de Kick en el ciclo de vida de la plataforma.",
"firebaseCloudMessagingIos": "Para Firebase Cloud Messaging en iOS: llama a KickCompanion.shared.firebaseCloudMessaging.handleApnsPayload(userInfo: userInfo) en AppDelegate cuando recibas payload APNS. Además pasa el token FCM y el Firebase Installation ID mediante KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) y setFirebaseInstallationId(...).",
"firebaseCloudMessagingGeneric": "Para Firebase Cloud Messaging, reenvía payloads push a los handlers de Kick en el ciclo de vida de la plataforma. En iOS también pasa el token FCM y el Firebase Installation ID mediante KickCompanion.shared.firebaseCloudMessaging.setFcmToken(...) y setFirebaseInstallationId(...).",
"firebaseAnalytics": "Para Firebase Analytics, agrega Kick.firebaseAnalytics.logEvent(...), Kick.firebaseAnalytics.setUserId(...), y Kick.firebaseAnalytics.setUserProperty(...) en los mismos puntos de tu wrapper de analytics.",
"controlPanel": "Lee valores de ControlPanel en tu código con Kick.controlPanel.getBoolean(...), Kick.controlPanel.getString(...), etc. Si agregaste botones, escucha clics con Kick.controlPanel.events.collect { event -> ... }.",
"overlay": "Muestra valores runtime en la ventana flotante Overlay con Kick.overlay.set(\"key\", value).",
Expand Down
Loading