Line data Source code
1 : import 'dart:convert'; 2 : import 'package:flutter/material.dart'; 3 : import 'package:provider/provider.dart'; 4 : 5 : import 'package:cwtch/cwtch_icons_icons.dart'; 6 : import 'package:cwtch/l10n/app_localizations.dart'; 7 : import 'package:cwtch/models/contact.dart'; 8 : import 'package:cwtch/models/profile.dart'; 9 : import 'package:cwtch/themes/opaque.dart'; 10 : 11 : import '../constants.dart'; 12 : import '../settings.dart'; 13 : 14 : // WithheldBubble is displayed for messages hidden by default due to +m 15 : class EventBubble extends StatefulWidget { 16 : final String content; 17 : dynamic? message; 18 : String? errorMessage = null; 19 : String? stringified = null; 20 : 21 0 : EventBubble(this.content) { 22 : try { 23 0 : this.message = jsonDecode(this.content); 24 : } catch (e) { 25 0 : this.errorMessage = "json decode error $e: " + this.content; 26 : } 27 : } 28 : 29 0 : @override 30 0 : EventBubbleState createState() => EventBubbleState(); 31 : } 32 : 33 : class EventBubbleState extends State<EventBubble> { 34 0 : @override 35 : Widget build(BuildContext context) { 36 0 : if (widget.stringified == null) { 37 : try { 38 0 : widget.stringified = stringify(); // + widget.message["b"];//.keys.join(","); 39 : } catch (e) { 40 0 : widget.stringified = "parse error $e"; 41 : } 42 : } 43 : 44 0 : return Align( 45 : alignment: Alignment.center, 46 0 : child: Padding( 47 0 : padding: EdgeInsets.all(5.0), 48 0 : child: Container( 49 0 : decoration: BoxDecoration( 50 0 : color: Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor, 51 0 : border: Border.all(color: Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor, width: 1), 52 0 : borderRadius: BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8), bottomLeft: Radius.circular(8), bottomRight: Radius.circular(8)), 53 : ), 54 0 : child: Padding( 55 0 : padding: EdgeInsets.all(9.0), 56 0 : child: Text(widget.stringified ?? "", style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle), maxLines: 3), 57 : ), 58 : ), 59 : ), 60 : ); 61 : } 62 : 63 0 : String stringify() { 64 0 : if (widget.errorMessage != null) { 65 0 : return widget.errorMessage!; 66 : } 67 : 68 0 : int eventCode = widget.message["e"]; 69 : switch (eventCode) { 70 0 : case AddMemberEvent: 71 0 : if (widget.message["a"].toString() == Provider.of<ContactInfoState>(context, listen: false).onion) { 72 0 : return r("\$handle joined the group"); 73 : } else { 74 0 : return r("\$author added \$handle to the group"); 75 : } 76 0 : case RemoveMemberEvent: 77 0 : dynamic params = jsonDecode(widget.message["b"]); 78 0 : if (widget.message["a"].toString() == params["h"]) { 79 0 : return r("\$author left the group"); 80 : } else { 81 0 : return r("\$author removed \$handle from the group"); 82 : } 83 0 : case ChangeMemberRoleEvent: 84 0 : return r("\$author changed \$handle to \$role"); 85 0 : case ChangeGroupModeEvent: 86 0 : return r("\$author changed the group mode to \$mode"); 87 0 : case ChangeGroupMaskEvent: 88 0 : return r("\$author changed the group mask to \$mask"); 89 0 : case UpdateGroupMetadataEvent: 90 0 : return r("\$author updated the group name/attributes"); 91 0 : case UpdateGroupPictureEvent: 92 0 : return r("\$author updated the group picture"); 93 : default: 94 0 : return r("(Missing message: \$author did an event code \$e"); 95 : } 96 : } 97 : 98 0 : String r(String input) { 99 : // widget.message { 100 : // g: group onion 101 : // a: author onion 102 : // m: member message id 103 : // b: event parameters { 104 : // Additive: "true"|"false" for mode/mask 105 : // a: mask bit 106 : // h: handle 107 : // n: mask bit 108 : // r: role 109 : // t: ? 110 : // s: sig? 111 : // e: event code 112 0 : String author = widget.message["a"].toString(); 113 0 : if (author == Provider.of<ProfileInfoState>(context).onion) { 114 : author = "You"; 115 : } else { 116 0 : ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.findContact(author); 117 : if (contact != null) { 118 0 : author = contact.nickname; 119 : } 120 : } 121 : 122 : String handle = ""; 123 : String role = ""; 124 : String mode = ""; 125 : String mask = ""; 126 : 127 0 : if (widget.message.containsKey("b")) { 128 0 : dynamic params = jsonDecode(widget.message["b"]); 129 : 130 0 : if (params.containsKey("h")) { 131 0 : handle = params["h"]; 132 0 : if (handle == Provider.of<ProfileInfoState>(context).onion) { 133 : handle = "you"; 134 : } else { 135 0 : ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.findContact(handle); 136 : if (contact != null) { 137 0 : handle = contact.nickname; 138 : } 139 : } 140 : } 141 : 142 0 : if (params.containsKey("r")) { 143 0 : role = params["r"]; 144 : } 145 : 146 0 : String additive = (params.containsKey("Additive") && params["Additive"]) ? "+" : "-"; 147 0 : if (params.containsKey("a")) { 148 0 : mode = additive + params["a"]; 149 : } 150 0 : if (params.containsKey("n")) { 151 0 : mask = additive + params["n"]; 152 : } 153 : } 154 : 155 : return input 156 0 : .replaceAll("\$author", author) 157 0 : .replaceAll("\$e", widget.message["e"].toString()) 158 0 : .replaceAll("\$handle", handle) 159 0 : .replaceAll("\$role", role) 160 0 : .replaceAll("\$mask", mask) 161 0 : .replaceAll("\$mode", mode); 162 : } 163 : }