Files
flutter_server_box/lib/view/page/server/tab/content.dart
lollipopkit🏳️‍⚧️ 8627ff823f optimization: desktop UI (#747)
2025-05-13 04:57:37 +08:00

196 lines
5.7 KiB
Dart

part of 'tab.dart';
extension on _ServerPageState {
Widget _buildServerCardTitle(Server s) {
return Padding(
padding: const EdgeInsets.only(left: 7, right: 13),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ConstrainedBox(
constraints: BoxConstraints(maxWidth: _media.size.width / 2.3),
child: Hero(
tag: 'home_card_title_${s.spi.id}',
transitionOnUserGestures: true,
child: Material(
color: Colors.transparent,
child: Text(
s.spi.name,
style: UIs.text13Bold.copyWith(
color: context.isDark ? Colors.white : Colors.black,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
),
const Icon(
Icons.keyboard_arrow_right,
size: 17,
color: Colors.grey,
),
const Spacer(),
_buildTopRightText(s),
_buildTopRightWidget(s),
],
),
);
}
Widget _buildTopRightWidget(Server s) {
final (child, onTap) = switch (s.conn) {
ServerConn.connecting || ServerConn.loading || ServerConn.connected => (
SizedBox(
width: 19,
height: 19,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(UIs.primaryColor),
),
),
null,
),
ServerConn.failed => (
const Icon(Icons.refresh, size: 21, color: Colors.grey),
() {
TryLimiter.reset(s.spi.id);
ServerProvider.refresh(spi: s.spi);
},
),
ServerConn.disconnected => (
const Icon(MingCute.link_3_line, size: 19, color: Colors.grey),
() => ServerProvider.refresh(spi: s.spi)
),
ServerConn.finished => (
const Icon(MingCute.unlink_2_line, size: 17, color: Colors.grey),
() => ServerProvider.closeServer(id: s.spi.id),
),
};
// Or the loading icon will be rescaled.
final wrapped = child is SizedBox
? child
: SizedBox(height: _ServerPageState._kCardHeightMin, width: 27, child: child);
if (onTap == null) return wrapped.paddingOnly(left: 10);
return InkWell(
borderRadius: BorderRadius.circular(7),
onTap: onTap,
child: wrapped,
).paddingOnly(left: 5);
}
Widget _buildTopRightText(Server s) {
final hasErr = s.conn == ServerConn.failed && s.status.err != null;
final str = s._getTopRightStr(s.spi);
if (str == null) return UIs.placeholder;
return GestureDetector(
onTap: () {
if (!hasErr) return;
_showFailReason(s.status);
},
child: Text(str, style: UIs.text13Grey),
);
}
void _showFailReason(ServerStatus ss) {
final md = '''
${ss.err?.solution ?? l10n.unknown}
```sh
${ss.err?.message ?? 'null'}
''';
context.showRoundDialog(
title: libL10n.error,
child: SingleChildScrollView(child: SimpleMarkdown(data: md)),
actions: [
TextButton(
onPressed: () => Pfs.copy(md),
child: Text(libL10n.copy),
)
],
);
}
Widget _buildDisk(ServerStatus ss, String id) {
final cardNoti = _getCardNoti(id);
return ListenableBuilder(
listenable: cardNoti,
builder: (_, __) {
final isSpeed = cardNoti.value.diskIO ?? !Stores.setting.serverTabPreferDiskAmount.fetch();
final (r, w) = ss.diskIO.cachedAllSpeed;
return AnimatedSwitcher(
duration: const Duration(milliseconds: 377),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(opacity: animation, child: child);
},
child: _buildIOData(
isSpeed ? '${l10n.read}:\n$r' : 'Total:\n${ss.diskUsage?.size.kb2Str}',
isSpeed ? '${l10n.write}:\n$w' : 'Used:\n${ss.diskUsage?.used.kb2Str}',
onTap: () {
cardNoti.value = cardNoti.value.copyWith(diskIO: !isSpeed);
},
key: ValueKey(isSpeed),
),
);
},
);
}
Widget _buildNet(ServerStatus ss, String id) {
final cardNoti = _getCardNoti(id);
final type = cardNoti.value.net ?? Stores.setting.netViewType.fetch();
final device = ServerProvider.pick(id: id)?.value.spi.custom?.netDev;
final (a, b) = type.build(ss, dev: device);
return AnimatedSwitcher(
duration: const Duration(milliseconds: 377),
transitionBuilder: (c, anim) => FadeTransition(opacity: anim, child: c),
child: _buildIOData(
a,
b,
onTap: () => cardNoti.value = cardNoti.value.copyWith(net: type.next),
key: ValueKey(type),
),
);
}
Widget _buildIOData(
String up,
String down, {
void Function()? onTap,
Key? key,
int maxLines = 2
}) {
final child = Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
up,
style: const TextStyle(fontSize: 10, color: Colors.grey),
textAlign: TextAlign.center,
textScaler: _textFactor,
maxLines: maxLines,
),
const SizedBox(height: 3),
Text(
down,
style: const TextStyle(fontSize: 10, color: Colors.grey),
textAlign: TextAlign.center,
textScaler: _textFactor,
maxLines: maxLines,
)
],
);
if (onTap == null) return child;
return IconButton(
key: key,
padding: const EdgeInsets.symmetric(horizontal: 3),
onPressed: onTap,
icon: child,
);
}
}