Merge branch 'master' of github.com-rustdesk:rustdesk/rustdesk
This commit is contained in:
commit
0cffb3651c
.github/workflows
flutter/lib
81
.github/workflows/flutter-build.yml
vendored
81
.github/workflows/flutter-build.yml
vendored
@ -617,7 +617,7 @@ jobs:
|
|||||||
build-rustdesk-lib-linux-amd64:
|
build-rustdesk-lib-linux-amd64:
|
||||||
needs: [generate-bridge-linux, build-vcpkg-deps-linux]
|
needs: [generate-bridge-linux, build-vcpkg-deps-linux]
|
||||||
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: [self-hosted]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -648,25 +648,26 @@ jobs:
|
|||||||
}
|
}
|
||||||
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
|
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
|
||||||
steps:
|
steps:
|
||||||
- name: Maximize build space
|
#- name: Maximize build space
|
||||||
run: |
|
# run: |
|
||||||
sudo rm -rf /opt/ghc
|
# sudo rm -rf /opt/ghc
|
||||||
sudo rm -rf /usr/local/lib/android
|
# sudo rm -rf /usr/local/lib/android
|
||||||
sudo rm -rf /usr/share/dotnet
|
# sudo rm -rf /usr/share/dotnet
|
||||||
sudo apt update -y
|
# sudo apt update -y
|
||||||
sudo apt install qemu-user-static
|
# sudo apt install qemu-user-static -y
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set Swap Space
|
#- name: Set Swap Space
|
||||||
uses: pierotofy/set-swap-space@master
|
# uses: pierotofy/set-swap-space@master
|
||||||
with:
|
# with:
|
||||||
swap-size-gb: 12
|
# swap-size-gb: 12
|
||||||
|
|
||||||
- name: Free Space
|
- name: Free Space
|
||||||
run: |
|
run: |
|
||||||
df
|
df -h
|
||||||
|
free -m
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
@ -765,7 +766,7 @@ jobs:
|
|||||||
if: ${{ inputs.upload-artifact }}
|
if: ${{ inputs.upload-artifact }}
|
||||||
needs: [generate-bridge-linux, build-vcpkg-deps-linux]
|
needs: [generate-bridge-linux, build-vcpkg-deps-linux]
|
||||||
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: [self-hosted]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -798,25 +799,26 @@ jobs:
|
|||||||
# - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" }
|
# - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" }
|
||||||
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
|
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
|
||||||
steps:
|
steps:
|
||||||
- name: Maximize build space
|
#- name: Maximize build space
|
||||||
run: |
|
# run: |
|
||||||
sudo rm -rf /opt/ghc
|
# sudo rm -rf /opt/ghc
|
||||||
sudo rm -rf /usr/local/lib/android
|
# sudo rm -rf /usr/local/lib/android
|
||||||
sudo rm -rf /usr/share/dotnet
|
# sudo rm -rf /usr/share/dotnet
|
||||||
sudo apt update -y
|
# sudo apt update -y
|
||||||
sudo apt install qemu-user-static
|
# sudo apt install qemu-user-static -y
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set Swap Space
|
#- name: Set Swap Space
|
||||||
uses: pierotofy/set-swap-space@master
|
# uses: pierotofy/set-swap-space@master
|
||||||
with:
|
# with:
|
||||||
swap-size-gb: 12
|
# swap-size-gb: 12
|
||||||
|
|
||||||
- name: Free Space
|
- name: Free Space
|
||||||
run: |
|
run: |
|
||||||
df
|
df -h
|
||||||
|
free -m
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
@ -908,7 +910,7 @@ jobs:
|
|||||||
if: ${{ inputs.upload-artifact }}
|
if: ${{ inputs.upload-artifact }}
|
||||||
needs: [build-vcpkg-deps-linux]
|
needs: [build-vcpkg-deps-linux]
|
||||||
name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: [self-hosted]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -927,25 +929,26 @@ jobs:
|
|||||||
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
|
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Maximize build space
|
#- name: Maximize build space
|
||||||
run: |
|
# run: |
|
||||||
sudo rm -rf /opt/ghc
|
# sudo rm -rf /opt/ghc
|
||||||
sudo rm -rf /usr/local/lib/android
|
# sudo rm -rf /usr/local/lib/android
|
||||||
sudo rm -rf /usr/share/dotnet
|
# sudo rm -rf /usr/share/dotnet
|
||||||
sudo apt update -y
|
# sudo apt update -y
|
||||||
sudo apt install qemu-user-static
|
# sudo apt install qemu-user-static -y
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set Swap Space
|
#- name: Set Swap Space
|
||||||
uses: pierotofy/set-swap-space@master
|
# uses: pierotofy/set-swap-space@master
|
||||||
with:
|
# with:
|
||||||
swap-size-gb: 12
|
# swap-size-gb: 12
|
||||||
|
|
||||||
- name: Free Space
|
- name: Free Space
|
||||||
run: |
|
run: |
|
||||||
df
|
df -h
|
||||||
|
free -m
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
@ -222,7 +222,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
],
|
],
|
||||||
).marginOnly(top: 2),
|
).marginOnly(top: 2),
|
||||||
),
|
),
|
||||||
checkBoxOrActionMoreDesktop(peer),
|
checkBoxOrActionMoreDesktop(peer, isTile: true),
|
||||||
],
|
],
|
||||||
).paddingOnly(left: 10.0, top: 3.0),
|
).paddingOnly(left: 10.0, top: 3.0),
|
||||||
),
|
),
|
||||||
@ -321,7 +321,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
)),
|
)),
|
||||||
]).paddingSymmetric(vertical: 8)),
|
]).paddingSymmetric(vertical: 8)),
|
||||||
checkBoxOrActionMoreDesktop(peer),
|
checkBoxOrActionMoreDesktop(peer, isTile: false),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 12.0),
|
).paddingSymmetric(horizontal: 12.0),
|
||||||
)
|
)
|
||||||
@ -387,7 +387,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget checkBoxOrActionMoreDesktop(Peer peer) {
|
Widget checkBoxOrActionMoreDesktop(Peer peer, {required bool isTile}) {
|
||||||
final PeerTabModel peerTabModel = Provider.of(context);
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
final selected = peerTabModel.isPeerSelected(peer.id);
|
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||||
if (peerTabModel.multiSelectionMode) {
|
if (peerTabModel.multiSelectionMode) {
|
||||||
@ -398,14 +398,15 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
)
|
)
|
||||||
: Icon(Icons.check_box_outline_blank);
|
: Icon(Icons.check_box_outline_blank);
|
||||||
bool last = peerTabModel.isShiftDown && peer.id == peerTabModel.lastId;
|
bool last = peerTabModel.isShiftDown && peer.id == peerTabModel.lastId;
|
||||||
|
double right = isTile ? 4 : 0;
|
||||||
if (last) {
|
if (last) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: MyTheme.accent, width: 1)),
|
border: Border.all(color: MyTheme.accent, width: 1)),
|
||||||
child: icon,
|
child: icon,
|
||||||
);
|
).marginOnly(right: right);
|
||||||
} else {
|
} else {
|
||||||
return icon;
|
return icon.marginOnly(right: right);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return _actionMore(peer);
|
return _actionMore(peer);
|
||||||
|
@ -111,31 +111,26 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
child: _createPeerViewTypeSwitch(context)),
|
child: _createPeerViewTypeSwitch(context)),
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: gFFI.peerTabModel.currentTab == 0,
|
offstage: gFFI.peerTabModel.currentTab == 0,
|
||||||
child: PeerSortDropdown().marginOnly(left: 8),
|
child: PeerSortDropdown(),
|
||||||
),
|
),
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: gFFI.peerTabModel.currentTab != 3,
|
offstage: gFFI.peerTabModel.currentTab != 3,
|
||||||
child: InkWell(
|
child: _hoverAction(
|
||||||
child: Obx(() => Container(
|
context: context,
|
||||||
padding: EdgeInsets.all(4.0),
|
hoverableWhenfalse: hideAbTagsPanel,
|
||||||
decoration: hideAbTagsPanel.value
|
child: Tooltip(
|
||||||
? null
|
message: translate('Toggle Tags'),
|
||||||
: BoxDecoration(
|
child: Icon(
|
||||||
color: Theme.of(context).colorScheme.background,
|
Icons.tag_rounded,
|
||||||
borderRadius: BorderRadius.circular(6)),
|
size: 18,
|
||||||
child: Tooltip(
|
)),
|
||||||
message: translate('Toggle Tags'),
|
|
||||||
child: Icon(
|
|
||||||
Icons.tag_rounded,
|
|
||||||
size: 18,
|
|
||||||
)))),
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await bind.mainSetLocalOption(
|
await bind.mainSetLocalOption(
|
||||||
key: "hideAbTagsPanel",
|
key: "hideAbTagsPanel",
|
||||||
value: hideAbTagsPanel.value ? "" : "Y");
|
value: hideAbTagsPanel.value ? "" : "Y");
|
||||||
hideAbTagsPanel.value = !hideAbTagsPanel.value;
|
hideAbTagsPanel.value = !hideAbTagsPanel.value;
|
||||||
},
|
},
|
||||||
).marginOnly(left: 8),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
@ -208,11 +203,10 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _createRefresh() {
|
Widget _createRefresh() {
|
||||||
if (gFFI.peerTabModel.currentTab < 3) return Offstage();
|
|
||||||
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
||||||
return Container(
|
return Offstage(
|
||||||
padding: EdgeInsets.all(4.0),
|
offstage: gFFI.peerTabModel.currentTab != PeerTabIndex.ab.index,
|
||||||
child: AnimatedRotationWidget(
|
child: RefreshWidget(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (gFFI.peerTabModel.currentTab < entries.length) {
|
if (gFFI.peerTabModel.currentTab < entries.length) {
|
||||||
entries[gFFI.peerTabModel.currentTab].load();
|
entries[gFFI.peerTabModel.currentTab].load();
|
||||||
@ -234,57 +228,45 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
Widget _createPeerViewTypeSwitch(BuildContext context) {
|
Widget _createPeerViewTypeSwitch(BuildContext context) {
|
||||||
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
||||||
final types = [PeerUiType.grid, PeerUiType.list];
|
final types = [PeerUiType.grid, PeerUiType.list];
|
||||||
final hover = false.obs;
|
|
||||||
final deco = BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.background,
|
|
||||||
borderRadius: BorderRadius.circular(5),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Obx(
|
return Obx(() => _hoverAction(
|
||||||
() => Container(
|
context: context,
|
||||||
padding: EdgeInsets.all(4.0),
|
onTap: () async {
|
||||||
decoration: hover.value ? deco : null,
|
final type = types
|
||||||
child: InkWell(
|
.elementAt(peerCardUiType.value == types.elementAt(0) ? 1 : 0);
|
||||||
onHover: (value) => hover.value = value,
|
await bind.setLocalFlutterOption(
|
||||||
onTap: () async {
|
k: 'peer-card-ui-type', v: type.index.toString());
|
||||||
final type = types.elementAt(
|
peerCardUiType.value = type;
|
||||||
peerCardUiType.value == types.elementAt(0) ? 1 : 0);
|
},
|
||||||
await bind.setLocalFlutterOption(
|
child: Tooltip(
|
||||||
k: 'peer-card-ui-type', v: type.index.toString());
|
message: peerCardUiType.value == PeerUiType.grid
|
||||||
peerCardUiType.value = type;
|
? translate('List View')
|
||||||
},
|
: translate('Grid View'),
|
||||||
child: Tooltip(
|
child: Icon(
|
||||||
message: peerCardUiType.value == PeerUiType.grid
|
peerCardUiType.value == PeerUiType.grid
|
||||||
? translate('List View')
|
? Icons.view_list_rounded
|
||||||
: translate('Grid View'),
|
: Icons.grid_view_rounded,
|
||||||
child: Icon(
|
size: 18,
|
||||||
peerCardUiType.value == PeerUiType.grid
|
color: textColor,
|
||||||
? Icons.view_list_rounded
|
))));
|
||||||
: Icons.grid_view_rounded,
|
|
||||||
size: 18,
|
|
||||||
color: textColor,
|
|
||||||
)))),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _createMultiSelection() {
|
Widget _createMultiSelection() {
|
||||||
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
||||||
final model = Provider.of<PeerTabModel>(context);
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
if (model.currentTabCachedPeers.isEmpty) return Offstage();
|
if (model.currentTabCachedPeers.isEmpty) return Offstage();
|
||||||
return Container(
|
return _hoverAction(
|
||||||
padding: EdgeInsets.all(4.0),
|
context: context,
|
||||||
child: InkWell(
|
onTap: () {
|
||||||
onTap: () {
|
model.setMultiSelectionMode(true);
|
||||||
model.setMultiSelectionMode(true);
|
},
|
||||||
},
|
child: Tooltip(
|
||||||
child: Tooltip(
|
message: translate('Select'),
|
||||||
message: translate('Select'),
|
child: Icon(
|
||||||
child: Icon(
|
IconFont.checkbox,
|
||||||
IconFont.checkbox,
|
size: 18,
|
||||||
size: 18,
|
color: textColor,
|
||||||
color: textColor,
|
)),
|
||||||
)),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +288,8 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
|
|
||||||
Widget deleteSelection() {
|
Widget deleteSelection() {
|
||||||
final model = Provider.of<PeerTabModel>(context);
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
return InkWell(
|
return _hoverAction(
|
||||||
|
context: context,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
onSubmit() async {
|
onSubmit() async {
|
||||||
final peers = model.selectedPeers;
|
final peers = model.selectedPeers;
|
||||||
@ -367,7 +350,8 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
return Offstage(
|
return Offstage(
|
||||||
offstage:
|
offstage:
|
||||||
model.currentTab != PeerTabIndex.recent.index, // show based on recent
|
model.currentTab != PeerTabIndex.recent.index, // show based on recent
|
||||||
child: InkWell(
|
child: _hoverAction(
|
||||||
|
context: context,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final peers = model.selectedPeers;
|
final peers = model.selectedPeers;
|
||||||
final favs = (await bind.mainGetFav()).toList();
|
final favs = (await bind.mainGetFav()).toList();
|
||||||
@ -381,10 +365,9 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
showToast(translate('Successful'));
|
showToast(translate('Successful'));
|
||||||
},
|
},
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: translate('Add to Favorites'),
|
message: translate('Add to Favorites'),
|
||||||
child: Icon(model.icons[PeerTabIndex.fav.index]))
|
child: Icon(model.icons[PeerTabIndex.fav.index])),
|
||||||
.marginOnly(left: isMobile ? 15 : 10),
|
).marginOnly(left: isMobile ? 11 : 6),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,7 +376,8 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
return Offstage(
|
return Offstage(
|
||||||
offstage:
|
offstage:
|
||||||
!gFFI.userModel.isLogin || model.currentTab == PeerTabIndex.ab.index,
|
!gFFI.userModel.isLogin || model.currentTab == PeerTabIndex.ab.index,
|
||||||
child: InkWell(
|
child: _hoverAction(
|
||||||
|
context: context,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (gFFI.abModel.isFull(true)) {
|
if (gFFI.abModel.isFull(true)) {
|
||||||
return;
|
return;
|
||||||
@ -409,10 +393,9 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: translate('Add to Address Book'),
|
message: translate('Add to Address Book'),
|
||||||
child: Icon(model.icons[PeerTabIndex.ab.index]))
|
child: Icon(model.icons[PeerTabIndex.ab.index])),
|
||||||
.marginOnly(left: isMobile ? 15 : 10),
|
).marginOnly(left: isMobile ? 11 : 6),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,7 +405,8 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
offstage: !gFFI.userModel.isLogin ||
|
offstage: !gFFI.userModel.isLogin ||
|
||||||
model.currentTab != PeerTabIndex.ab.index ||
|
model.currentTab != PeerTabIndex.ab.index ||
|
||||||
gFFI.abModel.tags.isEmpty,
|
gFFI.abModel.tags.isEmpty,
|
||||||
child: InkWell(
|
child: _hoverAction(
|
||||||
|
context: context,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
editAbTagDialog(List.empty(), (selectedTags) async {
|
editAbTagDialog(List.empty(), (selectedTags) async {
|
||||||
final peers = model.selectedPeers;
|
final peers = model.selectedPeers;
|
||||||
@ -435,7 +419,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
},
|
},
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: translate('Edit Tag'), child: Icon(Icons.tag)))
|
message: translate('Edit Tag'), child: Icon(Icons.tag)))
|
||||||
.marginOnly(left: isMobile ? 15 : 10),
|
.marginOnly(left: isMobile ? 11 : 6),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,26 +435,27 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
return Offstage(
|
return Offstage(
|
||||||
offstage:
|
offstage:
|
||||||
model.selectedPeers.length >= model.currentTabCachedPeers.length,
|
model.selectedPeers.length >= model.currentTabCachedPeers.length,
|
||||||
child: InkWell(
|
child: _hoverAction(
|
||||||
|
context: context,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
model.selectAll();
|
model.selectAll();
|
||||||
},
|
},
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: translate('Select All'), child: Icon(Icons.select_all))
|
message: translate('Select All'), child: Icon(Icons.select_all)),
|
||||||
.marginOnly(left: 10),
|
).marginOnly(left: 6),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget closeSelection() {
|
Widget closeSelection() {
|
||||||
final model = Provider.of<PeerTabModel>(context);
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
return InkWell(
|
return _hoverAction(
|
||||||
|
context: context,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
model.setMultiSelectionMode(false);
|
model.setMultiSelectionMode(false);
|
||||||
},
|
},
|
||||||
child:
|
child:
|
||||||
Tooltip(message: translate('Close'), child: Icon(Icons.clear)))
|
Tooltip(message: translate('Close'), child: Icon(Icons.clear)))
|
||||||
.marginOnly(left: 10);
|
.marginOnly(left: 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,15 +473,15 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return drawer
|
return drawer
|
||||||
? _buildSearchBar()
|
? _buildSearchBar()
|
||||||
: IconButton(
|
: _hoverAction(
|
||||||
alignment: Alignment.centerRight,
|
context: context,
|
||||||
padding: const EdgeInsets.only(right: 2),
|
padding: const EdgeInsets.only(right: 2),
|
||||||
onPressed: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
drawer = true;
|
drawer = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: Tooltip(
|
child: Tooltip(
|
||||||
message: translate('Search'),
|
message: translate('Search'),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.search_rounded,
|
Icons.search_rounded,
|
||||||
@ -514,7 +499,7 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
|
|||||||
extentOffset: peerSearchTextController.value.text.length);
|
extentOffset: peerSearchTextController.value.text.length);
|
||||||
});
|
});
|
||||||
return Container(
|
return Container(
|
||||||
width: 120,
|
width: isMobile ? 120 : 140,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
@ -638,7 +623,8 @@ class _PeerSortDropdownState extends State<PeerSortDropdown> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var menuPos = RelativeRect.fromLTRB(0, 0, 0, 0);
|
var menuPos = RelativeRect.fromLTRB(0, 0, 0, 0);
|
||||||
return InkWell(
|
return _hoverAction(
|
||||||
|
context: context,
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: translate('Sort by'),
|
message: translate('Sort by'),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
@ -659,3 +645,91 @@ class _PeerSortDropdownState extends State<PeerSortDropdown> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RefreshWidget extends StatefulWidget {
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final Widget child;
|
||||||
|
final RxBool? spinning;
|
||||||
|
const RefreshWidget(
|
||||||
|
{super.key, required this.onPressed, required this.child, this.spinning});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RefreshWidget> createState() => RefreshWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class RefreshWidgetState extends State<RefreshWidget> {
|
||||||
|
double turns = 0.0;
|
||||||
|
bool hover = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
widget.spinning?.listen((v) {
|
||||||
|
if (v && mounted) {
|
||||||
|
setState(() {
|
||||||
|
turns += 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final deco = BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
);
|
||||||
|
return AnimatedRotation(
|
||||||
|
turns: turns,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
onEnd: () {
|
||||||
|
if (widget.spinning?.value == true && mounted) {
|
||||||
|
setState(() => turns += 1.0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(4.0),
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 1),
|
||||||
|
decoration: hover ? deco : null,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
if (mounted) setState(() => turns += 1.0);
|
||||||
|
widget.onPressed();
|
||||||
|
},
|
||||||
|
onHover: (value) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
hover = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: widget.child),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _hoverAction(
|
||||||
|
{required BuildContext context,
|
||||||
|
required Widget child,
|
||||||
|
required Function() onTap,
|
||||||
|
GestureTapDownCallback? onTapDown,
|
||||||
|
RxBool? hoverableWhenfalse,
|
||||||
|
EdgeInsetsGeometry padding = const EdgeInsets.all(4.0)}) {
|
||||||
|
final hover = false.obs;
|
||||||
|
final deco = BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
);
|
||||||
|
return Obx(
|
||||||
|
() => Container(
|
||||||
|
padding: padding,
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 1),
|
||||||
|
decoration:
|
||||||
|
(hover.value || hoverableWhenfalse?.value == false) ? deco : null,
|
||||||
|
child: InkWell(
|
||||||
|
onHover: (value) => hover.value = value,
|
||||||
|
onTap: onTap,
|
||||||
|
onTapDown: onTapDown,
|
||||||
|
child: child)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -93,6 +93,8 @@ class AbModel {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
final data = jsonDecode(json['data']);
|
final data = jsonDecode(json['data']);
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
|
final oldOnlineIDs =
|
||||||
|
peers.where((e) => e.online).map((e) => e.id).toList();
|
||||||
tags.clear();
|
tags.clear();
|
||||||
peers.clear();
|
peers.clear();
|
||||||
if (data['tags'] is List) {
|
if (data['tags'] is List) {
|
||||||
@ -106,6 +108,11 @@ class AbModel {
|
|||||||
if (isFull(false)) {
|
if (isFull(false)) {
|
||||||
peers.removeRange(licensedDevices, peers.length);
|
peers.removeRange(licensedDevices, peers.length);
|
||||||
}
|
}
|
||||||
|
// restore online
|
||||||
|
peers
|
||||||
|
.where((e) => oldOnlineIDs.contains(e.id))
|
||||||
|
.map((e) => e.online = true)
|
||||||
|
.toList();
|
||||||
_saveCache(); // save on success
|
_saveCache(); // save on success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user