actions-test/lib/backend/room.dart

715 lines
17 KiB
Dart
Raw Normal View History

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<RoomVisibility> 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<RoomIcon> 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<List<Room>> listRooms() async {
final db = Localstore.instance;
final rooms = (await db.collection('rooms').get())!;
List<Room> 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<String, dynamic>) cb) async {
final db = Localstore.instance;
final stream = db.collection('rooms').stream;
stream.listen(cb);
}
factory Room.fromMap(Map<String, dynamic> 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<String, dynamic> 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<void> toDisk() async {
final db = Localstore.instance;
await db.collection('rooms').doc('$id@$serverTag').set(toMap());
}
Future<void> 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<Room> 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;
final bool isInvitePending;
const RoomMember(
2023-12-22 20:14:36 +01:00
{required this.id,
required this.serverTag,
required this.isAdmin,
this.isInvitePending = false});
factory RoomMember.fromJSON(dynamic json) {
return RoomMember(
2023-12-22 20:14:36 +01:00
id: json['name'],
serverTag: json['server'],
isAdmin: json['admin'],
isInvitePending: json['confirmed']);
}
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;
String name;
ColorSwatch<int> color;
final String room;
final String server;
RoomCategory(
{required this.id,
required this.name,
required this.color,
required this.server,
required this.room});
factory RoomCategory.fromJSON(String server, String room, dynamic json) {
return RoomCategory(
server: server,
room: room,
id: json['id'],
name: json['title'],
color: colorFromString(json['color']));
}
factory RoomCategory.other(String server, String room, BuildContext context) {
return RoomCategory(
server: server,
room: room,
id: null,
name: AppLocalizations.of(context)!.categoryNameOther,
color: Colors.grey);
}
static List<ColorSwatch<int>> 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();
}
// get list of all categories in a given room
static Future<List<RoomCategory>> list(String server, String room) async {
final db = Localstore.instance;
final rooms = (await db.collection('categories:$room@$server').get()) ?? {};
List<RoomCategory> builder = [];
for (MapEntry entry in rooms.entries) {
try {
builder.add(RoomCategory.fromMap(entry.value));
} catch (e) {
// skip invalid entries
// 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(
String server, String room, Function(Map<String, dynamic>) cb) async {
final db = Localstore.instance;
final stream = db.collection('categories:$room@$server').stream;
stream.listen(cb);
}
factory RoomCategory.fromMap(Map<String, dynamic> map) {
return RoomCategory(
server: map['server'],
room: map['room'],
id: map['id'],
name: map['name'],
color: colorFromString(map['color']));
}
Map<String, dynamic> toMap() {
return {
'server': server,
'room': room,
'id': id,
'name': name,
'color': colorIdFromColor(color)
};
}
Future<void> toDisk() async {
final db = Localstore.instance;
await db.collection('categories:$room@$server').doc('$id').set(toMap());
}
Future<void> removeDisk() async {
final db = Localstore.instance;
await db.collection('categories:$room@$server').doc('$id').delete();
}
static Future<RoomCategory> fromDisk(
{required int id, required String server, required String room}) async {
final db = Localstore.instance;
final raw =
await db.collection('categories:$room@$server').doc('$id').get();
return RoomCategory.fromMap(raw!);
}
}
ColorSwatch<int> 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<int> 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 {
final 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;
final String server;
final String room;
RoomProduct(
{required this.id,
required this.name,
required this.server,
required this.room,
this.description = '',
this.category = -1,
this.defaultUnit = 0,
this.defaultValue = '',
this.ean,
this.parent});
factory RoomProduct.fromJSON(String server, String room, dynamic json) {
return RoomProduct(
server: server,
room: room,
id: json['listProdID'],
name: json['title'],
description: json['description'],
category: json['category'],
defaultUnit: json['defUnit'],
defaultValue: json['defValue'],
ean: json['ean'],
parent: json['parent']);
}
// get list of all categories in a given room
static Future<List<RoomProduct>> list(String server, String room) async {
final db = Localstore.instance;
final rooms = (await db.collection('products:$room@$server').get()) ?? {};
List<RoomProduct> builder = [];
for (MapEntry entry in rooms.entries) {
try {
builder.add(RoomProduct.fromMap(entry.value));
} catch (e) {
// skip invalid entries
// 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(
String server, String room, Function(Map<String, dynamic>) cb) async {
final db = Localstore.instance;
final stream = db.collection('products:$room@$server').stream;
stream.listen(cb);
}
factory RoomProduct.fromMap(Map<String, dynamic> map) {
return RoomProduct(
server: map['server'],
room: map['room'],
id: map['id'],
name: map['name'],
description: map['description'],
category: map['category'],
defaultUnit: map['default_unit'],
defaultValue: map['default_value'],
ean: map['ean'],
parent: map['parent']);
}
Map<String, dynamic> toMap() {
return {
'server': server,
'room': room,
'id': id,
'name': name,
'description': description,
'category': category,
'default_unit': defaultUnit,
'default_value': defaultValue,
'ean': ean,
'parent': parent
};
}
Future<void> toDisk() async {
final db = Localstore.instance;
await db.collection('products:$room@$server').doc('$id').set(toMap());
}
Future<void> removeDisk() async {
final db = Localstore.instance;
await db.collection('products:$room@$server').doc('$id').delete();
}
static Future<RoomProduct> fromDisk(
{required int id, required String server, required String room}) async {
final db = Localstore.instance;
final raw = await db.collection('products:$room@$server').doc('$id').get();
return RoomProduct.fromMap(raw!);
}
}
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);
}
}