CH-J SM Mobileδιαχείριση διακομιστή πάνω από SSH
Μενού
Δημοσίευση πηγής

CH-J SM Mobile

Περιηγηθείτε σε καταλόγους και αρχεία για μια συγκεκριμένη έκδοση εφαρμογών.

Κατεβάστε την πηγή ZIP
CH-J / SM Mobile 134 αρχεία
sm-mobile-20261016T050549Z-3678f2a05fa9 Προστέθηκε: · Χτισμένο:
Αρχεία
workspace.dart Προστέθηκε: Ημερομηνία που δεν έχει καταγραφεί 7,2 KB · 209 γραμμές
Λήψη αρχείου
1import 'package:flutter/material.dart';
2import '../core/app_controller.dart';
3import '../l10n/strings.dart';
4import 'diagnostics_page.dart';
5import 'files_page.dart';
6import 'monitor_page.dart';
7import 'servers_page.dart';
8import 'settings_page.dart';
9import 'terminal_page.dart';
11class Workspace extends StatefulWidget {
12 final AppController controller;
13 const Workspace(this.controller, {super.key});
14 @override
15 State<Workspace> createState() => _WorkspaceState();
18class _WorkspaceState extends State<Workspace> {
19 int index = 0;
20 static const names = [
21 'servers',
22 'terminal',
23 'files',
24 'monitor',
25 'diagnostics',
26 'settings',
27 ];
28 static const icons = [
29 Icons.dns_outlined,
30 Icons.terminal,
31 Icons.folder_outlined,
32 Icons.monitor_heart_outlined,
33 Icons.network_check,
34 Icons.settings_outlined,
35 ];
36 @override
37 Widget build(BuildContext context) => ListenableBuilder(
38 listenable: widget.controller.ssh,
39 builder: (context, _) {
40 final c = widget.controller;
41 final tablet = MediaQuery.sizeOf(context).width >= 720;
42 final page = switch (index) {
43 0 => ServersPage(
44 c,
45 onConnected: () {
46 if (mounted) setState(() => index = 1);
47 },
48 ),
49 1 => TerminalPage(
50 c,
51 key: ValueKey(c.ssh.connected ? c.ssh.client : null),
52 ),
53 2 => FilesPage(c, key: ValueKey(c.ssh.connected ? c.ssh.client : null)),
54 3 => MonitorPage(
55 c,
56 key: ValueKey(c.ssh.connected ? c.ssh.client : null),
57 ),
58 4 => DiagnosticsPage(c),
59 _ => SettingsPage(c),
60 };
61 return Scaffold(
62 appBar: AppBar(
63 title: Text('SM Mobile · ${context.tr(names[index])}'),
64 actions: [
65 if (c.ssh.connecting)
66 IconButton(
67 onPressed: c.ssh.disconnect,
68 tooltip: context.tr('cancel'),
69 icon: const Icon(Icons.close),
70 ),
71 if (c.ssh.connected)
72 IconButton(
73 onPressed: c.ssh.disconnect,
74 tooltip: context.tr('disconnect'),
75 icon: const Icon(Icons.link_off),
76 ),
77 if (!c.ssh.connected && !c.ssh.connecting && c.ssh.profile != null)
78 IconButton(
79 onPressed: () => connectProfile(context, c, c.ssh.profile!),
80 tooltip: context.tr('reconnect'),
81 icon: const Icon(Icons.refresh),
82 ),
83 IconButton(
84 onPressed: c.lock,
85 tooltip: context.tr('lock'),
86 icon: const Icon(Icons.lock_outline),
87 ),
88 ],
89 ),
90 body: SafeArea(
91 child: Column(
92 children: [
93 if (c.ssh.connecting) const LinearProgressIndicator(),
94 if (c.ssh.error != null)
95 MaterialBanner(
96 content: Text(context.tr(c.ssh.error!)),
97 actions: [
98 TextButton(
99 onPressed: () {
100 c.ssh.error = null;
101 c.changed();
102 },
103 child: Text(context.tr('done')),
104 ),
105 ],
106 ),
107 if (c.ssh.connected)
108 Padding(
109 padding: const EdgeInsets.symmetric(
110 horizontal: 16,
111 vertical: 4,
112 ),
113 child: Row(
114 children: [
115 const Icon(
116 Icons.circle,
117 color: Color(0xff43d6a0),
118 size: 8,
119 ),
120 const SizedBox(width: 8),
121 Expanded(
122 child: Text(
123 '${c.ssh.profile!.name} · ${context.tr('connected')}',
124 maxLines: 1,
125 overflow: TextOverflow.ellipsis,
126 ),
127 ),
128 ],
129 ),
130 ),
131 Expanded(
132 child: Row(
133 children: [
134 if (tablet)
135 LayoutBuilder(
136 builder: (context, constraints) =>
137 SingleChildScrollView(
138 child: SizedBox(
139 height: constraints.maxHeight.clamp(
140 600 *
141 MediaQuery.textScalerOf(context).scale(1),
142 double.infinity,
143 ),
144 child: NavigationRail(
145 selectedIndex: index,
146 onDestinationSelected: (v) =>
147 setState(() => index = v),
148 labelType: NavigationRailLabelType.all,
149 destinations: List.generate(
150 6,
151 (i) => NavigationRailDestination(
152 icon: Icon(icons[i]),
153 label: Text(context.tr(names[i])),
154 ),
155 ),
156 ),
157 ),
158 ),
159 ),
160 Expanded(child: page),
161 ],
162 ),
163 ),
164 ],
165 ),
166 ),
167 bottomNavigationBar: tablet
168 ? null
169 : NavigationBar(
170 selectedIndex: index < 3 ? index : 3,
171 onDestinationSelected: (v) async {
172 if (v < 3) {
173 setState(() => index = v);
174 return;
175 }
176 final chosen = await showModalBottomSheet<int>(
177 context: context,
178 builder: (ctx) => SafeArea(
179 child: Column(
180 mainAxisSize: MainAxisSize.min,
181 children: [
182 for (var i = 3; i < 6; i++)
183 ListTile(
184 leading: Icon(icons[i]),
185 title: Text(ctx.tr(names[i])),
186 onTap: () => Navigator.pop(ctx, i),
187 ),
188 ],
189 ),
190 ),
191 );
192 if (chosen != null && mounted) setState(() => index = chosen);
193 },
194 destinations: [
195 for (var i = 0; i < 3; i++)
196 NavigationDestination(
197 icon: Icon(icons[i]),
198 label: context.tr(names[i]),
199 ),
200 NavigationDestination(
201 icon: Icon(index >= 3 ? icons[index] : Icons.more_horiz),
202 label: context.tr(index >= 3 ? names[index] : 'settings'),
203 ),
204 ],
205 ),
206 );
207 },
208 );
SHA-256be8c5171d17a8ad3920c432d278a01341a870edda851e78dc286f84535814d3f

CH-J Proprietary Software License 1.14 · Η πηγή παρέχεται στο CH-J Proprietary Software License 1.14. Η διαθεσιμότητά του δεν αλλάζει τους όρους της άδειας χρήσης ούτε χορηγεί πρόσθετα δικαιώματα.

Archive SHA-256c486a807e4828998b9c57ab0f2e760f7980b31965fe118e89d3e1a5a2b65be65