mirror of
https://github.com/Finb/Bark.git
synced 2025-12-08 21:36:01 +00:00
Compare commits
10 Commits
b8ae5a0e3f
...
4ba691ed3f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ba691ed3f | ||
|
|
3463510e14 | ||
|
|
862772d649 | ||
|
|
986a7fde4e | ||
|
|
995ab4c5ec | ||
|
|
98fade76fc | ||
|
|
15d2f62220 | ||
|
|
4a2729a71e | ||
|
|
230e86df6c | ||
|
|
e617852fbb |
2
.github/workflows/testflight.yaml
vendored
2
.github/workflows/testflight.yaml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
- name: Select Xcode Version
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 'latest-stable'
|
||||
xcode-version: '26.0'
|
||||
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
|
||||
2
.github/workflows/tests.yaml
vendored
2
.github/workflows/tests.yaml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- name: Select Xcode Version
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 'latest-stable'
|
||||
xcode-version: '26.0'
|
||||
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
|
||||
@ -121,6 +121,10 @@
|
||||
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 */; };
|
||||
069C6CA42ED03C72007244BB /* MarkdownProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 069C6CA32ED03C72007244BB /* MarkdownProcessor.swift */; };
|
||||
069C6CA52ED03E10007244BB /* MarkdownParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C777CF2ECEFF210032A044 /* MarkdownParser.swift */; };
|
||||
069C6CA72ED03E27007244BB /* Markdown in Frameworks */ = {isa = PBXBuildFile; productRef = 069C6CA62ED03E27007244BB /* Markdown */; };
|
||||
069C6CA92ED03F07007244BB /* BKColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06840DBA272298FB001B3193 /* BKColor.swift */; };
|
||||
06B1158D247BA6D5006D91FB /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06B1158C247BA6D5006D91FB /* CloudKit.framework */; };
|
||||
06B1158F247BB1FB006D91FB /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B1158E247BB1FB006D91FB /* Message.swift */; };
|
||||
06B11591247BC132006D91FB /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B1158E247BB1FB006D91FB /* Message.swift */; };
|
||||
@ -143,6 +147,8 @@
|
||||
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952C2480E3F8006B98F3 /* LabelCell.swift */; };
|
||||
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */; };
|
||||
06C595362481160F006B98F3 /* BKLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C595352481160F006B98F3 /* BKLabel.swift */; };
|
||||
06C777D02ECEFF210032A044 /* MarkdownParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C777CF2ECEFF210032A044 /* MarkdownParser.swift */; };
|
||||
06C777D32ECF05990032A044 /* Markdown in Frameworks */ = {isa = PBXBuildFile; productRef = 06C777D22ECF05990032A044 /* Markdown */; };
|
||||
06CF784721C7A50300A052D7 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
06CF784C21C7A51200A052D7 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CF784B21C7A51200A052D7 /* NotificationService.swift */; };
|
||||
06D69E202C1159E200161A35 /* glass.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320500250B6DD3001561EC /* glass.caf */; };
|
||||
@ -364,6 +370,7 @@
|
||||
068EC15927ED99E700D5D11E /* ServerListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListViewModel.swift; sourceTree = "<group>"; };
|
||||
068F66B2247BD84C00DAD25A /* MessageListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListViewController.swift; sourceTree = "<group>"; };
|
||||
0699473C2D223094008D5E40 /* CustomTapTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTapTextView.swift; sourceTree = "<group>"; };
|
||||
069C6CA32ED03C72007244BB /* MarkdownProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownProcessor.swift; sourceTree = "<group>"; };
|
||||
06B1158C247BA6D5006D91FB /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
||||
06B1158E247BB1FB006D91FB /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
|
||||
06B11590247BBC15006D91FB /* NotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationServiceExtension.entitlements; sourceTree = "<group>"; };
|
||||
@ -384,6 +391,7 @@
|
||||
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>"; };
|
||||
06C595352481160F006B98F3 /* BKLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKLabel.swift; sourceTree = "<group>"; };
|
||||
06C777CF2ECEFF210032A044 /* MarkdownParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownParser.swift; sourceTree = "<group>"; };
|
||||
06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
06CF784421C7A50300A052D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
06CF784B21C7A51200A052D7 /* NotificationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
@ -444,6 +452,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
06C777D32ECF05990032A044 /* Markdown in Frameworks */,
|
||||
06B1158D247BA6D5006D91FB /* CloudKit.framework in Frameworks */,
|
||||
3428272069AFAFE2C683FEB0 /* libPods-Bark.a in Frameworks */,
|
||||
);
|
||||
@ -454,6 +463,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
879AE4D4178855A9672009E4 /* libPods-NotificationServiceExtension.a in Frameworks */,
|
||||
069C6CA72ED03E27007244BB /* Markdown in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -624,6 +634,7 @@
|
||||
06E944742C07012E00AC86AB /* RealmConfiguration.swift */,
|
||||
0687F2A72CCB791A00B2A52F /* UIFont+Extension.swift */,
|
||||
06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */,
|
||||
06C777CF2ECEFF210032A044 /* MarkdownParser.swift */,
|
||||
);
|
||||
path = Common;
|
||||
sourceTree = "<group>";
|
||||
@ -719,6 +730,7 @@
|
||||
06D69E402C11983E00161A35 /* CallProcessor.swift */,
|
||||
06E944792C0704E500AC86AB /* ImageDownloader.swift */,
|
||||
06BCAE5A2CDB25120092867A /* MuteProcessor.swift */,
|
||||
069C6CA32ED03C72007244BB /* MarkdownProcessor.swift */,
|
||||
);
|
||||
path = Processor;
|
||||
sourceTree = "<group>";
|
||||
@ -904,6 +916,9 @@
|
||||
ja,
|
||||
);
|
||||
mainGroup = 0661A536204FDA4100965E4E;
|
||||
packageReferences = (
|
||||
06C777D12ECF05990032A044 /* XCRemoteSwiftPackageReference "swift-markdown" */,
|
||||
);
|
||||
productRefGroup = 0661A540204FDA4100965E4E /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
@ -1253,6 +1268,7 @@
|
||||
0642B55C27EB149900453D91 /* MutableTextCellViewModel.swift in Sources */,
|
||||
062B98C8251B27AE004562E7 /* UINavigationItem+Extension.swift in Sources */,
|
||||
060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */,
|
||||
06C777D02ECEFF210032A044 /* MarkdownParser.swift in Sources */,
|
||||
0699473D2D223094008D5E40 /* CustomTapTextView.swift in Sources */,
|
||||
06EEF333291CCFF400CA228A /* CryptoSettingController.swift in Sources */,
|
||||
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
|
||||
@ -1335,14 +1351,17 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
069C6CA92ED03F07007244BB /* BKColor.swift in Sources */,
|
||||
06E944752C07012E00AC86AB /* RealmConfiguration.swift in Sources */,
|
||||
06E944732C06FF9200AC86AB /* ArchiveProcessor.swift in Sources */,
|
||||
06E9447A2C0704E500AC86AB /* ImageDownloader.swift in Sources */,
|
||||
06CF784C21C7A51200A052D7 /* NotificationService.swift in Sources */,
|
||||
06FB04052C53575400F3A213 /* SharedDefines.swift in Sources */,
|
||||
069C6CA42ED03C72007244BB /* MarkdownProcessor.swift in Sources */,
|
||||
067AFB1D2E5D8BED00AE78E7 /* UNNotificationContent+Extension.swift in Sources */,
|
||||
0653677629B719BC0038BDB8 /* CryptoSettingManager.swift in Sources */,
|
||||
06E9446F2C06FF1E00AC86AB /* BadgeProcessor.swift in Sources */,
|
||||
069C6CA52ED03E10007244BB /* MarkdownParser.swift in Sources */,
|
||||
06BCAE582CDB19420092867A /* GroupMuteSettingManager.swift in Sources */,
|
||||
06BCAE5B2CDB25120092867A /* MuteProcessor.swift in Sources */,
|
||||
06E9446D2C06FEC900AC86AB /* LevelProcessor.swift in Sources */,
|
||||
@ -1794,6 +1813,30 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
06C777D12ECF05990032A044 /* XCRemoteSwiftPackageReference "swift-markdown" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/swiftlang/swift-markdown";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.7.3;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
069C6CA62ED03E27007244BB /* Markdown */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 06C777D12ECF05990032A044 /* XCRemoteSwiftPackageReference "swift-markdown" */;
|
||||
productName = Markdown;
|
||||
};
|
||||
06C777D22ECF05990032A044 /* Markdown */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 06C777D12ECF05990032A044 /* XCRemoteSwiftPackageReference "swift-markdown" */;
|
||||
productName = Markdown;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 0661A537204FDA4100965E4E /* Project object */;
|
||||
}
|
||||
|
||||
24
Bark.xcworkspace/xcshareddata/swiftpm/Package.resolved
Normal file
24
Bark.xcworkspace/xcshareddata/swiftpm/Package.resolved
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"originHash" : "bb354f5ee26f97a80bea96da70628d71c97b25e01e4e845afe4f5b1d0b6cf6e2",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "swift-cmark",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-cmark.git",
|
||||
"state" : {
|
||||
"revision" : "5d9bdaa4228b381639fff09403e39a04926e2dbe",
|
||||
"version" : "0.7.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-markdown",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-markdown",
|
||||
"state" : {
|
||||
"revision" : "7d9a5ce307528578dfa777d505496bd5f544ad94",
|
||||
"version" : "0.7.3"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
@ -1885,6 +1885,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"image" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Image"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "画像"
|
||||
}
|
||||
},
|
||||
"tr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "resim"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "图片"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imageParameter" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
||||
346
Common/MarkdownParser.swift
Normal file
346
Common/MarkdownParser.swift
Normal file
@ -0,0 +1,346 @@
|
||||
//
|
||||
// MarkdownParser.swift
|
||||
// Bark
|
||||
//
|
||||
// Created by huangfeng on 2025/11/20.
|
||||
// Copyright © 2025 Fin. All rights reserved.
|
||||
//
|
||||
|
||||
import Markdown
|
||||
import UIKit
|
||||
|
||||
class MarkdownParser {
|
||||
struct Configuration {
|
||||
let baseFont: UIFont
|
||||
let baseColor: UIColor
|
||||
let linkColor: UIColor
|
||||
let codeTextColor: UIColor
|
||||
let codeBackgroundColor: UIColor
|
||||
let codeBlockTextColor: UIColor
|
||||
let quoteColor: UIColor
|
||||
|
||||
init(
|
||||
baseFont: UIFont = UIFont.preferredFont(ofSize: 14),
|
||||
baseColor: UIColor = BKColor.grey.darken4,
|
||||
linkColor: UIColor = BKColor.blue.base,
|
||||
codeTextColor: UIColor = BKColor.blue.base,
|
||||
codeBackgroundColor: UIColor = BKColor.grey.lighten4,
|
||||
codeBlockTextColor: UIColor = BKColor.grey.darken4,
|
||||
quoteColor: UIColor = BKColor.grey.base
|
||||
) {
|
||||
self.baseFont = baseFont
|
||||
self.baseColor = baseColor
|
||||
self.linkColor = linkColor
|
||||
self.codeTextColor = codeTextColor
|
||||
self.codeBackgroundColor = codeBackgroundColor
|
||||
self.codeBlockTextColor = codeBlockTextColor
|
||||
self.quoteColor = quoteColor
|
||||
}
|
||||
}
|
||||
|
||||
private let configuration: Configuration
|
||||
|
||||
init(configuration: Configuration = Configuration()) {
|
||||
self.configuration = configuration
|
||||
}
|
||||
|
||||
func parse(_ markdown: String) -> NSAttributedString {
|
||||
let document = Document(parsing: markdown)
|
||||
var walker = AttributedStringWalker(configuration: configuration)
|
||||
walker.visit(document)
|
||||
return walker.attributedString
|
||||
}
|
||||
}
|
||||
|
||||
private struct AttributedStringWalker: MarkupWalker {
|
||||
let configuration: MarkdownParser.Configuration
|
||||
let attributedString = NSMutableAttributedString()
|
||||
|
||||
var attributes: [NSAttributedString.Key: Any] = [:]
|
||||
|
||||
enum ListType {
|
||||
case ordered
|
||||
case unordered
|
||||
}
|
||||
var listStack: [(type: ListType, index: Int)] = []
|
||||
|
||||
init(configuration: MarkdownParser.Configuration) {
|
||||
self.configuration = configuration
|
||||
self.attributes = [
|
||||
.font: configuration.baseFont,
|
||||
.foregroundColor: configuration.baseColor
|
||||
]
|
||||
}
|
||||
|
||||
mutating func visitDocument(_ document: Document) {
|
||||
for child in document.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 段落
|
||||
mutating func visitParagraph(_ paragraph: Paragraph) {
|
||||
// 如果是在列表项中,段落不需要额外的换行
|
||||
// 结尾已经有两个换行也不加了
|
||||
if attributedString.length > 0, !(paragraph.parent is ListItem), !attributedString.string.hasSuffix("\n\n") {
|
||||
attributedString.append(NSAttributedString(string: "\n\n", attributes: attributes))
|
||||
}
|
||||
|
||||
for child in paragraph.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 文本
|
||||
mutating func visitText(_ text: Text) {
|
||||
attributedString.append(NSAttributedString(string: text.string, attributes: attributes))
|
||||
}
|
||||
|
||||
/// 加粗
|
||||
mutating func visitStrong(_ strong: Strong) {
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
let originalFont = attributes[.font] as? UIFont ?? configuration.baseFont
|
||||
attributes[.font] = originalFont.bold()
|
||||
|
||||
for child in strong.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 斜体
|
||||
mutating func visitEmphasis(_ emphasis: Emphasis) {
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
let originalFont = attributes[.font] as? UIFont ?? configuration.baseFont
|
||||
attributes[.font] = originalFont.italic()
|
||||
|
||||
for child in emphasis.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除线
|
||||
mutating func visitStrikethrough(_ strikethrough: Strikethrough) {
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
attributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
|
||||
|
||||
for child in strikethrough.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 链接
|
||||
mutating func visitLink(_ link: Link) {
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
attributes[.foregroundColor] = configuration.linkColor
|
||||
attributes[.underlineStyle] = NSUnderlineStyle.single.rawValue
|
||||
if let destination = link.destination {
|
||||
attributes[.link] = destination
|
||||
}
|
||||
|
||||
for child in link.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 行内代码
|
||||
mutating func visitInlineCode(_ inlineCode: InlineCode) {
|
||||
var currentAttributes = attributes
|
||||
|
||||
currentAttributes[.font] = UIFont.monospacedSystemFont(ofSize: configuration.baseFont.pointSize, weight: .regular)
|
||||
currentAttributes[.foregroundColor] = configuration.codeTextColor
|
||||
currentAttributes[.backgroundColor] = configuration.codeBackgroundColor
|
||||
|
||||
attributedString.append(NSAttributedString(string: inlineCode.code, attributes: currentAttributes))
|
||||
}
|
||||
|
||||
/// 代码块
|
||||
mutating func visitCodeBlock(_ codeBlock: CodeBlock) {
|
||||
if attributedString.length > 0, !attributedString.string.hasSuffix("\n\n") {
|
||||
attributedString.append(NSAttributedString(string: "\n\n", attributes: attributes))
|
||||
}
|
||||
|
||||
var currentAttributes = attributes
|
||||
|
||||
currentAttributes[.font] = UIFont.monospacedSystemFont(ofSize: configuration.baseFont.pointSize, weight: .regular)
|
||||
currentAttributes[.foregroundColor] = configuration.codeBlockTextColor
|
||||
currentAttributes[.backgroundColor] = configuration.codeBackgroundColor
|
||||
|
||||
attributedString.append(NSAttributedString(string: codeBlock.code, attributes: currentAttributes))
|
||||
|
||||
attributedString.append(NSAttributedString(string: "\n", attributes: [.font: configuration.baseFont]))
|
||||
}
|
||||
|
||||
/// 标题
|
||||
mutating func visitHeading(_ heading: Heading) {
|
||||
if attributedString.length > 0, !attributedString.string.hasSuffix("\n\n") {
|
||||
attributedString.append(NSAttributedString(string: "\n\n", attributes: attributes))
|
||||
}
|
||||
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
let level = heading.level
|
||||
var size = configuration.baseFont.pointSize
|
||||
var weight = UIFont.Weight.bold
|
||||
|
||||
switch level {
|
||||
case 1: size += 6; weight = .heavy
|
||||
case 2: size += 4; weight = .bold
|
||||
case 3: size += 2; weight = .semibold
|
||||
default: break
|
||||
}
|
||||
|
||||
attributes[.font] = UIFont.systemFont(ofSize: size, weight: weight)
|
||||
|
||||
for child in heading.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 引用
|
||||
mutating func visitBlockQuote(_ blockQuote: BlockQuote) {
|
||||
if attributedString.length > 0 {
|
||||
attributedString.append(NSAttributedString(string: "\n", attributes: attributes))
|
||||
}
|
||||
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
attributes[.foregroundColor] = configuration.quoteColor
|
||||
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.firstLineHeadIndent = 10
|
||||
paragraphStyle.headIndent = 10
|
||||
attributes[.paragraphStyle] = paragraphStyle
|
||||
|
||||
for child in blockQuote.children {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// 有序列表
|
||||
mutating func visitOrderedList(_ orderedList: OrderedList) {
|
||||
if attributedString.length > 0 && listStack.isEmpty {
|
||||
attributedString.append(NSAttributedString(string: "\n", attributes: attributes))
|
||||
}
|
||||
|
||||
listStack.append((type: .ordered, index: Int(orderedList.startIndex)))
|
||||
|
||||
for child in orderedList.children {
|
||||
visit(child)
|
||||
}
|
||||
|
||||
listStack.removeLast()
|
||||
}
|
||||
|
||||
/// 无序列表
|
||||
mutating func visitUnorderedList(_ unorderedList: UnorderedList) {
|
||||
if attributedString.length > 0 && listStack.isEmpty {
|
||||
attributedString.append(NSAttributedString(string: "\n", attributes: attributes))
|
||||
}
|
||||
|
||||
listStack.append((type: .unordered, index: 0))
|
||||
|
||||
for child in unorderedList.children {
|
||||
visit(child)
|
||||
}
|
||||
|
||||
listStack.removeLast()
|
||||
}
|
||||
|
||||
/// 列表项
|
||||
mutating func visitListItem(_ listItem: ListItem) {
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
let level = CGFloat(listStack.count - 1)
|
||||
let indent: CGFloat = 20
|
||||
let baseIndent = level * indent
|
||||
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.firstLineHeadIndent = baseIndent
|
||||
paragraphStyle.headIndent = baseIndent + indent
|
||||
paragraphStyle.paragraphSpacingBefore = 4
|
||||
attributes[.paragraphStyle] = paragraphStyle
|
||||
|
||||
var prefix = ""
|
||||
if let last = listStack.last {
|
||||
if last.type == .ordered {
|
||||
prefix = "\(last.index). "
|
||||
} else if listItem.checkbox == nil {
|
||||
prefix = "• "
|
||||
}
|
||||
}
|
||||
if attributedString.length > 0 {
|
||||
attributedString.append(NSAttributedString(string: "\n", attributes: attributes))
|
||||
}
|
||||
attributedString.append(NSAttributedString(string: "\(prefix)", attributes: attributes))
|
||||
|
||||
if let checkbox = listItem.checkbox {
|
||||
let imageName = checkbox == .checked ? "checkmark.square" : "square"
|
||||
let font = attributes[.font] as? UIFont ?? configuration.baseFont
|
||||
let color = attributes[.foregroundColor] as? UIColor ?? configuration.baseColor
|
||||
|
||||
let symbolConfiguration = UIImage.SymbolConfiguration(font: font)
|
||||
if let image = UIImage(systemName: imageName, withConfiguration: symbolConfiguration)?.withTintColor(color, renderingMode: .alwaysOriginal) {
|
||||
let attachment = NSTextAttachment()
|
||||
attachment.image = image
|
||||
let y = (font.capHeight - image.size.height).rounded() / 2
|
||||
attachment.bounds = CGRect(x: 0, y: y, width: image.size.width, height: image.size.height)
|
||||
|
||||
attributedString.append(NSAttributedString(attachment: attachment))
|
||||
attributedString.append(NSAttributedString(string: " ", attributes: attributes))
|
||||
} else {
|
||||
let text = checkbox == .checked ? "☑ " : "☐ "
|
||||
attributedString.append(NSAttributedString(string: text, attributes: attributes))
|
||||
}
|
||||
}
|
||||
|
||||
for child in listItem.children {
|
||||
visit(child)
|
||||
}
|
||||
|
||||
// 更新 listStack 索引
|
||||
if !listStack.isEmpty {
|
||||
var last = listStack[listStack.count - 1]
|
||||
if last.type == .ordered {
|
||||
last.index += 1
|
||||
listStack[listStack.count - 1] = last
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 图片
|
||||
mutating func visitImage(_ image: Image) {
|
||||
let originalAttributes = attributes
|
||||
defer { attributes = originalAttributes }
|
||||
|
||||
attributes[.foregroundColor] = configuration.linkColor
|
||||
attributes[.underlineStyle] = NSUnderlineStyle.single.rawValue
|
||||
|
||||
if let source = image.source {
|
||||
attributes[.link] = source
|
||||
}
|
||||
|
||||
let altText = image.plainText.isEmpty ? "image".localized : image.plainText
|
||||
attributedString.append(NSAttributedString(string: "[\(altText)]", attributes: attributes))
|
||||
}
|
||||
|
||||
/// 软换行渲染为空格
|
||||
mutating func visitSoftBreak(_ softBreak: SoftBreak) {
|
||||
attributedString.append(NSAttributedString(string: " ", attributes: attributes))
|
||||
}
|
||||
|
||||
/// 硬换行
|
||||
mutating func visitLineBreak(_ lineBreak: LineBreak) {
|
||||
attributedString.append(NSAttributedString(string: "\n", attributes: attributes))
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ let kRealmDefaultConfiguration = {
|
||||
let fileUrl = groupUrl?.appendingPathComponent("bark.realm")
|
||||
let config = Realm.Configuration(
|
||||
fileURL: fileUrl,
|
||||
schemaVersion: 16,
|
||||
schemaVersion: 17,
|
||||
migrationBlock: { migration, oldSchemaVersion in
|
||||
switch oldSchemaVersion {
|
||||
case 0...13:
|
||||
|
||||
@ -13,3 +13,19 @@ extension UIFont {
|
||||
return UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: size, weight: weight))
|
||||
}
|
||||
}
|
||||
|
||||
extension UIFont {
|
||||
func bold() -> UIFont {
|
||||
if let descriptor = self.fontDescriptor.withSymbolicTraits(.traitBold) {
|
||||
return UIFont(descriptor: descriptor, size: self.pointSize)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func italic() -> UIFont {
|
||||
if let descriptor = self.fontDescriptor.withSymbolicTraits(.traitItalic) {
|
||||
return UIFont(descriptor: descriptor, size: self.pointSize)
|
||||
}
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,21 +11,27 @@ import SwiftyJSON
|
||||
import UIKit
|
||||
|
||||
class Message: Object {
|
||||
@objc dynamic var id = NSUUID().uuidString
|
||||
@objc dynamic var title: String?
|
||||
@objc dynamic var subtitle: String?
|
||||
@objc dynamic var body: String?
|
||||
@objc dynamic var url: String?
|
||||
@objc dynamic var image: String?
|
||||
@objc dynamic var group: String?
|
||||
@objc dynamic var createDate: Date?
|
||||
|
||||
override class func primaryKey() -> String? {
|
||||
return "id"
|
||||
enum BodyType: String {
|
||||
case plainText
|
||||
case markdown
|
||||
}
|
||||
@Persisted(primaryKey: true) var id = UUID().uuidString
|
||||
@Persisted var title: String?
|
||||
@Persisted var subtitle: String?
|
||||
@Persisted var body: String?
|
||||
@Persisted var bodyType: String?
|
||||
@Persisted var url: String?
|
||||
@Persisted var image: String?
|
||||
@Persisted(indexed: true) var group: String?
|
||||
@Persisted(indexed: true) var createDate: Date?
|
||||
|
||||
override class func indexedProperties() -> [String] {
|
||||
return ["group", "createDate"]
|
||||
var type: BodyType {
|
||||
get {
|
||||
guard let bodyType = bodyType else {
|
||||
return .plainText
|
||||
}
|
||||
return BodyType(rawValue: bodyType) ?? .plainText
|
||||
}
|
||||
}
|
||||
|
||||
/// 从 JSON 初始化
|
||||
@ -41,6 +47,7 @@ class Message: Object {
|
||||
self.title = json["title"].string
|
||||
self.subtitle = json["subtitle"].string
|
||||
self.body = json["body"].string
|
||||
self.bodyType = json["bodyType"].string
|
||||
self.url = json["url"].string
|
||||
self.image = json["image"].string
|
||||
self.group = json["group"].string
|
||||
|
||||
@ -46,10 +46,15 @@ class MessageItemModel {
|
||||
let body = message.body ?? ""
|
||||
let url = message.url ?? ""
|
||||
|
||||
let text = NSMutableAttributedString(
|
||||
string: body,
|
||||
attributes: [.font: UIFont.preferredFont(ofSize: 14), .foregroundColor: BKColor.grey.darken4]
|
||||
)
|
||||
let text: NSMutableAttributedString
|
||||
if message.type == .markdown {
|
||||
text = NSMutableAttributedString(attributedString: MarkdownParser().parse(body))
|
||||
} else {
|
||||
text = NSMutableAttributedString(
|
||||
string: body,
|
||||
attributes: [.font: UIFont.preferredFont(ofSize: 14), .foregroundColor: BKColor.grey.darken4]
|
||||
)
|
||||
}
|
||||
|
||||
if subtitle.count > 0 {
|
||||
// 插入一行空行当 spacer
|
||||
|
||||
@ -33,6 +33,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
.setIcon,
|
||||
.setImage,
|
||||
.mute,
|
||||
.markdown,
|
||||
.call
|
||||
]
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ class ArchiveProcessor: NotificationContentProcessor {
|
||||
let group = userInfo["group"] as? String
|
||||
let image = userInfo["image"] as? String
|
||||
let id = userInfo["id"] as? String
|
||||
let markdown = userInfo["markdown"] as? String
|
||||
|
||||
try? realm?.write {
|
||||
let message = Message()
|
||||
@ -41,6 +42,10 @@ class ArchiveProcessor: NotificationContentProcessor {
|
||||
message.title = title
|
||||
message.subtitle = subtitle
|
||||
message.body = body
|
||||
if let markdown, !markdown.isEmpty {
|
||||
message.body = markdown
|
||||
message.bodyType = Message.BodyType.markdown.rawValue
|
||||
}
|
||||
message.url = url
|
||||
message.image = image
|
||||
message.group = group
|
||||
|
||||
@ -28,7 +28,9 @@ class ImageDownloader {
|
||||
/// - Returns: 返回 Result
|
||||
class func downloadImage(url: URL) async -> Result<ImageLoadingResult, KingfisherError> {
|
||||
return await withCheckedContinuation { continuation in
|
||||
Kingfisher.ImageDownloader.default.downloadImage(with: url, options: nil) { result in
|
||||
let downloader = Kingfisher.ImageDownloader.default
|
||||
downloader.downloadTimeout = 10.0
|
||||
downloader.downloadImage(with: url, options: nil) { result in
|
||||
continuation.resume(returning: result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
//
|
||||
// MarkdownProcessor.swift
|
||||
// NotificationServiceExtension
|
||||
//
|
||||
// Created by huangfeng on 11/21/25.
|
||||
// Copyright © 2025 Fin. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class MarkdownProcessor: NotificationContentProcessor {
|
||||
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
|
||||
let userInfo = bestAttemptContent.userInfo
|
||||
guard let markdown = userInfo["markdown"] as? String, !markdown.isEmpty else {
|
||||
return bestAttemptContent
|
||||
}
|
||||
let config = MarkdownParser.Configuration(
|
||||
baseFont: UIFont.preferredFont(forTextStyle: .body),
|
||||
baseColor: UIColor.white,
|
||||
linkColor: UIColor.systemBlue,
|
||||
codeTextColor: UIColor.black,
|
||||
codeBackgroundColor: UIColor.gray,
|
||||
codeBlockTextColor: UIColor.black,
|
||||
quoteColor: UIColor.systemGray
|
||||
)
|
||||
bestAttemptContent.body = MarkdownParser(configuration: config).parse(markdown).string
|
||||
return bestAttemptContent
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,7 @@ enum NotificationContentProcessorItem {
|
||||
case setImage
|
||||
case call
|
||||
case mute
|
||||
case markdown
|
||||
|
||||
var processor: NotificationContentProcessor {
|
||||
switch self {
|
||||
@ -40,6 +41,8 @@ enum NotificationContentProcessorItem {
|
||||
return CallProcessor()
|
||||
case .mute:
|
||||
return MuteProcessor()
|
||||
case .markdown:
|
||||
return MarkdownProcessor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ DEPENDENCIES:
|
||||
- SwiftyStoreKit
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
trunk:
|
||||
- Alamofire
|
||||
- CryptoSwift
|
||||
- DefaultsKit
|
||||
|
||||
BIN
Sounds/alarm.caf
BIN
Sounds/alarm.caf
Binary file not shown.
Binary file not shown.
BIN
Sounds/bell.caf
BIN
Sounds/bell.caf
Binary file not shown.
Binary file not shown.
BIN
Sounds/bloom.caf
BIN
Sounds/bloom.caf
Binary file not shown.
Binary file not shown.
BIN
Sounds/chime.caf
BIN
Sounds/chime.caf
Binary file not shown.
BIN
Sounds/choo.caf
BIN
Sounds/choo.caf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Sounds/glass.caf
BIN
Sounds/glass.caf
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Sounds/horn.caf
BIN
Sounds/horn.caf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Sounds/noir.caf
BIN
Sounds/noir.caf
Binary file not shown.
Binary file not shown.
BIN
Sounds/shake.caf
BIN
Sounds/shake.caf
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Sounds/spell.caf
BIN
Sounds/spell.caf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 698 KiB |
@ -1,46 +1,66 @@
|
||||
#### Push usage limit <!-- {docsify-ignore-all} -->
|
||||
Normal requests (HTTP status code 200) have no limit.<br>
|
||||
But if more than 1000 error requests (HTTP status code 400 404 500) are made within 5 minutes, <b>the IP will be BAN for 24 hours</b>
|
||||
### Unable to Receive Push Notifications
|
||||
Check whether the Device Token is valid in the app settings.
|
||||
If it’s valid, try rebooting your device. If you still can’t receive notifications, check whether the push request returned HTTP 200.
|
||||
|
||||
#### Time-sensitive notifications not work
|
||||
You can try to <b>restart your device</b> to solve it.
|
||||
### DeviceToken Shows “Unknown”
|
||||
This usually means the device cannot connect to Apple’s servers. You may also notice iMessage not working or other apps not receiving notifications.
|
||||
Try switching networks, rebooting the device, or disabling any proxy/VPN affecting Apple services.
|
||||
This is a connectivity issue between your device and Apple’s servers, and cannot be fixed by the app author.
|
||||
|
||||
#### Unable to save notification history, or unable to copy by pulling down push or swiping left on lock screen without clicking copy button
|
||||
You can try to <b>restart your device</b> to solve it.<br />
|
||||
Due to some reasons, the push service extension ([UNNotificationServiceExtension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension)) failed to run normally, and the code for saving notifications was not executed properly.
|
||||
### Push Usage Limit
|
||||
Valid requests (HTTP 200) have no limit.
|
||||
If you send over **1000 error requests** (HTTP 400 / 404 / 500) within **5 minutes**, **your IP will be banned for 24 hours**.
|
||||
|
||||
#### Automatic copy push failure
|
||||
After iOS 14.5 version, due to permission tightening, it is not possible to automatically copy push content to clipboard when receiving push. <br/>
|
||||
You can temporarily pull down push or swipe left on lock screen and click view to automatically copy, or click copy button on pop-up push.
|
||||
### Receiving Unknown or Unexpected Pushes (e.g., “NoContent”)
|
||||
Possible causes:
|
||||
1. Safari may auto-complete the Bark API URL when typing in the address bar and trigger preloading.
|
||||
2. Chat apps like WeChat may periodically access a Bark API URL you sent earlier.
|
||||
3. Your push key was leaked — reset it in the server list page.
|
||||
|
||||
#### Open notification history page by default
|
||||
When you open APP again, it will jump to the last opened page.<br />
|
||||
Just exit APP when you are on history message page. When you open APP again, it will be history message page.
|
||||
### “Server Error” Prompt
|
||||
Occasional errors may be ignored. The app might have gone into background causing network timeouts.
|
||||
|
||||
#### Does push API support POST request?
|
||||
Bark supports GET POST , supports using Json <br>
|
||||
No matter which request method, parameter names are the same. Refer to [usage tutorial](/en-us/tutorial)
|
||||
### Time-Sensitive Notifications Not Working
|
||||
Try **rebooting your device**.
|
||||
|
||||
#### Pushing special characters causes push failure. For example: Push content contains link or Push abnormal such as + becomes space
|
||||
This is because of the problem of irregular link. It often happens<br>
|
||||
When splicing URL, pay attention to URL encoding parameters
|
||||
### Unable to Save Notification History or No Copy Button When Pulling Down Notification
|
||||
Try **rebooting your device**.
|
||||
The Notification Service Extension may have failed to run, so the saving logic didn’t execute.
|
||||
|
||||
### Multiple Devices Using the Same Key but Only One Receives Notifications
|
||||
A key can only be used by one device. Only the most recently opened app instance will receive notifications.
|
||||
|
||||
### Auto-Copy Not Working
|
||||
On iOS 14.5+, stricter permissions prevent auto-copy when receiving notifications.
|
||||
You can instead pull down the notification or swipe left on the lock screen to trigger auto-copy, or tap the copy button.
|
||||
|
||||
### Defaulting to Notification History on App Launch
|
||||
The app reopens to the last viewed page.
|
||||
If you exit the app on the history page, reopening it will return to the history page.
|
||||
|
||||
### Does the Push API Support POST Requests?
|
||||
Bark supports both GET and POST, as well as JSON format.
|
||||
Parameters are the same for all request types. See the tutorial for details.
|
||||
|
||||
### Push Fails Due to Special Characters (e.g., links, “+” becomes space)
|
||||
This happens when the URL is not properly encoded.
|
||||
|
||||
```sh
|
||||
# For example
|
||||
https://api.day.app/key/{push content}
|
||||
# Example
|
||||
https://api.day.app/key/{content}
|
||||
|
||||
# If {push content} is
|
||||
# If {content} is:
|
||||
"a/b/c/"
|
||||
|
||||
# Then the final spliced URL is
|
||||
# Final URL becomes:
|
||||
https://api.day.app/key/a/b/c/
|
||||
# The corresponding route will not be found and the backend program will return 404
|
||||
# -> No route matches, backend returns 404
|
||||
|
||||
#You should url encode {push content} before splicing
|
||||
# Correct (URL-encoded):
|
||||
https://api.day.app/key/a%2Fb%2Fc%2F
|
||||
```
|
||||
If you use a mature HTTP library, parameters will be automatically processed and you don’t need manual encoding. <br>
|
||||
But if you splice URL yourself, you need special attention for special characters in parameters. **It’s better not care whether there are special characters or not and blindly apply a layer of URL encoding.**
|
||||
HTTP libraries usually encode parameters automatically.
|
||||
If constructing URLs manually, always encode parameters.
|
||||
|
||||
#### How to ensure privacy and security
|
||||
Refer [privacy security](/en-us/privacy)
|
||||
See the [Privac](/en-us/privacy)
|
||||
@ -47,7 +47,7 @@ curl -X "POST" "https://api.day.app/your_key" \
|
||||
curl -X "POST" "https://api.day.app/push" \
|
||||
-H 'Content-Type: application/json; charset=utf-8' \
|
||||
-d $'{
|
||||
"body": "Test Bark Server",
|
||||
"markdown": "Hello **Markdown**",
|
||||
"title": "Test Title",
|
||||
"device_key": "your_key"
|
||||
}'
|
||||
@ -61,6 +61,7 @@ List of supported parameters, specific effects can be previewed in the APP.
|
||||
| title | Push title |
|
||||
| subtitle | Push subtitle |
|
||||
| body | Push content |
|
||||
| markdown | Push content in Markdown format. When this is provided, the body field is ignored. |
|
||||
| device_key | Device key |
|
||||
| device_keys | Key array, used for batch push |
|
||||
| level | Push interruption level.<br>critical: Important alert, will ring even in silent mode <br>active:Default value, the system will immediately light up the screen to display the notification<br>timeSensitive:Time-sensitive notification, can display the notification in focus mode.<br>passive:Only adds the notification to the notification list, will not light up the screen. |
|
||||
|
||||
@ -1,8 +1,3 @@
|
||||
#### 江苏部分地区无法访问 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/>
|
||||
如果正常,可以重启下设备,如果还不能接收到推送,检查推送请求返回状态码是否为 code 200。<br/>
|
||||
@ -33,6 +28,9 @@
|
||||
可以尝试<b>重启设备</b>来解决。<br />
|
||||
因某些原因导致推送服务扩展([UNNotificationServiceExtension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension))未能正常运行,执行通知保存的代码未能正常执行。
|
||||
|
||||
#### 多台设备使用同一个key,但只有其中一台设备可以收到推送
|
||||
同一个Key只能一台设备使用,只有最后打开的APP会收到推送
|
||||
|
||||
#### 自动复制推送失效
|
||||
iOS 14.5 之后的版本因权限收紧,不能在收到推送时自动复制推送内容到剪切板。<br/>
|
||||
可暂时先下拉推送或在锁屏界面左滑推送点查看即可自动复制,或点击弹出的推送复制按钮。
|
||||
|
||||
@ -47,7 +47,7 @@ curl -X "POST" "https://api.day.app/your_key" \
|
||||
curl -X "POST" "https://api.day.app/push" \
|
||||
-H 'Content-Type: application/json; charset=utf-8' \
|
||||
-d $'{
|
||||
"body": "Test Bark Server",
|
||||
"markdown": "Hello **Markdown**",
|
||||
"title": "Test Title",
|
||||
"device_key": "your_key"
|
||||
}'
|
||||
@ -61,6 +61,7 @@ curl -X "POST" "https://api.day.app/push" \
|
||||
| title | 推送标题 |
|
||||
| subtitle | 推送副标题 |
|
||||
| body | 推送内容 |
|
||||
| markdown | 推送内容,markdown格式。传递了此参数将忽略 body 字段, 发送时请注意处理特殊字符。|
|
||||
| device_key | 设备key |
|
||||
| device_keys | key 数组,用于批量推送 |
|
||||
| level | 推送中断级别。<br>critical: 重要警告, 在静音模式下也会响铃 <br>active:默认值,系统会立即亮屏显示通知<br>timeSensitive:时效性通知,可在专注状态下显示通知。<br>passive:仅将通知添加到通知列表,不会亮屏提醒。 |
|
||||
@ -87,9 +88,4 @@ curl -X "POST" "https://api.day.app/push" \
|
||||
* [浏览器扩展](https://github.com/ij369/bark-sender) 将网页内容发送到手机
|
||||
|
||||
## 快捷指令
|
||||
Bark 支持快捷指令直接发送推送,以下是当收到交警短信时,忽略静音模式持续响铃提醒用户的自动化示例。
|
||||
<img src="../_media/shortcuts_cn.png" />
|
||||
1. 创建个人自动化
|
||||
2. 选择信息、填写信息包含关键词触发自动化,选择立即执行,点击下一步
|
||||
3. 选择新建空白自动化,选择 Bark 发送推送到此设备快捷指令
|
||||
4. 填写推送配置,标题可以选择短信发件人、内容可以选择短信内容,或自己自定义。
|
||||
Bark 支持使用快捷指令直接发送推送
|
||||
Loading…
x
Reference in New Issue
Block a user