Rebrand app identifier and IPA name from Ditto to Agora

Renames the Capacitor app identifier from pub.agora.app to
spot.agora.app and cleans up Ditto-branded artifacts that don't refer
to upstream Ditto-the-project or Ditto-stack services.

App identifier (pub.agora.app -> spot.agora.app):
- capacitor.config.ts appId
- android applicationId, namespace, package_name string, custom_url_scheme
- iOS PRODUCT_BUNDLE_IDENTIFIER (Debug + Release)
- public/.well-known/assetlinks.json package_name
- public/.well-known/apple-app-site-association app id
- Info.plist BGTaskSchedulerPermittedIdentifiers and the matching
  Swift bgTaskIdentifier (previously mismatched: plist said
  pub.agora.app.notification-refresh, Swift said
  pub.ditto.app.notification-refresh, so background refresh would
  silently fail to register)
- src/lib/helpContent.ts Zapstore URLs
- .gitlab-ci.yml --package_name for fastlane supply

Android Java package (pub.ditto.app -> spot.agora.app):
- Move android/app/src/main/java/pub/ditto/app/ ->
  android/app/src/main/java/spot/agora/app/ (4 files: MainActivity,
  DittoNotificationPlugin, NostrPoller, NotificationRelayService)
- Update package declarations to match the new Android namespace
  (was a hard build failure with namespace = spot.agora.app)
- Update proguard -keep rule
- Update NotificationRelayService ACTION_FETCH intent string
  pub.ditto.app.ACTION_FETCH -> spot.agora.app.ACTION_FETCH

Fastlane (pub.ditto.app -> spot.agora.app):
- Appfile, Matchfile, Fastfile provisioning profile specifiers.
  Matchfile still points at Soapbox's certificates git repo; a new
  match repo with certs for spot.agora.app is required before iOS CI
  signing works.

IPA artifact name (Ditto.ipa -> Agora.ipa):
- Fastfile output_name and matching CI artifact paths
- .gitlab-ci.yml: artifacts/Ditto.ipa references and the GitLab
  Generic Packages path from /packages/generic/ditto/ ->
  /packages/generic/agora/ (matches how APK/AAB are already
  published). Existing release artifacts at the old path remain
  reachable; new releases land at the new path.

Release-notes script fallback (Ditto vX.Y.Z -> Agora vX.Y.Z):
- scripts/extract-release-notes.mjs fallback used as the App Store /
  Play Store 'What's New' blurb when a changelog section has no
  summary.

manifest.webmanifest:
- Update related_applications Play Store entry to spot.agora.app.
- Remove the iTunes related_applications entry that pointed at
  the existing Ditto App Store listing; not applicable to Agora
  until Agora has its own listing.

Capacitor sync incidentals:
- npm run cap:sync picked up @capacitor/barcode-scanner registration
  that had been missed in a prior plugin install
  (android/app/capacitor.build.gradle, capacitor.settings.gradle,
  ios/App/CapApp-SPM/Package.swift).

Intentionally NOT touched:
- ditto.json filename, DittoConfigSchema, DittoConfig, and JSDoc
  references to ditto.json. The config-system shape is shared with
  upstream Ditto by design.
- relay.ditto.pub, blossom.ditto.pub, ditto.pub/api/* and other
  Ditto-stack services Agora actively consumes.
- The DittoNotificationPlugin Android/iOS class name, the
  DittoNotification JS bridge name, ditto_notification_config
  SharedPreferences keys, ic_stat_ditto drawables, and the
  DittoBridgeViewController. Renaming requires a coordinated
  JS-side rename plus a SharedPreferences migration or existing
  users on the Ditto fork lose their notification config on upgrade.
- Ditto references in skill docs, NIP.md kind comments, README, and
  zapstore.yaml attribution \u2014 those correctly describe the upstream
  Ditto project that Agora forked from.

Follow-ups required before CI succeeds end-to-end (out of scope here):
- Stand up a new fastlane match git repo containing certs +
  provisioning profiles for spot.agora.app, or update Matchfile
  git_url to point at it.
- Register spot.agora.app in App Store Connect for team GZLTTH5DLM
  and create a new App Store listing.
- Create a new Google Play Console listing for spot.agora.app
  (package name is immutable per app on Play; the existing
  pub.agora.app listing cannot be reused).
- Re-publish to Zapstore under spot.agora.app so the URLs in
  helpContent.ts resolve.
This commit is contained in:
Chad Curtis
2026-05-17 18:30:25 -05:00
parent aacfb66e2c
commit 1b4399df68
23 changed files with 48 additions and 47 deletions
+11 -11
View File
@@ -104,7 +104,7 @@ release-notes:
# release-notes.md is the full section (summary + bulleted lists), used as
# the GitLab Release description. release-notes-summary.txt is the leading
# plaintext paragraph only, used as the App Store / Play Store release
# blurb. Falls back to "Ditto vX.Y.Z" when the section has no summary.
# blurb. Falls back to "Agora vX.Y.Z" when the section has no summary.
- mkdir -p artifacts
- node scripts/extract-release-notes.mjs "$CI_COMMIT_TAG" > artifacts/release-notes.md
- node scripts/extract-release-notes.mjs "$CI_COMMIT_TAG" --summary > artifacts/release-notes-summary.txt
@@ -280,27 +280,27 @@ build-ipa:
ios/App/App.xcodeproj/project.pbxproj
# Run match (cert verify + decrypt) and build_app to produce the IPA.
# build_app writes ./artifacts/Ditto.ipa relative to the project root.
# build_app writes ./artifacts/Agora.ipa relative to the project root.
- cd ios
- fastlane build_ipa
- cd ..
# Move the IPA to a stable name in the artifact directory.
- ls -lh artifacts/
- test -f artifacts/Ditto.ipa
- test -f artifacts/Agora.ipa
# Upload to the Generic Packages registry for a stable public download URL,
# mirroring how build-apk publishes the APK and AAB.
- |
curl --fail --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
--upload-file "artifacts/Ditto.ipa" \
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/ditto/${CI_COMMIT_TAG}/Ditto-${CI_COMMIT_TAG}.ipa"
--upload-file "artifacts/Agora.ipa" \
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/agora/${CI_COMMIT_TAG}/Agora-${CI_COMMIT_TAG}.ipa"
after_script:
# Wipe the API key so nothing sensitive sticks around between jobs.
- rm -f "$HOME/.private_keys"/AuthKey_*.p8 || true
artifacts:
paths:
- artifacts/Ditto.ipa
- artifacts/Agora.ipa
expire_in: 90 days
release:
@@ -333,8 +333,8 @@ release:
- name: Agora-${CI_COMMIT_TAG}.aab
url: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/agora/${CI_COMMIT_TAG}/Agora-${CI_COMMIT_TAG}.aab
link_type: package
- name: Ditto-${CI_COMMIT_TAG}.ipa
url: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/ditto/${CI_COMMIT_TAG}/Ditto-${CI_COMMIT_TAG}.ipa
- name: Agora-${CI_COMMIT_TAG}.ipa
url: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/agora/${CI_COMMIT_TAG}/Agora-${CI_COMMIT_TAG}.ipa
link_type: package
publish-zapstore:
@@ -395,7 +395,7 @@ publish-google-play:
- >-
fastlane supply
--aab artifacts/Agora.aab
--package_name pub.agora.app
--package_name spot.agora.app
--track production
--json_key /tmp/play-service-account.json
--metadata_path android/fastlane/metadata/android
@@ -446,7 +446,7 @@ publish-app-store:
# a JSON descriptor; we pass the API key inline via the Fastfile.
- unset APP_STORE_CONNECT_API_KEY_PATH || true
script:
- test -f artifacts/Ditto.ipa
- test -f artifacts/Agora.ipa
- test -f artifacts/release-notes-summary.txt
# Use the release summary paragraph as the App Store "What's New" text.
@@ -458,7 +458,7 @@ publish-app-store:
- echo "-------------------------"
# Submit the prebuilt IPA from build-ipa to App Store Connect for review.
- export IPA_PATH="$CI_PROJECT_DIR/artifacts/Ditto.ipa"
- export IPA_PATH="$CI_PROJECT_DIR/artifacts/Agora.ipa"
- cd ios
- fastlane submit_release
after_script:
+2 -2
View File
@@ -7,10 +7,10 @@ if (keystorePropertiesFile.exists()) {
}
android {
namespace = "pub.agora.app"
namespace = "spot.agora.app"
compileSdk = rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "pub.agora.app"
applicationId "spot.agora.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
+1
View File
@@ -10,6 +10,7 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-app')
implementation project(':capacitor-barcode-scanner')
implementation project(':capacitor-filesystem')
implementation project(':capacitor-haptics')
implementation project(':capacitor-keyboard')
+1 -1
View File
@@ -7,7 +7,7 @@
# Keep Capacitor classes (WebView JS bridge)
-keep class com.getcapacitor.** { *; }
-keep class pub.ditto.app.** { *; }
-keep class spot.agora.app.** { *; }
# Keep WebView JS interfaces
-keepclassmembers class * {
@@ -1,4 +1,4 @@
package pub.ditto.app;
package spot.agora.app;
import android.app.ForegroundServiceStartNotAllowedException;
import android.content.Context;
@@ -1,4 +1,4 @@
package pub.ditto.app;
package spot.agora.app;
import android.app.ForegroundServiceStartNotAllowedException;
import android.content.Context;
@@ -1,4 +1,4 @@
package pub.ditto.app;
package spot.agora.app;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -1,4 +1,4 @@
package pub.ditto.app;
package spot.agora.app;
import android.app.AlarmManager;
import android.app.ForegroundServiceStartNotAllowedException;
@@ -83,7 +83,7 @@ public class NotificationRelayService extends Service {
// + REQ + up to 5 events + EOSE + metadata fetch + disconnect.
private static final long FETCH_WAKELOCK_TIMEOUT_MS = 30_000;
private static final String ACTION_FETCH = "pub.ditto.app.ACTION_FETCH";
private static final String ACTION_FETCH = "spot.agora.app.ACTION_FETCH";
// Backoff bounds for relay connect failures (separate from alarm interval).
private static final long INITIAL_BACKOFF_MS = 1_000;
+2 -2
View File
@@ -2,6 +2,6 @@
<resources>
<string name="app_name">Agora</string>
<string name="title_activity_main">Agora</string>
<string name="package_name">pub.agora.app</string>
<string name="custom_url_scheme">pub.agora.app</string>
<string name="package_name">spot.agora.app</string>
<string name="custom_url_scheme">spot.agora.app</string>
</resources>
+3
View File
@@ -5,6 +5,9 @@ project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/
include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
include ':capacitor-barcode-scanner'
project(':capacitor-barcode-scanner').projectDir = new File('../node_modules/@capacitor/barcode-scanner/android')
include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')
+1 -1
View File
@@ -1,7 +1,7 @@
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'pub.agora.app',
appId: 'spot.agora.app',
appName: 'Agora',
webDir: 'dist',
server: {
+2 -2
View File
@@ -325,7 +325,7 @@
);
MARKETING_VERSION = 2.14.4;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = pub.agora.app;
PRODUCT_BUNDLE_IDENTIFIER = spot.agora.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
@@ -348,7 +348,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.8.0;
PRODUCT_BUNDLE_IDENTIFIER = pub.agora.app;
PRODUCT_BUNDLE_IDENTIFIER = spot.agora.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0;
+1 -1
View File
@@ -33,7 +33,7 @@ public class DittoNotificationPlugin: CAPPlugin, CAPBridgedPlugin {
// MARK: - Constants
static let bgTaskIdentifier = "pub.ditto.app.notification-refresh"
static let bgTaskIdentifier = "spot.agora.app.notification-refresh"
private static let prefsKey = "ditto_notification_config"
// MARK: - Plugin Methods
+1 -1
View File
@@ -61,7 +61,7 @@
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>pub.agora.app.notification-refresh</string>
<string>spot.agora.app.notification-refresh</string>
</array>
</dict>
</plist>
+2
View File
@@ -13,6 +13,7 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.2.0"),
.package(name: "CapacitorApp", path: "../../../node_modules/@capacitor/app"),
.package(name: "CapacitorBarcodeScanner", path: "../../../node_modules/@capacitor/barcode-scanner"),
.package(name: "CapacitorFilesystem", path: "../../../node_modules/@capacitor/filesystem"),
.package(name: "CapacitorHaptics", path: "../../../node_modules/@capacitor/haptics"),
.package(name: "CapacitorKeyboard", path: "../../../node_modules/@capacitor/keyboard"),
@@ -28,6 +29,7 @@ let package = Package(
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm"),
.product(name: "CapacitorApp", package: "CapacitorApp"),
.product(name: "CapacitorBarcodeScanner", package: "CapacitorBarcodeScanner"),
.product(name: "CapacitorFilesystem", package: "CapacitorFilesystem"),
.product(name: "CapacitorHaptics", package: "CapacitorHaptics"),
.product(name: "CapacitorKeyboard", package: "CapacitorKeyboard"),
+1 -1
View File
@@ -1,2 +1,2 @@
app_identifier("pub.ditto.app")
app_identifier("spot.agora.app")
team_id("GZLTTH5DLM")
+5 -5
View File
@@ -3,7 +3,7 @@ default_platform(:ios)
platform :ios do
# ─── Lanes ────────────────────────────────────────────────────────────
desc "Build and sign the App Store IPA. Output at ../artifacts/Ditto.ipa."
desc "Build and sign the App Store IPA. Output at ../artifacts/Agora.ipa."
lane :build_ipa do
setup_lane_signing!
build_release_ipa!
@@ -19,7 +19,7 @@ platform :ios do
submit_release_for_review!(ipa_path)
end
desc "Build, sign, and submit Ditto to the App Store for review (single-step convenience)."
desc "Build, sign, and submit Agora to the App Store for review (single-step convenience)."
lane :release do
setup_lane_signing!
build_release_ipa!
@@ -83,7 +83,7 @@ platform :ios do
configuration: "Release",
export_method: "app-store",
output_directory: "../artifacts",
output_name: "Ditto.ipa",
output_name: "Agora.ipa",
clean: true,
# Override the Xcode project's Automatic signing for this build only.
# Match has already installed the AppStore cert + profile into the
@@ -93,7 +93,7 @@ platform :ios do
xcargs: [
"CODE_SIGN_STYLE=Manual",
"CODE_SIGN_IDENTITY='Apple Distribution'",
"PROVISIONING_PROFILE_SPECIFIER='match AppStore pub.ditto.app'",
"PROVISIONING_PROFILE_SPECIFIER='match AppStore spot.agora.app'",
"DEVELOPMENT_TEAM=GZLTTH5DLM",
].join(" "),
export_options: {
@@ -101,7 +101,7 @@ platform :ios do
signingStyle: "manual",
teamID: "GZLTTH5DLM",
provisioningProfiles: {
"pub.ditto.app" => "match AppStore pub.ditto.app",
"spot.agora.app" => "match AppStore spot.agora.app",
},
},
)
+1 -1
View File
@@ -1,5 +1,5 @@
git_url("https://gitlab.com/soapbox-pub/certificates.git")
storage_mode("git")
type("appstore")
app_identifier(["pub.ditto.app"])
app_identifier(["spot.agora.app"])
team_id("GZLTTH5DLM")
@@ -1,7 +1,7 @@
{
"webcredentials": {
"apps": [
"GZLTTH5DLM.pub.agora.app"
"GZLTTH5DLM.spot.agora.app"
]
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
],
"target": {
"namespace": "android_app",
"package_name": "pub.agora.app",
"package_name": "spot.agora.app",
"sha256_cert_fingerprints": [
"7C:05:A8:5A:07:0F:84:AE:43:DE:85:67:A4:5F:7F:FB:42:0A:05:05:27:CE:B6:8C:DA:AF:A5:E0:12:E0:9E:71",
"E5:B1:A9:13:C9:37:35:3C:A5:E7:27:89:C0:9D:3D:0D:A5:4F:F5:26:88:06:BD:24:46:21:AB:61:6B:CC:C5:E5"
+2 -7
View File
@@ -91,13 +91,8 @@
"related_applications": [
{
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=pub.ditto.app",
"id": "pub.ditto.app"
},
{
"platform": "itunes",
"url": "https://apps.apple.com/us/app/ditto-fun-social-media/id6761851821",
"id": "6761851821"
"url": "https://play.google.com/store/apps/details?id=spot.agora.app",
"id": "spot.agora.app"
}
],
"prefer_related_applications": false
+3 -3
View File
@@ -25,7 +25,7 @@
* node scripts/extract-release-notes.mjs <version> [--summary] [--changelog <path>]
*
* --summary Print only the summary paragraph (no headings, no bullets).
* Falls back to "Ditto vX.Y.Z" if the section has no summary.
* Falls back to "Agora vX.Y.Z" if the section has no summary.
* --changelog Path to the changelog file. Defaults to CHANGELOG.md.
*
* Exits 0 with the extracted text on stdout. Exits non-zero if the version is
@@ -128,7 +128,7 @@ if (!section) {
if (summary) {
const text = extractSummary(section);
stdout.write(text ?? `Ditto v${version}`);
stdout.write(text ?? `Agora v${version}`);
stdout.write('\n');
} else {
const body = trimBlankEdges(section).join('\n');
@@ -136,6 +136,6 @@ if (summary) {
stdout.write(body);
stdout.write('\n');
} else {
stdout.write(`Ditto v${version}\n`);
stdout.write(`Agora v${version}\n`);
}
}
+2 -2
View File
@@ -121,7 +121,7 @@ const FAQ_TEMPLATE: FAQCategory[] = [
question: 'Can I download this on the App Store or Google Play?',
answer: [
'This site works as a web app right from your browser \u2014 no download needed! You can also "Add to Home Screen" on your phone to get an app-like experience.',
'On Android, you can download {appName} from [Zap Store](https://zapstore.dev/apps/pub.agora.app), a community-driven app store for the Nostr ecosystem. iOS support is planned for the future \u2014 stay tuned!',
'On Android, you can download {appName} from [Zap Store](https://zapstore.dev/apps/spot.agora.app), a community-driven app store for the Nostr ecosystem. iOS support is planned for the future \u2014 stay tuned!',
],
},
{
@@ -136,7 +136,7 @@ const FAQ_TEMPLATE: FAQCategory[] = [
id: 'nostr-app-store',
question: 'Is there a Nostr-specific app store?',
answer: [
'Yes! [Zap Store](https://zapstore.dev/) is a community-driven app store built specifically for the Nostr ecosystem. You can discover and download Nostr apps, and the apps are verified by the community rather than a corporation. {appName} is listed there \u2014 [get it on Zap Store](https://zapstore.dev/apps/pub.agora.app).',
'Yes! [Zap Store](https://zapstore.dev/) is a community-driven app store built specifically for the Nostr ecosystem. You can discover and download Nostr apps, and the apps are verified by the community rather than a corporation. {appName} is listed there \u2014 [get it on Zap Store](https://zapstore.dev/apps/spot.agora.app).',
'You can also browse a directory of Nostr apps at [nostrapps.com](https://nostrapps.com/).',
],
},