Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .expo/types/router.d.ts

Large diffs are not rendered by default.

18 changes: 16 additions & 2 deletions TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,22 @@
- [x] Оптимизировать отображение психологического профиля в ИИ-Чате.
- [x] Подготовить релиз v1.17.0 и обновить скриншоты.

# Список задач по улучшению приложения (Цикл 18) - ПЛАНИРОВАНИЕ 📅
# Список задач по улучшению приложения (Цикл 18) - ВЫПОЛНЕНО ✅

## 💬 Геймификация Сообщества
- [x] Внедрить функцию "Трезвый напарник" (поиск бадди) с постоянным сохранением через AsyncStorage.
- [x] Реализовать ежедневный импульс поддержки напарника с вознаграждением +15 очков Кармы.
- [x] Добавить тактильную отдачу (Haptics) при отправке импульса и выборе напарника.

## 🤖 AI-Ассистент и Исправления
- [x] Исправить критический ReferenceError в методе `AICoachService.getUserInsights` при интеграции ИИ-анализа сна.
- [x] Объединить и починить специализированные тесты анализа сна в `__tests__/AICoachSleepAnalysis.test.ts`.

## 🎨 Интерфейс и UX
- [x] Создать интуитивно понятную карточку "Трезвый напарник" с индикаторами статуса и прогресса на вкладке общения.
- [x] Подготовить и выпустить релиз v1.18.0.

# Список задач по улучшению приложения (Цикл 19) - ПЛАНИРОВАНИЕ 📅

## 🤖 AI-Персонализация
- [ ] Добавить ИИ-анализ физической активности (шаги) на основе данных здоровья.
Expand All @@ -326,7 +341,6 @@
## 💬 Геймификация Сообщества
- [ ] Реализовать еженедельные турниры между группами поддержки.
- [ ] Добавить систему "Коллективных наград" за общие достижения.
- [ ] Внедрить функцию "Трезвый напарник" (поиск бадди).

## 📚 Контент и Обучение
- [ ] Разработать курс "Биохакинг трезвости: как восстановить тело".
Expand Down
10 changes: 6 additions & 4 deletions __tests__/AICoachSleepAnalysis.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { AICoachService } from '../services/AICoachService';
import { JournalService } from '../services/journalService';

jest.mock('../services/journalService');

describe('AICoachService Sleep Quality Analysis', () => {
it('should handle empty or missing entries gracefully', () => {
Expand Down Expand Up @@ -42,11 +45,10 @@ describe('AICoachService Sleep Quality Analysis', () => {
const result = AICoachService.analyzeSleepFromJournal(entries);
expect(result.sleepQuality).toBe(3); // (5 + 1) / 2 = 3
expect(result.feedback).toContain('на среднем уровне');
import { JournalService } from '../services/journalService';

jest.mock('../services/journalService');
});
});

describe('AICoachService Sleep Analysis', () => {
describe('AICoachService Sleep Analysis from journalService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
Expand Down
71 changes: 71 additions & 0 deletions __tests__/CommunityBuddy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { CommunityService, SoberBuddy } from '../services/communityService';
import AsyncStorage from '@react-native-async-storage/async-storage';

describe('CommunityService Sober Buddy ("Трезвый напарник") ', () => {
beforeEach(async () => {
jest.clearAllMocks();
await AsyncStorage.clear();
});

it('should return a non-empty list of potential buddies', () => {
const list = CommunityService.getPotentialBuddies();
expect(list.length).toBeGreaterThan(0);
expect(list[0]).toHaveProperty('name');
expect(list[0]).toHaveProperty('daysSober');
expect(list[0]).toHaveProperty('avatar');
expect(list[0]).toHaveProperty('status');
});

it('should manage buddy selection and unpairing correctly', async () => {
// 1. Initial should be null
let buddy = await CommunityService.getSelectedBuddy();
expect(buddy).toBeNull();

// 2. Select a buddy
const potential = CommunityService.getPotentialBuddies()[0];
await CommunityService.selectBuddy(potential);

// 3. Get selected buddy
buddy = await CommunityService.getSelectedBuddy();
expect(buddy).not.toBeNull();
expect(buddy!.id).toBe(potential.id);

// 4. Remove buddy
await CommunityService.removeBuddy();
buddy = await CommunityService.getSelectedBuddy();
expect(buddy).toBeNull();
});

it('should send daily support pulse and award +15 Karma', async () => {
const potential = CommunityService.getPotentialBuddies()[0];
await CommunityService.selectBuddy(potential);

// Initial karma should be 0 (or default)
let karma = await CommunityService.getUserKarma();
expect(karma).toBe(0);

// Send support pulse
const success = await CommunityService.sendBuddyPulse();
expect(success).toBe(true);

// Karma should increase by 15
karma = await CommunityService.getUserKarma();
expect(karma).toBe(15);

// Buddy lastPulseSent should be set to today
const buddy = await CommunityService.getSelectedBuddy();
expect(buddy!.lastPulseSent).toBe(new Date().toDateString());

// Try sending again today - should fail and not award karma
const secondTry = await CommunityService.sendBuddyPulse();
expect(secondTry).toBe(false);

karma = await CommunityService.getUserKarma();
expect(karma).toBe(15); // Stays at 15
});

it('should return false when sending pulse with no selected buddy', async () => {
const success = await CommunityService.sendBuddyPulse();
expect(success).toBe(false);
});
});
14 changes: 1 addition & 13 deletions app/(tabs)/articles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,7 @@ import { useRecovery } from '../../hooks/useRecovery';

const { width: screenWidth } = Dimensions.get('window');

interface Article {
id: string;
title: string;
category: string;
readTime: number;
preview: string;
content: string;
tags: string[];
icon: string;
color: string;
}

import { articlesDatabase } from '../../services/articlesDatabase';
import { articlesDatabase, Article } from '../../services/articlesDatabase';
import { ArticleQuiz } from '../../components/ArticleQuiz';

const articles: Article[] = articlesDatabase;
Expand Down
Loading
Loading