Дай в долг

Мобильное приложение для учёта личных займов — iOS & Android. Expo SDK 54 + React Native + TypeScript.
TypeScript 95,4%
JavaScript 4,6%

Идея

«Дай в долг» решает одну из самых болезненных бытовых проблем: потерянные деньги из-за устных договорённостей о займах. Банковские переводы стираются в истории, мессенджеры не напоминают о сроках, блокноты теряются.

Приложение закрывает весь цикл работы с займом:
  • Создание записи с суммой, сроком, процентом и счётом
  • Автоматическое push-напоминание за 3 дня до срока
  • Отметка возврата с начислением кармы
  • Финансовая репутация пользователя через систему кармы

Все данные хранятся локально на устройстве — никакого сервера, никакой регистрации, никакой передачи данных третьим лицам.

Особенности

🎭 Система персонажей-советников

8 уникальных персонажей со своими характерами, взаимными историческими отношениями и 56 контекстными советами (7 экранов × 8 персонажей). Персонажи образуют единую вселенную с семейными и социальными связями — это превращает финансовый трекер в живой, эмоциональный продукт.

Персонаж

Роль

Связи

Градус

Бизнес-советник (20–35)

Сын Щавеля и Топы, влюблён в Лучу

Фофан

Вечный должник (20–35)

Друг Градуса, должен Луче

Бисер

Советник по стилю (20–35)

Ждёт пока Градус разберётся с долгами

Луча

Советник кармы (20–35)

Дочь Зюйда и Лейлы

Щавель

Семейный наставник (40–60)

Муж Топы, отец Градуса

Топа

Страж района (40–60)

Жена Щавеля, мать Градуса

Зюйд

Финансовый ментор (40–60)

Муж Лейлы, отец Лучи

Лейла

Мастер учёта (40–60)

Жена Зюйда, мать Лучи



⭐ Система кармы (геймификация)


Базовый

Стандарт

Премиум

Цена

Бесплатно

299 ₽/мес

599 ₽/мес

Займов

До 5

Безлимит

Безлимит

История

30 дней

Полная

Полная

PDF-экспорт

Premium ID

Аналитика

Персональная

Стек технологий

Категория

Технология

Фреймворк

Expo SDK 54 / React Native 0.81.5

Роутинг

expo-router ~6.0.17 (файловый, typed routes)

Язык

TypeScript (strict mode, 0 ошибок)

Хранилище

AsyncStorage — без сервера

Уведомления

expo-notifications ~0.29.9

Монетизация

RevenueCat / react-native-purchases

UI

@expo/vector-icons Feather, expo-linear-gradient, expo-blur

Анимации

react-native-reanimated ~4.1.1, Animated API

Жесты

react-native-gesture-handler ~2.28.0

Тактильная отдача

expo-haptics

Шрифты

Inter 400/500/600/700 (expo-google-fonts)

Архитектура

React Context (единый стор)

Компилятор

React Compiler (experimental)

Рендер

New Architecture (Fabric + JSI)

Сборка

EAS Build (dev / preview / production)

Архитектура

app/
_layout.tsx # Root: IAP init, push permission, шрифты
index.tsx # Guard: welcome → tabs
(auth)/welcome.tsx # Онбординг + выбор персонажа
(tabs)/
index.tsx # Главный экран
cabinet.tsx # История займов
karma.tsx # Карма и достижения
profile.tsx # Профиль
loan/
give.tsx # Форма «дать в долг»
take.tsx # Форма «занял»
[id].tsx # Детали займа
premium.tsx # IAP + выбор тарифа
calculator.tsx # Калькулятор → prefill в форму
karma-history.tsx # Лента кармы
notifications.tsx # Центр уведомлений
blacklist.tsx # Чёрный список
friends.tsx # Социальный граф
report.tsx # Финансовый отчёт
agreement.tsx # Пользовательское соглашение
faq.tsx / settings.tsx # FAQ, настройки

components/
LoanCard.tsx # Карточка займа с прогресс-баром
CharacterBubble.tsx # Пузырь персонажа

context/AppContext.tsx # Единый стор: User, Loan, Karma, Notifications
constants/characters.ts # 8 персонажей × 7 советов
constants/colors.ts # Design tokens (light + dark)
services/iap.ts # RevenueCat абстракция
utils/notifications.ts # Планировщик push-уведомлений

Ключевые технические решения

Строгая типизация иконок

export type FeatherIconName = React.ComponentProps<typeof Feather>["name"];
// Исключает опечатки в названиях иконок на этапе компиляции


Push-уведомления с хранением ID

interface Loan {
notificationId?: string; // ID запланированного push
}

// При создании займа:
const notifId = await scheduleLoanReminder(id, contact, amount, dueDate);
if (notifId) newLoan.notificationId = notifId;

// При возврате:
await cancelNotification(loan.notificationId);


RevenueCat абстракция (test mode → production за 1 шаг)

// services/iap.ts — раскомментировать блоки TODO после настройки аккаунта
export async function purchasePremium(tier: 1 | 2): Promise<PurchaseResult>
export async function restorePurchases(): Promise<RestoreResult>
export async function syncPremiumStatus(): Promise<PremiumTier>


Автоматическая обработка просрочек при загрузке

// Один раз в сутки (overdueCheckedDate)
// → помечает займы overdue
// → начисляет −5 кармы за каждый просроченный
// → создаёт уведомления в центре
// → уважает флаг autoWriteOff (автосписание)


Design tokens с авто dark mode

// constants/colors.ts: light + dark палитры
// hooks/useColors.ts: useColorScheme() → автопереключение
const colors = useColors(); // { background, foreground, primary, ... }

Масштаб проекта

  • 26 экранов (.tsx)
  • 8 персонажей × 7 контекстов = 56 уникальных советов
  • 3 тарифа с реальной IAP-интеграцией
  • TypeScript strict, 0 ошибок
  • iOS + Android + Web из единой кодовой базы
  • Полный EAS-пайплайн: dev → preview → production → submit

Разработано с использованием Expo + React Native. Дизайн и персонажи — оригинальные.

v1.1 — Bug fixes & Stability

Что изменилось в v1.1

Полный аудит кода, исправление всех найденных ошибок — от критических потерь данных до мелких UX-недочётов.
🔴 Критические исправления (потеря данных)

  • Stale closures в AppContext — все useCallback-хуки теперь читают живое состояние через useRef, устраняя молчаливую потерю данных при конкурентных обновлениях
  • SafeParse для AsyncStorage — каждое чтение из хранилища защищено от битого JSON; числовые поля (карма, суммы) санируются при загрузке
  • Провайдеры не монтировались — исправлен порядок рендера в _layout.tsx: AppProvider теперь всегда монтируется до экранов, устранена ошибка useApp must be used within AppProvider

🟡 Средние исправления (UI-краши, edge-cases)

  • Коллизии ID уведомлений — суффикс из Date.now() + случайная строка исключают перезапись уведомлений
  • Форма займа (give/take) — isLoading теперь сбрасывается в finally; добавлена валидация срока > 0 дней
  • NaN в расчёте процентов — parseInt с fallback на 30 дней в loan/[id].tsx
  • FlatList внутри ScrollView — заменён на ScrollView + map в profile.tsx; исправлен краш аватара при пустом имени (.filter(Boolean))
  • Анимации кармы — разделены на два useEffect: входная анимация срабатывает один раз, прогресс-бар реагирует на каждое изменение кармы

🟢 Мелкие улучшения (UX)

  • Инвайт друзей — карма +30 начисляется только один раз за сессию, даже если Share sheet открывали несколько раз
  • CharacterBubble — tips[context] ?? "" предотвращает краш при отсутствующей подсказке
  • Навигация персонажей — router.replace заменён на router.push, стек «назад» больше не ломается
  • RevenueCat ключи — вынесены в переменные окружения EXPO_PUBLIC_REVENUECAT_*; в код больше не попадают
  • Советник в настройках — кнопка открывает онбординг в режиме редактирования (mode=edit)
  • Экспресс-исправления импортов — добавлен CHARACTERS в импорты четырёх файлов, где использовался CHARACTERS[0]

v1.2 — Dependency cleanup & Security

Что изменилось в v1.2

Аудит конфигурации проекта: исправлена структура зависимостей, убраны захардкоженные credentials, расширен .gitignore.

📦 package.json — правильная структура зависимостей
33 runtime-пакета перенесены из devDependencies в dependencies:
  • expo, react, react-native и все expo-* модули
  • react-native-reanimated, react-native-gesture-handler и остальные RN-библиотеки
  • zod, @react-native-async-storage/async-storage, @expo/vector-icons и др.
В devDependencies остались только build-инструменты: @babel/core, typescript, @types/*, @expo/cli.

Почему важно: при npm install --production или строгом CI/CD devDependencies не устанавливаются — приложение не запустится.


🔐 eas.json — credentials через переменные окружения
Заменены захардкоженные плейсхолдеры Apple на env-переменные:
appleId → $EXPO_APPLE_ID
ascAppId → $EXPO_ASC_APP_ID
appleTeamId → $EXPO_APPLE_TEAM_ID

Устанавливаются через CI или командой eas credentials перед сабмитом.


🛡️ .gitignore — защита от случайного коммита credentials
Добавлены паттерны для файлов подписи и сервисных ключей:
*.p8 *.cer *.p12 *.mobileprovision *.jks
google-services-key.json


🧹 Репозиторий
  • Удалён scripts/build.js — Replit-специфичный скрипт, не используется с EAS Build
  • Добавлены в .gitignore: .replit-artifact/, server/, .claude/launch.json

v1.3 — Исправления безопасности

🔒 Безопасность

IAP: тест-режим закрыт в production

  • purchasePremium() и restorePurchases() в режиме заглушки теперь работают только в __DEV__
  • В production-сборке без подключённого RevenueCat возвращается ошибка вместо бесплатного доступа к Premium — исключает возможность получить платный тариф без оплаты

🐛 Исправления

Удаление займа отменяет push-уведомление
  • removeLoan() теперь вызывает cancelNotification(loan.notificationId) перед удалением
  • Раньше уведомление оставалось в очереди ОС, и тап по нему после удаления займа открывал экран «Займ не найден»

Другие проекты GitHub

Проекты

Реализованные

Портфолио и услуги

Made on
Tilda