import 'package:flutter/material.dart'; import 'package:localstore/localstore.dart'; import 'package:outbag_app/tools/assets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class RoomVisibility { final int type; const RoomVisibility(this.type); static RoomVisibility get private { return const RoomVisibility(0); } static RoomVisibility get local { return const RoomVisibility(1); } static RoomVisibility get public { return const RoomVisibility(2); } IconData get icon { if (type == 2) { return Icons.public; } else if (type == 1) { return Icons.home; } return Icons.lock; } String text(BuildContext context) { final trans = AppLocalizations.of(context); if (type == 2) { return trans!.roomVisibilityGlobal; } else if (type == 1) { return trans!.roomVisibilityLocal; } return trans!.roomVisibilityPrivate; } static List list() { return [ RoomVisibility.private, RoomVisibility.local, RoomVisibility.public, ]; } } class RoomIcon { final String type; RoomIcon({required this.type}); static RoomIcon get love { return RoomIcon(type: "Love"); } static RoomIcon get sports { return RoomIcon(type: "Sports"); } static RoomIcon get pets { return RoomIcon(type: "Pets"); } static RoomIcon get vacation { return RoomIcon(type: "Vacation"); } static RoomIcon get gifts { return RoomIcon(type: "Gifts"); } static RoomIcon get groceries { return RoomIcon(type: "Groceries"); } static RoomIcon get fashion { return RoomIcon(type: "Fashion"); } static RoomIcon get art { return RoomIcon(type: "Art"); } static RoomIcon get tech { return RoomIcon(type: "Tech"); } static RoomIcon get home { return RoomIcon(type: "Home"); } static RoomIcon get family { return RoomIcon(type: "Family"); } static RoomIcon get social { return RoomIcon(type: "Social"); } static RoomIcon get other { return RoomIcon(type: "Other"); } static List list() { return [ RoomIcon.love, RoomIcon.sports, RoomIcon.pets, RoomIcon.vacation, RoomIcon.gifts, RoomIcon.groceries, RoomIcon.fashion, RoomIcon.art, RoomIcon.tech, RoomIcon.home, RoomIcon.family, RoomIcon.social, RoomIcon.other, ]; } String get text { switch (type.toLowerCase()) { case 'love': return 'Friends'; case 'sports': return 'Sports'; case 'pets': return 'Pets'; case 'vacation': return 'Vacation'; case 'gifts': return 'Gifts'; case 'groceries': return 'Groceries'; case 'fashion': return 'Clothing'; case 'art': return 'Arts & Crafts'; case 'tech': return 'Electronics'; case 'home': return 'Home supplies'; case 'family': return 'Family'; case 'social': return 'Social'; case 'other': default: return 'Other'; } } // return image name String get img { String path = ""; switch (type.toLowerCase()) { case 'love': path = 'undraw/undraw_couple.svg'; break; case 'sports': path = 'undraw/undraw_greek_freak.svg'; break; case 'pets': path = 'undraw/undraw_dog.svg'; break; case 'vacation': path = 'undraw/undraw_trip.svg'; break; case 'gifts': path = 'undraw/undraw_gifts.svg'; break; case 'groceries': path = 'undraw/undraw_gone_shopping.svg'; break; case 'fashion': path = 'undraw/undraw_jewelry.svg'; break; case 'art': path = 'undraw/undraw_sculpting.svg'; break; case 'tech': path = 'undraw/undraw_progressive_app.svg'; break; case 'home': path = 'undraw/undraw_under_construction.svg'; break; case 'family': path = 'undraw/undraw_family.svg'; break; case 'social': path = 'undraw/undraw_pizza_sharing.svg'; break; case 'other': default: path = 'undraw/undraw_file_manager.svg'; } return asset(path); } } class Room { String id; String serverTag; String name; String description; RoomIcon? icon = RoomIcon.other; RoomVisibility? visibility = RoomVisibility.private; Room( {required this.id, required this.serverTag, this.name = "", this.description = "", this.icon, this.visibility}); @override bool operator ==(Object r) { if (r.runtimeType != runtimeType) { return false; } final me = humanReadable; final other = (r as Room).humanReadable; return me == other; } String get humanReadable { return '$id@$serverTag'; } // get list of all known rooms static Future> listRooms() async { final db = Localstore.instance; final rooms = (await db.collection('rooms').get())!; List builder = []; for (MapEntry entry in rooms.entries) { try { builder.add(Room.fromMap(entry.value)); } catch (e) { // skip invalid rooms // NOTE: might want to autodelete them in the future // although keeping them might be ok, // in case we ever get a new dataset to fix the current state } } return builder; } // listen to room change static listen(Function(Map) cb) async { final db = Localstore.instance; final stream = db.collection('rooms').stream; stream.listen(cb); } factory Room.fromMap(Map map) { return Room( id: map['id'], serverTag: map['server'], name: map['name'], description: map['description'] ?? '', icon: RoomIcon(type: map['icon'] ?? 'Other'), visibility: RoomVisibility(map['visibility'] ?? 0)); } Map toMap() { return { 'id': id, 'server': serverTag, 'description': description, 'name': name, 'icon': icon?.type, 'visibility': visibility?.type }; } factory Room.fromJSON(dynamic json) { return Room( id: json['name'], serverTag: json['server'], name: json['title'], description: json['description'], icon: RoomIcon(type: json['icon']), visibility: RoomVisibility(json['visibility'])); } Future toDisk() async { final db = Localstore.instance; await db.collection('rooms').doc('$id@$serverTag').set(toMap()); } Future removeDisk() async { final db = Localstore.instance; // NOTE: Because categories, products and entries // are supposed to be stored in the room object // (they are just not read by Room.fromDisk) // deleting the document from the collection // should also get rid of affiliated collections await db.collection('rooms').doc('$id@$serverTag').delete(); } static Future fromDisk( {required String id, required String serverTag}) async { final db = Localstore.instance; final raw = await db.collection('rooms').doc('$id@$serverTag').get(); return Room.fromMap(raw!); } } class RoomMember { final String id; final String serverTag; final bool isAdmin; const RoomMember( {required this.id, required this.serverTag, required this.isAdmin}); factory RoomMember.fromJSON(dynamic json) { return RoomMember( id: json['name'], serverTag: json['server'], isAdmin: json['admin'] == 1); } String get humanReadableName { return '$id@$serverTag'; } } class RoomInfo { final String owner; final bool isAdmin; final bool isOwner; final int permissions; const RoomInfo( {required this.permissions, required this.owner, required this.isAdmin, required this.isOwner}); factory RoomInfo.fromJSON(dynamic json) { return RoomInfo( permissions: json['rights'], owner: json['owner'], isAdmin: json['isAdmin'], isOwner: json['isOwner']); } } class RoomCategory { final int? id; final String name; final ColorSwatch color; const RoomCategory( {required this.id, required this.name, required this.color}); factory RoomCategory.fromJSON(dynamic json) { return RoomCategory( id: json['id'], name: json['title'], color: colorFromString(json['color'])); } factory RoomCategory.other(BuildContext context) { return RoomCategory( id: null, name: AppLocalizations.of(context)!.categoryNameOther, color: Colors.grey); } static List> listColors() { return [ "red", "green", "yellow", "blue", "aqua", "purple", "red-acc", "green-acc", "yellow-acc", "blue-acc", "aqua-acc", "purple-acc", ].map((txt) => colorFromString(txt)).toList(); } } ColorSwatch colorFromString(String text) { switch (text.toLowerCase()) { case 'red-acc': return Colors.redAccent; case 'green-acc': return Colors.greenAccent; case 'yellow-acc': return Colors.yellowAccent; case 'blue-acc': return Colors.blueAccent; case 'aqua-acc': return Colors.tealAccent; case 'purple-acc': return Colors.purpleAccent; case 'red': return Colors.red; case 'green': return Colors.green; case 'yellow': return Colors.yellow; case 'blue': return Colors.blue; case 'aqua': return Colors.teal; case 'purple': default: return Colors.purple; } } String colorIdFromColor(ColorSwatch color) { if (color == Colors.redAccent) { return 'red-acc'; } if (color == Colors.greenAccent) { return 'green-acc'; } if (color == Colors.yellowAccent) { return 'yellow-acc'; } if (color == Colors.blueAccent) { return 'blue-acc'; } if (color == Colors.tealAccent) { return 'teal-acc'; } if (color == Colors.purpleAccent) { return 'purple-acc'; } if (color == Colors.red) { return 'red'; } if (color == Colors.green) { return 'green'; } if (color == Colors.yellow) { return 'yellow'; } if (color == Colors.blue) { return 'blue'; } if (color == Colors.teal) { return 'teal'; } if (color == Colors.purple) { return 'purple'; } return 'purple'; } class RoomProduct { int id; String name; String description; // category ID // or null for category: "other" int? category; // unitID int defaultUnit; // NOTE: has to be string, // as it may hold plain text, // integers or doubles String defaultValue; String? ean; // parent product ID int? parent; RoomProduct( {required this.id, required this.name, this.description = '', this.category = -1, this.defaultUnit = 0, this.defaultValue = '', this.ean, this.parent}); factory RoomProduct.fromJSON(dynamic json) { return RoomProduct( id: json['listProdID'], name: json['title'], description: json['description'], category: json['category'], defaultUnit: json['defUnit'], defaultValue: json['defValue'], ean: json['ean'], parent: json['parent']); } } class RoomItem { int id; int state; String name; String description; // may link to a category // null for other int? category; int unit; String value; // may link to a product int? link; RoomItem( {required this.id, required this.name, this.description = '', this.state = 0, this.category = -1, this.unit = 0, this.value = '', this.link}); factory RoomItem.fromJSON(dynamic json) { return RoomItem( id: json['listItemID'], name: json['title'], description: json['description'], category: json['listCatID'], state: json['state'], unit: json['unit'], value: json['value'], link: json['listProdID']); } RoomItem clone() { return RoomItem( id: id, name: name, description: description, category: category, unit: unit, value: value, link: link); } }