# Shared UI Components Analysis

> **Superseded (2026-05-30).** This was the original feasibility analysis that
> motivated the `commons` module (then called "shared-ui"). The module now
> exists and is documented authoritatively in **`commons/ARCHITECTURE.md`**,
> which defines the package taxonomy, the CLI-safe / UI boundary, and the
> "where does my code go?" guide. Keep this file only as historical context.

Analysis of Android vs Desktop UI to identify reusable Compose Multiplatform components.

## Executive Summary

| Category | Android Files | Shareable As-Is | Needs Abstraction |
|----------|---------------|-----------------|-------------------|
| Formatters | 4 | 2 | 2 |
| Theme | 4 | 4 | 0 |
| Note Components | 35 | ~10 | ~25 |
| Total Potential | ~450+ | ~100+ | ~200+ |

## Immediately Shareable Components

These have **zero Android dependencies** and can be moved to a shared module today:

### Formatters (Pure Kotlin)
```
amethyst/ui/note/PubKeyFormatter.kt     -> shared/ui/formatters/
amethyst/ui/note/ZapFormatter.kt        -> shared/ui/formatters/
amethyst/ui/note/ZapFormatterNoDecimals.kt -> shared/ui/formatters/
```

### Theme (Pure Compose)
```
amethyst/ui/theme/Color.kt   -> shared/ui/theme/
amethyst/ui/theme/Shape.kt   -> shared/ui/theme/
amethyst/ui/theme/Type.kt    -> shared/ui/theme/
```

### Layout Primitives
```
amethyst/ui/theme/ButtonBorder (padding values)
amethyst/ui/theme/Size* (dimension constants)
```

## Components Requiring Abstraction

### 1. String Resources (R.string.*)

**Problem:** Android uses `R.string.xxx` for localized strings.

**Solution:** Create `StringProvider` interface:

```kotlin
// commonMain
interface StringProvider {
    fun get(key: String): String
    fun get(key: String, vararg args: Any): String
}

// androidMain
class AndroidStringProvider(private val context: Context) : StringProvider {
    override fun get(key: String) = context.getString(keyToId(key))
}

// jvmMain
class DesktopStringProvider : StringProvider {
    private val strings = mapOf(
        "post_not_found" to "Post not found",
        "now" to "now",
        // ...
    )
    override fun get(key: String) = strings[key] ?: key
}
```

**Affected files:**
- TimeAgoFormatter.kt
- BlankNote.kt
- All UI components with text

### 2. AccountViewModel

**Problem:** Most UI components depend on `AccountViewModel` for:
- User profile data
- Settings (image loading, NSFW, etc.)
- Actions (follow, mute, report)

**Solution:** Create interface in shared module:

```kotlin
// commonMain
interface IAccountState {
    val userPubKey: String
    val userNpub: String
    val settings: IUserSettings
}

interface IUserSettings {
    val showImages: Boolean
    val showSensitiveContent: Boolean
}
```

### 3. Navigation (INav)

**Problem:** Android uses custom `INav` interface tied to Compose Navigation.

**Solution:** Already abstracted! `INav` is an interface - just needs to be in shared module.

### 4. Image Loading

**Problem:** Uses Coil with Android-specific caching.

**Solution:** Coil 3.x supports Compose Multiplatform. Create shared wrapper:

```kotlin
// commonMain
@Composable
expect fun AsyncImage(
    url: String,
    contentDescription: String?,
    modifier: Modifier,
)
```

## Recommended Shared Module Structure

```
shared-ui/
├── src/
│   ├── commonMain/kotlin/
│   │   ├── formatters/
│   │   │   ├── PubKeyFormatter.kt
│   │   │   ├── ZapFormatter.kt
│   │   │   └── TimeAgoFormatter.kt (abstracted)
│   │   ├── theme/
│   │   │   ├── Color.kt
│   │   │   ├── Shape.kt
│   │   │   ├── Type.kt
│   │   │   └── Dimensions.kt
│   │   ├── components/
│   │   │   ├── NoteCard.kt
│   │   │   ├── UserAvatar.kt
│   │   │   └── LoadingIndicator.kt
│   │   └── providers/
│   │       ├── StringProvider.kt
│   │       └── ImageProvider.kt
│   ├── androidMain/kotlin/
│   │   └── providers/
│   │       └── AndroidStringProvider.kt
│   └── jvmMain/kotlin/
│       └── providers/
│           └── DesktopStringProvider.kt
└── build.gradle.kts
```

## Implementation Priority

### Phase 1: Low-Hanging Fruit (1-2 days)
1. Create `shared-ui` module
2. Move pure Kotlin formatters (PubKeyFormatter, ZapFormatter)
3. Move theme colors and dimensions
4. Update both apps to use shared module

### Phase 2: String Abstraction (2-3 days)
1. Create StringProvider interface
2. Implement for Android and Desktop
3. Migrate TimeAgoFormatter
4. Create composable wrappers for common strings

### Phase 3: Core UI Components (1 week)
1. Abstract AccountViewModel to IAccountState
2. Move simple components (BlankNote, LoadingIndicator)
3. Create NoteCard shared component
4. Integrate with both apps

### Phase 4: Complex Components (2+ weeks)
1. Image loading abstraction
2. RichTextViewer
3. ReactionsRow
4. Full NoteCompose migration

## Desktop vs Android Comparison

| Feature | Android | Desktop | Share Strategy |
|---------|---------|---------|----------------|
| Note display | NoteCompose.kt (51KB) | NoteCard in FeedScreen | Extract common layout |
| Time formatting | TimeAgoFormatter.kt | SimpleDateFormat inline | Abstract with interface |
| Key display | PubKeyFormatter.kt | Manual truncation | Use shared formatter |
| Colors | Color.kt | darkColorScheme() | Share Color.kt |
| Navigation | INav + Compose Nav | enum Screen | Abstract navigation |
| Images | Coil + OkHttp | Not implemented | Coil Multiplatform |

## Current Desktop Implementation Gaps

Desktop currently has basic implementations that could benefit from sharing:

1. **NoteCard** - Has inline date formatting, could use TimeAgoFormatter
2. **Key display** - Manual truncation, should use PubKeyFormatter
3. **Theme** - Uses default darkColorScheme, could use shared theme
4. **No image loading** - Needs Coil integration

## Quick Wins

Immediate improvements to desktop using patterns from Android:

```kotlin
// Instead of:
val npub = event.pubKey.take(16) + "..."

// Use shared formatter:
val npub = event.pubKey.toDisplayHexKey()

// Instead of:
val format = SimpleDateFormat("MMM d, HH:mm", Locale.getDefault())

// Use shared formatter:
val timeAgo = timeAgoNoDot(event.createdAt, stringProvider)
```

## Next Steps

1. **Create shared-ui module** in project structure
2. **Move formatters** as proof of concept
3. **Create StringProvider** abstraction
4. **Iterate** on more complex components
