Compare commits

..

5 Commits

Author SHA1 Message Date
fish 1ff250fa17 过滤剪贴板无效占位符 2026-07-15 22:51:42 +08:00
fish 42e647c932 修复后台返回首页收集箱数量不刷新 2026-07-15 22:49:26 +08:00
fish 080c7f00ce 首页菜单支持自定义排序 2026-07-15 22:46:56 +08:00
fish 545e023ffc 修复收集箱有内容时无法进入 2026-07-15 22:43:45 +08:00
fish 10ba84e349 修复快捷指令运行时崩溃 2026-07-15 22:40:19 +08:00
2 changed files with 112 additions and 20 deletions
+18 -3
View File
@@ -25,13 +25,28 @@ struct SaveClipIntent: AppIntent {
self.source = source self.source = source
} }
func perform() async throws -> some IntentResult { func perform() async throws -> some IntentResult & ReturnsValue<String> {
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { guard !trimmed.isEmpty else {
return .result(dialog: "文案为空,未保存。") return .result(value: "文案为空,未保存。")
}
guard !isSystemClipboardPlaceholder(trimmed) else {
return .result(value: "剪贴板没有有效文本,未保存。")
} }
try await ClipStore.shared.save(text: trimmed, source: source) try await ClipStore.shared.save(text: trimmed, source: source)
return .result(dialog: "已保存到收集箱。") return .result(value: "已保存到收集箱。")
}
private func isSystemClipboardPlaceholder(_ text: String) -> Bool {
let lowercased = text.lowercased()
if lowercased.hasPrefix("clipboard ") && lowercased.contains(" at ") {
return true
}
if text == "获取剪贴板" {
return true
}
return false
} }
} }
+94 -17
View File
@@ -10,26 +10,32 @@ import UIKit
class ViewController: UIViewController { class ViewController: UIViewController {
private var tableView: UITableView! private var tableView: UITableView!
private var editButton: UIBarButtonItem!
private let menuOrderKey = "homeMenuOrder"
private struct MenuItem { private struct MenuItem {
let icon: String let icon: String
let iconColor: UIColor let iconColor: UIColor
let title: String let title: String
let identifier: String
} }
private var menuItems: [[MenuItem]] = [ private let defaultMenuItems: [MenuItem] = [
[ MenuItem(icon: "tray.full", iconColor: .systemIndigo, title: "收集箱", identifier: "inbox"),
MenuItem(icon: "tray.full", iconColor: .systemIndigo, title: "收集箱"), MenuItem(icon: "note.text", iconColor: .systemYellow, title: "打开备忘录", identifier: "notes"),
MenuItem(icon: "note.text", iconColor: .systemYellow, title: "打开备忘录"), MenuItem(icon: "doc.text", iconColor: .systemBlue, title: "格式化日志", identifier: "formatLog"),
MenuItem(icon: "doc.text", iconColor: .systemBlue, title: "格式化日志"), MenuItem(icon: "text.alignleft", iconColor: .systemGreen, title: "格式化笔记", identifier: "formatNote"),
MenuItem(icon: "text.alignleft", iconColor: .systemGreen, title: "格式化笔记"), MenuItem(icon: "folder", iconColor: .systemOrange, title: "打开文件", identifier: "files")
MenuItem(icon: "folder", iconColor: .systemOrange, title: "打开文件")
]
] ]
private var menuItems: [[MenuItem]] = []
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
menuItems = [loadMenuOrder()]
setupUI() setupUI()
setupAppStateObserver()
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
@@ -37,12 +43,32 @@ class ViewController: UIViewController {
updateInboxBadge() updateInboxBadge()
} }
deinit {
NotificationCenter.default.removeObserver(self)
}
private func setupAppStateObserver() {
NotificationCenter.default.addObserver(
self,
selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
}
@objc private func appDidBecomeActive() {
updateInboxBadge()
}
private func setupUI() { private func setupUI() {
title = "首页" title = "首页"
navigationController?.navigationBar.prefersLargeTitles = true navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always navigationItem.largeTitleDisplayMode = .always
view.backgroundColor = .systemGroupedBackground view.backgroundColor = .systemGroupedBackground
editButton = UIBarButtonItem(title: "编辑", style: .plain, target: self, action: #selector(didTapEditButton))
navigationItem.rightBarButtonItem = editButton
tableView = UITableView(frame: .zero, style: .insetGrouped) tableView = UITableView(frame: .zero, style: .insetGrouped)
tableView.translatesAutoresizingMaskIntoConstraints = false tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self tableView.delegate = self
@@ -58,6 +84,34 @@ class ViewController: UIViewController {
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor) tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
]) ])
} }
@objc private func didTapEditButton() {
tableView.setEditing(!tableView.isEditing, animated: true)
editButton.title = tableView.isEditing ? "完成" : "编辑"
}
private func loadMenuOrder() -> [MenuItem] {
guard let savedOrder = UserDefaults.standard.array(forKey: menuOrderKey) as? [String] else {
return defaultMenuItems
}
var orderedItems: [MenuItem] = []
for identifier in savedOrder {
if let item = defaultMenuItems.first(where: { $0.identifier == identifier }) {
orderedItems.append(item)
}
}
let remaining = defaultMenuItems.filter { item in
!orderedItems.contains { $0.identifier == item.identifier }
}
return orderedItems + remaining
}
private func saveMenuOrder() {
let identifiers = menuItems.flatMap { $0.map(\.identifier) }
UserDefaults.standard.set(identifiers, forKey: menuOrderKey)
}
} }
extension ViewController: UITableViewDataSource { extension ViewController: UITableViewDataSource {
@@ -80,37 +134,59 @@ extension ViewController: UITableViewDataSource {
config.imageProperties.tintColor = item.iconColor config.imageProperties.tintColor = item.iconColor
config.imageToTextPadding = 12 config.imageToTextPadding = 12
cell.contentConfiguration = config cell.contentConfiguration = config
cell.accessoryType = .disclosureIndicator cell.accessoryType = tableView.isEditing ? .none : .disclosureIndicator
cell.selectionStyle = tableView.isEditing ? .none : .default
return cell return cell
} }
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
guard sourceIndexPath != destinationIndexPath else { return }
let item = menuItems[sourceIndexPath.section].remove(at: sourceIndexPath.row)
menuItems[destinationIndexPath.section].insert(item, at: destinationIndexPath.row)
saveMenuOrder()
}
} }
extension ViewController: UITableViewDelegate { extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
guard !tableView.isEditing else { return }
let item = menuItems[indexPath.section][indexPath.row] let item = menuItems[indexPath.section][indexPath.row]
switch item.title { switch item.identifier {
case "收集箱": case "inbox":
let viewController = ClipInboxViewController() let viewController = ClipInboxViewController()
navigationController?.pushViewController(viewController, animated: true) navigationController?.pushViewController(viewController, animated: true)
case "打开备忘录": case "notes":
openSystemNotesApp() openSystemNotesApp()
case "格式化日志": case "formatLog":
let viewController = FormatLogViewController() let viewController = FormatLogViewController()
navigationController?.pushViewController(viewController, animated: true) navigationController?.pushViewController(viewController, animated: true)
case "格式化笔记": case "formatNote":
let viewController = FormatNoteViewController() let viewController = FormatNoteViewController()
navigationController?.pushViewController(viewController, animated: true) navigationController?.pushViewController(viewController, animated: true)
case "打开文件": case "files":
openFilesApp() openFilesApp()
default: default:
break break
} }
} }
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .none
}
func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
private func updateInboxBadge() { private func updateInboxBadge() {
Task { Task {
guard let count = try? await ClipStore.shared.count(), count > 0 else { guard let count = try? await ClipStore.shared.count(), count > 0 else {
@@ -128,11 +204,12 @@ extension ViewController: UITableViewDelegate {
private func updateInboxTitle(_ title: String) { private func updateInboxTitle(_ title: String) {
for (sectionIndex, section) in menuItems.enumerated() { for (sectionIndex, section) in menuItems.enumerated() {
for (rowIndex, item) in section.enumerated() { for (rowIndex, item) in section.enumerated() {
if item.title.hasPrefix("收集箱") { if item.identifier == "inbox" {
menuItems[sectionIndex][rowIndex] = MenuItem( menuItems[sectionIndex][rowIndex] = MenuItem(
icon: item.icon, icon: item.icon,
iconColor: item.iconColor, iconColor: item.iconColor,
title: title title: title,
identifier: item.identifier
) )
let indexPath = IndexPath(row: rowIndex, section: sectionIndex) let indexPath = IndexPath(row: rowIndex, section: sectionIndex)
tableView.reloadRows(at: [indexPath], with: .none) tableView.reloadRows(at: [indexPath], with: .none)