Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a6524cf
chore: drizzleとsqliteを導入
273Do May 5, 2026
df85d7c
feat: dbスキーマを実装
273Do May 5, 2026
d3b7088
feat: dbマイグレーションを実行
273Do May 5, 2026
8fb169f
feat: drizzle providerを実装
273Do May 6, 2026
049584b
chore: claude initを更新
273Do May 6, 2026
81c4836
feat: expo-drizzle-studio-pluginを導入
273Do May 6, 2026
521a233
refactor: db providerのリファクタ
273Do May 6, 2026
e8bd3a9
refactor: db providerのリファクタ
273Do May 6, 2026
29774e9
feat: ジャーナルを取得するクエリを実装
273Do May 6, 2026
2ef170d
feat: ジャーナルを作成する関数を実装
273Do May 6, 2026
7a50df6
refactor: ディレクトリ構成を変更
273Do May 6, 2026
717c8c5
feat: dbからjournal一覧を取得する処理を追加
273Do May 6, 2026
4ae2e05
fix: 軽微な修正
273Do May 6, 2026
db4d607
feat: entry一覧を取得して表示する処理を実装
273Do May 6, 2026
3fbb363
refactor: エントリー取得処理や型を別ファイルに移動
273Do May 6, 2026
c7ac630
fix: 型の呼び出しを修正
273Do May 6, 2026
67e7d83
fix: 型の呼び出しを修正
273Do May 6, 2026
6fc82a7
fix: エントリー詳細画面のヘッダーを修正
273Do May 7, 2026
f8d2008
refactor: propsコメントを修正
273Do May 7, 2026
cdd5ce0
refactor: 型とpropsをリファクタ
273Do May 7, 2026
b292d21
feat: エントリー一覧を表示
273Do May 7, 2026
f35b6fa
feat: 不要な型を削除
273Do May 7, 2026
ec61463
feat: 日付フォーマット関数に数値を受け付けるよう変更
273Do May 8, 2026
e24c00b
refactor: 関数に返り値の型を追記
273Do May 8, 2026
0c9a8e0
feat: ジャーナル作成後戻るボタンでジャーナル一覧に戻れるよう実装
273Do May 8, 2026
14b9cf5
chore: ビルドスクリプトの実行を許可する設定を追加
273Do May 8, 2026
6188734
chore: ciのpnpmのバージョンをローカルと同様のものに変更
273Do May 8, 2026
a199ad6
chore: ローカルとciのpnpmのバージョンを11.0.0に更新
273Do May 8, 2026
a732b49
chore: CIのversionを削除してpackageManagerに一本化する
273Do May 8, 2026
dc99d0d
fix: SFSymbolの呼び出し元を修正
273Do May 8, 2026
1530198
chore: lefthookに型チェックを追加
273Do May 8, 2026
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: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ jobs:
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
version: latest
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
Expand Down
71 changes: 37 additions & 34 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
# Nicky - Journaling App
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Nicky is a React Native journaling app built with Expo and Expo Router. It features native iOS UI via `@expo/ui/swift-ui` (SwiftUI components), native tab navigation, and file-based routing.
Nicky is a React Native journaling app built with Expo and Expo Router. It features native iOS UI via `@expo/ui/swift-ui` (SwiftUI components), native tab navigation, and SQLite persistence via Drizzle ORM.

## Development Commands

```bash
pnpm expo run:ios # Build and run on iOS simulator
pnpm expo start # Start Expo dev server
pnpm lint # Run ESLint
pnpm lint-fix # Run ESLint with auto-fix
pnpm expo run:ios # Build and run on iOS simulator
pnpm expo start --clear # Start Expo dev server (clear cache)
pnpm lint # Run ESLint
pnpm lint-fix # Run ESLint with auto-fix
pnpm typecheck # TypeScript type check
pnpm drizzle-kit generate # Generate migration files from schema
```

## Commit Convention
Expand All @@ -30,7 +34,7 @@ chore: tooling / config changes

```
src/app/
_layout.tsx # NativeTabs (root) — tab bar always visible
_layout.tsx # Root — wraps everything in DrizzleProvider + NativeTabs
(journal)/
_layout.tsx # Stack — scoped to journal tab
index.tsx # Journal list /
Expand All @@ -45,46 +49,46 @@ src/app/

**Key rule:** `NativeTabs` is the root navigator; `Stack` lives inside `(journal)` group. This keeps the tab bar visible when pushing screens.

### Directory Structure
### Database Layer

```
src/
app/ # Routes (Expo Router file-based)
components/
app-tabs.tsx # NativeTabs definition
journal/ # Journal-related components
journal-view.tsx # Grid list of journal cards
journal-card.tsx # Tappable gradient card
journal-create-view.tsx
entry/ # Entry-related components
entry-list-view.tsx # List grouped by month
entry-row.tsx # Date + title + preview row
mocks/
journals.ts # JournalObj type + JOURNALS array
entries.ts # Entry type + ENTRIES array
constants/
hooks/
```
Drizzle ORM + expo-sqlite. Schema is split by domain in `src/db/schemas/`:

| File | Tables |
|---|---|
| `journals.ts` | `journals` |
| `fields.ts` | `fields` (field definitions per journal) |
| `entries.ts` | `entries`, `entry_values` |

`src/db/schemas/index.ts` re-exports all schemas. `src/components/drizzle-provider.tsx` opens the DB, runs migrations via `useMigrations`, and exports `db`.

**Adding a schema change:** edit the relevant schema file → `pnpm drizzle-kit generate` → commit the generated files in `drizzle/`.

**Drizzle config notes:**
- Schema files must not import React Native packages (`expo-crypto`, `expo-symbols` runtime imports) — drizzle-kit runs in Node.js. Use `import type` for RN types.
- `$defaultFn` with `Crypto.randomUUID()` cannot be used in schema — generate IDs at the application layer instead.

### Key Technologies

| Package | Usage |
|---|---|
| `expo-router` | File-based routing, `useRouter`, `useLocalSearchParams` |
| `@expo/ui/swift-ui` | SwiftUI components: `Host`, `ZStack`, `VStack`, `Grid`, `ScrollView`, `List`, `Section`, `Button`, `Image`, `Text`, `RoundedRectangle` |
| `@expo/ui/swift-ui/modifiers` | `frame`, `padding`, `font`, `foregroundStyle`, `onTapGesture`, `clipShape`, `lineLimit`, `headerProminence`, `listStyle` |
| `@expo/ui/swift-ui` | SwiftUI components: `Host`, `ZStack`, `VStack`, `Grid`, `ScrollView`, `List`, `Section`, `Button`, `Image`, `Text`, `RoundedRectangle`, `ColorPicker`, `BottomSheet` |
| `@expo/ui/swift-ui/modifiers` | `frame`, `padding`, `foregroundStyle`, `onTapGesture`, `listStyle`, `presentationDetents`, `environment`, `fixedSize` |
| `expo-router/unstable-native-tabs` | `NativeTabs` — iOS native tab bar |
| `expo-symbols` | `SymbolView` — SF Symbols in RN header components |
| `expo-symbols` | `SymbolView` — SF Symbols in RN (non-SwiftUI) header components |
| `expo-sqlite` + `drizzle-orm` | Local SQLite persistence |
| `expo-crypto` | `Crypto.randomUUID()` for ID generation at the app layer |
| `PlatformColor` | Adaptive system colors: `"label"`, `"systemBackground"`, `"systemIndigo"` |

### SwiftUI Component Rules

- Always wrap SwiftUI components in `<Host>` with `useViewportSizeMeasurement`
- Use `onTapGesture` modifier for taps — `onPress` prop does NOT work on layout components
- Gradients: `RoundedRectangle` + `foregroundStyle({ type: "linearGradient", ... })` + `clipShape` on parent `ZStack`
- Adaptive colors: use `PlatformColor("label")` directly — no need for `useColorScheme`
- Secondary text: `foregroundStyle({ type: "hierarchical", style: "secondary" })`
- `Button` inside `List` gets a blue tint by default — use `foregroundStyle({ type: "hierarchical", style: "primary" })` to keep the tap highlight without the blue color
- `List` manages its own scrolling — never nest it inside `ScrollView`
- Adaptive colors: use `PlatformColor("label")` directly — no need for `useColorScheme`
- `fixedSize()` on a component prevents it from stretching in an HStack, allowing siblings to fill remaining space
- Gradients: `RoundedRectangle` + `foregroundStyle({ type: "linearGradient", ... })` + `clipShape` on parent `ZStack`

### Naming Conventions

Expand All @@ -96,8 +100,7 @@ src/
## Code Style

- **Import order:** external → internal (`@/`) → relative; always separated by newlines
- **Unused imports:** auto-enforced by ESLint (`eslint-plugin-unused-imports`)
- **Path alias:** `@/*` → `src/*`
- **TypeScript:** strict mode enabled
- **Formatter:** Prettier (enforced via ESLint)
- **React Compiler:** enabled (`reactCompiler: true` in app.json)
- **React Compiler:** enabled — do not manually add `useMemo`/`useCallback` unless there is a specific reason
3 changes: 2 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"imageWidth": 76
}
}
]
],
"expo-sqlite"
],
"experiments": {
"typedRoutes": true,
Expand Down
8 changes: 8 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('@babel/core').TransformOptions} */
module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [["inline-import", { extensions: [".sql"] }]],
};
};
8 changes: 8 additions & 0 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Config } from "drizzle-kit";

export default {
dialect: "sqlite",
driver: "expo",
schema: "./src/db/schemas",
out: "./drizzle",
} satisfies Config;
36 changes: 36 additions & 0 deletions drizzle/0000_young_bulldozer.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
CREATE TABLE `entries` (
`id` text PRIMARY KEY NOT NULL,
`journalId` text NOT NULL,
`bookmark` integer DEFAULT false NOT NULL,
`createdAt` integer NOT NULL,
`updatedAt` integer NOT NULL,
FOREIGN KEY (`journalId`) REFERENCES `journals`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `entry_values` (
`id` text PRIMARY KEY NOT NULL,
`entryId` text NOT NULL,
`fieldId` text NOT NULL,
`value` text,
FOREIGN KEY (`entryId`) REFERENCES `entries`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`fieldId`) REFERENCES `fields`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `entry_field_unique` ON `entry_values` (`entryId`,`fieldId`);--> statement-breakpoint
CREATE TABLE `fields` (
`id` text PRIMARY KEY NOT NULL,
`journalId` text NOT NULL,
`type` text NOT NULL,
`label` text NOT NULL,
`sortOrder` integer NOT NULL,
FOREIGN KEY (`journalId`) REFERENCES `journals`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `journals` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`icon` text NOT NULL,
`color` text NOT NULL,
`createdAt` integer NOT NULL,
`updatedAt` integer NOT NULL
);
Loading
Loading