Files
2026-09-21 04:06:43 +03:00

4.4 KiB
Raw Permalink Blame History

Please Pay Me — Flutter mobile

Мобильный кабинет для бюджета «от зарплаты до зарплаты». UI построен на iOS-ките (Cupertino, Apple HIG), данные — REST API из src/PleasePayMe.Api.

Запуск

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.

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, и виджет-тесты гоняют настоящие экраны без сети.

Тесты

cd mobile && flutter test              # модели, форматтеры, контроллеры, сквозной сценарий
cd mobile/widgetbook && flutter test   # рендер каждого use-case в light и dark