From 1f035f4a6e7599eeb1dff04fa15bf2a6212dc113 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 23 Feb 2026 11:50:00 -0600 Subject: [PATCH] Rename Mew to Ditto across entire codebase - Update all branding: titles, meta tags, OG tags, manifest - Rename MewLogo component to DittoLogo - Update Capacitor config: appId to pub.ditto.app, hostname to ditto.pub - Migrate Android Java package from com.mew.app to pub.ditto.app - Rename MewNotificationPlugin to DittoNotificationPlugin - Update all localStorage keys from mew: to ditto: prefix - Update Nostr encrypted settings d-tag from mew-metadata to ditto-metadata - Update all page titles, zap comments, and UI strings - Rename ic_stat_mew.xml drawable to ic_stat_ditto.xml - Update build script references (mew.apk -> ditto.apk) - Update all domain references (mew.shakespeare.wtf -> ditto.pub) --- android/app/build.gradle | 4 +-- .../ditto/app/DittoNotificationPlugin.java} | 10 +++---- .../mew => pub/ditto}/app/MainActivity.java | 6 ++-- .../mew => pub/ditto}/app/NostrPoller.java | 18 ++++++------ .../ditto}/app/NotificationRelayService.java | 28 +++++++++---------- .../{ic_stat_mew.xml => ic_stat_ditto.xml} | 2 +- .../main/res/drawable/splash_icon_vector.xml | 2 +- android/app/src/main/res/values/strings.xml | 8 +++--- capacitor.config.ts | 6 ++-- index.html | 20 ++++++------- package-lock.json | 20 ------------- public/manifest.webmanifest | 4 +-- scripts/build-apk.sh | 12 ++++---- src/App.tsx | 2 +- src/components/AdvancedSettings.tsx | 4 +-- src/components/ContentSettings.tsx | 22 +++++++-------- src/components/{MewLogo.tsx => DittoLogo.tsx} | 8 +++--- src/components/Feed.tsx | 8 +++--- src/components/InitialSyncGate.tsx | 16 +++++------ src/components/LeftSidebar.tsx | 6 ++-- src/components/LiveStreamPage.tsx | 2 +- src/components/MobileDrawer.tsx | 2 +- src/components/MobileTopBar.tsx | 6 ++-- src/components/auth/LoginDialog.tsx | 2 +- src/hooks/useEncryptedSettings.ts | 4 +-- src/hooks/useFeed.ts | 4 +-- src/hooks/useFollowActions.ts | 2 +- src/hooks/useInitialSync.ts | 6 ++-- src/hooks/useMuteList.ts | 2 +- src/hooks/useNativeNotifications.ts | 10 +++---- src/pages/BookmarksPage.tsx | 2 +- src/pages/DomainFeedPage.tsx | 2 +- src/pages/HashtagPage.tsx | 2 +- src/pages/Index.tsx | 2 +- src/pages/KindFeedPage.tsx | 2 +- src/pages/NotFound.tsx | 2 +- src/pages/NotificationsPage.tsx | 2 +- src/pages/PlaceholderPage.tsx | 2 +- src/pages/PostDetailPage.tsx | 7 ++--- src/pages/ProfilePage.tsx | 2 +- src/pages/SearchPage.tsx | 2 +- src/pages/SettingsPage.tsx | 4 +-- src/pages/StreamsFeedPage.tsx | 2 +- src/test/TestApp.tsx | 2 +- 44 files changed, 130 insertions(+), 151 deletions(-) rename android/app/src/main/java/{com/mew/app/MewNotificationPlugin.java => pub/ditto/app/DittoNotificationPlugin.java} (85%) rename android/app/src/main/java/{com/mew => pub/ditto}/app/MainActivity.java (92%) rename android/app/src/main/java/{com/mew => pub/ditto}/app/NostrPoller.java (97%) rename android/app/src/main/java/{com/mew => pub/ditto}/app/NotificationRelayService.java (95%) rename android/app/src/main/res/drawable/{ic_stat_mew.xml => ic_stat_ditto.xml} (98%) rename src/components/{MewLogo.tsx => DittoLogo.tsx} (78%) diff --git a/android/app/build.gradle b/android/app/build.gradle index 7baa0009..ebef8e26 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,10 +7,10 @@ if (keystorePropertiesFile.exists()) { } android { - namespace = "com.mew.app" + namespace = "pub.ditto.app" compileSdk = rootProject.ext.compileSdkVersion defaultConfig { - applicationId "com.mew.app" + applicationId "pub.ditto.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 20260222 diff --git a/android/app/src/main/java/com/mew/app/MewNotificationPlugin.java b/android/app/src/main/java/pub/ditto/app/DittoNotificationPlugin.java similarity index 85% rename from android/app/src/main/java/com/mew/app/MewNotificationPlugin.java rename to android/app/src/main/java/pub/ditto/app/DittoNotificationPlugin.java index 5244f540..8abcb2c4 100644 --- a/android/app/src/main/java/com/mew/app/MewNotificationPlugin.java +++ b/android/app/src/main/java/pub/ditto/app/DittoNotificationPlugin.java @@ -1,4 +1,4 @@ -package com.mew.app; +package pub.ditto.app; import android.content.SharedPreferences; import android.content.Context; @@ -15,11 +15,11 @@ import org.json.JSONArray; * Capacitor plugin that allows the JS layer to configure the native * notification polling service with the user's pubkey and relay URLs. */ -@CapacitorPlugin(name = "MewNotification") -public class MewNotificationPlugin extends Plugin { +@CapacitorPlugin(name = "DittoNotification") +public class DittoNotificationPlugin extends Plugin { - private static final String TAG = "MewNotificationPlugin"; - private static final String PREFS_NAME = "mew_notification_config"; + private static final String TAG = "DittoNotificationPlugin"; + private static final String PREFS_NAME = "ditto_notification_config"; @PluginMethod public void configure(PluginCall call) { diff --git a/android/app/src/main/java/com/mew/app/MainActivity.java b/android/app/src/main/java/pub/ditto/app/MainActivity.java similarity index 92% rename from android/app/src/main/java/com/mew/app/MainActivity.java rename to android/app/src/main/java/pub/ditto/app/MainActivity.java index 3285017f..f7dd8827 100644 --- a/android/app/src/main/java/com/mew/app/MainActivity.java +++ b/android/app/src/main/java/pub/ditto/app/MainActivity.java @@ -1,4 +1,4 @@ -package com.mew.app; +package pub.ditto.app; import android.content.Intent; import android.net.Uri; @@ -12,7 +12,7 @@ public class MainActivity extends BridgeActivity { @Override protected void onCreate(Bundle savedInstanceState) { // Register the native notification config plugin before super.onCreate - registerPlugin(MewNotificationPlugin.class); + registerPlugin(DittoNotificationPlugin.class); super.onCreate(savedInstanceState); @@ -42,7 +42,7 @@ public class MainActivity extends BridgeActivity { private void handleNotificationIntent(Intent intent) { if (intent == null) return; Uri data = intent.getData(); - if (data != null && "mew.app".equals(data.getHost())) { + if (data != null && "ditto.pub".equals(data.getHost())) { String path = data.getPath(); if (path != null && !path.isEmpty()) { // Wait for WebView to be ready, then navigate diff --git a/android/app/src/main/java/com/mew/app/NostrPoller.java b/android/app/src/main/java/pub/ditto/app/NostrPoller.java similarity index 97% rename from android/app/src/main/java/com/mew/app/NostrPoller.java rename to android/app/src/main/java/pub/ditto/app/NostrPoller.java index 1202f480..87ed617b 100644 --- a/android/app/src/main/java/com/mew/app/NostrPoller.java +++ b/android/app/src/main/java/pub/ditto/app/NostrPoller.java @@ -1,4 +1,4 @@ -package com.mew.app; +package pub.ditto.app; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -38,10 +38,10 @@ import okhttp3.WebSocketListener; public class NostrPoller { private static final String TAG = "NostrPoller"; - private static final String PREFS_NAME = "mew_notifications"; - private static final String META_PREFS_NAME = "mew_metadata_cache"; + private static final String PREFS_NAME = "ditto_notifications"; + private static final String META_PREFS_NAME = "ditto_metadata_cache"; private static final String KEY_LAST_SEEN = "nostr:notification-last-seen"; - private static final String CHANNEL_ID = "mew_notifications"; + private static final String CHANNEL_ID = "ditto_notifications"; private static final int MAX_NOTIFICATION_ID = 2147483646; private final Context context; @@ -70,7 +70,7 @@ public class NostrPoller { String action = kindToAction(event); showNotification( hashId(event.optString("id")), - "Mew", + "Ditto", displayName + " " + action ); @@ -119,7 +119,7 @@ public class NostrPoller { if (filtered.size() > 3) { showNotification( hashId(filtered.get(0).optString("id") + "-summary"), - "Mew", + "Ditto", "You have " + filtered.size() + " new notifications" ); } else { @@ -130,7 +130,7 @@ public class NostrPoller { String action = kindToAction(event); showNotification( hashId(event.optString("id")), - "Mew", + "Ditto", displayName + " " + action ); } @@ -384,7 +384,7 @@ public class NostrPoller { if (manager == null) return; Intent intent = new Intent(context, MainActivity.class); - intent.setData(Uri.parse("https://mew.app/notifications")); + intent.setData(Uri.parse("https://ditto.pub/notifications")); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity( context, id, intent, @@ -394,7 +394,7 @@ public class NostrPoller { NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID) .setContentTitle(title) .setContentText(body) - .setSmallIcon(R.drawable.ic_stat_mew) + .setSmallIcon(R.drawable.ic_stat_ditto) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setAutoCancel(true); diff --git a/android/app/src/main/java/com/mew/app/NotificationRelayService.java b/android/app/src/main/java/pub/ditto/app/NotificationRelayService.java similarity index 95% rename from android/app/src/main/java/com/mew/app/NotificationRelayService.java rename to android/app/src/main/java/pub/ditto/app/NotificationRelayService.java index 125d65e0..4951bfc5 100644 --- a/android/app/src/main/java/com/mew/app/NotificationRelayService.java +++ b/android/app/src/main/java/pub/ditto/app/NotificationRelayService.java @@ -1,4 +1,4 @@ -package com.mew.app; +package pub.ditto.app; import android.app.AlarmManager; import android.app.Notification; @@ -44,7 +44,7 @@ import okhttp3.WebSocketListener; * for real-time notification delivery. * * Battery strategy: - * - NO permanent WakeLock — the CPU is allowed to sleep between events + * - NO permanent WakeLock -- the CPU is allowed to sleep between events * - OkHttp pings are disabled (the OS will kill idle TCP in Doze anyway) * - An AlarmManager alarm fires every ~8 minutes using setAndAllowWhileIdle(), * which penetrates Doze maintenance windows @@ -70,9 +70,9 @@ import okhttp3.WebSocketListener; public class NotificationRelayService extends Service { private static final String TAG = "NotificationRelaySvc"; - private static final String CHANNEL_ID = "mew_background_service"; + private static final String CHANNEL_ID = "ditto_background_service"; private static final int NOTIFICATION_ID = 1; - private static final String PREFS_NAME = "mew_notification_config"; + private static final String PREFS_NAME = "ditto_notification_config"; // Keepalive alarm fires every 8 minutes. setAndAllowWhileIdle() has a minimum // enforcement interval of ~9 minutes in Doze, so 8 min is effectively the most @@ -83,7 +83,7 @@ public class NotificationRelayService extends Service { // How long to hold a WakeLock for the keepalive ping + potential reconnect private static final long KEEPALIVE_WAKELOCK_TIMEOUT_MS = 15_000; - private static final String ACTION_KEEPALIVE = "com.mew.app.ACTION_KEEPALIVE"; + private static final String ACTION_KEEPALIVE = "pub.ditto.app.ACTION_KEEPALIVE"; // Backoff bounds private static final long INITIAL_BACKOFF_MS = 1_000; @@ -126,22 +126,22 @@ public class NotificationRelayService extends Service { // The system requires startForeground() within 5 seconds of startForegroundService(). startForeground(NOTIFICATION_ID, buildForegroundNotification()); - // No ping interval — we handle keepalive ourselves via AlarmManager. + // No ping interval -- we handle keepalive ourselves via AlarmManager. // OkHttp's built-in pings would only work while the CPU is awake anyway. httpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(0, TimeUnit.SECONDS) // No read timeout for persistent connection .writeTimeout(5, TimeUnit.SECONDS) - .pingInterval(0, TimeUnit.SECONDS) // Disabled — we use AlarmManager keepalive + .pingInterval(0, TimeUnit.SECONDS) // Disabled -- we use AlarmManager keepalive .build(); poller = new NostrPoller(this); alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - // Create a WakeLock for keepalive pings — always acquired with a timeout, + // Create a WakeLock for keepalive pings -- always acquired with a timeout, // never held indefinitely. PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - keepaliveWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mew:keepalive-ping"); + keepaliveWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ditto:keepalive-ping"); keepaliveWakeLock.setReferenceCounted(false); registerKeepaliveReceiver(); @@ -199,7 +199,7 @@ public class NotificationRelayService extends Service { backoffMs = INITIAL_BACKOFF_MS; connectIfConfigured(); } else { - // Connection exists — check if last ping got a pong. + // Connection exists -- check if last ping got a pong. // If we sent a ping last cycle and never got a pong, the connection // is likely dead (TCP half-open after Doze). if (lastPingSentTimestamp > 0 && lastPongTimestamp < lastPingSentTimestamp) { @@ -208,7 +208,7 @@ public class NotificationRelayService extends Service { backoffMs = INITIAL_BACKOFF_MS; connectIfConfigured(); } else { - // Connection seems alive — send a ping for the next cycle to verify. + // Connection seems alive -- send a ping for the next cycle to verify. // OkHttp WebSocket handles ping/pong at the protocol level when we // call send() or when the server sends data, but we need an explicit // application-level check since OkHttp's built-in pings are disabled. @@ -578,9 +578,9 @@ public class NotificationRelayService extends Service { ); return new NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle("Mew") + .setContentTitle("Ditto") .setContentText("Connected for notifications") - .setSmallIcon(R.drawable.ic_stat_mew) + .setSmallIcon(R.drawable.ic_stat_ditto) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_LOW) .setOngoing(true) @@ -595,7 +595,7 @@ public class NotificationRelayService extends Service { "Background Connection", NotificationManager.IMPORTANCE_LOW ); - channel.setDescription("Keeps Mew connected for instant notifications"); + channel.setDescription("Keeps Ditto connected for instant notifications"); channel.setShowBadge(false); NotificationManager manager = getSystemService(NotificationManager.class); diff --git a/android/app/src/main/res/drawable/ic_stat_mew.xml b/android/app/src/main/res/drawable/ic_stat_ditto.xml similarity index 98% rename from android/app/src/main/res/drawable/ic_stat_mew.xml rename to android/app/src/main/res/drawable/ic_stat_ditto.xml index e1bc926c..6fd6c60e 100644 --- a/android/app/src/main/res/drawable/ic_stat_mew.xml +++ b/android/app/src/main/res/drawable/ic_stat_ditto.xml @@ -4,7 +4,7 @@ android:viewportWidth="1024" android:viewportHeight="1024"> - + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b961a136..bd99949f 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ - mew - mew - com.mew.app - com.mew.app + ditto + ditto + pub.ditto.app + pub.ditto.app diff --git a/capacitor.config.ts b/capacitor.config.ts index 1bf96e9d..c4393e96 100644 --- a/capacitor.config.ts +++ b/capacitor.config.ts @@ -1,12 +1,12 @@ import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { - appId: 'com.mew.app', - appName: 'mew', + appId: 'pub.ditto.app', + appName: 'ditto', webDir: 'dist', server: { // Handle deep links from your domain - hostname: 'mew.app', + hostname: 'ditto.pub', androidScheme: 'https' }, android: { diff --git a/index.html b/index.html index 2c170657..871b192f 100644 --- a/index.html +++ b/index.html @@ -1,33 +1,33 @@ - Mew — A Nostr Client + Ditto — A Nostr Client - + - + - + - - + + - + - + - +