Compare commits

...

25 Commits

Author SHA1 Message Date
Fin
95676acc30 修复导入时subtitle丢失的问题。
fix: #325
2025-10-13 09:40:48 +08:00
Fin
6343b44120 调整打包系统版本 2025-10-11 11:19:43 +08:00
Fin
8fca7486df iOS26适配 2025-10-11 11:17:55 +08:00
Fin
3b63fe3045 update doc 2025-09-28 08:57:54 +08:00
Fin
f611682227 update doc 2025-09-25 11:17:20 +08:00
Fin
2d81639fa3 update doc 2025-09-25 11:16:26 +08:00
Fin
fd735801cf update doc 2025-09-25 11:15:28 +08:00
Fin
02399d9759 update doc 2025-09-25 11:13:43 +08:00
Fin
b5b61f4686 修复布局约束警告 2025-09-08 14:34:55 +08:00
Fin
df9e971fba 优化查找未使用翻译的脚本 2025-09-05 12:01:15 +08:00
Fin
1e340c6578 修改本地化方法 2025-09-05 11:47:32 +08:00
Fin
91bc905628 适配无障碍 2025-09-05 10:26:28 +08:00
Fin
f1127df001 设置Xcode版本 2025-09-03 10:17:40 +08:00
Fin
4f8d72c5f6 修改 cocoapods source 2025-09-03 10:14:41 +08:00
Fin
0a8b3f741a 消息记录页面适配无障碍 2025-09-02 11:36:33 +08:00
Feng
1e20be0297
更新文档 2025-09-01 02:02:21 -07:00
Fin
b1969edf34 清理未使用的翻译字段 2025-09-01 15:30:44 +08:00
Fin
37a3c791a2 修复复制内容不完整的问题
fix: #314
2025-08-26 14:31:44 +08:00
Fin
b1b93afe9b 更新文档 2025-07-31 10:55:49 +08:00
Fin
98c48c52a3 更新文档 2025-07-30 14:52:19 +08:00
Fin
4f57fdb082 更新文档 2025-07-30 14:50:10 +08:00
Fin
d4ae4d38e5 更新文档 2025-07-21 17:59:10 +08:00
Fin
659c10fff8 更新文档 2025-07-21 17:54:13 +08:00
Fin
1903797c8d 支持更新或撤回已发送的推送。 2025-07-17 17:42:18 +08:00
Fin
fcdcaceede 添加 Xcode 构建服务器配置文件 2025-07-17 17:32:54 +08:00
55 changed files with 797 additions and 981 deletions

View File

@ -23,7 +23,7 @@ jobs:
- name: Select Xcode Version - name: Select Xcode Version
uses: maxim-lobanov/setup-xcode@v1 uses: maxim-lobanov/setup-xcode@v1
with: with:
xcode-version: '16.1' xcode-version: 'latest-stable'
- name: Setup ruby - name: Setup ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1

View File

@ -104,7 +104,8 @@
066E0C8C2BB6AC9A00873838 /* AddSoundCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 066E0C8B2BB6AC9A00873838 /* AddSoundCell.swift */; }; 066E0C8C2BB6AC9A00873838 /* AddSoundCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 066E0C8B2BB6AC9A00873838 /* AddSoundCell.swift */; };
0672CB06256903F700570C9D /* MessageListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0672CB05256903F700570C9D /* MessageListViewModel.swift */; }; 0672CB06256903F700570C9D /* MessageListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0672CB05256903F700570C9D /* MessageListViewModel.swift */; };
06787C392A710568008ABDD7 /* GesturePassTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06787C382A710568008ABDD7 /* GesturePassTextView.swift */; }; 06787C392A710568008ABDD7 /* GesturePassTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06787C382A710568008ABDD7 /* GesturePassTextView.swift */; };
06787C3B2AB82BDB008ABDD7 /* CrashReportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06787C3A2AB82BDB008ABDD7 /* CrashReportViewController.swift */; }; 067AFB1C2E5D8BE300AE78E7 /* UNNotificationContent+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067AFB1B2E5D8BE300AE78E7 /* UNNotificationContent+Extension.swift */; };
067AFB1D2E5D8BED00AE78E7 /* UNNotificationContent+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067AFB1B2E5D8BE300AE78E7 /* UNNotificationContent+Extension.swift */; };
067B2EB525693E38008B6BE1 /* MessageSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067B2EB425693E38008B6BE1 /* MessageSection.swift */; }; 067B2EB525693E38008B6BE1 /* MessageSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067B2EB425693E38008B6BE1 /* MessageSection.swift */; };
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 */; };
@ -117,6 +118,8 @@
068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068EC15727ED99C900D5D11E /* ServerListViewController.swift */; }; 068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068EC15727ED99C900D5D11E /* ServerListViewController.swift */; };
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068EC15927ED99E700D5D11E /* ServerListViewModel.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 */; };
069332222E6A8E3100F9387F /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0603706A20E20A7C00F4CA05 /* String+Extension.swift */; };
069332232E6A8E3100F9387F /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0603706A20E20A7C00F4CA05 /* String+Extension.swift */; };
0699473D2D223094008D5E40 /* CustomTapTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0699473C2D223094008D5E40 /* CustomTapTextView.swift */; }; 0699473D2D223094008D5E40 /* CustomTapTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0699473C2D223094008D5E40 /* CustomTapTextView.swift */; };
06B1158D247BA6D5006D91FB /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06B1158C247BA6D5006D91FB /* CloudKit.framework */; }; 06B1158D247BA6D5006D91FB /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06B1158C247BA6D5006D91FB /* CloudKit.framework */; };
06B1158F247BB1FB006D91FB /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B1158E247BB1FB006D91FB /* Message.swift */; }; 06B1158F247BB1FB006D91FB /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B1158E247BB1FB006D91FB /* Message.swift */; };
@ -134,10 +137,10 @@
06BCAE582CDB19420092867A /* GroupMuteSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */; }; 06BCAE582CDB19420092867A /* GroupMuteSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */; };
06BCAE5B2CDB25120092867A /* MuteProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE5A2CDB25120092867A /* MuteProcessor.swift */; }; 06BCAE5B2CDB25120092867A /* MuteProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE5A2CDB25120092867A /* MuteProcessor.swift */; };
06BD4DAA2901352E003364DB /* Object+Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BD4DA92901352E003364DB /* Object+Dictionary.swift */; }; 06BD4DAA2901352E003364DB /* Object+Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BD4DA92901352E003364DB /* Object+Dictionary.swift */; };
06BE84042E6EAE7100E6F856 /* InsetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BE84032E6EAE7100E6F856 /* InsetView.swift */; };
06C2CF232685B88D0034B127 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF222685B88D0034B127 /* TextCell.swift */; }; 06C2CF232685B88D0034B127 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF222685B88D0034B127 /* TextCell.swift */; };
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF242685BDB80034B127 /* SpacerCell.swift */; }; 06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF242685BDB80034B127 /* SpacerCell.swift */; };
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952C2480E3F8006B98F3 /* LabelCell.swift */; }; 06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952C2480E3F8006B98F3 /* LabelCell.swift */; };
06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */; };
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */; }; 06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */; };
06C595362481160F006B98F3 /* BKLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C595352481160F006B98F3 /* BKLabel.swift */; }; 06C595362481160F006B98F3 /* BKLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C595352481160F006B98F3 /* BKLabel.swift */; };
06CF784721C7A50300A052D7 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 06CF784721C7A50300A052D7 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@ -197,8 +200,6 @@
06F08EA529B1DDA7006AB9CA /* Algorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061894C629A75BEA00E001C2 /* Algorithm.swift */; }; 06F08EA529B1DDA7006AB9CA /* Algorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061894C629A75BEA00E001C2 /* Algorithm.swift */; };
06F08EA729B1DDFE006AB9CA /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */; }; 06F08EA729B1DDFE006AB9CA /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */; };
06F08EA829B1DE0A006AB9CA /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */; }; 06F08EA829B1DE0A006AB9CA /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */; };
06F08EAC29B1DECD006AB9CA /* NSLocalizedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */; };
06F08EAD29B1DED6006AB9CA /* NSLocalizedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */; };
06F08EAF29B5D9FF006AB9CA /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EAE29B5D9FF006AB9CA /* HUD.swift */; }; 06F08EAF29B5D9FF006AB9CA /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EAE29B5D9FF006AB9CA /* HUD.swift */; };
06F11E7727D9D5FB00F00298 /* QRScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */; }; 06F11E7727D9D5FB00F00298 /* QRScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */; };
06FB04042C53575400F3A213 /* SharedDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FB04032C53575400F3A213 /* SharedDefines.swift */; }; 06FB04042C53575400F3A213 /* SharedDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FB04032C53575400F3A213 /* SharedDefines.swift */; };
@ -349,7 +350,7 @@
066E0C8B2BB6AC9A00873838 /* AddSoundCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSoundCell.swift; sourceTree = "<group>"; }; 066E0C8B2BB6AC9A00873838 /* AddSoundCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSoundCell.swift; sourceTree = "<group>"; };
0672CB05256903F700570C9D /* MessageListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListViewModel.swift; sourceTree = "<group>"; }; 0672CB05256903F700570C9D /* MessageListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListViewModel.swift; sourceTree = "<group>"; };
06787C382A710568008ABDD7 /* GesturePassTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GesturePassTextView.swift; sourceTree = "<group>"; }; 06787C382A710568008ABDD7 /* GesturePassTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GesturePassTextView.swift; sourceTree = "<group>"; };
06787C3A2AB82BDB008ABDD7 /* CrashReportViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportViewController.swift; sourceTree = "<group>"; }; 067AFB1B2E5D8BE300AE78E7 /* UNNotificationContent+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UNNotificationContent+Extension.swift"; path = "notificationContentExtension/UNNotificationContent+Extension.swift"; sourceTree = SOURCE_ROOT; };
067B2EB425693E38008B6BE1 /* MessageSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSection.swift; sourceTree = "<group>"; }; 067B2EB425693E38008B6BE1 /* MessageSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSection.swift; sourceTree = "<group>"; };
0683486A2050F1310024B6DA /* Bark.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Bark.entitlements; sourceTree = "<group>"; }; 0683486A2050F1310024B6DA /* Bark.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Bark.entitlements; sourceTree = "<group>"; };
0683487020510FB20024B6DA /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; 0683487020510FB20024B6DA /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
@ -376,10 +377,10 @@
06BCAE592CDB19590092867A /* NotificationContentExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationContentExtension.entitlements; sourceTree = "<group>"; }; 06BCAE592CDB19590092867A /* NotificationContentExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationContentExtension.entitlements; sourceTree = "<group>"; };
06BCAE5A2CDB25120092867A /* MuteProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteProcessor.swift; sourceTree = "<group>"; }; 06BCAE5A2CDB25120092867A /* MuteProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteProcessor.swift; sourceTree = "<group>"; };
06BD4DA92901352E003364DB /* Object+Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Object+Dictionary.swift"; sourceTree = "<group>"; }; 06BD4DA92901352E003364DB /* Object+Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Object+Dictionary.swift"; sourceTree = "<group>"; };
06BE84032E6EAE7100E6F856 /* InsetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsetView.swift; sourceTree = "<group>"; };
06C2CF222685B88D0034B127 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; }; 06C2CF222685B88D0034B127 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; };
06C2CF242685BDB80034B127 /* SpacerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacerCell.swift; sourceTree = "<group>"; }; 06C2CF242685BDB80034B127 /* SpacerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacerCell.swift; sourceTree = "<group>"; };
06C5952C2480E3F8006B98F3 /* LabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCell.swift; sourceTree = "<group>"; }; 06C5952C2480E3F8006B98F3 /* LabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCell.swift; sourceTree = "<group>"; };
06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudStatusCell.swift; sourceTree = "<group>"; };
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingCell.swift; sourceTree = "<group>"; }; 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingCell.swift; sourceTree = "<group>"; };
06C5953224811505006B98F3 /* ArchiveSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingManager.swift; sourceTree = "<group>"; }; 06C5953224811505006B98F3 /* ArchiveSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingManager.swift; sourceTree = "<group>"; };
06C595352481160F006B98F3 /* BKLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKLabel.swift; sourceTree = "<group>"; }; 06C595352481160F006B98F3 /* BKLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKLabel.swift; sourceTree = "<group>"; };
@ -408,7 +409,6 @@
06EF49182D682B34008B91D2 /* PushToOtherIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushToOtherIntent.swift; sourceTree = "<group>"; }; 06EF49182D682B34008B91D2 /* PushToOtherIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushToOtherIntent.swift; sourceTree = "<group>"; };
06F08EA329B098DD006AB9CA /* CryptoSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSettingManager.swift; sourceTree = "<group>"; }; 06F08EA329B098DD006AB9CA /* CryptoSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSettingManager.swift; sourceTree = "<group>"; };
06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Extension.swift"; sourceTree = "<group>"; }; 06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Extension.swift"; sourceTree = "<group>"; };
06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLocalizedString+Extension.swift"; sourceTree = "<group>"; };
06F08EAE29B5D9FF006AB9CA /* HUD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; }; 06F08EAE29B5D9FF006AB9CA /* HUD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; };
06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerViewController.swift; sourceTree = "<group>"; }; 06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerViewController.swift; sourceTree = "<group>"; };
06FB04032C53575400F3A213 /* SharedDefines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDefines.swift; sourceTree = "<group>"; }; 06FB04032C53575400F3A213 /* SharedDefines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDefines.swift; sourceTree = "<group>"; };
@ -489,7 +489,6 @@
068EC15927ED99E700D5D11E /* ServerListViewModel.swift */, 068EC15927ED99E700D5D11E /* ServerListViewModel.swift */,
06EEF332291CCFF400CA228A /* CryptoSettingController.swift */, 06EEF332291CCFF400CA228A /* CryptoSettingController.swift */,
06EEF334291CD00000CA228A /* CryptoSettingViewModel.swift */, 06EEF334291CD00000CA228A /* CryptoSettingViewModel.swift */,
06787C3A2AB82BDB008ABDD7 /* CrashReportViewController.swift */,
1E73F99D2C282822002BF649 /* SectionViewController-iPad.swift */, 1E73F99D2C282822002BF649 /* SectionViewController-iPad.swift */,
1EFB545E2C32514000B8E51B /* SectionViewModel-iPad.swift */, 1EFB545E2C32514000B8E51B /* SectionViewModel-iPad.swift */,
1EFB545C2C314A6800B8E51B /* BarkSplitViewController.swift */, 1EFB545C2C314A6800B8E51B /* BarkSplitViewController.swift */,
@ -506,7 +505,6 @@
0603706820E1F89500F4CA05 /* PreviewCardCell.swift */, 0603706820E1F89500F4CA05 /* PreviewCardCell.swift */,
064CAB9D256BE9090018155C /* PreviewCardCellViewModel.swift */, 064CAB9D256BE9090018155C /* PreviewCardCellViewModel.swift */,
06C5952C2480E3F8006B98F3 /* LabelCell.swift */, 06C5952C2480E3F8006B98F3 /* LabelCell.swift */,
06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */,
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */, 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */,
06BBB8BB2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift */, 06BBB8BB2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift */,
060481EF250F51CA00BC9799 /* SoundCell.swift */, 060481EF250F51CA00BC9799 /* SoundCell.swift */,
@ -531,6 +529,7 @@
0635A8042CE47DFE0027E00F /* SettingSectionHeader.swift */, 0635A8042CE47DFE0027E00F /* SettingSectionHeader.swift */,
0635A8062CE4883A0027E00F /* DonateCell.swift */, 0635A8062CE4883A0027E00F /* DonateCell.swift */,
0647DB672CE604AA00102066 /* MessageSettingFooter.swift */, 0647DB672CE604AA00102066 /* MessageSettingFooter.swift */,
06BE84032E6EAE7100E6F856 /* InsetView.swift */,
); );
path = View; path = View;
sourceTree = "<group>"; sourceTree = "<group>";
@ -595,6 +594,7 @@
0632CE2220EC9098003FDF46 /* NotificationViewController.swift */, 0632CE2220EC9098003FDF46 /* NotificationViewController.swift */,
0632CE2420EC9098003FDF46 /* MainInterface.storyboard */, 0632CE2420EC9098003FDF46 /* MainInterface.storyboard */,
0632CE2720EC9098003FDF46 /* Info.plist */, 0632CE2720EC9098003FDF46 /* Info.plist */,
067AFB1B2E5D8BE300AE78E7 /* UNNotificationContent+Extension.swift */,
); );
path = NotificationContentExtension; path = NotificationContentExtension;
sourceTree = "<group>"; sourceTree = "<group>";
@ -621,7 +621,6 @@
06F08EA329B098DD006AB9CA /* CryptoSettingManager.swift */, 06F08EA329B098DD006AB9CA /* CryptoSettingManager.swift */,
0653677729B727A60038BDB8 /* CryptoSettingRelay.swift */, 0653677729B727A60038BDB8 /* CryptoSettingRelay.swift */,
06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */, 06F08EA629B1DDFE006AB9CA /* Error+Extension.swift */,
06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */,
06E944742C07012E00AC86AB /* RealmConfiguration.swift */, 06E944742C07012E00AC86AB /* RealmConfiguration.swift */,
0687F2A72CCB791A00B2A52F /* UIFont+Extension.swift */, 0687F2A72CCB791A00B2A52F /* UIFont+Extension.swift */,
06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */, 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */,
@ -866,7 +865,7 @@
attributes = { attributes = {
BuildIndependentTargetsInParallel = YES; BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1300; LastSwiftUpdateCheck = 1300;
LastUpgradeCheck = 1600; LastUpgradeCheck = 1640;
ORGANIZATIONNAME = Fin; ORGANIZATIONNAME = Fin;
TargetAttributes = { TargetAttributes = {
0632CE1D20EC9098003FDF46 = { 0632CE1D20EC9098003FDF46 = {
@ -1225,8 +1224,10 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
19BE8EBF2D03514A009BF080 /* ImageDownloader.swift in Sources */, 19BE8EBF2D03514A009BF080 /* ImageDownloader.swift in Sources */,
067AFB1C2E5D8BE300AE78E7 /* UNNotificationContent+Extension.swift in Sources */,
0687F2AA2CCB7FA500B2A52F /* UIFont+Extension.swift in Sources */, 0687F2AA2CCB7FA500B2A52F /* UIFont+Extension.swift in Sources */,
0632CE2320EC9098003FDF46 /* NotificationViewController.swift in Sources */, 0632CE2320EC9098003FDF46 /* NotificationViewController.swift in Sources */,
069332222E6A8E3100F9387F /* String+Extension.swift in Sources */,
06BCAE572CDB19420092867A /* GroupMuteSettingManager.swift in Sources */, 06BCAE572CDB19420092867A /* GroupMuteSettingManager.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -1278,7 +1279,6 @@
064CABA6256BE9510018155C /* PreviewModel.swift in Sources */, 064CABA6256BE9510018155C /* PreviewModel.swift in Sources */,
0637FA7E20E0969800E80174 /* Client.swift in Sources */, 0637FA7E20E0969800E80174 /* Client.swift in Sources */,
0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */, 0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */,
06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */,
06BBB88725650C6C0076F63E /* ArchiveSettingManager.swift in Sources */, 06BBB88725650C6C0076F63E /* ArchiveSettingManager.swift in Sources */,
065BE44B2563D8E1002A8CA4 /* Reusable.swift in Sources */, 065BE44B2563D8E1002A8CA4 /* Reusable.swift in Sources */,
0637FA8620E0AB6600E80174 /* UIColor+Extension.swift in Sources */, 0637FA8620E0AB6600E80174 /* UIColor+Extension.swift in Sources */,
@ -1293,7 +1293,6 @@
06E944762C07013000AC86AB /* RealmConfiguration.swift in Sources */, 06E944762C07013000AC86AB /* RealmConfiguration.swift in Sources */,
06EF49152D682A99008B91D2 /* PushResponse.swift in Sources */, 06EF49152D682A99008B91D2 /* PushResponse.swift in Sources */,
0603706720E1E31600F4CA05 /* Defines.swift in Sources */, 0603706720E1E31600F4CA05 /* Defines.swift in Sources */,
06787C3B2AB82BDB008ABDD7 /* CrashReportViewController.swift in Sources */,
064CAB9E256BE9090018155C /* PreviewCardCellViewModel.swift in Sources */, 064CAB9E256BE9090018155C /* PreviewCardCellViewModel.swift in Sources */,
065BE4552565055F002A8CA4 /* HomeViewModel.swift in Sources */, 065BE4552565055F002A8CA4 /* HomeViewModel.swift in Sources */,
062B98C3251B2762004562E7 /* BKButton.swift in Sources */, 062B98C3251B2762004562E7 /* BKButton.swift in Sources */,
@ -1305,6 +1304,7 @@
06787C392A710568008ABDD7 /* GesturePassTextView.swift in Sources */, 06787C392A710568008ABDD7 /* GesturePassTextView.swift in Sources */,
068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */, 068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */,
0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */, 0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */,
06BE84042E6EAE7100E6F856 /* InsetView.swift in Sources */,
061E35862D1E5028009A2D6F /* MessageItemModel.swift in Sources */, 061E35862D1E5028009A2D6F /* MessageItemModel.swift in Sources */,
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */, 06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */,
06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */, 06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */,
@ -1328,7 +1328,6 @@
06EF49172D682AC4008B91D2 /* OptionsProvider.swift in Sources */, 06EF49172D682AC4008B91D2 /* OptionsProvider.swift in Sources */,
06F08EAF29B5D9FF006AB9CA /* HUD.swift in Sources */, 06F08EAF29B5D9FF006AB9CA /* HUD.swift in Sources */,
065AE76B2987777F00323230 /* ArchiveSettingRelay.swift in Sources */, 065AE76B2987777F00323230 /* ArchiveSettingRelay.swift in Sources */,
06F08EAC29B1DECD006AB9CA /* NSLocalizedString+Extension.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1341,7 +1340,7 @@
06E9447A2C0704E500AC86AB /* ImageDownloader.swift in Sources */, 06E9447A2C0704E500AC86AB /* ImageDownloader.swift in Sources */,
06CF784C21C7A51200A052D7 /* NotificationService.swift in Sources */, 06CF784C21C7A51200A052D7 /* NotificationService.swift in Sources */,
06FB04052C53575400F3A213 /* SharedDefines.swift in Sources */, 06FB04052C53575400F3A213 /* SharedDefines.swift in Sources */,
06F08EAD29B1DED6006AB9CA /* NSLocalizedString+Extension.swift in Sources */, 067AFB1D2E5D8BED00AE78E7 /* UNNotificationContent+Extension.swift in Sources */,
0653677629B719BC0038BDB8 /* CryptoSettingManager.swift in Sources */, 0653677629B719BC0038BDB8 /* CryptoSettingManager.swift in Sources */,
06E9446F2C06FF1E00AC86AB /* BadgeProcessor.swift in Sources */, 06E9446F2C06FF1E00AC86AB /* BadgeProcessor.swift in Sources */,
06BCAE582CDB19420092867A /* GroupMuteSettingManager.swift in Sources */, 06BCAE582CDB19420092867A /* GroupMuteSettingManager.swift in Sources */,
@ -1351,6 +1350,7 @@
06F08EA529B1DDA7006AB9CA /* Algorithm.swift in Sources */, 06F08EA529B1DDA7006AB9CA /* Algorithm.swift in Sources */,
06E944782C0701F300AC86AB /* IconProcessor.swift in Sources */, 06E944782C0701F300AC86AB /* IconProcessor.swift in Sources */,
06BBB89125650CCF0076F63E /* ArchiveSettingManager.swift in Sources */, 06BBB89125650CCF0076F63E /* ArchiveSettingManager.swift in Sources */,
069332232E6A8E3100F9387F /* String+Extension.swift in Sources */,
06D69E412C11983E00161A35 /* CallProcessor.swift in Sources */, 06D69E412C11983E00161A35 /* CallProcessor.swift in Sources */,
06B11591247BC132006D91FB /* Message.swift in Sources */, 06B11591247BC132006D91FB /* Message.swift in Sources */,
06E9447C2C07052F00AC86AB /* ImageProcessor.swift in Sources */, 06E9447C2C07052F00AC86AB /* ImageProcessor.swift in Sources */,
@ -1420,7 +1420,6 @@
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = NotificationContentExtension/Info.plist; INFOPLIST_FILE = NotificationContentExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -1447,7 +1446,6 @@
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = NotificationContentExtension/Info.plist; INFOPLIST_FILE = NotificationContentExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -1501,6 +1499,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1563,6 +1562,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 5U8LBRXG3A;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1592,7 +1592,6 @@
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = Bark/Info.plist; INFOPLIST_FILE = Bark/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -1619,7 +1618,6 @@
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = Bark/Info.plist; INFOPLIST_FILE = Bark/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -1646,7 +1644,6 @@
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = NotificationServiceExtension/Info.plist; INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -1675,7 +1672,6 @@
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = NotificationServiceExtension/Info.plist; INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -1703,7 +1699,6 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = BarkTests/Info.plist; INFOPLIST_FILE = BarkTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -1732,7 +1727,6 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5U8LBRXG3A;
INFOPLIST_FILE = BarkTests/Info.plist; INFOPLIST_FILE = BarkTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1600" LastUpgradeVersion = "1640"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1600" LastUpgradeVersion = "1640"
wasCreatedForAppExtension = "YES" wasCreatedForAppExtension = "YES"
version = "2.0"> version = "2.0">
<BuildAction <BuildAction

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1600" LastUpgradeVersion = "1640"
wasCreatedForAppExtension = "YES" wasCreatedForAppExtension = "YES"
version = "2.0"> version = "2.0">
<BuildAction <BuildAction

View File

@ -53,10 +53,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().delegate = self
var actions = [ var actions = [
UNNotificationAction(identifier: "copy", title: NSLocalizedString("Copy2"), options: UNNotificationActionOptions.foreground) UNNotificationAction(identifier: "copy", title: "Copy2".localized, options: UNNotificationActionOptions.foreground)
] ]
if #available(iOSApplicationExtension 15.0, *) { if #available(iOSApplicationExtension 15.0, *) {
actions.append(UNNotificationAction(identifier: "mute", title: NSLocalizedString("muteGroup1Hour"), options: UNNotificationActionOptions.foreground)) actions.append(UNNotificationAction(identifier: "mute", title: "muteGroup1Hour".localized, options: UNNotificationActionOptions.foreground))
} }
UNUserNotificationCenter.current().setNotificationCategories([ UNUserNotificationCenter.current().setNotificationCategories([
// customDismissAction clear APP便 DeviceToken // customDismissAction clear APP便 DeviceToken
@ -101,7 +101,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} }
notificatonHandler(userInfo: response.notification.request.content.userInfo) notificatonHandler(userInfo: response.notification.request.content.userInfo)
} }
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard let delete = userInfo["delete"] as? String, delete == "1", let id = userInfo["id"] as? String else {
completionHandler(.noData)
return
}
//
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [id])
//
if let realm = try? Realm(),
let message = realm.objects(Message.self).filter("id == %@", id).first
{
try? realm.write {
realm.delete(message)
}
}
completionHandler(.newData)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions { func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
return .alert return .alert
} }
@ -152,7 +171,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
ServerManager.shared.addServer(server: server) ServerManager.shared.addServer(server: server)
ServerManager.shared.setCurrentServer(serverId: server.id) ServerManager.shared.setCurrentServer(serverId: server.id)
ServerManager.shared.syncAllServers() ServerManager.shared.syncAllServers()
HUDSuccess(NSLocalizedString("AddedSuccessfully")) HUDSuccess("AddedSuccessfully".localized)
} }
return true return true
} }
@ -169,14 +188,14 @@ extension AppDelegate {
let url = try? (userInfo["url"] as? String)?.asURL() let url = try? (userInfo["url"] as? String)?.asURL()
let alertController = UIAlertController(title: title, message: body, preferredStyle: .alert) let alertController = UIAlertController(title: title, message: body, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("CopyContent"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "CopyContent".localized, style: .default, handler: { _ in
if let copy = userInfo["copy"] as? String { if let copy = userInfo["copy"] as? String {
UIPasteboard.general.string = copy UIPasteboard.general.string = copy
} else { } else {
UIPasteboard.general.string = body UIPasteboard.general.string = body
} }
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("MoreActions"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "MoreActions".localized, style: .default, handler: { _ in
var shareContent = "" var shareContent = ""
if let title = title { if let title = title {
shareContent += "\(title)\n" shareContent += "\(title)\n"
@ -207,7 +226,7 @@ extension AppDelegate {
} }
controller?.present(activityController, animated: true, completion: nil) controller?.present(activityController, animated: true, completion: nil)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
let viewController = Client.shared.currentSnackbarController let viewController = Client.shared.currentSnackbarController

View File

@ -146,35 +146,6 @@
} }
} }
}, },
"appSC" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "App Source Code"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "アプリのソースコード"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Uygulama Kaynak Kodu"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "App源代码"
}
}
}
},
"archiveNote" : { "archiveNote" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -320,64 +291,6 @@
} }
} }
}, },
"available" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enabled"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "有効"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Etkin"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "已开启"
}
}
}
},
"backendSC" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Backend Source Code"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "バックエンドのソースコード"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Arka Uç Kaynak Kodu"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "后端源代码"
}
}
}
},
"badge" : { "badge" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -726,6 +639,35 @@
} }
} }
}, },
"close" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Close"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "閉じる"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Kapat"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "关闭"
}
}
}
},
"confirm" : { "confirm" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -843,6 +785,7 @@
} }
}, },
"Copy" : { "Copy" : {
"extractionState" : "manual",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -928,35 +871,6 @@
} }
} }
}, },
"CopyAll" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy All"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "すべてをコピー"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Tümünü Kopyala"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制全部内容"
}
}
}
},
"CopyContent" : { "CopyContent" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -986,35 +900,6 @@
} }
} }
}, },
"copyCrashLog" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy Crash Log"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "クラッシュログをコピー"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy Crash Log"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制闪退日志"
}
}
}
},
"copyExample" : { "copyExample" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -1073,64 +958,6 @@
} }
} }
}, },
"crashContent" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "You need to restart the app! \n\nIf the problem persists, you can try to check the FAQ to see if there is a solution.\nhttps://bark.day.app/#/en-us/faq\n\nBark will not upload any logs! If you need help (or wish to help the developer fix the crash), you can also send the crash log to me, and I will contact you as soon as possible.\n\nEmail: to@day.app\nTelegram: https://t.me/joinchat/OsCbLzovUAE0YjY1\nGithub Issue: https://github.com/Finb/Bark/issues\n"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "問題が解決しない場合は、FAQを確認して解決策があるかどうかを確認できます。 \nhttps://bark.day.app/#/en-us/faq \n\nBarkはログをアップロードしません助けが必要な場合または開発者がクラッシュを修正するのを手伝いたい場合、クラッシュログを私に送ってください。できるだけ早く連絡します。 \n\nメール: to@day.app \nTelegram: https://t.me/joinchat/OsCbLzovUAE0YjY1 \nGithub Issue: https://github.com/Finb/Bark/issues "
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Uygulamayı yeniden başlatmanız gerekiyor!\n\nSorun devam ederse bir çözüm olup olmadığını görmek için SSS'yi kontrol etmeyi deneyebilirsiniz.\nhttps://bark.day.app/#/en-us/faq\n\nBark herhangi bir günlük yüklemeyecek! Yardıma ihtiyacınız varsa (veya geliştiricinin çökmeyi düzeltmesine yardım etmek istiyorsanız), kilitlenme günlüğünü bana da gönderebilirsiniz; mümkün olan en kısa sürede sizinle iletişime geçeceğim.\n\nE-posta: to@day.app\nTelgraf: https://t.me/joinchat/OsCbLzovUAE0YjY1\nGithub Sorunları: https://github.com/Finb/Bark/issues"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "您需要重启APP\n\n如还是提示闪退可以尝试查看FAQ看是否有解决办法\nhttps://bark.day.app/#/faq\n\nBark 不会上传任何日志!如您需帮助(或帮助作者修复闪退),可以将闪退日志手动复制发送给我,我会尽快与您联系。\n\n邮箱: to@day.app\nTelegram: https://t.me/joinchat/OsCbLzov\nGithub Issue: https://github.com/Finb/Bark/issues\n"
}
}
}
},
"crashed" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Crashed!"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "クラッシュしました!"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "düştü!"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "好像闪退了!"
}
}
}
},
"criticalAlert" : { "criticalAlert" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -1450,6 +1277,35 @@
} }
} }
}, },
"demo" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Demo"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "デモ"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Demo"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "演示"
}
}
}
},
"DeploymentDocuments" : { "DeploymentDocuments" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -1915,7 +1771,6 @@
} }
}, },
"group" : { "group" : {
"extractionState" : "manual",
"localizations" : { "localizations" : {
"en" : { "en" : {
"stringUnit" : { "stringUnit" : {
@ -1932,13 +1787,13 @@
"tr" : { "tr" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "Gruplar" "value" : "grup"
} }
}, },
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "组" "value" : "组"
} }
} }
} }
@ -2001,35 +1856,6 @@
} }
} }
}, },
"hideAllGroups" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Unselect All Groups"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "すべてのグループの選択を解除"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Tüm Grupların Seçimini Kaldır"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "取消所有选择"
}
}
}
},
"historyMessage" : { "historyMessage" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -2059,64 +1885,6 @@
} }
} }
}, },
"iCloudSatatus" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud Sync"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud同期"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud Senkronizasyonu"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud 同步"
}
}
}
},
"iCloudSync" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Sync notifications through iCloud"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloudを通じて通知を同期"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bildirimleri iCloud aracılığıyla senkronize edin"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "通知消息将在iCloud可用时同步"
}
}
}
},
"imageParameter" : { "imageParameter" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -2233,35 +2001,6 @@
} }
} }
}, },
"InsecureConnection" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Insecure Connection (HTTP)"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "安全でない接続HTTP"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Güvensiz Bağlantı (HTTP)"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "连接不安全请使用HTTPS安全连接"
}
}
}
},
"interruptionLevel" : { "interruptionLevel" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -2929,64 +2668,6 @@
} }
} }
}, },
"preview" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Preview"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "プレビュー"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Önizleme"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "预览"
}
}
}
},
"previewSound" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Click to Play"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "クリックして再生"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Oynamak için Tıklayın"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "点击可预览"
}
}
}
},
"privacyPolicy" : { "privacyPolicy" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -3306,35 +2987,6 @@
} }
} }
}, },
"restricted" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Disabled"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "無効"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Devre Dışı"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "未开启"
}
}
}
},
"ringtone" : { "ringtone" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -3480,35 +3132,6 @@
} }
} }
}, },
"SecureConnection" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Secure Connection (HTTPS)"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "安全な接続HTTPS"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Güvenli Bağlantı (HTTPS)"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "HTTPS安全连接"
}
}
}
},
"sendPushNotification" : { "sendPushNotification" : {
"localizations" : { "localizations" : {
"en" : { "en" : {
@ -3855,35 +3478,6 @@
} }
} }
}, },
"showAllGroups" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Select All Groups"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "すべてのグループを選択"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Tüm Grupları Seç"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "选择所有群组"
}
}
}
},
"showLess" : { "showLess" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -4174,6 +3768,35 @@
} }
} }
}, },
"toggle" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Toggle"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "表示切替"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Görünümü Değiştir"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "切换"
}
}
}
},
"unknown" : { "unknown" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@ -4203,35 +3826,6 @@
} }
} }
}, },
"UnregisteredDevice" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Unregistered Device"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "未登録デバイス"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Kayıtsız Cihaz"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "设备未注册,不能使用推送服务"
}
}
}
},
"uploadSound" : { "uploadSound" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {

View File

@ -33,14 +33,14 @@ extension Date {
} }
if hour > 0 { if hour > 0 {
if minute > 0 { if minute > 0 {
return String(format: NSLocalizedString("timeMinHourAgo"), hour, minute) return "timeMinHourAgo".localized(with: hour, minute)
} }
return String(format: NSLocalizedString("timeHourAgo"), hour) return "timeHourAgo".localized(with: hour)
} }
if minute > 1 { if minute > 1 {
return String(format: NSLocalizedString("timeMinAgo"), minute) return "timeMinAgo".localized(with: minute)
} }
return NSLocalizedString("timeJustNow") return "timeJustNow".localized
} }
} }

View File

@ -1,13 +0,0 @@
//
// NSLocalizedString+Extension.swift
// Bark
//
// Created by huangfeng on 2023/3/3.
// Copyright © 2023 Fin. All rights reserved.
//
import Foundation
func NSLocalizedString(_ key: String) -> String {
return NSLocalizedString(key, comment: "")
}

View File

@ -53,3 +53,13 @@ extension String {
return String(format: self, arguments) return String(format: self, arguments)
} }
} }
extension String {
var localized: String {
return NSLocalizedString(self, comment: "")
}
func localized(with arguments: CVarArg...) -> String {
return String(format: NSLocalizedString(self, comment: ""), arguments: arguments)
}
}

View File

@ -20,9 +20,9 @@ class BarkTabBarController: StateStorageTabBarController {
BarkNavigationController(rootViewController: MessageSettingsViewController(viewModel: MessageSettingsViewModel())) BarkNavigationController(rootViewController: MessageSettingsViewController(viewModel: MessageSettingsViewModel()))
] ]
let tabBarItems = [UITabBarItem(title: NSLocalizedString("service"), image: UIImage(named: "baseline_gite_black_24pt"), tag: 0), let tabBarItems = [UITabBarItem(title: "service".localized, image: UIImage(named: "baseline_gite_black_24pt"), tag: 0),
UITabBarItem(title: NSLocalizedString("historyMessage"), image: Icon.history, tag: 1), UITabBarItem(title: "historyMessage".localized, image: Icon.history, tag: 1),
UITabBarItem(title: NSLocalizedString("settings"), image: UIImage(named: "baseline_manage_accounts_black_24pt"), tag: 2)] UITabBarItem(title: "settings".localized, image: UIImage(named: "baseline_manage_accounts_black_24pt"), tag: 2)]
for (index, viewController) in self.viewControllers!.enumerated() { for (index, viewController) in self.viewControllers!.enumerated() {
viewController.tabBarItem = tabBarItems[index] viewController.tabBarItem = tabBarItems[index]
} }

View File

@ -1,76 +0,0 @@
//
// CrashReportViewController.swift
// Bark
//
// Created by huangfeng on 2023/9/18.
// Copyright © 2023 Fin. All rights reserved.
//
import UIKit
class CrashReportViewController: UIViewController {
var crashLog = ""
override func viewDidLoad() {
super.viewDidLoad()
self.overrideUserInterfaceStyle = .light
self.view.backgroundColor = UIColor.white
let warningIcon = UIImageView(image: UIImage(named: "warning"))
self.view.addSubview(warningIcon)
let crashedTitle = UILabel()
crashedTitle.text = NSLocalizedString("crashed")
crashedTitle.font = UIFont.preferredFont(ofSize: 30, weight: .bold)
crashedTitle.adjustsFontForContentSizeCategory = true
crashedTitle.textColor = UIColor(r255: 239, g255: 77, b255: 77)
self.view.addSubview(crashedTitle)
let contentlabel = UITextView()
contentlabel.backgroundColor = UIColor.clear
contentlabel.isEditable = false
contentlabel.dataDetectorTypes = [.link]
contentlabel.isScrollEnabled = false
contentlabel.textContainerInset = .zero
contentlabel.textContainer.lineFragmentPadding = 0
contentlabel.font = UIFont.preferredFont(ofSize: 14)
contentlabel.adjustsFontForContentSizeCategory = true
contentlabel.textColor = UIColor(r255: 51, g255: 51, b255: 51)
contentlabel.text = NSLocalizedString("crashContent")
self.view.addSubview(contentlabel)
let copyButton = UIButton()
copyButton.titleLabel?.font = UIFont.preferredFont(ofSize: 16, weight: .bold)
copyButton.titleLabel?.adjustsFontForContentSizeCategory = true
copyButton.setTitleColor(UIColor.white, for: .normal)
copyButton.setTitle(NSLocalizedString("copyCrashLog"), for: .normal)
copyButton.backgroundColor = UIColor(r255: 239, g255: 77, b255: 77)
copyButton.clipsToBounds = true
copyButton.layer.cornerRadius = 6
self.view.addSubview(copyButton)
warningIcon.snp.makeConstraints { make in
make.top.equalTo(kSafeAreaInsets.top + 60)
make.left.equalTo(15)
make.width.height.equalTo(42)
}
crashedTitle.snp.makeConstraints { make in
make.left.equalTo(warningIcon.snp.right).offset(10)
make.centerY.equalTo(warningIcon)
}
contentlabel.snp.makeConstraints { make in
make.left.equalTo(20)
make.right.equalTo(-20)
make.top.equalTo(warningIcon.snp.bottom).offset(40)
}
copyButton.snp.makeConstraints { make in
make.left.right.equalTo(contentlabel)
make.top.equalTo(contentlabel.snp.bottom).offset(40)
make.height.equalTo(44)
}
copyButton.rx.tap.subscribe { [weak self] _ in
UIPasteboard.general.string = self?.crashLog
ProgressHUD.inform(NSLocalizedString("Copy"))
}.disposed(by: rx.disposeBag)
}
}

View File

@ -18,7 +18,7 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
let textField = BorderTextField(title: "Key") let textField = BorderTextField(title: "Key")
textField.font = UIFont.preferredFont(ofSize: 14) textField.font = UIFont.preferredFont(ofSize: 14)
textField.adjustsFontForContentSizeCategory = true textField.adjustsFontForContentSizeCategory = true
textField.placeholder = String(format: NSLocalizedString("enterKey"), 16) textField.placeholder = "enterKey".localized(with: 16)
return textField return textField
}() }()
@ -31,7 +31,7 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
let doneButton: BKButton = { let doneButton: BKButton = {
let btn = BKButton() let btn = BKButton()
btn.setTitle(NSLocalizedString("done"), for: .normal) btn.setTitle("done".localized, for: .normal)
btn.setTitleColor(BKColor.lightBlue.darken3, for: .normal) btn.setTitleColor(BKColor.lightBlue.darken3, for: .normal)
btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40) btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
btn.fontSize = 14 btn.fontSize = 14
@ -40,7 +40,7 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
let copyButton: UIButton = { let copyButton: UIButton = {
let btn = GradientButton() let btn = GradientButton()
btn.setTitle(NSLocalizedString("copyExample"), for: .normal) btn.setTitle("copyExample".localized, for: .normal)
btn.setTitleColor(UIColor.white, for: .normal) btn.setTitleColor(UIColor.white, for: .normal)
btn.titleLabel?.font = UIFont.preferredFont(ofSize: 14, weight: .medium) btn.titleLabel?.font = UIFont.preferredFont(ofSize: 14, weight: .medium)
btn.titleLabel?.adjustsFontForContentSizeCategory = true btn.titleLabel?.adjustsFontForContentSizeCategory = true
@ -62,7 +62,7 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
} }
override func makeUI() { override func makeUI() {
self.title = NSLocalizedString("encryptionSettings") self.title = "encryptionSettings".localized
self.navigationItem.setRightBarButtonItem(item: UIBarButtonItem(customView: doneButton)) self.navigationItem.setRightBarButtonItem(item: UIBarButtonItem(customView: doneButton))
self.view.addSubview(scrollView) self.view.addSubview(scrollView)
@ -79,8 +79,8 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
return label return label
} }
let algorithmLabel = getTitleLabel(title: NSLocalizedString("algorithm")) let algorithmLabel = getTitleLabel(title: "algorithm".localized)
let modeLabel = getTitleLabel(title: NSLocalizedString("mode")) let modeLabel = getTitleLabel(title: "mode".localized)
let paddingLabel = getTitleLabel(title: "Padding") let paddingLabel = getTitleLabel(title: "Padding")
let keyLabel = getTitleLabel(title: "Key") let keyLabel = getTitleLabel(title: "Key")
let ivLabel = getTitleLabel(title: "Iv") let ivLabel = getTitleLabel(title: "Iv")
@ -226,7 +226,7 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
output.keyLengthChanged.drive(onNext: { [weak self] keyLength in output.keyLengthChanged.drive(onNext: { [weak self] keyLength in
self?.keyTextField.placeholder = String(format: NSLocalizedString("enterKey"), keyLength) self?.keyTextField.placeholder = "enterKey".localized(with: keyLength)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
self.modeFeild self.modeFeild
@ -246,7 +246,7 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
output.copy.drive(onNext: { text in output.copy.drive(onNext: { text in
UIPasteboard.general.string = text UIPasteboard.general.string = text
HUDSuccess(NSLocalizedString("Copy")) HUDSuccess("Copy".localized)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
} }
@ -255,7 +255,7 @@ class CryptoSettingController: BaseViewController<CryptoSettingViewModel> {
return return
} }
if let length = ["CBC": 16, "GCM": 12][mode] { if let length = ["CBC": 16, "GCM": 12][mode] {
self.ivTextField.placeholder = String(format: NSLocalizedString("enterIv"), length) self.ivTextField.placeholder = "enterIv".localized(with: length)
} else { } else {
self.ivTextField.placeholder = "" self.ivTextField.placeholder = ""
} }

View File

@ -96,7 +96,7 @@ class CryptoSettingViewModel: ViewModel, ViewModelType {
let copy = Driver.combineLatest(copyScript, dependencies.deviceKey, dependencies.serverAddress) let copy = Driver.combineLatest(copyScript, dependencies.deviceKey, dependencies.serverAddress)
.compactMap { fields, deviceKey, serverAddress -> String? in .compactMap { fields, deviceKey, serverAddress -> String? in
guard fields.mode != "GCM" else { guard fields.mode != "GCM" else {
showSnackbar.accept(NSLocalizedString("gcmNotSupported")) showSnackbar.accept("gcmNotSupported".localized)
return nil return nil
} }
let key = fields.key ?? "" let key = fields.key ?? ""
@ -105,7 +105,7 @@ class CryptoSettingViewModel: ViewModel, ViewModelType {
""" """
#!/usr/bin/env bash #!/usr/bin/env bash
# Documentation: \(NSLocalizedString("encryptionUrl")) # Documentation: \("encryptionUrl".localized)
set -e set -e
@ -114,22 +114,22 @@ class CryptoSettingViewModel: ViewModel, ViewModelType {
# push payload # push payload
json='{"body": "test", "sound": "birdsong"}' json='{"body": "test", "sound": "birdsong"}'
# \(String(format: NSLocalizedString("keyComment"), Int(fields.algorithm.suffix(3))! / 8)) # \("keyComment".localized(with: Int(fields.algorithm.suffix(3))! / 8)) )
key='\(key)' key='\(key)'
# \(NSLocalizedString("ivComment")) # \("ivComment".localized)
iv='\(iv)' iv='\(iv)'
# \(NSLocalizedString("opensslEncodingComment")) # \("opensslEncodingComment".localized)
key=$(printf $key | xxd -ps -c 200) key=$(printf $key | xxd -ps -c 200)
iv=$(printf $iv | xxd -ps -c 200) iv=$(printf $iv | xxd -ps -c 200)
# \(NSLocalizedString("base64Notice")) # \("base64Notice".localized)
ciphertext=$(echo -n $json | openssl enc -aes-\(fields.algorithm.suffix(3))-\(fields.mode.lowercased()) -K $key \(iv.count > 0 ? "-iv $iv " : "")| base64) ciphertext=$(echo -n $json | openssl enc -aes-\(fields.algorithm.suffix(3))-\(fields.mode.lowercased()) -K $key \(iv.count > 0 ? "-iv $iv " : "")| base64)
# \(NSLocalizedString("consoleComment")) "\((try? AESCryptoModel(cryptoFields: fields).encrypt(text: "{\"body\": \"test\", \"sound\": \"birdsong\"}")) ?? "")" # \("consoleComment".localized) "\((try? AESCryptoModel(cryptoFields: fields).encrypt(text: "{\"body\": \"test\", \"sound\": \"birdsong\"}")) ?? "")"
echo $ciphertext echo $ciphertext
# \(NSLocalizedString("ciphertextComment")) # \("ciphertextComment".localized)
curl --data-urlencode "ciphertext=$ciphertext"\(iv.count == 0 ? "" : " --data-urlencode \"iv=\(iv)\"") \(serverAddress)/$deviceKey curl --data-urlencode "ciphertext=$ciphertext"\(iv.count == 0 ? "" : " --data-urlencode \"iv=\(iv)\"") \(serverAddress)/$deviceKey
""" """
} }

View File

@ -1,5 +1,5 @@
// //
// ViewController.swift // HomeViewController.swift
// Bark // Bark
// //
// Created by huangfeng on 2018/3/7. // Created by huangfeng on 2018/3/7.
@ -18,7 +18,8 @@ class HomeViewController: BaseViewController<HomeViewModel> {
let btn = BKButton() let btn = BKButton()
btn.setImage(Icon.add, for: .normal) btn.setImage(Icon.add, for: .normal)
btn.imageView?.tintColor = BKColor.grey.darken4 btn.imageView?.tintColor = BKColor.grey.darken4
btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40) // btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
btn.accessibilityIdentifier = "AddServer".localized
return btn return btn
}() }()
@ -26,12 +27,13 @@ class HomeViewController: BaseViewController<HomeViewModel> {
let btn = BKButton() let btn = BKButton()
btn.setImage(UIImage(named: "baseline_filter_drama_black_24pt"), for: .normal) btn.setImage(UIImage(named: "baseline_filter_drama_black_24pt"), for: .normal)
btn.imageView?.tintColor = BKColor.grey.darken4 btn.imageView?.tintColor = BKColor.grey.darken4
btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40) // btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
btn.accessibilityIdentifier = "serverList".localized
return btn return btn
}() }()
let startButton: FABButton = { let startButton: FABButton = {
let button = FABButton(title: NSLocalizedString("RegisterDevice")) let button = FABButton(title: "RegisterDevice".localized)
button.backgroundColor = BKColor.grey.lighten5 button.backgroundColor = BKColor.grey.lighten5
button.transition([.scale(0.75), .opacity(0)]) button.transition([.scale(0.75), .opacity(0)])
return button return button
@ -182,7 +184,7 @@ class HomeViewController: BaseViewController<HomeViewModel> {
output.copy output.copy
.drive(onNext: { [weak self] text in .drive(onNext: { [weak self] text in
UIPasteboard.general.string = text UIPasteboard.general.string = text
self?.showSnackbar(text: NSLocalizedString("Copy")) self?.showSnackbar(text: "Copy".localized)
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
@ -226,14 +228,14 @@ class HomeViewController: BaseViewController<HomeViewModel> {
} }
func alertServerError(error: String) { func alertServerError(error: String) {
let alertController = UIAlertController(title: NSLocalizedString("ServerError"), message: error, preferredStyle: .alert) let alertController = UIAlertController(title: "ServerError".localized, message: error, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("faq"), style: .default, handler: { [weak self] _ in alertController.addAction(UIAlertAction(title: "faq".localized, style: .default, handler: { [weak self] _ in
guard let url = try? NSLocalizedString("faqUrl").asURL() else { guard let url = try? "faqUrl".localized.asURL() else {
return return
} }
self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil) self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil) self.present(alertController, animated: true, completion: nil)
} }
} }

View File

@ -42,85 +42,85 @@ class HomeViewModel: ViewModel, ViewModelType {
let previews: [PreviewModel] = [ let previews: [PreviewModel] = [
PreviewModel( PreviewModel(
body: NSLocalizedString("CustomedNotificationContent"), body: "CustomedNotificationContent".localized,
notice: NSLocalizedString("Notice1") notice: "Notice1".localized
), ),
PreviewModel( PreviewModel(
title: NSLocalizedString("CustomedNotificationTitle"), title: "CustomedNotificationTitle".localized,
body: NSLocalizedString("CustomedNotificationContent"), body: "CustomedNotificationContent".localized,
notice: NSLocalizedString("Notice2") notice: "Notice2".localized
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("notificationSound"), body: "notificationSound".localized,
notice: NSLocalizedString("setSounds"), notice: "setSounds".localized,
queryParameter: "sound=minuet", queryParameter: "sound=minuet",
moreInfo: NSLocalizedString("viewAllSounds"), moreInfo: "viewAllSounds".localized,
moreViewModel: SoundsViewModel() moreViewModel: SoundsViewModel()
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("ringtone"), body: "ringtone".localized,
notice: NSLocalizedString("ringtoneNotice"), notice: "ringtoneNotice".localized,
queryParameter: "call=1" queryParameter: "call=1"
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("archiveNotificationMessageTitle"), body: "archiveNotificationMessageTitle".localized,
notice: NSLocalizedString("archiveNotificationMessage"), notice: "archiveNotificationMessage".localized,
queryParameter: "isArchive=1" queryParameter: "isArchive=1"
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("notificationIcon"), body: "notificationIcon".localized,
notice: NSLocalizedString("notificationIconNotice"), notice: "notificationIconNotice".localized,
queryParameter: "icon=https://day.app/assets/images/avatar.jpg", queryParameter: "icon=https://day.app/assets/images/avatar.jpg",
image: UIImage(named: "icon") image: UIImage(named: "icon")
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("messageGroup"), body: "messageGroup".localized,
notice: NSLocalizedString("groupMessagesNotice"), notice: "groupMessagesNotice".localized,
queryParameter: "group=groupName", queryParameter: "group=groupName",
image: UIImage(named: "group") image: UIImage(named: "group")
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("pushNotificationEncryption"), body: "pushNotificationEncryption".localized,
notice: NSLocalizedString("encryptionNotice"), notice: "encryptionNotice".localized,
queryParameter: "ciphertext=ciphertext", queryParameter: "ciphertext=ciphertext",
moreInfo: NSLocalizedString("encryptionSettings"), moreInfo: "encryptionSettings".localized,
moreViewModel: CryptoSettingViewModel() moreViewModel: CryptoSettingViewModel()
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("criticalAlert"), body: "criticalAlert".localized,
notice: NSLocalizedString("criticalAlertNotice"), notice: "criticalAlertNotice".localized,
queryParameter: "level=critical&volume=5", queryParameter: "level=critical&volume=5",
image: UIImage(named: "criticalAlert") image: UIImage(named: "criticalAlert")
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("interruptionLevel"), body: "interruptionLevel".localized,
notice: NSLocalizedString("interruptionLevelNotice"), notice: "interruptionLevelNotice".localized,
queryParameter: "level=timeSensitive" queryParameter: "level=timeSensitive"
), ),
PreviewModel( PreviewModel(
body: "URL Test", body: "URL Test",
notice: NSLocalizedString("urlParameter"), notice: "urlParameter".localized,
queryParameter: "url=https://www.baidu.com" queryParameter: "url=https://www.baidu.com"
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("imagePushNotification"), body: "imagePushNotification".localized,
notice: NSLocalizedString("imageParameter"), notice: "imageParameter".localized,
queryParameter: "image=https://day.app/assets/images/avatar.jpg" queryParameter: "image=https://day.app/assets/images/avatar.jpg"
), ),
PreviewModel( PreviewModel(
body: "Copy Test", body: "Copy Test",
notice: NSLocalizedString("copyParameter"), notice: "copyParameter".localized,
queryParameter: "copy=test", queryParameter: "copy=test",
image: UIImage(named: "copyTest") image: UIImage(named: "copyTest")
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("badge"), body: "badge".localized,
notice: NSLocalizedString("badgeNotice"), notice: "badgeNotice".localized,
queryParameter: "badge=1" queryParameter: "badge=1"
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("automaticallyCopyTitle"), body: "automaticallyCopyTitle".localized,
notice: NSLocalizedString("automaticallyCopy"), notice: "automaticallyCopy".localized,
queryParameter: "autoCopy=1&copy=optional" queryParameter: "autoCopy=1&copy=optional"
) )
] ]
@ -182,7 +182,7 @@ class HomeViewModel: ViewModel, ViewModelType {
.skip(1) .skip(1)
.compactMap { granted -> String? in .compactMap { granted -> String? in
if !granted { if !granted {
return NSLocalizedString("AllowNotifications") return "AllowNotifications".localized
} }
return nil return nil
} }
@ -204,7 +204,7 @@ class HomeViewModel: ViewModel, ViewModelType {
case .ok: break case .ok: break
case .serverError(let error): case .serverError(let error):
if serverErrorCount < 2 { if serverErrorCount < 2 {
showSnackbar.accept("\(NSLocalizedString("ServerError")): \(error.rawString())") showSnackbar.accept("\("ServerError".localized): \(error.rawString())")
} else { } else {
alertServerError.accept(error.rawString()) alertServerError.accept(error.rawString())
} }

View File

@ -33,17 +33,20 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
} }
subMenuElements.append(action) subMenuElements.append(action)
} }
menuElements.append(UIMenu(title: NSLocalizedString("more"), children: subMenuElements)) menuElements.append(UIMenu(title: "more".localized, children: subMenuElements))
let addNewMenu = UIMenu( let addNewMenu = UIMenu(
title: NSLocalizedString("clearFrom"), title: "clearFrom".localized,
children: menuElements children: menuElements
) )
return UIBarButtonItem(image: UIImage(named: "baseline_delete_outline_black_24pt"), menu: addNewMenu) let item = UIBarButtonItem(image: UIImage(named: "baseline_delete_outline_black_24pt"), menu: addNewMenu)
item.accessibilityLabel = "clear".localized
return item
} else { } else {
let btn = BKButton() let btn = BKButton()
btn.setImage(UIImage(named: "baseline_delete_outline_black_24pt"), for: .normal) btn.setImage(UIImage(named: "baseline_delete_outline_black_24pt"), for: .normal)
btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40) btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
btn.accessibilityLabel = "clear".localized
return UIBarButtonItem(customView: btn) return UIBarButtonItem(customView: btn)
} }
@ -55,6 +58,7 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
btn.setImage(UIImage(named: "group_collapse")?.withRenderingMode(.alwaysTemplate), for: .selected) btn.setImage(UIImage(named: "group_collapse")?.withRenderingMode(.alwaysTemplate), for: .selected)
btn.imageView?.tintColor = BKColor.black btn.imageView?.tintColor = BKColor.black
btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40) btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
btn.accessibilityLabel = "toggle".localized
return UIBarButtonItem(customView: btn) return UIBarButtonItem(customView: btn)
}() }()
@ -242,10 +246,10 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
output.errorAlert output.errorAlert
.drive(onNext: { [weak self] error in .drive(onNext: { [weak self] error in
let alertController = UIAlertController(title: "Error", message: error, preferredStyle: .alert) let alertController = UIAlertController(title: "Error", message: error, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Copy2"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "Copy2".localized, style: .default, handler: { _ in
UIPasteboard.general.string = error UIPasteboard.general.string = error
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
self?.present(alertController, animated: true, completion: nil) self?.present(alertController, animated: true, completion: nil)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
@ -260,20 +264,20 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
deleteBtn.rx.tap.subscribe(onNext: { [weak self] _ in deleteBtn.rx.tap.subscribe(onNext: { [weak self] _ in
guard let self else { return } guard let self else { return }
let alertController = UIAlertController(title: nil, message: NSLocalizedString("clearFrom"), preferredStyle: .actionSheet) let alertController = UIAlertController(title: nil, message: "clearFrom".localized, preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("lastHour"), style: .default, handler: { [weak self] _ in alertController.addAction(UIAlertAction(title: "lastHour".localized, style: .default, handler: { [weak self] _ in
self?.clearAlert(.lastHour) self?.clearAlert(.lastHour)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("today"), style: .default, handler: { [weak self] _ in alertController.addAction(UIAlertAction(title: "today".localized, style: .default, handler: { [weak self] _ in
self?.clearAlert(.today) self?.clearAlert(.today)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("todayAndYesterday"), style: .default, handler: { [weak self] _ in alertController.addAction(UIAlertAction(title: "todayAndYesterday".localized, style: .default, handler: { [weak self] _ in
self?.clearAlert(.todayAndYesterday) self?.clearAlert(.todayAndYesterday)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("allTime"), style: .default, handler: { [weak self] _ in alertController.addAction(UIAlertAction(title: "allTime".localized, style: .default, handler: { [weak self] _ in
self?.clearAlert(.allTime) self?.clearAlert(.allTime)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
if UIDevice.current.userInterfaceIdiom == .pad { if UIDevice.current.userInterfaceIdiom == .pad {
alertController.modalPresentationStyle = .popover alertController.modalPresentationStyle = .popover
if #available(iOS 16.0, *) { if #available(iOS 16.0, *) {
@ -287,11 +291,11 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
} }
func clearAlert(_ range: MessageDeleteTimeRange) { func clearAlert(_ range: MessageDeleteTimeRange) {
let alertController = UIAlertController(title: nil, message: "\(NSLocalizedString("clearFrom"))\n\(range.string)", preferredStyle: .alert) let alertController = UIAlertController(title: nil, message: "\("clearFrom".localized)\n\(range.string)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("clear"), style: .destructive, handler: { [weak self] _ in alertController.addAction(UIAlertAction(title: "clear".localized, style: .destructive, handler: { [weak self] _ in
self?.clearRelay.accept(range) self?.clearRelay.accept(range)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
self.navigationController?.present(alertController, animated: true, completion: nil) self.navigationController?.present(alertController, animated: true, completion: nil)
} }
@ -299,7 +303,7 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// //
alertController.addAction(UIAlertAction(title: NSLocalizedString("Copy2"), style: .default, handler: { [weak self] alertController.addAction(UIAlertAction(title: "Copy2".localized, style: .default, handler: { [weak self]
(_: UIAlertAction) in (_: UIAlertAction) in
if #available(iOS 14.0, *) { if #available(iOS 14.0, *) {
var items = [[String: Any]]() var items = [[String: Any]]()
@ -311,10 +315,10 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
} else { } else {
UIPasteboard.general.string = message.attributedText?.string ?? "" UIPasteboard.general.string = message.attributedText?.string ?? ""
} }
self?.showSnackbar(text: NSLocalizedString("Copy")) self?.showSnackbar(text: "Copy".localized)
})) }))
// //
alertController.addAction(UIAlertAction(title: NSLocalizedString("removeMessage"), style: .destructive, handler: { [weak self] alertController.addAction(UIAlertAction(title: "removeMessage".localized, style: .destructive, handler: { [weak self]
(_: UIAlertAction) in (_: UIAlertAction) in
guard let self, let indexPath = self.tableView.indexPath(for: sourceCell) else { return } guard let self, let indexPath = self.tableView.indexPath(for: sourceCell) else { return }
if sourceCell is MessageTableViewCell { if sourceCell is MessageTableViewCell {
@ -326,7 +330,7 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
} }
})) }))
// //
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: { _ in })) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: { _ in }))
if UIDevice.current.userInterfaceIdiom == .pad { if UIDevice.current.userInterfaceIdiom == .pad {
alertController.popoverPresentationController?.sourceView = sourceView.superview alertController.popoverPresentationController?.sourceView = sourceView.superview
@ -346,7 +350,7 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
extension MessageListViewController: UITableViewDelegate { extension MessageListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .destructive, title: NSLocalizedString("removeMessage")) { [weak self] _, _, actionPerformed in let action = UIContextualAction(style: .destructive, title: "removeMessage".localized) { [weak self] _, _, actionPerformed in
guard let self else { return } guard let self else { return }
if self.tableView.cellForRow(at: indexPath) is MessageTableViewCell { if self.tableView.cellForRow(at: indexPath) is MessageTableViewCell {
@ -357,12 +361,12 @@ extension MessageListViewController: UITableViewDelegate {
} }
// //
let alertView = UIAlertController(title: nil, message: NSLocalizedString("removeNotice"), preferredStyle: .alert) let alertView = UIAlertController(title: nil, message: "removeNotice".localized, preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: NSLocalizedString("removeMessage"), style: .destructive, handler: { _ in alertView.addAction(UIAlertAction(title: "removeMessage".localized, style: .destructive, handler: { _ in
self.tableView.dataSource?.tableView?(self.tableView, commit: .delete, forRowAt: indexPath) self.tableView.dataSource?.tableView?(self.tableView, commit: .delete, forRowAt: indexPath)
actionPerformed(true) actionPerformed(true)
})) }))
alertView.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: { _ in alertView.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: { _ in
actionPerformed(false) actionPerformed(false)
})) }))
self.present(alertView, animated: true, completion: nil) self.present(alertView, animated: true, completion: nil)

View File

@ -176,7 +176,7 @@ class MessageListViewModel: ViewModel, ViewModelType {
items.append(.message(model: messages[0])) items.append(.message(model: messages[0]))
} else if messages.count > 0 { } else if messages.count > 0 {
// //
items.append(.messageGroup(name: group ?? NSLocalizedString("default"), totalCount: messageResult.count, messages: messages)) items.append(.messageGroup(name: group ?? "default".localized, totalCount: messageResult.count, messages: messages))
} }
} }
return items return items
@ -212,7 +212,7 @@ class MessageListViewModel: ViewModel, ViewModelType {
func transform(input: Input) -> Output { func transform(input: Input) -> Output {
// //
let titleRelay = BehaviorRelay<String>(value: NSLocalizedString("historyMessage")) let titleRelay = BehaviorRelay<String>(value: "historyMessage".localized)
// //
let messagesRelay = BehaviorRelay<[MessageSection]>(value: []) let messagesRelay = BehaviorRelay<[MessageSection]>(value: [])
// //
@ -232,9 +232,9 @@ class MessageListViewModel: ViewModel, ViewModelType {
filterGroups filterGroups
.subscribe(onNext: { filterGroups in .subscribe(onNext: { filterGroups in
if filterGroups.count <= 0 { if filterGroups.count <= 0 {
titleRelay.accept(NSLocalizedString("historyMessage")) titleRelay.accept("historyMessage".localized)
} else { } else {
titleRelay.accept(filterGroups.map { $0 ?? NSLocalizedString("default") }.joined(separator: " , ")) titleRelay.accept(filterGroups.map { $0 ?? "default".localized }.joined(separator: " , "))
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)

View File

@ -21,13 +21,17 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
tableView.separatorColor = BKColor.grey.lighten3 tableView.separatorColor = BKColor.grey.lighten3
tableView.backgroundColor = BKColor.background.primary tableView.backgroundColor = BKColor.background.primary
tableView.register(LabelCell.self, forCellReuseIdentifier: "\(LabelCell.self)") tableView.register(LabelCell.self, forCellReuseIdentifier: "\(LabelCell.self)")
tableView.register(iCloudStatusCell.self, forCellReuseIdentifier: "\(iCloudStatusCell.self)")
tableView.register(ArchiveSettingCell.self, forCellReuseIdentifier: "\(ArchiveSettingCell.self)") tableView.register(ArchiveSettingCell.self, forCellReuseIdentifier: "\(ArchiveSettingCell.self)")
tableView.register(DetailTextCell.self, forCellReuseIdentifier: "\(DetailTextCell.self)") tableView.register(DetailTextCell.self, forCellReuseIdentifier: "\(DetailTextCell.self)")
tableView.register(MutableTextCell.self, forCellReuseIdentifier: "\(MutableTextCell.self)") tableView.register(MutableTextCell.self, forCellReuseIdentifier: "\(MutableTextCell.self)")
tableView.register(SpacerCell.self, forCellReuseIdentifier: "\(SpacerCell.self)") tableView.register(SpacerCell.self, forCellReuseIdentifier: "\(SpacerCell.self)")
tableView.register(DonateCell.self, forCellReuseIdentifier: "\(DonateCell.self)") tableView.register(DonateCell.self, forCellReuseIdentifier: "\(DonateCell.self)")
if #available(iOS 26.0, *) {
// iOS26
tableView.setValue(10, forKeyPath: "sectionCornerRadius")
}
tableView.estimatedSectionHeaderHeight = 10 tableView.estimatedSectionHeaderHeight = 10
tableView.sectionHeaderHeight = UITableView.automaticDimension tableView.sectionHeaderHeight = UITableView.automaticDimension
@ -45,7 +49,7 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
private var footers: [String?] = [] private var footers: [String?] = []
override func makeUI() { override func makeUI() {
self.title = NSLocalizedString("settings") self.title = "settings".localized
self.view.addSubview(tableView) self.view.addSubview(tableView)
tableView.snp.makeConstraints { make in tableView.snp.makeConstraints { make in
@ -63,8 +67,8 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
SwiftyStoreKit.purchaseProduct(productId) { result in SwiftyStoreKit.purchaseProduct(productId) { result in
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
if case .success = result { if case .success = result {
let alert = UIAlertController(title: NSLocalizedString("successfulDonation"), message: NSLocalizedString("thankYouSupport"), preferredStyle: .alert) let alert = UIAlertController(title: "successfulDonation".localized, message: "thankYouSupport".localized, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("donateOK"), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: "donateOK".localized, style: .default, handler: nil))
self?.present(alert, animated: true) self?.present(alert, animated: true)
} }
} }
@ -115,11 +119,11 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
} }
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("export"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "export".localized, style: .default, handler: { _ in
strongSelf.backupOrRestoreActionRelay.accept(.export) strongSelf.backupOrRestoreActionRelay.accept(.export)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("import"), style: .default, handler: { [weak self] _ in alertController.addAction(UIAlertAction(title: "import".localized, style: .default, handler: { [weak self] _ in
if #available(iOS 14.0, *) { if #available(iOS 14.0, *) {
let supportedType: [UTType] = [UTType.json] let supportedType: [UTType] = [UTType.json]
let pickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: supportedType, asCopy: false) let pickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: supportedType, asCopy: false)
@ -128,7 +132,7 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
} }
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
if UIDevice.current.userInterfaceIdiom == .pad { if UIDevice.current.userInterfaceIdiom == .pad {
if let cell = strongSelf.tableView.cellForRow(at: indexPath) { if let cell = strongSelf.tableView.cellForRow(at: indexPath) {
alertController.popoverPresentationController?.sourceView = strongSelf.tableView alertController.popoverPresentationController?.sourceView = strongSelf.tableView
@ -178,10 +182,6 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
cell.textLabel?.text = text cell.textLabel?.text = text
return cell return cell
} }
case .iCloudStatus:
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") {
return cell
}
case .backup(let viewModel): case .backup(let viewModel):
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(MutableTextCell.self)") as? MutableTextCell { if let cell = tableView.dequeueReusableCell(withIdentifier: "\(MutableTextCell.self)") as? MutableTextCell {
cell.textLabel?.textColor = BKColor.blue.darken1 cell.textLabel?.textColor = BKColor.blue.darken1
@ -246,7 +246,7 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
// deviceToken // deviceToken
output.copyDeviceToken.drive { [weak self] deviceToken in output.copyDeviceToken.drive { [weak self] deviceToken in
UIPasteboard.general.string = deviceToken UIPasteboard.general.string = deviceToken
self?.showSnackbar(text: NSLocalizedString("Copy")) self?.showSnackbar(text: "Copy".localized)
}.disposed(by: rx.disposeBag) }.disposed(by: rx.disposeBag)
// //
@ -292,7 +292,7 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
), animated: true, completion: nil) ), animated: true, completion: nil)
case "restoreSubscription": case "restoreSubscription":
SwiftyStoreKit.restorePurchases { [weak self] _ in SwiftyStoreKit.restorePurchases { [weak self] _ in
self?.showSnackbar(text: NSLocalizedString("done")) self?.showSnackbar(text: "done".localized)
} }
default: break default: break
} }

View File

@ -43,25 +43,9 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
} }
try? realm.write { try? realm.write {
for message in arr { for message in arr {
guard let id = message["id"].string else { guard let messageObject = Message(json: message) else {
continue continue
} }
guard let createDate = message["createDate"].int64 else {
continue
}
let title = message["title"].string
let body = message["body"].string
let url = message["url"].string
let group = message["group"].string
let messageObject = Message()
messageObject.id = id
messageObject.title = title
messageObject.body = body
messageObject.url = url
messageObject.group = group
messageObject.createDate = Date(timeIntervalSince1970: TimeInterval(createDate))
realm.add(messageObject, update: .modified) realm.add(messageObject, update: .modified)
} }
} }
@ -74,7 +58,7 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
// //
var messageSettings = [MessageSettingItem]() var messageSettings = [MessageSettingItem]()
messageSettings.append(.backup(viewModel: MutableTextCellViewModel( messageSettings.append(.backup(viewModel: MutableTextCellViewModel(
title: "\(NSLocalizedString("export"))/\(NSLocalizedString("import"))", title: "\("export".localized)/\("import".localized)",
text: Observable.merge([restoreSuccess, input.viewDidAppear]) text: Observable.merge([restoreSuccess, input.viewDidAppear])
.map { _ in .map { _ in
if let realm = try? Realm() { if let realm = try? Realm() {
@ -84,7 +68,7 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
return 0 return 0
} }
.map { count in .map { count in
"\(count) \(NSLocalizedString("items"))" "\(count) \("items".localized)"
} }
.asDriver(onErrorDriveWith: .empty()) .asDriver(onErrorDriveWith: .empty())
) )
@ -94,7 +78,7 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
settings.append( settings.append(
SectionModel( SectionModel(
model: MessageSettingSection(header: NSLocalizedString("historyMessage"), footer: NSLocalizedString("archiveNote")), model: MessageSettingSection(header: "historyMessage".localized, footer: "archiveNote".localized),
items: messageSettings items: messageSettings
) )
) )
@ -111,7 +95,7 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
if let deviceToken = deviceToken { if let deviceToken = deviceToken {
return "\(deviceToken.prefix(2))****\(deviceToken.suffix(4))" return "\(deviceToken.prefix(2))****\(deviceToken.suffix(4))"
} }
return NSLocalizedString("unknown") return "unknown".localized
} }
) )
)) ))
@ -128,7 +112,7 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
} }
settings.append( settings.append(
SectionModel( SectionModel(
model: MessageSettingSection(header: NSLocalizedString("info"), footer: NSLocalizedString("buildDesc")), model: MessageSettingSection(header: "info".localized, footer: "buildDesc".localized),
items: infosettings items: infosettings
) )
) )
@ -136,20 +120,20 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
// //
var otherSettings = [MessageSettingItem]() var otherSettings = [MessageSettingItem]()
otherSettings.append(.detail( otherSettings.append(.detail(
title: NSLocalizedString("faq"), title: "faq".localized,
text: nil, text: nil,
textColor: nil, textColor: nil,
url: URL(string: NSLocalizedString("faqUrl")) url: URL(string: "faqUrl".localized)
)) ))
otherSettings.append(.detail( otherSettings.append(.detail(
title: NSLocalizedString("documentation"), title: "documentation".localized,
text: nil, text: nil,
textColor: nil, textColor: nil,
url: URL(string: NSLocalizedString("docUrl")) url: URL(string: "docUrl".localized)
)) ))
otherSettings.append(.detail( otherSettings.append(.detail(
title: NSLocalizedString("sourceCode"), title: "sourceCode".localized,
text: nil, text: nil,
textColor: nil, textColor: nil,
url: URL(string: "https://github.com/Finb/Bark") url: URL(string: "https://github.com/Finb/Bark")
@ -157,19 +141,19 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
settings.append( settings.append(
SectionModel( SectionModel(
model: MessageSettingSection(header: NSLocalizedString("other")), model: MessageSettingSection(header: "other".localized),
items: otherSettings items: otherSettings
) )
) )
// //
var donateSettings = [MessageSettingItem]() var donateSettings = [MessageSettingItem]()
donateSettings.append(.donate(title: NSLocalizedString("oneTimeDonation"), productId: "bark.oneTimeDonation.18")) donateSettings.append(.donate(title: "oneTimeDonation".localized, productId: "bark.oneTimeDonation.18"))
donateSettings.append(.donate(title: NSLocalizedString("continuousSupport"), productId: "bark.continuousSupport.18")) donateSettings.append(.donate(title: "continuousSupport".localized, productId: "bark.continuousSupport.18"))
settings.append( settings.append(
SectionModel( SectionModel(
model: MessageSettingSection( model: MessageSettingSection(
header: NSLocalizedString("donate"), header: "donate".localized,
footer: nil footer: nil
), ),
items: donateSettings items: donateSettings
@ -227,8 +211,6 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
enum MessageSettingItem { enum MessageSettingItem {
// //
case label(text: String) case label(text: String)
// iCloud
case iCloudStatus
// //
case archiveSetting(viewModel: ArchiveSettingCellViewModel) case archiveSetting(viewModel: ArchiveSettingCellViewModel)
// cell // cell

View File

@ -20,14 +20,15 @@ class NewServerViewController: BaseViewController<NewServerViewModel> {
button.frame = CGRect(x: 0, y: 0, width: 24, height: 24) button.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
button.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10) button.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
button.tintColor = BKColor.grey.darken3 button.tintColor = BKColor.grey.darken3
button.isAccessibilityElement = false
return button return button
}() }()
lazy var addressTextField: TextField = { lazy var addressTextField: TextField = {
let textField = TextField() let textField = TextField()
textField.keyboardType = .URL textField.keyboardType = .URL
textField.placeholder = NSLocalizedString("ServerAddress") textField.placeholder = "ServerAddress".localized
textField.detail = NSLocalizedString("ServerExample") textField.detail = "ServerExample".localized
textField.transition([.scale(0.85), .opacity(0)]) textField.transition([.scale(0.85), .opacity(0)])
textField.detailLabel.transition([.scale(0.85), .opacity(0)]) textField.detailLabel.transition([.scale(0.85), .opacity(0)])
textField.textColor = BKColor.grey.darken4 textField.textColor = BKColor.grey.darken4
@ -41,7 +42,7 @@ class NewServerViewController: BaseViewController<NewServerViewModel> {
let noticeLabel: UILabel = { let noticeLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.text = NSLocalizedString("DeploymentDocuments") label.text = "DeploymentDocuments".localized
label.textColor = BKColor.blue.base label.textColor = BKColor.blue.base
label.font = UIFont.preferredFont(ofSize: 12) label.font = UIFont.preferredFont(ofSize: 12)
label.adjustsFontForContentSizeCategory = true label.adjustsFontForContentSizeCategory = true
@ -62,7 +63,7 @@ class NewServerViewController: BaseViewController<NewServerViewModel> {
override func makeUI() { override func makeUI() {
self.navigationItem.largeTitleDisplayMode = .never self.navigationItem.largeTitleDisplayMode = .never
navigationItem.title = NSLocalizedString("AddServer") navigationItem.title = "AddServer".localized
self.view.layout(addressTextField) self.view.layout(addressTextField)
.top(kNavigationHeight + 40).left(10).right(10) .top(kNavigationHeight + 40).left(10).right(10)
@ -78,7 +79,7 @@ class NewServerViewController: BaseViewController<NewServerViewModel> {
// //
let noticeTap = noticeLabel.gestureRecognizers!.first!.rx let noticeTap = noticeLabel.gestureRecognizers!.first!.rx
.event .event
.map { _ -> () in .map { _ in
() ()
} }
.asDriver(onErrorJustReturn: ()) .asDriver(onErrorJustReturn: ())
@ -97,7 +98,7 @@ class NewServerViewController: BaseViewController<NewServerViewModel> {
.asDriver(onErrorDriveWith: .empty()) .asDriver(onErrorDriveWith: .empty())
// //
let scannerDidScan = self.scanButton.rx.tap.flatMapLatest {[weak self] _ -> Observable<String> in let scannerDidScan = self.scanButton.rx.tap.flatMapLatest { [weak self] _ -> Observable<String> in
let controller = QRScannerViewController() let controller = QRScannerViewController()
self?.navigationController?.present(controller, animated: true, completion: nil) self?.navigationController?.present(controller, animated: true, completion: nil)
return controller.scannerDidSuccess return controller.scannerDidSuccess
@ -115,8 +116,7 @@ class NewServerViewController: BaseViewController<NewServerViewModel> {
output.showKeyboard.drive(onNext: { [weak self] show in output.showKeyboard.drive(onNext: { [weak self] show in
if show { if show {
_ = self?.addressTextField.becomeFirstResponder() _ = self?.addressTextField.becomeFirstResponder()
} } else {
else {
self?.addressTextField.resignFirstResponder() self?.addressTextField.resignFirstResponder()
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)

View File

@ -38,7 +38,7 @@ class NewServerViewModel: ViewModel, ViewModelType {
let showSnackbar = PublishRelay<String>() let showSnackbar = PublishRelay<String>()
let notice = input.noticeClick let notice = input.noticeClick
.map { URL(string: NSLocalizedString("deployUrl"))! } .map { URL(string: "deployUrl".localized)! }
.asDriver() .asDriver()
input.viewDidAppear input.viewDidAppear
@ -66,9 +66,8 @@ class NewServerViewModel: ViewModel, ViewModelType {
return BarkApi.provider return BarkApi.provider
.request(.ping(baseURL: url)) .request(.ping(baseURL: url))
.filterResponseError() .filterResponseError()
} } else {
else { showSnackbar.accept("InvalidURL".localized)
showSnackbar.accept(NSLocalizedString("InvalidURL"))
return .empty() return .empty()
} }
} }
@ -82,9 +81,9 @@ class NewServerViewModel: ViewModel, ViewModelType {
ServerManager.shared.syncAllServers() ServerManager.shared.syncAllServers()
strongSelf.pop.accept(URL(string: strongSelf.url)?.host ?? "") strongSelf.pop.accept(URL(string: strongSelf.url)?.host ?? "")
showSnackbar.accept(NSLocalizedString("AddedSuccessfully")) showSnackbar.accept("AddedSuccessfully".localized)
case .failure(let error): case .failure(let error):
showSnackbar.accept("\(NSLocalizedString("InvalidServer"))\(error.rawString())") showSnackbar.accept("\("InvalidServer".localized)\(error.rawString())")
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)

View File

@ -7,10 +7,10 @@
// //
import Foundation import Foundation
import Material
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import RxSwift import RxSwift
import Material
struct SectionItem { struct SectionItem {
let image: UIImage? let image: UIImage?
@ -29,9 +29,9 @@ class SectionViewModel: ViewModel, ViewModelType {
func initSectionItems() -> Observable<[SectionModel<String, SectionItem>]> { func initSectionItems() -> Observable<[SectionModel<String, SectionItem>]> {
let sectionItems = [ let sectionItems = [
SectionItem(image: UIImage(named: "baseline_gite_black_24pt"), title: NSLocalizedString("service")), SectionItem(image: UIImage(named: "baseline_gite_black_24pt"), title: "service".localized),
SectionItem(image: Icon.history, title: NSLocalizedString("historyMessage")), SectionItem(image: Icon.history, title: "historyMessage".localized),
SectionItem(image: UIImage(named: "baseline_manage_accounts_black_24pt"), title: NSLocalizedString("settings")), SectionItem(image: UIImage(named: "baseline_manage_accounts_black_24pt"), title: "settings".localized)
] ]
let section = [SectionModel(model: "", items: sectionItems)] let section = [SectionModel(model: "", items: sectionItems)]
return Observable.just(section) return Observable.just(section)

View File

@ -40,6 +40,7 @@ class ServerListViewController: BaseViewController<ServerListViewModel> {
closeButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40) closeButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
closeButton.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10) closeButton.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
closeButton.tintColor = BKColor.grey.darken4 closeButton.tintColor = BKColor.grey.darken4
closeButton.accessibilityLabel = "close".localized
return closeButton return closeButton
}() }()
@ -53,7 +54,7 @@ class ServerListViewController: BaseViewController<ServerListViewModel> {
}() }()
override func makeUI() { override func makeUI() {
self.title = NSLocalizedString("serverList") self.title = "serverList".localized
navigationItem.setRightBarButtonItem(item: UIBarButtonItem(customView: closeButton)) navigationItem.setRightBarButtonItem(item: UIBarButtonItem(customView: closeButton))
@ -125,7 +126,7 @@ class ServerListViewController: BaseViewController<ServerListViewModel> {
output.copy output.copy
.drive(onNext: { [weak self] text in .drive(onNext: { [weak self] text in
UIPasteboard.general.string = text UIPasteboard.general.string = text
self?.showSnackbar(text: NSLocalizedString("Copy")) self?.showSnackbar(text: "Copy".localized)
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
@ -147,50 +148,50 @@ class ServerListViewController: BaseViewController<ServerListViewModel> {
} }
let alertController = UIAlertController(title: nil, message: "\(URL(string: viewModel.server.address)?.host ?? "")", preferredStyle: .actionSheet) let alertController = UIAlertController(title: nil, message: "\(URL(string: viewModel.server.address)?.host ?? "")", preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("copyAddressAndKey"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "copyAddressAndKey".localized, style: .default, handler: { _ in
relay.accept((viewModel.server, .copy)) relay.accept((viewModel.server, .copy))
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("resetKey"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "resetKey".localized, style: .default, handler: { _ in
let alertController = UIAlertController(title: NSLocalizedString("resetKey"), message: NSLocalizedString("resetKeyDesc"), preferredStyle: .alert) let alertController = UIAlertController(title: "resetKey".localized, message: "resetKeyDesc".localized, preferredStyle: .alert)
alertController.addTextField { textField in alertController.addTextField { textField in
textField.placeholder = NSLocalizedString("resetKeyPlaceholder") textField.placeholder = "resetKeyPlaceholder".localized
} }
alertController.addAction(UIAlertAction(title: NSLocalizedString("confirm"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "confirm".localized, style: .default, handler: { _ in
relay.accept((viewModel.server, .reset(key: alertController.textFields?.first?.text))) relay.accept((viewModel.server, .reset(key: alertController.textFields?.first?.text)))
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
self.navigationController?.present(alertController, animated: true, completion: nil) self.navigationController?.present(alertController, animated: true, completion: nil)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("setAsDefaultServer"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "setAsDefaultServer".localized, style: .default, handler: { _ in
relay.accept((viewModel.server, .select)) relay.accept((viewModel.server, .select))
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("setServerName"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "setServerName".localized, style: .default, handler: { _ in
let alertController = UIAlertController(title: NSLocalizedString("setServerName"), message: nil, preferredStyle: .alert) let alertController = UIAlertController(title: "setServerName".localized, message: nil, preferredStyle: .alert)
alertController.addTextField { textField in alertController.addTextField { textField in
textField.text = viewModel.server.name textField.text = viewModel.server.name
} }
alertController.addAction(UIAlertAction(title: NSLocalizedString("confirm"), style: .default, handler: { _ in alertController.addAction(UIAlertAction(title: "confirm".localized, style: .default, handler: { _ in
relay.accept((viewModel.server, .setName(name: alertController.textFields?.first?.text))) relay.accept((viewModel.server, .setName(name: alertController.textFields?.first?.text)))
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
self.navigationController?.present(alertController, animated: true, completion: nil) self.navigationController?.present(alertController, animated: true, completion: nil)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("deleteServer"), style: .destructive, handler: { _ in alertController.addAction(UIAlertAction(title: "deleteServer".localized, style: .destructive, handler: { _ in
let alertController = UIAlertController(title: nil, message: NSLocalizedString("confirmDeleteServer"), preferredStyle: .alert) let alertController = UIAlertController(title: nil, message: "confirmDeleteServer".localized, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("confirm"), style: .destructive, handler: { _ in alertController.addAction(UIAlertAction(title: "confirm".localized, style: .destructive, handler: { _ in
relay.accept((viewModel.server, .delete)) relay.accept((viewModel.server, .delete))
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
self.navigationController?.present(alertController, animated: true, completion: nil) self.navigationController?.present(alertController, animated: true, completion: nil)
})) }))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
if UIDevice.current.userInterfaceIdiom == .pad { if UIDevice.current.userInterfaceIdiom == .pad {
if let cell = self.tableView.cellForRow(at: indexPath) { if let cell = self.tableView.cellForRow(at: indexPath) {

View File

@ -54,7 +54,7 @@ class ServerListViewModel: ViewModel, ViewModelType {
// //
deleteCheck.filter { $0 == nil } deleteCheck.filter { $0 == nil }
.map { _ in NSLocalizedString("deleteFailed") } .map { _ in "deleteFailed".localized }
.bind(to: showSnackbar) .bind(to: showSnackbar)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
@ -79,7 +79,7 @@ class ServerListViewModel: ViewModel, ViewModelType {
}.share() }.share()
// //
serverDeleted.map { NSLocalizedString("deletedSuccessfully") } serverDeleted.map { "deletedSuccessfully".localized }
.bind(to: showSnackbar) .bind(to: showSnackbar)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
@ -90,7 +90,7 @@ class ServerListViewModel: ViewModel, ViewModelType {
// //
resetServer.filter { ($0.2?.count ?? 0) <= 0 } resetServer.filter { ($0.2?.count ?? 0) <= 0 }
.map { _ in NSLocalizedString("resetFailed2") } .map { _ in "resetFailed2".localized }
.bind(to: showSnackbar) .bind(to: showSnackbar)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
@ -131,7 +131,7 @@ class ServerListViewModel: ViewModel, ViewModelType {
// //
serverReseted.filter { $0 == nil } serverReseted.filter { $0 == nil }
.map { _ in NSLocalizedString("resetFailed") } .map { _ in "resetFailed".localized }
.bind(to: showSnackbar) .bind(to: showSnackbar)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
@ -153,7 +153,7 @@ class ServerListViewModel: ViewModel, ViewModelType {
// //
let serverSelected = input.selectServer.asObservable().map { server in let serverSelected = input.selectServer.asObservable().map { server in
ServerManager.shared.setCurrentServer(serverId: server.id) ServerManager.shared.setCurrentServer(serverId: server.id)
showSnackbar.accept(NSLocalizedString("setSuccessfully")) showSnackbar.accept("setSuccessfully".localized)
return () return ()
} }

View File

@ -32,7 +32,7 @@ class SoundsViewController: BaseViewController<SoundsViewModel> {
var playingAudio: CFURL? var playingAudio: CFURL?
override func makeUI() { override func makeUI() {
self.title = NSLocalizedString("notificationSound") self.title = "notificationSound".localized
self.view.addSubview(self.tableView) self.view.addSubview(self.tableView)
self.tableView.delegate = self self.tableView.delegate = self
@ -82,14 +82,14 @@ class SoundsViewController: BaseViewController<SoundsViewModel> {
output.copyNameAction.drive(onNext: { [unowned self] name in output.copyNameAction.drive(onNext: { [unowned self] name in
UIPasteboard.general.string = name.trimmingCharacters(in: .whitespacesAndNewlines) UIPasteboard.general.string = name.trimmingCharacters(in: .whitespacesAndNewlines)
self.navigationController?.showSnackbar(text: NSLocalizedString("Copy")) self.navigationController?.showSnackbar(text: "Copy".localized)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
output.playAction.drive(onNext: { url in output.playAction.drive(onNext: { url in
/// ///
AudioServicesDisposeSystemSoundID(self.currentSoundID) AudioServicesDisposeSystemSoundID(self.currentSoundID)
/// ///
if self.playingAudio == url{ if self.playingAudio == url {
self.playingAudio = nil self.playingAudio = nil
self.currentSoundID = 0 self.currentSoundID = 0
return return
@ -121,7 +121,7 @@ extension SoundsViewController: UITableViewDelegate {
guard section == 0 else { guard section == 0 else {
return 0 return 0
} }
return NSLocalizedString("uploadSoundNoticeFullText").count <= 30 ? 50 : 60 return "uploadSoundNoticeFullText".localized.count <= 30 ? 50 : 60
} }
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
@ -130,7 +130,7 @@ extension SoundsViewController: UITableViewDelegate {
let view = UIView() let view = UIView()
let label = UILabel() let label = UILabel()
label.text = NSLocalizedString(sectionTitle) label.text = sectionTitle.localized
label.fontSize = 14 label.fontSize = 14
label.textColor = BKColor.grey.darken3 label.textColor = BKColor.grey.darken3
view.addSubview(label) view.addSubview(label)
@ -148,8 +148,8 @@ extension SoundsViewController: UITableViewDelegate {
} }
let view = UIView() let view = UIView()
let fullText = NSLocalizedString("uploadSoundNoticeFullText") let fullText = "uploadSoundNoticeFullText".localized
let highlightText = NSLocalizedString("uploadSoundNoticeHighlightText") let highlightText = "uploadSoundNoticeHighlightText".localized
let attrStr = NSMutableAttributedString( let attrStr = NSMutableAttributedString(
string: fullText, string: fullText,
attributes: [ attributes: [

View File

@ -62,7 +62,7 @@ struct AESCryptoModel {
} }
guard algorithm.keyLength == key.count else { guard algorithm.keyLength == key.count else {
throw String(format: NSLocalizedString("enterKey"), algorithm.keyLength) throw String(format: "enterKey".localized, algorithm.keyLength)
} }
var iv = "" var iv = ""
@ -74,9 +74,8 @@ struct AESCryptoModel {
if let ivField = cryptoFields.iv, ivField.count == expectIVLength { if let ivField = cryptoFields.iv, ivField.count == expectIVLength {
iv = ivField iv = ivField
} } else {
else { throw String(format: "enterIv".localized, expectIVLength)
throw String(format: NSLocalizedString("enterIv"), expectIVLength)
} }
} }
@ -103,6 +102,6 @@ struct AESCryptoModel {
} }
func decrypt(ciphertext: String) throws -> String { func decrypt(ciphertext: String) throws -> String {
return String(data: Data(try aes.decrypt(Array(base64: ciphertext))), encoding: .utf8) ?? "" return try String(data: Data(aes.decrypt(Array(base64: ciphertext))), encoding: .utf8) ?? ""
} }
} }

View File

@ -7,6 +7,7 @@
// //
import RealmSwift import RealmSwift
import SwiftyJSON
import UIKit import UIKit
class Message: Object { class Message: Object {
@ -26,4 +27,23 @@ class Message: Object {
override class func indexedProperties() -> [String] { override class func indexedProperties() -> [String] {
return ["group", "createDate"] return ["group", "createDate"]
} }
/// JSON
convenience init?(json: JSON) {
self.init()
guard let id = json["id"].string else {
return nil
}
guard let createDate = json["createDate"].int64 else {
return nil
}
self.id = id
self.title = json["title"].string
self.subtitle = json["subtitle"].string
self.body = json["body"].string
self.url = json["url"].string
self.image = json["image"].string
self.group = json["group"].string
self.createDate = Date(timeIntervalSince1970: TimeInterval(createDate))
}
} }

View File

@ -32,23 +32,23 @@ enum MessageDeleteTimeRange {
var string: String { var string: String {
switch self { switch self {
case .lastHour: case .lastHour:
return NSLocalizedString("lastHour") return "lastHour".localized
case .today: case .today:
return NSLocalizedString("today") return "today".localized
case .todayAndYesterday: case .todayAndYesterday:
return NSLocalizedString("todayAndYesterday") return "todayAndYesterday".localized
case .lastMonth: case .lastMonth:
return NSLocalizedString("lastMonth") return "lastMonth".localized
case .allTime: case .allTime:
return NSLocalizedString("allTime") return "allTime".localized
case .beforeOneHour: case .beforeOneHour:
return NSLocalizedString("beforeAnHour") return "beforeAnHour".localized
case .beforeToday: case .beforeToday:
return NSLocalizedString("beforeToday") return "beforeToday".localized
case .beforeYesterday: case .beforeYesterday:
return NSLocalizedString("beforeYesterday") return "beforeYesterday".localized
case .beforeOneMonth: case .beforeOneMonth:
return NSLocalizedString("beforeAMonth") return "beforeAMonth".localized
} }
} }

View File

@ -1,5 +1,5 @@
// //
// MessageTableViewCellViewModel.swift // MessageSection.swift
// Bark // Bark
// //
// Created by huangfeng on 2020/11/21. // Created by huangfeng on 2020/11/21.
@ -51,9 +51,9 @@ extension MessageListCellItem: IdentifiableType {
var identity: String { var identity: String {
switch self { switch self {
case .message(let model): case .message(let model):
return "list_\(model.id)" return "list_\(model.id)_\(model.createDate?.timeInterval ?? 0)"
case .messageGroup(_, _, let messages): case .messageGroup(_, _, let messages):
return "group_\(messages.first?.group ?? NSLocalizedString("Default"))" return "group_\(messages.first?.group ?? "default".localized)_\(messages.first?.createDate?.timeInterval ?? 0)"
} }
} }
} }

View File

@ -31,9 +31,13 @@ class ArchiveProcessor: NotificationContentProcessor {
let url = userInfo["url"] as? String let url = userInfo["url"] as? String
let group = userInfo["group"] as? String let group = userInfo["group"] as? String
let image = userInfo["image"] as? String let image = userInfo["image"] as? String
let id = userInfo["id"] as? String
try? realm?.write { try? realm?.write {
let message = Message() let message = Message()
if let id, !id.isEmpty {
message.id = id
}
message.title = title message.title = title
message.subtitle = subtitle message.subtitle = subtitle
message.body = body message.body = body
@ -41,7 +45,7 @@ class ArchiveProcessor: NotificationContentProcessor {
message.image = image message.image = image
message.group = group message.group = group
message.createDate = Date() message.createDate = Date()
realm?.add(message) realm?.add(message, update: .all)
} }
} }
return bestAttemptContent return bestAttemptContent

View File

@ -17,7 +17,7 @@ class AutoCopyProcessor: NotificationContentProcessor {
if let copy = userInfo["copy"] as? String { if let copy = userInfo["copy"] as? String {
UIPasteboard.general.string = copy UIPasteboard.general.string = copy
} else { } else {
UIPasteboard.general.string = bestAttemptContent.body UIPasteboard.general.string = bestAttemptContent.bodyText
} }
} }
return bestAttemptContent return bestAttemptContent

View File

@ -1,4 +1,4 @@
source 'https://github.com/CocoaPods/Specs.git' source 'https://cdn.cocoapods.org/'
platform:ios,'13.0' platform:ios,'13.0'
inhibit_all_warnings! inhibit_all_warnings!

View File

@ -6,28 +6,26 @@ PODS:
- DropDown (2.3.13) - DropDown (2.3.13)
- FDFullscreenPopGesture (1.1) - FDFullscreenPopGesture (1.1)
- ImageViewer.swift (3.3.8) - ImageViewer.swift (3.3.8)
- IQKeyboardCore (1.0.7) - IQKeyboardCore (1.0.8)
- IQKeyboardManagerSwift/Core (8.0.0): - IQKeyboardManagerSwift/Core (8.0.1):
- IQKeyboardNotification - IQKeyboardNotification
- IQTextInputViewNotification - IQTextInputViewNotification
- IQKeyboardManagerSwift/IQKeyboardToolbarManager (8.0.0): - IQKeyboardManagerSwift/IQKeyboardToolbarManager (8.0.1):
- IQKeyboardManagerSwift/Core - IQKeyboardManagerSwift/Core
- IQKeyboardToolbarManager - IQKeyboardToolbarManager
- IQKeyboardNotification (1.0.3) - IQKeyboardNotification (1.0.6)
- IQKeyboardToolbar (1.1.1): - IQKeyboardToolbar (1.1.2):
- IQKeyboardCore - IQKeyboardToolbar/Core (= 1.1.2)
- IQKeyboardToolbar/Core (= 1.1.1) - IQKeyboardToolbar/Core (1.1.2):
- IQKeyboardToolbar/Core (1.1.1):
- IQKeyboardCore - IQKeyboardCore
- IQKeyboardToolbar/Placeholderable - IQKeyboardToolbar/Placeholderable
- IQKeyboardToolbar/Placeholderable (1.1.1): - IQKeyboardToolbar/Placeholderable (1.1.2)
- IQKeyboardCore - IQKeyboardToolbarManager (1.1.4):
- IQKeyboardToolbarManager (1.1.2):
- IQKeyboardToolbar - IQKeyboardToolbar
- IQTextInputViewNotification - IQTextInputViewNotification
- IQTextInputViewNotification (1.0.8): - IQTextInputViewNotification (1.0.9):
- IQKeyboardCore - IQKeyboardCore
- Kingfisher (8.1.3) - Kingfisher (8.6.0)
- Material (3.1.8): - Material (3.1.8):
- Material/Core (= 3.1.8) - Material/Core (= 3.1.8)
- Material/Core (3.1.8): - Material/Core (3.1.8):
@ -45,14 +43,14 @@ PODS:
- "NSObject+Rx (5.2.2)": - "NSObject+Rx (5.2.2)":
- RxSwift (~> 6.2) - RxSwift (~> 6.2)
- ObjectMapper (4.4.2) - ObjectMapper (4.4.2)
- Realm (20.0.1): - Realm (20.0.3):
- Realm/Headers (= 20.0.1) - Realm/Headers (= 20.0.3)
- Realm/Headers (20.0.1) - Realm/Headers (20.0.3)
- RealmSwift (20.0.1): - RealmSwift (20.0.3):
- Realm (= 20.0.1) - Realm (= 20.0.3)
- RxCocoa (6.8.0): - RxCocoa (6.9.0):
- RxRelay (= 6.8.0) - RxRelay (= 6.9.0)
- RxSwift (= 6.8.0) - RxSwift (= 6.9.0)
- RxDataSources (5.0.0): - RxDataSources (5.0.0):
- Differentiator (~> 5.0) - Differentiator (~> 5.0)
- RxCocoa (~> 6.0) - RxCocoa (~> 6.0)
@ -60,9 +58,9 @@ PODS:
- RxGesture (4.0.4): - RxGesture (4.0.4):
- RxCocoa (~> 6.0) - RxCocoa (~> 6.0)
- RxSwift (~> 6.0) - RxSwift (~> 6.0)
- RxRelay (6.8.0): - RxRelay (6.9.0):
- RxSwift (= 6.8.0) - RxSwift (= 6.9.0)
- RxSwift (6.8.0) - RxSwift (6.9.0)
- SnapKit (5.7.1) - SnapKit (5.7.1)
- SVProgressHUD (2.3.1): - SVProgressHUD (2.3.1):
- SVProgressHUD/Core (= 2.3.1) - SVProgressHUD/Core (= 2.3.1)
@ -145,13 +143,13 @@ SPEC CHECKSUMS:
DropDown: 8a2116376c1981888557f72ec2ffc9a5e0e456ec DropDown: 8a2116376c1981888557f72ec2ffc9a5e0e456ec
FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0 FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0
ImageViewer.swift: 284cd8127d31af8e5938674fb9f8e695a4cdf6c6 ImageViewer.swift: 284cd8127d31af8e5938674fb9f8e695a4cdf6c6
IQKeyboardCore: cb7f0a9a17dd32599569f2f478c1418dc28bcebb IQKeyboardCore: 8652977ec919cf5351aa2977fedd1a6546476fbc
IQKeyboardManagerSwift: 0c6fbbaa2e60739e48d7cf59f25661471a7a3a65 IQKeyboardManagerSwift: 835fc9c6e4732398113406d84900ad2e8f141218
IQKeyboardNotification: d7382c4466c5a5adef92c7452ebf861b36050088 IQKeyboardNotification: eb4910401f5a0e68f97e71c62f8a0c5b7e9d535c
IQKeyboardToolbar: d4bdccfb78324aec2f3920659c77bb89acd33312 IQKeyboardToolbar: a8aab764a27d55892b951e58ebfffdde14a01ce8
IQKeyboardToolbarManager: 6f4072ac620c2572d4af8c09f42a801f3e4909f7 IQKeyboardToolbarManager: c8a575e8b5fffe5873d0e75312244498a0759473
IQTextInputViewNotification: f5e954d8881fd9808b744e49e024cc0d4bcfe572 IQTextInputViewNotification: 3b9fb27a16e7ee8958cc9092cfb07a1a9e1fd559
Kingfisher: f2af9028b16baf9dc6c07c570072bc41cbf009ef Kingfisher: 64278f126a815d0e2d391cdf71311b85882c4de0
Material: a2a3f400a3b549d53ef89e56c58c4535b29db387 Material: a2a3f400a3b549d53ef89e56c58c4535b29db387
MercariQRScanner: cd024685242f78fe40879cca9734bb7bb2fceb93 MercariQRScanner: cd024685242f78fe40879cca9734bb7bb2fceb93
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78 MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
@ -159,18 +157,18 @@ SPEC CHECKSUMS:
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
"NSObject+Rx": 61cf1f7306a73dcef8b36649198af0813ec18dfd "NSObject+Rx": 61cf1f7306a73dcef8b36649198af0813ec18dfd
ObjectMapper: e6e4d91ff7f2861df7aecc536c92d8363f4c9677 ObjectMapper: e6e4d91ff7f2861df7aecc536c92d8363f4c9677
Realm: 9572204903ef28caeb3c03bb30c1147f354ee3e7 Realm: 853e5089d6042dff807bda000277eadfe2da93d2
RealmSwift: d230b44017a55a1ba97de6a944da1fe3bedb92b5 RealmSwift: f33c19577cefcbf681345d721fcbc7b42be4c949
RxCocoa: 2d33c1e1e5d66492052ad46b11024ae287572880 RxCocoa: ac16414696ae706516be3e1ab00fcce5bdc9be8a
RxDataSources: aa47cc1ed6c500fa0dfecac5c979b723542d79cf RxDataSources: aa47cc1ed6c500fa0dfecac5c979b723542d79cf
RxGesture: f3efb47ed2d26a8082f7b660d4a59970e275a7f8 RxGesture: f3efb47ed2d26a8082f7b660d4a59970e275a7f8
RxRelay: 335c78b926a2aea8d863a6d25f1ed3b5ad8e8705 RxRelay: 6b0c930e5cef57d5fe2032571e5e65b78e3cbdb1
RxSwift: 4e28be97cbcfeee614af26d83415febbf2bf6f45 RxSwift: 31649ace6aceeb422e16ff71c60804f9c3281ed9
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22 SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
SwiftyJSON: f5b1bf1cd8dd53cd25887ac0eabcfd92301c6a5a SwiftyJSON: f5b1bf1cd8dd53cd25887ac0eabcfd92301c6a5a
SwiftyStoreKit: 6b9c08810269f030586dac1fae8e75871a82e84a SwiftyStoreKit: 6b9c08810269f030586dac1fae8e75871a82e84a
PODFILE CHECKSUM: 3d8263a3dcdc33edad82e369b6e182961d45cc98 PODFILE CHECKSUM: 83208e7dd6cca7045d0dd51f1b6a4c06c1688c40
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2

View File

@ -86,6 +86,7 @@ Critical alerts will ignore silent and do not disturb modes, always playing the
``` ```
## Others ## Others
- [Browser Extension](https://github.com/ij369/bark-sender)
- [Online Scheduled Sending](https://api.ihint.me/bark.html) - [Online Scheduled Sending](https://api.ihint.me/bark.html)
- [Windows Push Client](https://github.com/HsuDan/BarkHelper) - [Windows Push Client](https://github.com/HsuDan/BarkHelper)
- [Cross-platform Command Line Application](https://github.com/JasonkayZK/bark-cli) - [Cross-platform Command Line Application](https://github.com/JasonkayZK/bark-cli)

View File

@ -1,8 +1,10 @@
**[English](README.en.md)** | 中文 **[English](README.en.md)** | 中文
## Bark ## Bark
Bark 是一款推送通知工具 App。它免费、简单且安全依赖 APNs 不会消耗设备的电量。<br/> Bark 是一款免费的推送通知工具 App。<br/>
Bark 支持 iOS 通知的多项高级特性,包括推送分组、定制推送图标、铃声,发送时效性通知、重要警告等等。<br/> 它简单、安全,基于 APNs 实现,不会额外消耗设备电量。<br/>
此外Bark 允许用户自行部署服务端还有推送加密APP由开源代码自动构建与发布等措施保证隐私安全。<br/> <br/>
Bark 支持 iOS 通知的多种高级功能:推送分组、自定义图标和铃声、时效性通知、重要警告等。<br/>
此外Bark 还支持用户自建服务端并提供端到端推送加密。APP 是由 Github Action 自动构建和发布,从根本上保障隐私与安全。<br/>
## 下载 ## 下载
<a target='_blank' href='https://apps.apple.com/app/bark-custom-notifications/id1403753865'> <a target='_blank' href='https://apps.apple.com/app/bark-custom-notifications/id1403753865'>
@ -85,6 +87,7 @@ https://api.day.app/yourkey/时效性通知?level=critical
重要警告会忽略静音和勿扰模式,始终播放通知声音并在屏幕上显示。 重要警告会忽略静音和勿扰模式,始终播放通知声音并在屏幕上显示。
``` ```
## 其他 ## 其他
- [浏览器扩展](https://github.com/ij369/bark-sender)
- [在线定时发送](https://api.ihint.me/bark.html) - [在线定时发送](https://api.ihint.me/bark.html)
- [Windows推送客户端](https://github.com/HsuDan/BarkHelper) - [Windows推送客户端](https://github.com/HsuDan/BarkHelper)
- [跨平台的命令行应用](https://github.com/JasonkayZK/bark-cli) - [跨平台的命令行应用](https://github.com/JasonkayZK/bark-cli)

View File

@ -11,7 +11,7 @@ import UIKit
class AddSoundCell: UITableViewCell { class AddSoundCell: UITableViewCell {
let button: UIButton = { let button: UIButton = {
let button = UIButton(type: .system) let button = UIButton(type: .system)
button.setTitle(NSLocalizedString("uploadSound"), for: .normal) button.setTitle("uploadSound".localized, for: .normal)
button.setImage(UIImage(named: "music_note-music_note_symbol"), for: .normal) button.setImage(UIImage(named: "music_note-music_note_symbol"), for: .normal)
button.setTitleColor(BKColor.lightBlue.darken3, for: .normal) button.setTitleColor(BKColor.lightBlue.darken3, for: .normal)
button.tintColor = BKColor.lightBlue.darken3 button.tintColor = BKColor.lightBlue.darken3
@ -31,8 +31,8 @@ class AddSoundCell: UITableViewCell {
} }
} }
@available(*, unavailable)
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
} }

View File

@ -18,7 +18,7 @@ class ArchiveSettingCell: BaseTableViewCell<ArchiveSettingCellViewModel> {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none self.selectionStyle = .none
self.backgroundColor = BKColor.background.secondary self.backgroundColor = BKColor.background.secondary
self.textLabel?.text = NSLocalizedString("defaultArchiveSettings") self.textLabel?.text = "defaultArchiveSettings".localized
contentView.addSubview(switchButton) contentView.addSubview(switchButton)
switchButton.snp.makeConstraints { make in switchButton.snp.makeConstraints { make in

23
View/InsetView.swift Normal file
View File

@ -0,0 +1,23 @@
//
// InsetView.swift
// Bark
//
// Created by huangfeng on 9/8/25.
// Copyright © 2025 Fin. All rights reserved.
//
import UIKit
class InsetView: UIView {
init(subView: UIView, insets: UIEdgeInsets) {
super.init(frame: .zero)
self.addSubview(subView)
subView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(insets)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -25,7 +25,7 @@ class MessageGroupMoreView: UIView {
var count: Int = 0 { var count: Int = 0 {
didSet { didSet {
moreLabel.text = NSLocalizedString("viewAllMessages").format(count) moreLabel.text = "viewAllMessages".localized(with: count)
} }
} }

View File

@ -104,6 +104,9 @@ class MessageItemView: UIView {
contentStackView.addArrangedSubview(bodyLabel) contentStackView.addArrangedSubview(bodyLabel)
contentStackView.addArrangedSubview(imageView) contentStackView.addArrangedSubview(imageView)
self.isAccessibilityElement = true
self.subviews.forEach { $0.isAccessibilityElement = false }
layoutView() layoutView()
// //
@ -177,6 +180,7 @@ extension MessageItemView {
func setMessage(message: MessageItemModel) { func setMessage(message: MessageItemModel) {
self.bodyLabel.attributedText = message.attributedText self.bodyLabel.attributedText = message.attributedText
self.accessibilityLabel = message.attributedText?.string
self.dateLabel.text = message.dateText self.dateLabel.text = message.dateText
if let image = message.image { if let image = message.image {
imageView.isHidden = false imageView.isHidden = false
@ -201,7 +205,7 @@ extension MessageItemView {
.contentMode(.scaleAspectFit) .contentMode(.scaleAspectFit)
] ]
if #available(iOS 14.0, *) { if #available(iOS 14.0, *) {
options.append(.rightNavItemTitle(NSLocalizedString("save"), onTap: { [weak self] _ in options.append(.rightNavItemTitle("save".localized, onTap: { [weak self] _ in
// image // image
self?.saveImageToAlbum(image) self?.saveImageToAlbum(image)
})) }))
@ -244,7 +248,7 @@ extension MessageItemView {
PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in
guard status == .authorized || status == .limited else { guard status == .authorized || status == .limited else {
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.showInfo(withStatus: NSLocalizedString("noPermission")) SVProgressHUD.showInfo(withStatus: "noPermission".localized)
} }
return return
} }
@ -253,7 +257,7 @@ extension MessageItemView {
}) { success, error in }) { success, error in
DispatchQueue.main.async { DispatchQueue.main.async {
if success { if success {
SVProgressHUD.showSuccess(withStatus: NSLocalizedString("saveSuccess")) SVProgressHUD.showSuccess(withStatus: "saveSuccess".localized)
} else { } else {
SVProgressHUD.showError(withStatus: error?.localizedDescription) SVProgressHUD.showError(withStatus: error?.localizedDescription)
} }

View File

@ -107,7 +107,7 @@ class MessageGroupTableViewCell: UITableViewCell {
if let newValue, !newValue.isEmpty { if let newValue, !newValue.isEmpty {
groupHeader.groupName = newValue groupHeader.groupName = newValue
} else { } else {
groupHeader.groupName = NSLocalizedString("default") groupHeader.groupName = "default".localized
} }
} }
get { get {

View File

@ -86,7 +86,7 @@ private class ShowLessView: UIView {
let label = UILabel() let label = UILabel()
label.font = UIFont.preferredFont(ofSize: 12) label.font = UIFont.preferredFont(ofSize: 12)
label.textColor = BKColor.grey.darken3 label.textColor = BKColor.grey.darken3
label.text = NSLocalizedString("showLess") label.text = "showLess".localized
return label return label
}() }()
@ -148,7 +148,7 @@ private class ClearView: UIView {
let label = UILabel() let label = UILabel()
label.font = UIFont.preferredFont(ofSize: 12) label.font = UIFont.preferredFont(ofSize: 12)
label.textColor = BKColor.grey.darken3 label.textColor = BKColor.grey.darken3
label.text = NSLocalizedString("clear") label.text = "clear".localized
label.alpha = 0 label.alpha = 0
return label return label
}() }()

View File

@ -20,12 +20,12 @@ class MessageSettingFooter: UITextView, UITextViewDelegate {
// build // build
let buildVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" let buildVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
let attr = NSMutableAttributedString(string: "\(NSLocalizedString("version")) \(appVersion) (\(buildVersion))\n", attributes: [.font: UIFont.preferredFont(ofSize: 12), .foregroundColor: BKColor.grey.darken1]) let attr = NSMutableAttributedString(string: "\("version".localized) \(appVersion) (\(buildVersion))\n", attributes: [.font: UIFont.preferredFont(ofSize: 12), .foregroundColor: BKColor.grey.darken1])
attr.append(NSAttributedString(string: NSLocalizedString("privacyPolicy"), attributes: [.link: "privacyPolicy"])) attr.append(NSAttributedString(string: "privacyPolicy".localized, attributes: [.link: "privacyPolicy"]))
attr.append(NSAttributedString(string: " · ")) attr.append(NSAttributedString(string: " · "))
attr.append(NSAttributedString(string: NSLocalizedString("userAgreement"), attributes: [.link: "userAgreement"])) attr.append(NSAttributedString(string: "userAgreement".localized, attributes: [.link: "userAgreement"]))
attr.append(NSAttributedString(string: " · ")) attr.append(NSAttributedString(string: " · "))
attr.append(NSAttributedString(string: NSLocalizedString("restoreSubscription"), attributes: [.link: "restoreSubscription"])) attr.append(NSAttributedString(string: "restoreSubscription".localized, attributes: [.link: "restoreSubscription"]))
let style = NSMutableParagraphStyle() let style = NSMutableParagraphStyle()
style.lineSpacing = 6 style.lineSpacing = 6

View File

@ -10,8 +10,17 @@ import Material
import UIKit import UIKit
class PreviewCardCell: BaseTableViewCell<PreviewCardCellViewModel> { class PreviewCardCell: BaseTableViewCell<PreviewCardCellViewModel> {
let previewButton = IconButton(image: Icon.cm.skipForward, tintColor: BKColor.grey.base) let previewButton: IconButton = {
let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: BKColor.grey.base) let button = IconButton(image: Icon.cm.skipForward, tintColor: BKColor.grey.base)
button.accessibilityLabel = "demo".localized
return button
}()
let copyButton: IconButton = {
let button = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: BKColor.grey.base)
button.accessibilityLabel = "Copy".localized
return button
}()
let titleLabel: UILabel = { let titleLabel: UILabel = {
let label = UILabel() let label = UILabel()
@ -98,18 +107,13 @@ class PreviewCardCell: BaseTableViewCell<PreviewCardCellViewModel> {
let titleStackView = UIStackView() let titleStackView = UIStackView()
titleStackView.axis = .vertical titleStackView.axis = .vertical
titleStackView.isLayoutMarginsRelativeArrangement = true
titleStackView.layoutMargins = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0)
titleStackView.addArrangedSubview(titleLabel) titleStackView.addArrangedSubview(titleLabel)
titleStackView.addArrangedSubview(bodyLabel) titleStackView.addArrangedSubview(bodyLabel)
card.addSubview(titleStackView) card.addSubview(titleStackView)
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
}
bodyLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
}
titleStackView.snp.makeConstraints { make in titleStackView.snp.makeConstraints { make in
make.centerY.equalTo(copyButton) make.centerY.equalTo(copyButton)
make.left.equalToSuperview() make.left.equalToSuperview()
@ -123,20 +127,9 @@ class PreviewCardCell: BaseTableViewCell<PreviewCardCellViewModel> {
card.addSubview(contentStackView) card.addSubview(contentStackView)
contentStackView.addArrangedSubview(contentImageView) contentStackView.addArrangedSubview(contentImageView)
contentStackView.addArrangedSubview(contentLabel) contentStackView.addArrangedSubview(InsetView(subView: contentLabel, insets: .init(top: 0, left: 12, bottom: 0, right: 12)))
contentStackView.addArrangedSubview(noticeLabel) contentStackView.addArrangedSubview(InsetView(subView: noticeLabel, insets: .init(top: 0, left: 10, bottom: 0, right: 10)))
contentLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.right.equalToSuperview().offset(-12)
}
contentImageView.snp.remakeConstraints { make in
make.left.right.equalToSuperview()
}
noticeLabel.snp.makeConstraints { make in
make.left.equalTo(10)
make.right.equalTo(-10)
}
contentStackView.snp.makeConstraints { make in contentStackView.snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
make.top.equalTo(previewButton.snp.bottom).offset(20) make.top.equalTo(previewButton.snp.bottom).offset(20)

View File

@ -11,7 +11,12 @@ import Material
import UIKit import UIKit
class SoundCell: BaseTableViewCell<SoundCellViewModel> { class SoundCell: BaseTableViewCell<SoundCellViewModel> {
let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: BKColor.grey.base) let copyButton: IconButton = {
let button = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: BKColor.grey.base)
button.accessibilityLabel = "Copy".localized
return button
}()
let nameLabel: UILabel = { let nameLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.fontSize = 14 label.fontSize = 14

View File

@ -28,14 +28,24 @@ extension UINavigationItem {
} }
func setBarButtonItems(items: [UIBarButtonItem], position: UINavigationItemPosition) { func setBarButtonItems(items: [UIBarButtonItem], position: UINavigationItemPosition) {
if #available(iOS 26.0, *) {
// iOS 26
if position == .left {
self.leftBarButtonItems = items
} else {
self.rightBarButtonItems = items
}
return
}
guard items.count > 0 else { guard items.count > 0 else {
self.leftBarButtonItems = nil self.leftBarButtonItems = nil
return return
} }
var buttonItems = items var buttonItems = items
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
buttonItems.forEach { item in for item in buttonItems {
guard let view = item.customView else { return } guard let view = item.customView else { continue }
item.customView?.translatesAutoresizingMaskIntoConstraints = false item.customView?.translatesAutoresizingMaskIntoConstraints = false
(item.customView as? HitTestSlopable)?.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10) (item.customView as? HitTestSlopable)?.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
(item.customView as? AlignmentRectInsetsOverridable)?.alignmentRectInsetsOverride = UIEdgeInsets(top: 0, left: position == .left ? 8 : -8, bottom: 0, right: position == .left ? -8 : 8) (item.customView as? AlignmentRectInsetsOverridable)?.alignmentRectInsetsOverride = UIEdgeInsets(top: 0, left: position == .left ? 8 : -8, bottom: 0, right: position == .left ? -8 : 8)
@ -45,16 +55,14 @@ extension UINavigationItem {
} }
} }
buttonItems.insert(UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil), at: 0) buttonItems.insert(UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil), at: 0)
} } else {
else {
let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
spacer.width = -8 spacer.width = -8
buttonItems.insert(spacer, at: 0) buttonItems.insert(spacer, at: 0)
} }
if position == .left { if position == .left {
self.leftBarButtonItems = buttonItems self.leftBarButtonItems = buttonItems
} } else {
else {
self.rightBarButtonItems = buttonItems self.rightBarButtonItems = buttonItems
} }
} }

View File

@ -1,40 +0,0 @@
//
// iCloudStatusCell.swift
// Bark
//
// Created by huangfeng on 2020/5/29.
// Copyright © 2020 Fin. All rights reserved.
//
import CloudKit
import UIKit
class iCloudStatusCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .value1, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
self.backgroundColor = BKColor.background.secondary
self.textLabel?.text = NSLocalizedString("iCloudSatatus")
self.detailTextLabel?.text = ""
self.detailTextLabel?.textColor = BKColor.grey.darken2
CKContainer.default().accountStatus { status, _ in
dispatch_sync_safely_main_queue {
switch status {
case .available:
self.detailTextLabel?.text = NSLocalizedString("available")
case .noAccount, .restricted, .temporarilyUnavailable:
self.detailTextLabel?.text = NSLocalizedString("restricted")
case .couldNotDetermine:
self.detailTextLabel?.text = NSLocalizedString("unknown")
@unknown default:
break
}
}
}
}
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

19
buildServer.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "xcode build server",
"version": "0.2",
"bspVersion": "2.0",
"languages": [
"c",
"cpp",
"objective-c",
"objective-cpp",
"swift"
],
"argv": [
"/opt/homebrew/bin/xcode-build-server"
],
"workspace": "/Users/huangfeng/Documents/Bark/Bark.xcworkspace",
"build_root": "/Users/huangfeng/Library/Developer/Xcode/DerivedData/Bark-cyorhgfjlviqbhgulankypxaozte",
"scheme": "Bark",
"kind": "xcode"
}

View File

@ -0,0 +1,226 @@
#!/usr/bin/env python3
"""
Bark项目本地化字符串分析工具
这个脚本会扫描整个Bark项目找出 Localizable.xcstrings 中未使用的翻译key
检测方式
1. 任何在双引号内且在本地化文件中定义的字符串都会被认为是被使用的key
2. "key".localized "key".localized(with:) 模式
使用方法:
python3 check_unused_translations.py
输出:
- 控制台显示分析结果
"""
import json
import os
import re
import sys
from pathlib import Path
class BarkLocalizationAnalyzer:
def __init__(self, project_root):
self.project_root = Path(project_root)
self.localization_file = self.project_root / "Bark" / "Localizable.xcstrings"
def extract_all_keys(self):
"""提取 Localizable.xcstrings 中的所有key"""
try:
with open(self.localization_file, 'r', encoding='utf-8') as f:
data = json.load(f)
return set(data['strings'].keys())
except Exception as e:
print(f"❌ 读取本地化文件失败: {e}")
return set()
def find_swift_files(self):
"""查找所有Swift源码文件"""
swift_files = []
for path in self.project_root.rglob("*.swift"):
# 跳过Pods和build目录
if "Pods" not in str(path) and "build" not in str(path):
swift_files.append(path)
return swift_files
def extract_used_keys_from_file(self, file_path, all_defined_keys):
"""从Swift文件中提取使用的本地化key"""
used_keys = set()
localized_keys = set() # 收集.localized中的key
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# 方法1: 查找所有在双引号内的字符串,包括多行和转义字符
quoted_strings = re.findall(r'"([^"]*)"', content, re.MULTILINE | re.DOTALL)
# 检查哪些引号内的字符串是已定义的本地化key
for quoted_string in quoted_strings:
# 去掉前后空白字符
quoted_string = quoted_string.strip()
if quoted_string and quoted_string in all_defined_keys:
used_keys.add(quoted_string)
# 方法2: 查找 "key".localized 和 "key".localized(with:) 模式
localized_patterns = [
r'"([^"]+)"\s*\.\s*localized\b', # "key".localized
r'"([^"]+)"\s*\.\s*localized\s*\(\s*with:', # "key".localized(with:
r'\'([^\']+)\'\s*\.\s*localized\b', # 'key'.localized
r'\'([^\']+)\'\s*\.\s*localized\s*\(\s*with:', # 'key'.localized(with:
]
for pattern in localized_patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL)
for match in matches:
match = match.strip()
if match:
localized_keys.add(match) # 收集所有.localized中的key
if match in all_defined_keys:
used_keys.add(match)
except Exception as e:
print(f"⚠️ 读取文件失败 {file_path}: {e}")
return used_keys, localized_keys
def find_all_used_keys(self, all_defined_keys):
"""在整个项目中查找所有使用的本地化 key"""
# 查找所有Swift文件
swift_files = self.find_swift_files()
print(f"📁 找到 {len(swift_files)} 个Swift文件")
# 提取使用的key
used_keys = set()
all_localized_keys = set() # 收集所有.localized中的key
files_with_keys = 0
for file_path in swift_files:
file_keys, localized_keys = self.extract_used_keys_from_file(file_path, all_defined_keys)
if file_keys:
files_with_keys += 1
used_keys.update(file_keys)
all_localized_keys.update(localized_keys)
print(f"🔑 在 {files_with_keys} 个文件中找到 {len(used_keys)} 个使用的key")
# 计算在代码中使用但未在本地化文件中定义的key
missing_in_localization = all_localized_keys - all_defined_keys
return used_keys, files_with_keys, missing_in_localization
def analyze(self):
"""执行完整的本地化分析"""
print("🔍 开始分析Bark项目的本地化使用情况...")
# 提取所有定义的key
print("📖 读取 Localizable.xcstrings...")
all_keys = self.extract_all_keys()
if not all_keys:
return None
print(f"✅ 找到 {len(all_keys)} 个本地化key")
# 查找使用的key
used_keys, files_with_keys, missing_in_localization = self.find_all_used_keys(all_keys)
# 计算未使用的key
unused_keys = all_keys - used_keys
missing_keys = used_keys - all_keys # 这个应该为空因为used_keys是从all_keys中筛选的
result = {
'total_keys': len(all_keys),
'used_keys': len(used_keys),
'unused_keys': len(unused_keys),
'missing_keys': len(missing_keys),
'missing_in_localization': len(missing_in_localization),
'all_keys': sorted(list(all_keys)),
'used_keys_list': sorted(list(used_keys)),
'unused_keys_list': sorted(list(unused_keys)),
'missing_keys_list': sorted(list(missing_keys)),
'missing_in_localization_list': sorted(list(missing_in_localization)),
'files_scanned': len(self.find_swift_files()),
'files_with_keys': files_with_keys
}
return result
def save_results(self, result):
"""保存分析结果到文件"""
# 已移除文件保存功能,只在控制台显示结果
pass
def print_summary(self, result):
"""打印分析摘要"""
if not result:
return
print("\n" + "=" * 60)
print("📊 分析结果摘要")
print("=" * 60)
print(f"总本地化key数量: {result['total_keys']}")
print(f"使用中的key数量: {result['used_keys']}")
print(f"未使用的key数量: {result['unused_keys']}")
print(f"缺失的key数量: {result['missing_keys']} (代码中使用但未定义)")
print(f"代码中缺失的key: {result['missing_in_localization']}")
if result['unused_keys_list']:
print(f"\n🗑️ 未使用的翻译key ({result['unused_keys']} 个):")
for i, key in enumerate(result['unused_keys_list'], 1):
print(f" {i:2d}. {key}")
if result['missing_keys_list']:
print(f"\n⚠️ 缺失的翻译key ({result['missing_keys']} 个):")
for i, key in enumerate(result['missing_keys_list'], 1):
print(f" {i:2d}. {key}")
if result['missing_in_localization_list']:
print(f"\n❌ 代码中使用但未在Localizable.xcstrings中定义的key ({result['missing_in_localization']} 个):")
for i, key in enumerate(result['missing_in_localization_list'], 1):
print(f" {i:2d}. {key}")
if not result['unused_keys_list'] and not result['missing_keys_list'] and not result['missing_in_localization_list']:
print("\n🎉 完美所有翻译key都被正确使用和定义")
# 计算使用率
usage_rate = (result['used_keys'] / result['total_keys']) * 100 if result['total_keys'] > 0 else 0
print(f"\n📈 翻译使用率: {usage_rate:.1f}%")
def main():
project_root = "/Users/huangfeng/Documents/Bark"
if not os.path.exists(project_root):
print(f"❌ 项目目录不存在: {project_root}")
sys.exit(1)
analyzer = BarkLocalizationAnalyzer(project_root)
if not analyzer.localization_file.exists():
print(f"❌ 本地化文件不存在: {analyzer.localization_file}")
sys.exit(1)
# 执行分析
result = analyzer.analyze()
if result:
# 显示结果
analyzer.print_summary(result)
print(f"\n💡 建议:")
if result['unused_keys'] > 0:
print(f" - 考虑删除 {result['unused_keys']} 个未使用的翻译key以减小包体积")
if result['missing_keys'] > 0:
print(f" - 为 {result['missing_keys']} 个缺失的key添加翻译")
if result['missing_in_localization'] > 0:
print(f" - 为 {result['missing_in_localization']} 个代码中使用的key添加本地化定义")
print(" - 检查是否有动态构建的key名称(脚本可能无法检测)")
print(" - 手动检查Storyboard/XIB文件中的硬编码字符串")
else:
print("❌ 分析失败")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -76,3 +76,5 @@ List of supported parameters, specific effects can be previewed in the APP.
| isArchive | Pass 1 to save the push, pass other values to not save the push, if not passed, it will be decided by the APP settings whether to save. | | isArchive | Pass 1 to save the push, pass other values to not save the push, if not passed, it will be decided by the APP settings whether to save. |
| url | URL to jump to when the push is clicked, supports URL Scheme and Universal Link | | url | URL to jump to when the push is clicked, supports URL Scheme and Universal Link |
| action | Pass "none" to prevent a popup when the push is clicked | | action | Pass "none" to prevent a popup when the push is clicked |
| id | When using the same ID value, it will update the corresponding push notification content<br>Requires Bark v1.5.2, bark-server v2.2.5 or above |
| delete | Pass "1" to delete the notification from the system notification center and APP history, must be used with the id parameter<br>Requires "Background App Refresh" to be enabled in settings, otherwise it will not work. |

View File

@ -1,3 +1,8 @@
#### 江苏部分地区无法访问 https://api.day.app
江苏部分地区存在DNS污染和网络阻断预计会持续一到两周。<br/>
可以尝试更换DNS、翻墙或修改 hosts 添加一条记录 43.155.109.24 api.day.app。<br/>
如果无法解决,可暂时使用 https://api.bbark.top ,只需在发送端将 api.day.app 改为 api.bbark.top。<br/>api.bbark.top是临时域名随时可能终止解析请勿长期使用
#### 无法收到推送 #### 无法收到推送
在 App 设置中检查 Device Token 是否正常。如果不正常,参考 [这里](#DeviceToken显示未知)<br/> 在 App 设置中检查 Device Token 是否正常。如果不正常,参考 [这里](#DeviceToken显示未知)<br/>
如果正常,可以重启下设备,如果还不能接收到推送,检查推送请求返回状态码是否为 code 200。<br/> 如果正常,可以重启下设备,如果还不能接收到推送,检查推送请求返回状态码是否为 code 200。<br/>

View File

@ -76,6 +76,15 @@ curl -X "POST" "https://api.day.app/push" \
| isArchive | 传 1 保存推送传其他的不保存推送不传按APP内设置来决定是否保存。 | | isArchive | 传 1 保存推送传其他的不保存推送不传按APP内设置来决定是否保存。 |
| url | 点击推送时跳转的URL 支持URL Scheme 和 Universal Link | | url | 点击推送时跳转的URL 支持URL Scheme 和 Universal Link |
| action | 传 "none" 时,点击推送不会弹窗 | | action | 传 "none" 时,点击推送不会弹窗 |
| id | 使用相同的ID值时将更新对应推送的通知内容<br>需 Bark v1.5.2, bark-server v2.2.5 以上Json传参需使用字符串类型 |
| delete | 传 "1" 时将从系统通知中心和APP内历史记录中删除通知需搭配 id 参数使用<br>需在设置里开启”后台App刷新“否则无效。|
## Bark 支持的应用程序和插件
* [SmsForwarder](https://github.com/pppscn/SmsForwarder) 监控 Android 手机短信、来电、APP通知并根据指定规则转发到Bark。
* [acme.sh](https://github.com/acmesh-official/acme.sh/wiki/notify#16-set-notification-for-ios-bark) 从 ZeroSSLLet's Encrypt 等 CA 生成免费的证书。可以使用 Bark 接收 acme.sh cronjob 任务通知。
* [Uptime-Kuma](https://github.com/louislam/uptime-kuma) 自托管监控工具, 支持Bark作为告警通道。
* [Apprise](https://github.com/caronc/apprise) 可以给几乎所有平台发送通知支持Bark。
* [浏览器扩展](https://github.com/ij369/bark-sender) 将网页内容发送到手机
## 快捷指令 ## 快捷指令
Bark 支持快捷指令直接发送推送,以下是当收到交警短信时,忽略静音模式持续响铃提醒用户的自动化示例。 Bark 支持快捷指令直接发送推送,以下是当收到交警短信时,忽略静音模式持续响铃提醒用户的自动化示例。

View File

@ -56,7 +56,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
if let copy = notification.request.content.userInfo["copy"] as? String { if let copy = notification.request.content.userInfo["copy"] as? String {
UIPasteboard.general.string = copy UIPasteboard.general.string = copy
} else { } else {
UIPasteboard.general.string = notification.request.content.body UIPasteboard.general.string = notification.request.content.bodyText
} }
} }
@ -85,8 +85,8 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
if !response.notification.request.content.subtitle.isEmpty { if !response.notification.request.content.subtitle.isEmpty {
content += "\(response.notification.request.content.subtitle)\n" content += "\(response.notification.request.content.subtitle)\n"
} }
if !response.notification.request.content.body.isEmpty { if !response.notification.request.content.bodyText.isEmpty {
content += "\(response.notification.request.content.body)\n" content += "\(response.notification.request.content.bodyText)\n"
} }
if let url = userInfo["url"] as? String, !url.isEmpty { if let url = userInfo["url"] as? String, !url.isEmpty {
content += "\(url)\n" content += "\(url)\n"
@ -95,7 +95,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
UIPasteboard.general.string = content UIPasteboard.general.string = content
} }
showTips(text: NSLocalizedString("Copy", comment: "")) showTips(text: "Copy".localized)
completion(.doNotDismiss) completion(.doNotDismiss)
} }
@ -105,7 +105,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
// //
GroupMuteSettingManager().settings[groupName] = Date() + 60 * 60 GroupMuteSettingManager().settings[groupName] = Date() + 60 * 60
showTips(text: String(format: NSLocalizedString("groupMuted", comment: ""), groupName.isEmpty ? "default" : groupName)) showTips(text: String(format: "groupMuted".localized, groupName.isEmpty ? "default".localized : groupName))
completion(.doNotDismiss) completion(.doNotDismiss)
} }

View File

@ -0,0 +1,21 @@
//
// UNNotificationContent+Extension.swift
// NotificationContentExtension
//
// Created by huangfeng on 8/26/25.
// Copyright © 2025 Fin. All rights reserved.
//
import UIKit
extension UNNotificationContent {
var bodyText: String {
guard let aps = self.userInfo["aps"] as? [String: Any],
let alert = aps["alert"] as? [String: Any],
let bodyText = alert["body"] as? String
else {
return self.body
}
return bodyText
}
}