# 通知設計書 42-ファイルパスコピー完了

## 概要

本ドキュメントは、ファイルパスをクリップボードにコピーした際にユーザーに表示される完了通知の設計を定義します。

### 本通知の処理概要

この通知は、ブラウザ版のActualでノートに含まれるファイルパスリンクをクリックした際に、ファイルパスがクリップボードにコピーされたことをユーザーに知らせるメッセージ通知です。Electron版ではファイルマネージャーで直接開く機能が利用できますが、ブラウザ版ではセキュリティ制約によりファイルシステムへの直接アクセスができないため、代替手段としてクリップボードへのコピーを行います。

**業務上の目的・背景**：Actualは取引やカテゴリにノートを追加でき、そのノートにはファイルパスへのリンクを含めることができます。ユーザーが領収書や契約書などの関連ドキュメントへの参照を記録する際に使用されます。ブラウザ版では直接ファイルを開けないため、パスをコピーしてファイルエクスプローラーに貼り付ける運用をサポートします。

**通知の送信タイミング**：ブラウザ版でノート内のファイルパスリンクをクリックし、クリップボードへのコピーが成功した時点で即座に通知が表示されます。

**通知の受信者**：ファイルパスリンクをクリックした現在のユーザー（ローカル通知）

**通知内容の概要**：「File path copied to clipboard」（ファイルパスがクリップボードにコピーされました）という簡潔な成功メッセージを表示します。

**期待されるアクション**：ユーザーは通知を確認後、OSのファイルエクスプローラーを開き、コピーしたパスを貼り付けて対象ファイルにアクセスできます。

## 通知種別

アプリ内通知（トースト通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（Redux dispatch） |
| 優先度 | 低 |
| リトライ | 無し |

### 送信先決定ロジック

現在操作中のユーザーのみに対してローカル通知を表示します。ブラウザ版でのみ発行され、Electron版ではファイルマネージャーで直接開くため通知は発行されません。

## 通知テンプレート

### アプリ内通知の場合

| 項目 | 内容 |
|-----|------|
| 通知タイプ | message |
| タイトル | なし |
| メッセージ | File path copied to clipboard |
| タイムアウト | 6.5秒（デフォルト） |

### 本文テンプレート

```
File path copied to clipboard
```

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| なし | 固定メッセージのため変数なし | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | ファイルパスリンククリック | isFilePath === true かつ !isElectron() | ブラウザ版でのファイルパスクリック時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Electron版の場合 | ファイルマネージャーで直接開くため通知不要 |
| URLリンクの場合 | ファイルパスではなくURLの場合は通知しない |
| クリップボードコピー失敗時 | エラーが発生した場合は通知しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[リンクをクリック] --> B{isFilePath?}
    B -->|No| C[URLをブラウザで開く]
    B -->|Yes| D{isElectron?}
    D -->|Yes| E[ファイルマネージャーで開く]
    D -->|No| F[navigator.clipboard.writeText]
    F --> G{コピー成功?}
    G -->|Yes| H[addNotification dispatch]
    G -->|No| I[エラー処理]
    H --> J[通知表示]
    J --> K[6.5秒後自動消去]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | クライアント側のみの処理 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | 通知はクライアント側のRedux storeのみ更新 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| クリップボードアクセス拒否 | ブラウザの権限設定 | 通知せず処理終了 |
| 通知表示失敗 | Redux storeの異常 | 処理継続（コピー自体は完了） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回 |
| リトライ間隔 | - |
| リトライ対象エラー | なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（ユーザー操作に即時応答）

## セキュリティ考慮事項

- ローカル通知のためネットワーク経由での情報漏洩リスクなし
- ファイルパス自体はクリップボードにコピーされるが、通知メッセージにはパスを含めない
- navigator.clipboard APIはHTTPS環境またはlocalhostでのみ動作

## 備考

- ブラウザ版専用の通知であり、Electron版では発行されない
- `isElectron()`関数で実行環境を判定している
- ノートのリンクパーサーがファイルパスとURLを区別して処理する

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示します。

### 推奨読解順序

#### Step 1: データ構造を理解する

通知の型定義を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notificationsSlice.ts | `packages/desktop-client/src/notifications/notificationsSlice.ts` | Notification型の定義（9-26行目） |

#### Step 2: エントリーポイントを理解する

通知を発行するコンポーネントを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DesktopLinkedNotes.tsx | `packages/desktop-client/src/notes/DesktopLinkedNotes.tsx` | handleClick関数（36-57行目）、環境判定ロジック（38行目） |

**主要処理フロー**:
1. **38行目**: `isElectron()`で実行環境を判定
2. **40行目**: Electron版の場合は`window.Actual?.openInFileManager(url)`を呼び出し
3. **43行目**: ブラウザ版の場合は`navigator.clipboard.writeText(url)`を呼び出し
4. **44-50行目**: コピー成功時に`addNotification`をdispatch

#### Step 3: 環境判定ロジックを理解する

Electron判定の仕組みを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | environment.ts | `packages/loot-core/src/shared/environment.ts` | isElectron関数の実装 |

### プログラム呼び出し階層図

```
DesktopLinkedNotes.tsx
    │
    ├─ handleClick()
    │      │
    │      ├─ isElectron() 判定
    │      │      │
    │      │      ├─ true: window.Actual?.openInFileManager(url)
    │      │      │
    │      │      └─ false: navigator.clipboard.writeText(url)
    │      │              │
    │      │              └─ dispatch(addNotification({...}))
    │      │                      │
    │      │                      └─ notificationsSlice.reducer
    │      │
    │      └─ Notifications コンポーネント
    │             └─ 通知を画面に描画
```

### データフロー図

```
[入力]                    [処理]                         [出力]

ファイルパスリンク ───▶ handleClick() ───────────────▶ クリップボード
     クリック                │                              │
                             ├─ isFilePath判定               │
                             ├─ isElectron判定               │
                             │                              ▼
                             └─ clipboard.writeText() ───▶ Redux Store更新
                                     │                       │
                                     ▼                       ▼
                               addNotification ───────▶ Notifications.tsx
                                                            │
                                                            ▼
                                                      トースト表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DesktopLinkedNotes.tsx | `packages/desktop-client/src/notes/DesktopLinkedNotes.tsx` | ソース | デスクトップ版リンク付きノートコンポーネント |
| MobileLinkedNotes.tsx | `packages/desktop-client/src/notes/MobileLinkedNotes.tsx` | ソース | モバイル版リンク付きノートコンポーネント |
| notificationsSlice.ts | `packages/desktop-client/src/notifications/notificationsSlice.ts` | ソース | 通知の状態管理（Redux slice） |
| Notifications.tsx | `packages/desktop-client/src/components/Notifications.tsx` | ソース | 通知表示コンポーネント |
| linkParser.ts | `packages/desktop-client/src/notes/linkParser.ts` | ソース | リンクパーサー（ファイルパス/URL判定） |
| environment.ts | `packages/loot-core/src/shared/environment.ts` | ソース | 実行環境判定ユーティリティ |
