支持多服务器
@ -14,6 +14,8 @@
|
|||||||
060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060481ED250F404500BC9799 /* SoundsViewController.swift */; };
|
060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060481ED250F404500BC9799 /* SoundsViewController.swift */; };
|
||||||
060481F0250F51CA00BC9799 /* SoundCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060481EF250F51CA00BC9799 /* SoundCell.swift */; };
|
060481F0250F51CA00BC9799 /* SoundCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060481EF250F51CA00BC9799 /* SoundCell.swift */; };
|
||||||
0604F7DF20620D4900B32F09 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0604F7DE20620D4900B32F09 /* ServerManager.swift */; };
|
0604F7DF20620D4900B32F09 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0604F7DE20620D4900B32F09 /* ServerManager.swift */; };
|
||||||
|
06172FDA27F6DAEF002333A4 /* ServerListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06172FD927F6DAEF002333A4 /* ServerListTableViewCell.swift */; };
|
||||||
|
06172FDC27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */; };
|
||||||
062B98C3251B2762004562E7 /* BKButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B98C2251B2762004562E7 /* BKButton.swift */; };
|
062B98C3251B2762004562E7 /* BKButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B98C2251B2762004562E7 /* BKButton.swift */; };
|
||||||
062B98C8251B27AE004562E7 /* UINavigationItem+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */; };
|
062B98C8251B27AE004562E7 /* UINavigationItem+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */; };
|
||||||
0632050F250B6DD4001561EC /* gotosleep.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F0250B6DD1001561EC /* gotosleep.caf */; };
|
0632050F250B6DD4001561EC /* gotosleep.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F0250B6DD1001561EC /* gotosleep.caf */; };
|
||||||
@ -88,6 +90,8 @@
|
|||||||
06802E5320ECC40C00767047 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0661A549204FDA4100965E4E /* Assets.xcassets */; };
|
06802E5320ECC40C00767047 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0661A549204FDA4100965E4E /* Assets.xcassets */; };
|
||||||
06840DBB272298FB001B3193 /* BKColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06840DBA272298FB001B3193 /* BKColor.swift */; };
|
06840DBB272298FB001B3193 /* BKColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06840DBA272298FB001B3193 /* BKColor.swift */; };
|
||||||
06885EB6247FB9880004A303 /* MessageSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */; };
|
06885EB6247FB9880004A303 /* MessageSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */; };
|
||||||
|
068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068EC15727ED99C900D5D11E /* ServerListViewController.swift */; };
|
||||||
|
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068EC15927ED99E700D5D11E /* ServerListViewModel.swift */; };
|
||||||
068F66B3247BD84C00DAD25A /* MessageListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068F66B2247BD84C00DAD25A /* MessageListViewController.swift */; };
|
068F66B3247BD84C00DAD25A /* MessageListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068F66B2247BD84C00DAD25A /* MessageListViewController.swift */; };
|
||||||
06AE3118266F4E2E00B39FBB /* GroupFilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */; };
|
06AE3118266F4E2E00B39FBB /* GroupFilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */; };
|
||||||
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */; };
|
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */; };
|
||||||
@ -174,6 +178,8 @@
|
|||||||
060481ED250F404500BC9799 /* SoundsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundsViewController.swift; sourceTree = "<group>"; };
|
060481ED250F404500BC9799 /* SoundsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundsViewController.swift; sourceTree = "<group>"; };
|
||||||
060481EF250F51CA00BC9799 /* SoundCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundCell.swift; sourceTree = "<group>"; };
|
060481EF250F51CA00BC9799 /* SoundCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundCell.swift; sourceTree = "<group>"; };
|
||||||
0604F7DE20620D4900B32F09 /* ServerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerManager.swift; sourceTree = "<group>"; };
|
0604F7DE20620D4900B32F09 /* ServerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerManager.swift; sourceTree = "<group>"; };
|
||||||
|
06172FD927F6DAEF002333A4 /* ServerListTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListTableViewCellViewModel.swift; sourceTree = "<group>"; };
|
||||||
062B98C2251B2762004562E7 /* BKButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKButton.swift; sourceTree = "<group>"; };
|
062B98C2251B2762004562E7 /* BKButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKButton.swift; sourceTree = "<group>"; };
|
||||||
062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Extension.swift"; sourceTree = "<group>"; };
|
062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Extension.swift"; sourceTree = "<group>"; };
|
||||||
063204F0250B6DD1001561EC /* gotosleep.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = gotosleep.caf; sourceTree = "<group>"; };
|
063204F0250B6DD1001561EC /* gotosleep.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = gotosleep.caf; sourceTree = "<group>"; };
|
||||||
@ -252,6 +258,8 @@
|
|||||||
0683487220510FB20024B6DA /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
|
0683487220510FB20024B6DA /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
|
||||||
06840DBA272298FB001B3193 /* BKColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKColor.swift; sourceTree = "<group>"; };
|
06840DBA272298FB001B3193 /* BKColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKColor.swift; sourceTree = "<group>"; };
|
||||||
06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSettingsViewController.swift; sourceTree = "<group>"; };
|
06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSettingsViewController.swift; sourceTree = "<group>"; };
|
||||||
|
068EC15727ED99C900D5D11E /* ServerListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListViewController.swift; sourceTree = "<group>"; };
|
||||||
|
068EC15927ED99E700D5D11E /* ServerListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListViewModel.swift; sourceTree = "<group>"; };
|
||||||
068F66B2247BD84C00DAD25A /* MessageListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListViewController.swift; sourceTree = "<group>"; };
|
068F66B2247BD84C00DAD25A /* MessageListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListViewController.swift; sourceTree = "<group>"; };
|
||||||
06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupFilterViewController.swift; sourceTree = "<group>"; };
|
06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupFilterViewController.swift; sourceTree = "<group>"; };
|
||||||
06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupFilterViewModel.swift; sourceTree = "<group>"; };
|
06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupFilterViewModel.swift; sourceTree = "<group>"; };
|
||||||
@ -356,6 +364,8 @@
|
|||||||
06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */,
|
06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */,
|
||||||
06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */,
|
06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */,
|
||||||
06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */,
|
06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */,
|
||||||
|
068EC15727ED99C900D5D11E /* ServerListViewController.swift */,
|
||||||
|
068EC15927ED99E700D5D11E /* ServerListViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = Controller;
|
path = Controller;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -383,6 +393,8 @@
|
|||||||
06C2CF242685BDB80034B127 /* SpacerCell.swift */,
|
06C2CF242685BDB80034B127 /* SpacerCell.swift */,
|
||||||
0642B55927EB13F100453D91 /* MutableTextCell.swift */,
|
0642B55927EB13F100453D91 /* MutableTextCell.swift */,
|
||||||
0642B55B27EB149900453D91 /* MutableTextCellViewModel.swift */,
|
0642B55B27EB149900453D91 /* MutableTextCellViewModel.swift */,
|
||||||
|
06172FD927F6DAEF002333A4 /* ServerListTableViewCell.swift */,
|
||||||
|
06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -941,8 +953,11 @@
|
|||||||
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
|
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
|
||||||
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */,
|
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */,
|
||||||
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */,
|
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */,
|
||||||
|
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */,
|
||||||
|
06172FDC27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift in Sources */,
|
||||||
065BE4502563D939002A8CA4 /* SoundCellViewModel.swift in Sources */,
|
065BE4502563D939002A8CA4 /* SoundCellViewModel.swift in Sources */,
|
||||||
06B1158F247BB1FB006D91FB /* Message.swift in Sources */,
|
06B1158F247BB1FB006D91FB /* Message.swift in Sources */,
|
||||||
|
06172FDA27F6DAEF002333A4 /* ServerListTableViewCell.swift in Sources */,
|
||||||
06C595362481160F006B98F3 /* BKLabel.swift in Sources */,
|
06C595362481160F006B98F3 /* BKLabel.swift in Sources */,
|
||||||
0637FA7820E0926D00E80174 /* BarkTargetType.swift in Sources */,
|
0637FA7820E0926D00E80174 /* BarkTargetType.swift in Sources */,
|
||||||
064CABA6256BE9510018155C /* PreviewModel.swift in Sources */,
|
064CABA6256BE9510018155C /* PreviewModel.swift in Sources */,
|
||||||
@ -969,6 +984,7 @@
|
|||||||
06885EB6247FB9880004A303 /* MessageSettingsViewController.swift in Sources */,
|
06885EB6247FB9880004A303 /* MessageSettingsViewController.swift in Sources */,
|
||||||
06F11E7727D9D5FB00F00298 /* QRScannerViewController.swift in Sources */,
|
06F11E7727D9D5FB00F00298 /* QRScannerViewController.swift in Sources */,
|
||||||
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */,
|
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */,
|
||||||
|
068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */,
|
||||||
0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */,
|
0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */,
|
||||||
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */,
|
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */,
|
||||||
06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */,
|
06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */,
|
||||||
|
|||||||
26
Bark/Assets.xcassets/baseline_keyboard_arrow_down_black_24pt.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "baseline_keyboard_arrow_down_black_24pt_1x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_keyboard_arrow_down_black_24pt_2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_keyboard_arrow_down_black_24pt_3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 126 B |
|
After Width: | Height: | Size: 186 B |
|
After Width: | Height: | Size: 243 B |
26
Bark/Assets.xcassets/baseline_remove_black_20pt.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "baseline_remove_black_20pt_1x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_remove_black_20pt_2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_remove_black_20pt_3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Bark/Assets.xcassets/baseline_remove_black_20pt.imageset/baseline_remove_black_20pt_1x.png
vendored
Normal file
|
After Width: | Height: | Size: 83 B |
BIN
Bark/Assets.xcassets/baseline_remove_black_20pt.imageset/baseline_remove_black_20pt_2x.png
vendored
Normal file
|
After Width: | Height: | Size: 107 B |
BIN
Bark/Assets.xcassets/baseline_remove_black_20pt.imageset/baseline_remove_black_20pt_3x.png
vendored
Normal file
|
After Width: | Height: | Size: 111 B |
26
Bark/Assets.xcassets/baseline_wifi_black_20pt.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "baseline_wifi_black_20pt_1x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_wifi_black_20pt_2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_wifi_black_20pt_3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Bark/Assets.xcassets/baseline_wifi_black_20pt.imageset/baseline_wifi_black_20pt_1x.png
vendored
Normal file
|
After Width: | Height: | Size: 263 B |
BIN
Bark/Assets.xcassets/baseline_wifi_black_20pt.imageset/baseline_wifi_black_20pt_2x.png
vendored
Normal file
|
After Width: | Height: | Size: 455 B |
BIN
Bark/Assets.xcassets/baseline_wifi_black_20pt.imageset/baseline_wifi_black_20pt_3x.png
vendored
Normal file
|
After Width: | Height: | Size: 636 B |
26
Bark/Assets.xcassets/baseline_wifi_off_black_20pt.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "baseline_wifi_off_black_20pt_1x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_wifi_off_black_20pt_2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "baseline_wifi_off_black_20pt_3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Bark/Assets.xcassets/baseline_wifi_off_black_20pt.imageset/baseline_wifi_off_black_20pt_1x.png
vendored
Normal file
|
After Width: | Height: | Size: 304 B |
BIN
Bark/Assets.xcassets/baseline_wifi_off_black_20pt.imageset/baseline_wifi_off_black_20pt_2x.png
vendored
Normal file
|
After Width: | Height: | Size: 523 B |
BIN
Bark/Assets.xcassets/baseline_wifi_off_black_20pt.imageset/baseline_wifi_off_black_20pt_3x.png
vendored
Normal file
|
After Width: | Height: | Size: 726 B |
21
Bark/Assets.xcassets/offline.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "offline@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Bark/Assets.xcassets/offline.imageset/offline@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
21
Bark/Assets.xcassets/online.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "online@3X.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Bark/Assets.xcassets/online.imageset/online@3X.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
@ -105,3 +105,17 @@ badge = "Modify a Notification Badge";
|
|||||||
badgeNotice = "The notification badge can be set with a number.";
|
badgeNotice = "The notification badge can be set with a number.";
|
||||||
|
|
||||||
deviceTokenInfo = "Token for APNs, click to copy to clipboard.";
|
deviceTokenInfo = "Token for APNs, click to copy to clipboard.";
|
||||||
|
|
||||||
|
|
||||||
|
serverList = "Server List";
|
||||||
|
copyAddressAndKey = "Copy address and key";
|
||||||
|
resetKey = "Reset or Restore Key";
|
||||||
|
resetKeyDesc = "Click confirm to reset, enter the old Key to restore";
|
||||||
|
resetKeyPlaceholder = "Do not enter or enter the old Key";
|
||||||
|
confirm = "Confirm";
|
||||||
|
deleteServer = "Delete server";
|
||||||
|
confirmDeleteServer = "Sure to delete the server?";
|
||||||
|
deleteFailed = "Cannot be deleted, the server must keep at least one";
|
||||||
|
deletedSuccessfully = "Deleted successfully";
|
||||||
|
resetFailed = "Reset failed";
|
||||||
|
resetFailed2 = "Failed to reset, did not get DeviceToken";
|
||||||
|
|||||||
@ -108,3 +108,17 @@ badge = "设置角标";
|
|||||||
badgeNotice = "可以为推送设置角标,角标可以是任意数字。";
|
badgeNotice = "可以为推送设置角标,角标可以是任意数字。";
|
||||||
|
|
||||||
deviceTokenInfo = "用于 APNs 推送的 Token,请勿泄露,点击可复制到剪切板。";
|
deviceTokenInfo = "用于 APNs 推送的 Token,请勿泄露,点击可复制到剪切板。";
|
||||||
|
|
||||||
|
|
||||||
|
serverList = "服务器列表";
|
||||||
|
copyAddressAndKey = "复制地址和Key";
|
||||||
|
resetKey = "重置或还原Key";
|
||||||
|
resetKeyDesc = "重置直接点确定,还原请先输入旧的Key";
|
||||||
|
resetKeyPlaceholder = "不输入或输入旧的Key";
|
||||||
|
confirm = "确定";
|
||||||
|
deleteServer = "删除服务器";
|
||||||
|
confirmDeleteServer = "确定删除服务器吗?";
|
||||||
|
deleteFailed = "不能删除,服务器至少需保留一个";
|
||||||
|
deletedSuccessfully = "删除成功";
|
||||||
|
resetFailed = "重置失败";
|
||||||
|
resetFailed2 = "重置失败,没有获取到 DeviceToken";
|
||||||
|
|||||||
@ -81,6 +81,12 @@ class ServerManager: NSObject {
|
|||||||
saveServers()
|
saveServers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateServerKey(server: Server) {
|
||||||
|
let foundServer = self.servers.first{ $0.id == server.id }
|
||||||
|
foundServer?.key = server.key
|
||||||
|
saveServers()
|
||||||
|
}
|
||||||
|
|
||||||
/// 移除 server,移除后如果 server 为`空`, `会新增一个默认server`
|
/// 移除 server,移除后如果 server 为`空`, `会新增一个默认server`
|
||||||
func removeServer(server: Server) {
|
func removeServer(server: Server) {
|
||||||
self.servers.removeAll { $0.id == server.id }
|
self.servers.removeAll { $0.id == server.id }
|
||||||
@ -88,6 +94,8 @@ class ServerManager: NSObject {
|
|||||||
self.servers.append(
|
self.servers.append(
|
||||||
Server(id: UUID().uuidString, address: defaultServer, key: "")
|
Server(id: UUID().uuidString, address: defaultServer, key: "")
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
if self.currentServer.id == server.id {
|
||||||
self.setCurrentServer(serverId: self.servers[0].id)
|
self.setCurrentServer(serverId: self.servers[0].id)
|
||||||
}
|
}
|
||||||
saveServers()
|
saveServers()
|
||||||
@ -140,7 +148,9 @@ class ServerManager: NSObject {
|
|||||||
.merge(apis)
|
.merge(apis)
|
||||||
.subscribe { result in
|
.subscribe { result in
|
||||||
// 更新所有的 server 状态
|
// 更新所有的 server 状态
|
||||||
result.0.key = result.1
|
if result.2 == .ok {
|
||||||
|
result.0.key = result.1
|
||||||
|
}
|
||||||
result.0.state = result.2
|
result.0.state = result.2
|
||||||
|
|
||||||
// 通知客户端 当前 server 状态改变
|
// 通知客户端 当前 server 状态改变
|
||||||
|
|||||||
@ -51,7 +51,7 @@ class HomeViewController: BaseViewController<HomeViewModel> {
|
|||||||
|
|
||||||
navigationItem.setBarButtonItems(items: [
|
navigationItem.setBarButtonItems(items: [
|
||||||
UIBarButtonItem(customView: newButton),
|
UIBarButtonItem(customView: newButton),
|
||||||
// UIBarButtonItem(customView: serversButton),
|
UIBarButtonItem(customView: serversButton),
|
||||||
], position: .right)
|
], position: .right)
|
||||||
|
|
||||||
self.view.addSubview(self.tableView)
|
self.view.addSubview(self.tableView)
|
||||||
@ -98,6 +98,7 @@ class HomeViewController: BaseViewController<HomeViewModel> {
|
|||||||
let output = viewModel.transform(
|
let output = viewModel.transform(
|
||||||
input: HomeViewModel.Input(
|
input: HomeViewModel.Input(
|
||||||
addCustomServerTap: newButton.rx.tap.asDriver(),
|
addCustomServerTap: newButton.rx.tap.asDriver(),
|
||||||
|
serverListTap: serversButton.rx.tap.asDriver(),
|
||||||
viewDidAppear: self.rx.methodInvoked(#selector(viewDidAppear(_:)))
|
viewDidAppear: self.rx.methodInvoked(#selector(viewDidAppear(_:)))
|
||||||
.map { _ in () }
|
.map { _ in () }
|
||||||
.asDriver(onErrorDriveWith: .empty()),
|
.asDriver(onErrorDriveWith: .empty()),
|
||||||
@ -132,6 +133,11 @@ class HomeViewController: BaseViewController<HomeViewModel> {
|
|||||||
self?.pushViewModel(viewModel: viewModel)
|
self?.pushViewModel(viewModel: viewModel)
|
||||||
})
|
})
|
||||||
.disposed(by: rx.disposeBag)
|
.disposed(by: rx.disposeBag)
|
||||||
|
output.present
|
||||||
|
.drive(onNext: { [weak self] viewModel in
|
||||||
|
self?.presentViewModel(viewModel: viewModel)
|
||||||
|
})
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
// 通过ping服务器,判断 clienState
|
// 通过ping服务器,判断 clienState
|
||||||
output.clienStateChanged
|
output.clienStateChanged
|
||||||
@ -201,4 +207,13 @@ class HomeViewController: BaseViewController<HomeViewModel> {
|
|||||||
self.navigationController?.pushViewController(viewController, animated: true)
|
self.navigationController?.pushViewController(viewController, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func presentViewModel(viewModel: ViewModel) {
|
||||||
|
if let viewModel = viewModel as? ServerListViewModel {
|
||||||
|
let controller = BarkSnackbarController(
|
||||||
|
rootViewController: BarkNavigationController(
|
||||||
|
rootViewController: ServerListViewController(viewModel: viewModel)))
|
||||||
|
self.navigationController?.present(controller, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import UserNotifications
|
|||||||
class HomeViewModel: ViewModel, ViewModelType {
|
class HomeViewModel: ViewModel, ViewModelType {
|
||||||
struct Input {
|
struct Input {
|
||||||
let addCustomServerTap: Driver<Void>
|
let addCustomServerTap: Driver<Void>
|
||||||
|
let serverListTap: Driver<Void>
|
||||||
let viewDidAppear: Driver<Void>
|
let viewDidAppear: Driver<Void>
|
||||||
let start: Driver<Void>
|
let start: Driver<Void>
|
||||||
let clientState: Driver<Client.ClienState>
|
let clientState: Driver<Client.ClienState>
|
||||||
@ -26,6 +27,7 @@ class HomeViewModel: ViewModel, ViewModelType {
|
|||||||
struct Output {
|
struct Output {
|
||||||
let previews: Driver<[SectionModel<String, PreviewCardCellViewModel>]>
|
let previews: Driver<[SectionModel<String, PreviewCardCellViewModel>]>
|
||||||
let push: Driver<ViewModel>
|
let push: Driver<ViewModel>
|
||||||
|
let present: Driver<ViewModel>
|
||||||
let title: Driver<String>
|
let title: Driver<String>
|
||||||
let clienStateChanged: Driver<Client.ClienState>
|
let clienStateChanged: Driver<Client.ClienState>
|
||||||
let tableViewHidden: Driver<Bool>
|
let tableViewHidden: Driver<Bool>
|
||||||
@ -178,10 +180,24 @@ class HomeViewModel: ViewModel, ViewModelType {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.disposed(by: rx.disposeBag)
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
let serverList = input.serverListTap.map { ServerListViewModel() as ViewModel }
|
||||||
|
|
||||||
|
// 服务器发生了改变
|
||||||
|
serverList
|
||||||
|
.flatMapLatest { model -> Driver<Server> in
|
||||||
|
(model as! ServerListViewModel).currentServerChanged.asDriver(onErrorDriveWith: .empty())
|
||||||
|
}
|
||||||
|
.map { server -> String in
|
||||||
|
(try? server.address.asURL().host) ?? ""
|
||||||
|
}
|
||||||
|
.drive(title)
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
return Output(
|
return Output(
|
||||||
previews: Driver.just([sectionModel]),
|
previews: Driver.just([sectionModel]),
|
||||||
push: Driver<ViewModel>.merge(customServer, noticeTap),
|
push: Driver<ViewModel>.merge(customServer, noticeTap),
|
||||||
|
present: serverList.asDriver(),
|
||||||
title: title.asDriver(),
|
title: title.asDriver(),
|
||||||
clienStateChanged: clienState.asDriver(onErrorDriveWith: .empty()),
|
clienStateChanged: clienState.asDriver(onErrorDriveWith: .empty()),
|
||||||
tableViewHidden: tableViewHidden,
|
tableViewHidden: tableViewHidden,
|
||||||
|
|||||||
165
Controller/ServerListViewController.swift
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
//
|
||||||
|
// ServerListViewController.swift
|
||||||
|
// Bark
|
||||||
|
//
|
||||||
|
// Created by huangfeng on 2022/3/25.
|
||||||
|
// Copyright © 2022 Fin. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Material
|
||||||
|
import RxCocoa
|
||||||
|
import RxDataSources
|
||||||
|
import RxSwift
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
enum ServerActionType {
|
||||||
|
case copy
|
||||||
|
case reset(key: String?)
|
||||||
|
case delete
|
||||||
|
}
|
||||||
|
|
||||||
|
func == (lhs: ServerActionType, rhs: ServerActionType) -> Bool {
|
||||||
|
switch (lhs, rhs) {
|
||||||
|
case (.copy, .copy),
|
||||||
|
(.delete, .delete):
|
||||||
|
return true
|
||||||
|
case let (.reset(a), .reset(b)):
|
||||||
|
return a == b
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerListViewController: BaseViewController<ServerListViewModel> {
|
||||||
|
let closeButton: BKButton = {
|
||||||
|
let closeButton = BKButton()
|
||||||
|
closeButton.setImage(UIImage(named: "baseline_keyboard_arrow_down_black_24pt")?.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
closeButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
|
||||||
|
closeButton.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
|
||||||
|
closeButton.tintColor = BKColor.grey.darken4
|
||||||
|
return closeButton
|
||||||
|
}()
|
||||||
|
|
||||||
|
let tableView: UITableView = {
|
||||||
|
let tableView = UITableView()
|
||||||
|
tableView.separatorStyle = .none
|
||||||
|
tableView.backgroundColor = BKColor.background.primary
|
||||||
|
tableView.register(ServerListTableViewCell.self, forCellReuseIdentifier: "\(ServerListTableViewCell.self)")
|
||||||
|
tableView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
|
||||||
|
return tableView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override func makeUI() {
|
||||||
|
self.title = NSLocalizedString("serverList")
|
||||||
|
|
||||||
|
navigationItem.setRightBarButtonItem(item: UIBarButtonItem(customView: closeButton))
|
||||||
|
|
||||||
|
self.view.addSubview(tableView)
|
||||||
|
tableView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
closeButton.rx.tap.subscribe {[weak self] in
|
||||||
|
self?.dismiss(animated: true, completion: nil)
|
||||||
|
} onError: { _ in
|
||||||
|
|
||||||
|
}.disposed(by: rx.disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func bindViewModel() {
|
||||||
|
|
||||||
|
let action = getServerAction()
|
||||||
|
|
||||||
|
// 复制 server
|
||||||
|
let copyServer = action.filter { $0.1 == ServerActionType.copy }
|
||||||
|
.map { $0.0 }.asDriver(onErrorDriveWith: .empty())
|
||||||
|
|
||||||
|
// 删除 server
|
||||||
|
let deleteServer = action.filter { $0.1 == ServerActionType.delete }
|
||||||
|
.map { $0.0 }.asDriver(onErrorDriveWith: .empty())
|
||||||
|
|
||||||
|
// 重置 server key
|
||||||
|
let resetServer = action.compactMap { r -> (Server, String?)? in
|
||||||
|
if case let ServerActionType.reset(key) = r.1 {
|
||||||
|
return (r.0, key)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}.asDriver(onErrorDriveWith: .empty())
|
||||||
|
|
||||||
|
let output = viewModel.transform(input: ServerListViewModel.Input(
|
||||||
|
copyServer: copyServer,
|
||||||
|
deleteServer: deleteServer,
|
||||||
|
resetServer: resetServer
|
||||||
|
))
|
||||||
|
|
||||||
|
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, ServerListTableViewCellViewModel>> { _, tableView, _, item -> UITableViewCell in
|
||||||
|
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(ServerListTableViewCell.self)") as? ServerListTableViewCell {
|
||||||
|
cell.bindViewModel(model: item)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
return UITableViewCell()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableView数据源
|
||||||
|
output.servers
|
||||||
|
.drive(self.tableView.rx.items(dataSource: dataSource))
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 复制文本
|
||||||
|
output.copy
|
||||||
|
.drive(onNext: { [weak self] text in
|
||||||
|
UIPasteboard.general.string = text
|
||||||
|
self?.showSnackbar(text: NSLocalizedString("Copy"))
|
||||||
|
})
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 弹出提示
|
||||||
|
output.showSnackbar
|
||||||
|
.drive(onNext: { [weak self] text in
|
||||||
|
self?.showSnackbar(text: text)
|
||||||
|
})
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServerAction() -> Driver<(Server, ServerActionType)> {
|
||||||
|
|
||||||
|
return tableView.rx
|
||||||
|
.modelSelected(ServerListTableViewCellViewModel.self)
|
||||||
|
.flatMapLatest { viewModel -> PublishRelay<(Server, ServerActionType)> in
|
||||||
|
let relay = PublishRelay<(Server, ServerActionType)>()
|
||||||
|
|
||||||
|
let alertController = UIAlertController(title: nil, message: "\(viewModel.address.value)", preferredStyle: .actionSheet)
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("copyAddressAndKey"), style: .default, handler: { _ in
|
||||||
|
relay.accept((viewModel.server, .copy))
|
||||||
|
}))
|
||||||
|
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("resetKey"), style: .default, handler: { _ in
|
||||||
|
let alertController = UIAlertController(title: NSLocalizedString("resetKey"), message: NSLocalizedString("resetKeyDesc"), preferredStyle: .alert)
|
||||||
|
alertController.addTextField { textField in
|
||||||
|
textField.placeholder = NSLocalizedString("resetKeyPlaceholder")
|
||||||
|
}
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("confirm"), style: .default, handler: { _ in
|
||||||
|
relay.accept((viewModel.server, .reset(key: alertController.textFields?.first?.text)))
|
||||||
|
}))
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil))
|
||||||
|
self.navigationController?.present(alertController, animated: true, completion: nil)
|
||||||
|
}))
|
||||||
|
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("deleteServer"), style: .destructive, handler: { _ in
|
||||||
|
|
||||||
|
let alertController = UIAlertController(title: nil, message: NSLocalizedString("confirmDeleteServer"), preferredStyle: .alert)
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("confirm"), style: .destructive, handler: { _ in
|
||||||
|
relay.accept((viewModel.server, .delete))
|
||||||
|
}))
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil))
|
||||||
|
self.navigationController?.present(alertController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
}))
|
||||||
|
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil))
|
||||||
|
self.navigationController?.present(alertController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
return relay
|
||||||
|
}.asDriver(onErrorDriveWith: .empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
158
Controller/ServerListViewModel.swift
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
//
|
||||||
|
// ServerListViewModel.swift
|
||||||
|
// Bark
|
||||||
|
//
|
||||||
|
// Created by huangfeng on 2022/3/25.
|
||||||
|
// Copyright © 2022 Fin. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Differentiator
|
||||||
|
import Foundation
|
||||||
|
import Moya
|
||||||
|
import RxCocoa
|
||||||
|
import RxSwift
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
class ServerListViewModel: ViewModel, ViewModelType {
|
||||||
|
struct Input {
|
||||||
|
let copyServer: Driver<Server>
|
||||||
|
let deleteServer: Driver<Server>
|
||||||
|
let resetServer: Driver<(Server, String?)>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Output {
|
||||||
|
let servers: Driver<[SectionModel<String, ServerListTableViewCellViewModel>]>
|
||||||
|
let showSnackbar: Driver<String>
|
||||||
|
let copy: Driver<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentServerChanged = PublishRelay<Server>()
|
||||||
|
|
||||||
|
func transform(input: Input) -> Output {
|
||||||
|
// 弹出提示消息
|
||||||
|
let showSnackbar = PublishRelay<String>()
|
||||||
|
|
||||||
|
// 复制 Server
|
||||||
|
let copy = input.copyServer.map { server -> String in
|
||||||
|
"\(server.address)/\(server.key)/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除检查,需要至少保留一个服务器
|
||||||
|
let deleteCheck = input.deleteServer.map { server -> Server? in
|
||||||
|
if ServerManager.shared.servers.count > 1 {
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}.asObservable().share()
|
||||||
|
|
||||||
|
// 删除检查错误提示
|
||||||
|
deleteCheck.filter { $0 == nil }
|
||||||
|
.map { _ in NSLocalizedString("deleteFailed") }
|
||||||
|
.bind(to: showSnackbar)
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 对即将删除的服务器发送错误的 deviceToken,防止服务器依然保留旧的推送链接。
|
||||||
|
let delete = deleteCheck.compactMap { $0 }
|
||||||
|
.flatMapLatest { server -> Observable<Result<JSON, ApiError>>in
|
||||||
|
if server.key.count > 0 {
|
||||||
|
return BarkApi.provider
|
||||||
|
.request(.register(address: server.address, key: server.key, devicetoken: "deleted"))
|
||||||
|
.filterResponseError()
|
||||||
|
}
|
||||||
|
return Observable.just(Result<JSON, ApiError>.success(JSON()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务器远程注销后,再本地删除
|
||||||
|
// withLatestFrom 将在 delete 产生新的事件时,
|
||||||
|
// 取 input.deleteServer 最后一个事件的元素。(这里是对应的 server )
|
||||||
|
let serverDeleted = delete.withLatestFrom(input.deleteServer)
|
||||||
|
.map { server in
|
||||||
|
ServerManager.shared.removeServer(server: server)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹出删除提示
|
||||||
|
serverDeleted.map { NSLocalizedString("deletedSuccessfully") }
|
||||||
|
.bind(to: showSnackbar)
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 重置服务器之前,先检查 DeviceToken
|
||||||
|
let resetServer = input.resetServer
|
||||||
|
.map { ($0.0, $0.1, Client.shared.deviceToken.value) }
|
||||||
|
.asObservable()
|
||||||
|
|
||||||
|
// 重置检查错误提示
|
||||||
|
resetServer.filter { ($0.2?.count ?? 0) <= 0 }
|
||||||
|
.map { _ in NSLocalizedString("resetFailed2") }
|
||||||
|
.bind(to: showSnackbar)
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 发送重置请求
|
||||||
|
let serverReseted = resetServer.filter { ($0.2?.count ?? 0) > 0 }
|
||||||
|
.flatMapLatest { r -> Observable<Result<JSON, ApiError>> in
|
||||||
|
let server = r.0
|
||||||
|
let newKey = r.1
|
||||||
|
let deviceToken = r.2!
|
||||||
|
return BarkApi.provider
|
||||||
|
.request(.register(address: server.address, key: newKey, devicetoken: deviceToken))
|
||||||
|
.filterResponseError()
|
||||||
|
}
|
||||||
|
.map { result -> String? in
|
||||||
|
switch result {
|
||||||
|
case .success(let json):
|
||||||
|
return json["data", "key"].rawString()
|
||||||
|
case .failure:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}.share()
|
||||||
|
|
||||||
|
// 重置成功后,更新本地服务器列表
|
||||||
|
let serverResetSuccess = serverReseted.compactMap { $0 }.withLatestFrom(input.resetServer) { newKey, r in
|
||||||
|
let server = r.0
|
||||||
|
server.key = newKey
|
||||||
|
ServerManager.shared.updateServerKey(server: server)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置失败提示
|
||||||
|
serverReseted.filter { $0 == nil }
|
||||||
|
.map { _ in NSLocalizedString("resetFailed") }
|
||||||
|
.bind(to: showSnackbar)
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 服务器列表
|
||||||
|
let servers = Observable
|
||||||
|
.merge(
|
||||||
|
Observable.just(()),
|
||||||
|
serverDeleted,
|
||||||
|
serverResetSuccess
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
[SectionModel(
|
||||||
|
model: "servers",
|
||||||
|
items: ServerManager.shared.servers.map { ServerListTableViewCellViewModel(server: $0) }
|
||||||
|
)]
|
||||||
|
}.asDriver(onErrorDriveWith: .empty())
|
||||||
|
|
||||||
|
// 当前服务器有改动
|
||||||
|
let serverChanged = Observable.merge(serverDeleted, serverResetSuccess)
|
||||||
|
.share()
|
||||||
|
|
||||||
|
serverChanged.map {
|
||||||
|
ServerManager.shared.currentServer
|
||||||
|
}
|
||||||
|
.bind(to: self.currentServerChanged)
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
// 服务器改变时,同步 Client.shared.state。
|
||||||
|
serverChanged.map {
|
||||||
|
ServerManager.shared.currentServer.state
|
||||||
|
}
|
||||||
|
.bind(to: Client.shared.state)
|
||||||
|
.disposed(by: rx.disposeBag)
|
||||||
|
|
||||||
|
return Output(
|
||||||
|
servers: servers,
|
||||||
|
showSnackbar: showSnackbar.asDriver(onErrorDriveWith: .empty()),
|
||||||
|
copy: copy
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
117
View/ServerListTableViewCell.swift
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
//
|
||||||
|
// ServerListTableViewCell.swift
|
||||||
|
// Bark
|
||||||
|
//
|
||||||
|
// Created by huangfeng on 2022/4/1.
|
||||||
|
// Copyright © 2022 Fin. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Material
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class ServerListTableViewCell: BaseTableViewCell<ServerListTableViewCellViewModel> {
|
||||||
|
|
||||||
|
let backgroundPanel: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.layer.cornerRadius = 3
|
||||||
|
view.clipsToBounds = true
|
||||||
|
view.backgroundColor = BKColor.background.secondary
|
||||||
|
view.layer.cornerRadius = 25
|
||||||
|
view.clipsToBounds = true
|
||||||
|
view.layer.borderColor = BKColor.grey.lighten3.cgColor
|
||||||
|
view.layer.borderWidth = 1
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
let addressLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = RobotoFont.medium(with: 14)
|
||||||
|
label.textColor = BKColor.grey.darken4
|
||||||
|
label.numberOfLines = 0
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
let keyLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = RobotoFont.regular(with: 12)
|
||||||
|
label.textColor = BKColor.grey.darken4
|
||||||
|
label.numberOfLines = 0
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
let stateImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.contentMode = .scaleAspectFit
|
||||||
|
imageView.layer.cornerRadius = 15
|
||||||
|
imageView.clipsToBounds = true
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
var state: Bool = false {
|
||||||
|
didSet {
|
||||||
|
if state {
|
||||||
|
stateImageView.image = UIImage(named: "online")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
stateImageView.image = UIImage(named: "offline")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
self.selectionStyle = .none
|
||||||
|
self.backgroundColor = BKColor.background.primary
|
||||||
|
|
||||||
|
addSubview(backgroundPanel)
|
||||||
|
addSubview(stateImageView)
|
||||||
|
addSubview(addressLabel)
|
||||||
|
addSubview(keyLabel)
|
||||||
|
|
||||||
|
backgroundPanel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(18)
|
||||||
|
make.right.equalToSuperview().offset(-18)
|
||||||
|
make.top.equalToSuperview().offset(5)
|
||||||
|
make.bottom.equalToSuperview().offset(-5)
|
||||||
|
make.height.equalTo(50)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateImageView.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalTo(backgroundPanel)
|
||||||
|
make.left.equalTo(backgroundPanel).offset(13)
|
||||||
|
make.width.height.equalTo(30)
|
||||||
|
}
|
||||||
|
addressLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(stateImageView.snp.right).offset(8)
|
||||||
|
make.top.equalTo(backgroundPanel).offset(8)
|
||||||
|
make.right.equalTo(backgroundPanel).offset(-8)
|
||||||
|
}
|
||||||
|
keyLabel.snp.makeConstraints { make in
|
||||||
|
make.top.equalTo(addressLabel.snp.bottom).offset(1)
|
||||||
|
make.left.right.equalTo(addressLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func bindViewModel(model: ServerListTableViewCellViewModel) {
|
||||||
|
super.bindViewModel(model: model)
|
||||||
|
|
||||||
|
model.address
|
||||||
|
.bind(to: addressLabel.rx.text)
|
||||||
|
.disposed(by: rx.reuseBag)
|
||||||
|
|
||||||
|
model.key
|
||||||
|
.bind(to: keyLabel.rx.text)
|
||||||
|
.disposed(by: rx.reuseBag)
|
||||||
|
|
||||||
|
model.state
|
||||||
|
.subscribe { state in
|
||||||
|
self.state = state
|
||||||
|
} onError: { _ in }
|
||||||
|
.disposed(by: rx.reuseBag)
|
||||||
|
}
|
||||||
|
}
|
||||||
30
View/ServerListTableViewCellViewModel.swift
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// ServerListTableViewCellViewModel.swift
|
||||||
|
// Bark
|
||||||
|
//
|
||||||
|
// Created by huangfeng on 2022/4/1.
|
||||||
|
// Copyright © 2022 Fin. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import RxRelay
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class ServerListTableViewCellViewModel: ViewModel {
|
||||||
|
let server: Server
|
||||||
|
|
||||||
|
let address: BehaviorRelay<String>
|
||||||
|
let key: BehaviorRelay<String>
|
||||||
|
let state: BehaviorRelay<Bool>
|
||||||
|
|
||||||
|
init(server: Server) {
|
||||||
|
self.server = server
|
||||||
|
|
||||||
|
self.address = BehaviorRelay<String>(value: {
|
||||||
|
URL(string: server.address)?.host ?? "Invalid Server"
|
||||||
|
}())
|
||||||
|
self.key = BehaviorRelay<String>(value: !server.key.isEmpty ? server.key : "none")
|
||||||
|
self.state = BehaviorRelay<Bool>(value: server.state == .ok)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
}
|
||||||