@@ -92,51 +92,163 @@ extension _NotificationCentreHelpers on _SolidNotificationCentreState {
9292
9393 final dateTime =
9494 DateTime .fromMillisecondsSinceEpoch (notification.timestamp);
95+ final senderName = extractName (notification.senderWebId);
96+ final structured = _parseStructuredContent (notification.content);
97+
98+ final fileTitle = structured? ['fileTitle' ] ?? notification.title;
99+ final permissions = structured? ['permissions' ];
95100
96101 showDialog (
97102 context: context,
98- builder: (ctx) => AlertDialog (
99- title: Row (
100- children: [
101- Expanded (child: Text (notification.title)),
102- if (priorityIcon (notification.priority) != null )
103- priorityIcon (notification.priority)! ,
104- ],
105- ),
106- content: SingleChildScrollView (
107- child: Column (
108- crossAxisAlignment: CrossAxisAlignment .start,
109- mainAxisSize: MainAxisSize .min,
103+ builder: (ctx) {
104+ final theme = Theme .of (ctx);
105+
106+ return AlertDialog (
107+ title: Row (
110108 children: [
111- Text (
112- formatDateTime (dateTime),
113- style: const TextStyle (color: Colors .grey, fontSize: 13 ),
114- ),
115- const SizedBox (height: 12 ),
116- detailRow ('From' , notification.senderWebId),
117- const SizedBox (height: 8 ),
118- detailRow ('To' , notification.recipientWebId),
119- if (notification.content != null ) ...[
120- const SizedBox (height: 16 ),
121- const Divider (),
122- const SizedBox (height: 8 ),
123- SelectableText (notification.content! ),
124- ],
109+ const Expanded (child: Text ('Notification' )),
110+ if (priorityIcon (notification.priority) != null )
111+ priorityIcon (notification.priority)! ,
125112 ],
126113 ),
127- ),
128- actions: [
129- TextButton .icon (
130- icon: const Icon (Icons .delete, color: Colors .red),
131- label: const Text ('Delete' , style: TextStyle (color: Colors .red)),
132- onPressed: () {
133- Navigator .pop (ctx);
134- confirmAndDelete (notification);
135- },
114+ content: SizedBox (
115+ width: double .maxFinite,
116+ child: Scrollbar (
117+ thumbVisibility: true ,
118+ child: SingleChildScrollView (
119+ child: Column (
120+ crossAxisAlignment: CrossAxisAlignment .start,
121+ mainAxisSize: MainAxisSize .min,
122+ children: [
123+ Text (
124+ formatDateTime (dateTime),
125+ style:
126+ const TextStyle (color: Colors .grey, fontSize: 13 ),
127+ ),
128+ const SizedBox (height: 16 ),
129+ Text .rich (
130+ TextSpan (
131+ style: theme.textTheme.bodyLarge,
132+ children: [
133+ TextSpan (
134+ text: '$senderName shared the\n ' ,
135+ style: const TextStyle (
136+ fontWeight: FontWeight .bold,
137+ ),
138+ ),
139+ TextSpan (
140+ text: '$fileTitle \n ' ,
141+ style: const TextStyle (
142+ fontWeight: FontWeight .bold,
143+ ),
144+ ),
145+ const TextSpan (
146+ text: 'file to you' ,
147+ style: TextStyle (fontWeight: FontWeight .bold),
148+ ),
149+ ],
150+ ),
151+ ),
152+ if (permissions != null ) ...[
153+ const SizedBox (height: 12 ),
154+ Text ('with $permissions permission' ),
155+ ],
156+ const SizedBox (height: 16 ),
157+ const Divider (),
158+ _buildDetailsExpansionTile (
159+ notification,
160+ dateTime,
161+ structured,
162+ theme,
163+ ),
164+ ],
165+ ),
166+ ),
167+ ),
136168 ),
137- ElevatedButton (
138- onPressed: () => Navigator .pop (ctx),
139- child: const Text ('Close' ),
169+ actions: [
170+ ElevatedButton (
171+ style: ElevatedButton .styleFrom (
172+ backgroundColor: Colors .red,
173+ foregroundColor: Colors .white,
174+ ),
175+ onPressed: () {
176+ Navigator .pop (ctx);
177+ confirmAndDelete (notification);
178+ },
179+ child: const Text ('Delete' ),
180+ ),
181+ ElevatedButton (
182+ onPressed: () => Navigator .pop (ctx),
183+ child: const Text ('Close' ),
184+ ),
185+ ],
186+ );
187+ },
188+ );
189+ }
190+
191+ /// Attempts to parse JSON-structured content from a notification.
192+ /// Returns null for legacy plain-text content.
193+
194+ Map <String , dynamic >? _parseStructuredContent (String ? content) {
195+ if (content == null ) return null ;
196+ try {
197+ final decoded = jsonDecode (content);
198+ if (decoded is Map <String , dynamic >) return decoded;
199+ } on FormatException catch (_) {
200+ // Legacy plain-text content; not JSON.
201+ }
202+ return null ;
203+ }
204+
205+ Widget _buildDetailsExpansionTile (
206+ PodNotification notification,
207+ DateTime dateTime,
208+ Map <String , dynamic >? structured,
209+ ThemeData theme,
210+ ) {
211+ final smallStyle = theme.textTheme.bodySmall;
212+
213+ final fileUrl = structured? ['fileUrl' ] ?? notification.title;
214+ final fileTitle =
215+ structured? ['fileTitle' ] ?? notification.title;
216+ final sharedBy =
217+ structured? ['sharedBy' ] ?? notification.senderWebId;
218+ final owner =
219+ structured? ['owner' ] ?? notification.senderWebId;
220+ final permissions = structured? ['permissions' ] ?? '' ;
221+
222+ return ExpansionTile (
223+ title: const Text ('Details' ),
224+ tilePadding: EdgeInsets .zero,
225+ childrenPadding: const EdgeInsets .only (bottom: 8 ),
226+ children: [
227+ _detailLine ('Date' , formatDateTime (dateTime), smallStyle),
228+ _detailLine ('File' , fileUrl, smallStyle),
229+ _detailLine ('Title' , fileTitle, smallStyle),
230+ _detailLine ('Shared by' , sharedBy, smallStyle),
231+ _detailLine ('Owner' , owner, smallStyle),
232+ _detailLine ('Permissions' , permissions, smallStyle),
233+ ],
234+ );
235+ }
236+
237+ Widget _detailLine (String label, String value, TextStyle ? style) {
238+ return Padding (
239+ padding: const EdgeInsets .symmetric (vertical: 2 ),
240+ child: Row (
241+ crossAxisAlignment: CrossAxisAlignment .start,
242+ children: [
243+ SizedBox (
244+ width: 100 ,
245+ child: Text (
246+ '$label :' ,
247+ style: style? .copyWith (fontWeight: FontWeight .bold),
248+ ),
249+ ),
250+ Expanded (
251+ child: SelectableText (value, style: style),
140252 ),
141253 ],
142254 ),
0 commit comments