✨ Features implemented: - Smart inventory tracking with Hive database - Barcode scanning with auto-populated product info - Multiple API fallbacks (Open Food Facts, UPCItemDB) - Smart expiration date predictions by category - Discord webhook notifications (persisted) - Custom sage leaf vector icon - Material Design 3 UI with sage green theme - Privacy Policy & Terms of Service - Local-first, privacy-focused architecture 🎨 UI/UX: - Home dashboard with inventory stats - Add Item screen with barcode integration - Inventory list with expiration indicators - Settings with persistent preferences - About section with legal docs 🔧 Technical: - Flutter 3.35.5 with Riverpod state management - Hive 2.2.3 for local database - Mobile scanner for barcode detection - Feature-first architecture 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
69 lines
1.7 KiB
Dart
69 lines
1.7 KiB
Dart
import 'dart:convert';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
class DiscordService {
|
|
String? webhookUrl;
|
|
|
|
/// Send a notification to Discord
|
|
Future<bool> sendNotification({
|
|
required String title,
|
|
required String message,
|
|
String? imageUrl,
|
|
}) async {
|
|
if (webhookUrl == null || webhookUrl!.isEmpty) {
|
|
print('Discord webhook URL not configured');
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
final embed = {
|
|
'title': title,
|
|
'description': message,
|
|
'color': 0x4CAF50, // Sage green (hex color)
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
};
|
|
|
|
if (imageUrl != null) {
|
|
embed['thumbnail'] = {'url': imageUrl};
|
|
}
|
|
|
|
final response = await http.post(
|
|
Uri.parse(webhookUrl!),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: jsonEncode({
|
|
'embeds': [embed],
|
|
}),
|
|
);
|
|
|
|
return response.statusCode == 204;
|
|
} catch (e) {
|
|
print('Error sending Discord notification: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Send expiration alert
|
|
Future<void> sendExpirationAlert({
|
|
required String itemName,
|
|
required int daysUntilExpiration,
|
|
}) async {
|
|
String emoji = '⚠️';
|
|
String urgency = 'Warning';
|
|
|
|
if (daysUntilExpiration <= 0) {
|
|
emoji = '🚨';
|
|
urgency = 'Expired';
|
|
} else if (daysUntilExpiration <= 3) {
|
|
emoji = '⚠️';
|
|
urgency = 'Critical';
|
|
}
|
|
|
|
await sendNotification(
|
|
title: '$emoji Food Expiration Alert - $urgency',
|
|
message: daysUntilExpiration <= 0
|
|
? '**$itemName** has expired!'
|
|
: '**$itemName** expires in $daysUntilExpiration day${daysUntilExpiration == 1 ? '' : 's'}!',
|
|
);
|
|
}
|
|
}
|