94 lines
4.4 KiB
Markdown
94 lines
4.4 KiB
Markdown
# Please Pay Me — Flutter mobile
|
||
|
||
Мобильный кабинет для бюджета «от зарплаты до зарплаты». UI построен на
|
||
iOS-ките (Cupertino, Apple HIG), данные — REST API из `src/PleasePayMe.Api`.
|
||
|
||
## Запуск
|
||
|
||
```powershell
|
||
cd mobile
|
||
copy .env.example .env # один раз
|
||
# отредактируй PPM_API_BASE_URL в .env
|
||
.\run.ps1
|
||
.\run.ps1 -Device chrome
|
||
.\run.ps1 -Target widgetbook
|
||
```
|
||
|
||
`run.ps1` читает `mobile/.env` и передаёт его во Flutter как
|
||
`--dart-define-from-file`. Если файла нет — копирует `.env.example`.
|
||
|
||
```bash
|
||
export PATH="$HOME/flutter/bin:$PATH"
|
||
cd mobile
|
||
flutter pub get
|
||
flutter run -d windows --dart-define-from-file=.env
|
||
```
|
||
|
||
### Конфигурация (`mobile/.env`)
|
||
|
||
| Переменная | Назначение |
|
||
| --- | --- |
|
||
| `PPM_API_BASE_URL` | адрес API; если пусто — `https://please-pay-me.ru` |
|
||
| `PPM_WEB_URL` | веб-кабинет; если пусто — тот же origin, что API |
|
||
| `PPM_DEMO` | `true` — принудительный in-memory backend |
|
||
|
||
### Авторизация
|
||
|
||
На iOS и Android:
|
||
|
||
- **Telegram** — кабинет в WebView, JWT забирается из
|
||
`localStorage.ppm_session_jwt` через канал `PpmAuth`.
|
||
- **Яндекс** — WebView на `oauth.yandex.ru`. Код возвращается на
|
||
`{кабинет}/` (как Callback URL в кабинете Яндекса), приложение шлёт
|
||
его в `POST /api/auth/yandex`. Client secret живёт только на API.
|
||
|
||
Приложение проверяет JWT через `GET /api/me` и кладёт сессию в
|
||
`SharedPreferences`.
|
||
|
||
На Windows / в браузере WebView нет: остаётся ручной ввод токена. Альтернатива
|
||
на любой платформе — «Демо-режим» (`DemoBackend` без сети).
|
||
|
||
В кабинете Яндекса должен быть Redirect URI `https://<домен>/`
|
||
(для продакшена — `https://please-pay-me.ru/`). BotFather → `/setdomain`
|
||
для Telegram — тот же домен.
|
||
|
||
## Экраны
|
||
|
||
| Вкладка | Что делает |
|
||
| --- | --- |
|
||
| Обзор | остаток бюджета, дневной лимит, трата в один тап, ближайшая выплата |
|
||
| Журнал | операции по дням, фильтр «текущий / все бюджеты», подгрузка страниц |
|
||
| Бюджеты | выбор активного конверта, создание, редактирование, архив, удаление |
|
||
| Работа | оклад, дни выплат, правило выходных, график ближайших зарплат |
|
||
| Профиль | пользователь, режим подключения, выход |
|
||
|
||
## Структура
|
||
|
||
```
|
||
mobile/
|
||
├── lib/
|
||
│ ├── app/ # CupertinoApp, session gate, таб-бар
|
||
│ ├── core/ # config, storage, AsyncValue, форматтеры
|
||
│ ├── data/
|
||
│ │ ├── api/ # ApiClient (http + маппинг ошибок)
|
||
│ │ ├── models/ # Budget/Expense/Job/AuthUser + JSON-хелперы
|
||
│ │ ├── repositories/ # интерфейсы + REST-реализации
|
||
│ │ └── demo/ # in-memory backend (превью, тесты, демо-режим)
|
||
│ ├── features/ # overview / journal / budgets / work / profile / auth
|
||
│ ├── theme/ # токены Apple HIG + CupertinoThemeData
|
||
│ └── ui/ # дизайн-система (atoms → molecules → navigation)
|
||
└── widgetbook/ # переносимый каталог компонентов и экранов
|
||
```
|
||
|
||
Слои связаны через интерфейсы репозиториев: экраны знают только
|
||
`BudgetRepository` / `ExpenseRepository` / `JobRepository`, а `SessionController`
|
||
подставляет REST- или demo-реализацию. Поэтому и Widgetbook, и виджет-тесты
|
||
гоняют настоящие экраны без сети.
|
||
|
||
## Тесты
|
||
|
||
```bash
|
||
cd mobile && flutter test # модели, форматтеры, контроллеры, сквозной сценарий
|
||
cd mobile/widgetbook && flutter test # рендер каждого use-case в light и dark
|
||
```
|