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