New shortcut + Localized
This commit is contained in:
parent
1a982bdf1e
commit
185b74d8c3
|
@ -1621,6 +1621,118 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"intent.publish.any.issue" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "There was an issue while posting."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Il y a eu un soucis lors de la publication."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intent.publish.any.visibility-dialog" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "How visible do you want your post to be?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Quelle visibilité voulez-vous que cette publication ai ?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intent.publish.text" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Publish a text-based post"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Publier du texte dans une publication"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intent.publish.text.account-dialog" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Select the account the post will be published on"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Sélectionnez le compte sur lequel la publication sera publiée"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intent.publish.text.content-dialog" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Write the content of your post"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Écrivez le contenu de votre publication"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intent.publish.text.description" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Publish a text-based post on Mastodon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Publie du texte dans une publication sur Mastodon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intent.publish.text.summary-${content}" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Post \"${content}\""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Publier « ${content} »"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"login.instance.unsafe" : {
|
"login.instance.unsafe" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
|
@ -2519,7 +2631,8 @@
|
||||||
"value" : "Envoyer vers l'Apple Watch"
|
"value" : "Envoyer vers l'Apple Watch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
"settings.cancel" : {
|
"settings.cancel" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
@ -4602,6 +4715,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widget.followers" : {
|
"widget.followers" : {
|
||||||
|
"comment" : "Lowercase, shown in the \"Follow Count\" widget",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
|
@ -4667,4 +4781,4 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version" : "1.0"
|
"version" : "1.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ import RevenueCat
|
||||||
@main
|
@main
|
||||||
struct ThreadedApp: App {
|
struct ThreadedApp: App {
|
||||||
init() {
|
init() {
|
||||||
guard let plist = AppDelegate.readSecret() else { return }
|
guard let plist = AppDelegate.readSecret() else { fatalError("Missing Secret.plist file") }
|
||||||
|
|
||||||
if let apiKey = plist["RevenueCat_public"], let deviceId = UIDevice.current.identifierForVendor?.uuidString {
|
if let apiKey = plist["RevenueCat_public"], let deviceId = UIDevice.current.identifierForVendor?.uuidString {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Purchases.logLevel = .debug
|
Purchases.logLevel = .debug
|
||||||
|
@ -16,6 +17,8 @@ struct ThreadedApp: App {
|
||||||
Purchases.configure(withAPIKey: apiKey, appUserID: deviceId)
|
Purchases.configure(withAPIKey: apiKey, appUserID: deviceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadedShortcuts.updateAppShortcutParameters()
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
|
|
@ -5,6 +5,23 @@ import SwiftData
|
||||||
import WidgetKit
|
import WidgetKit
|
||||||
import AppIntents
|
import AppIntents
|
||||||
|
|
||||||
|
// MARK: - Shortcuts
|
||||||
|
|
||||||
|
struct ThreadedShortcuts: AppShortcutsProvider {
|
||||||
|
static var appShortcuts: [AppShortcut] = [
|
||||||
|
.init(
|
||||||
|
intent: OpenComposerIntent(),
|
||||||
|
phrases: [
|
||||||
|
"Start a \(.applicationName) post",
|
||||||
|
"Post on \(.applicationName)"
|
||||||
|
],
|
||||||
|
shortTitle: "status.posting",
|
||||||
|
systemImageName: "square.and.pencil"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
static var shortcutTileColor: ShortcutTileColor = .grayBlue
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Account Intents
|
// MARK: - Account Intents
|
||||||
|
|
||||||
/// Widgets that require to select only an account will use this `ConfigurationIntent`
|
/// Widgets that require to select only an account will use this `ConfigurationIntent`
|
||||||
|
@ -31,6 +48,7 @@ struct AccountEntity: AppEntity {
|
||||||
let client: Client
|
let client: Client
|
||||||
let id: String
|
let id: String
|
||||||
let username: String
|
let username: String
|
||||||
|
let server: String
|
||||||
/// Bearer token
|
/// Bearer token
|
||||||
let token: OauthToken
|
let token: OauthToken
|
||||||
|
|
||||||
|
@ -42,14 +60,16 @@ struct AccountEntity: AppEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
init(acct: String, username: String, token: OauthToken) {
|
init(acct: String, username: String, token: OauthToken) {
|
||||||
self.client = Client(server: String(acct.split(separator: "@")[1]), version: .v2, oauthToken: token)
|
self.server = String(acct.split(separator: "@")[1])
|
||||||
|
self.client = Client(server: self.server, version: .v2, oauthToken: token)
|
||||||
self.id = acct
|
self.id = acct
|
||||||
self.username = username
|
self.username = username
|
||||||
self.token = token
|
self.token = token
|
||||||
}
|
}
|
||||||
|
|
||||||
init(loggedAccount: LoggedAccount) {
|
init(loggedAccount: LoggedAccount) {
|
||||||
self.client = Client(server: String(loggedAccount.acct.split(separator: "@")[1]), version: .v2, oauthToken: loggedAccount.token)
|
self.server = loggedAccount.app?.server ?? ""
|
||||||
|
self.client = Client(server: self.server, version: .v2, oauthToken: loggedAccount.token)
|
||||||
self.id = loggedAccount.acct
|
self.id = loggedAccount.acct
|
||||||
self.username = String(loggedAccount.acct.split(separator: "@")[0])
|
self.username = String(loggedAccount.acct.split(separator: "@")[0])
|
||||||
self.token = loggedAccount.token
|
self.token = loggedAccount.token
|
||||||
|
@ -106,6 +126,22 @@ struct AccountQuery: EntityQuery {
|
||||||
|
|
||||||
// MARK: - Post Intents
|
// MARK: - Post Intents
|
||||||
|
|
||||||
|
extension Visibility: AppEnum {
|
||||||
|
public static var caseDisplayRepresentations: [Visibility : DisplayRepresentation] {
|
||||||
|
[
|
||||||
|
.pub : DisplayRepresentation(title: "status.posting.visibility.public"),
|
||||||
|
.priv : DisplayRepresentation(title: "status.posting.visibility.private"),
|
||||||
|
.unlisted : DisplayRepresentation(title: "status.posting.visibility.unlisted"),
|
||||||
|
.direct : DisplayRepresentation(title: "status.posting.visibility.direct")
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var typeDisplayRepresentation: TypeDisplayRepresentation {
|
||||||
|
TypeDisplayRepresentation(name: "status.posting.visibility")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct OpenComposerIntent: AppIntent {
|
struct OpenComposerIntent: AppIntent {
|
||||||
static var title: LocalizedStringResource = "intent.open.composer"
|
static var title: LocalizedStringResource = "intent.open.composer"
|
||||||
static var description: IntentDescription? = IntentDescription("intent.open.composer.description")
|
static var description: IntentDescription? = IntentDescription("intent.open.composer.description")
|
||||||
|
@ -121,3 +157,86 @@ struct OpenComposerIntent: AppIntent {
|
||||||
return .result()
|
return .result()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PublishTextIntent: AppIntent {
|
||||||
|
static var title: LocalizedStringResource = "intent.publish.text"
|
||||||
|
static var description: IntentDescription? = IntentDescription("intent.publish.text.description")
|
||||||
|
|
||||||
|
static var isDiscoverable: Bool = true
|
||||||
|
static var openAppWhenRun: Bool = false
|
||||||
|
|
||||||
|
static var authenticationPolicy: IntentAuthenticationPolicy = .requiresLocalDeviceAuthentication
|
||||||
|
|
||||||
|
@Parameter(title: "account", requestDisambiguationDialog: IntentDialog("intent.publish.text.account-dialog"))
|
||||||
|
var account: AccountEntity?
|
||||||
|
|
||||||
|
@Parameter(title: "status.posting.placeholder", requestValueDialog: IntentDialog("intent.publish.text.content-dialog"))
|
||||||
|
var content: String
|
||||||
|
|
||||||
|
@Parameter(title: "status.posting.visibility", requestDisambiguationDialog: IntentDialog("intent.publish.any.visibility-dialog"))
|
||||||
|
var visibility: Visibility
|
||||||
|
|
||||||
|
static var parameterSummary: any ParameterSummary {
|
||||||
|
Summary("intent.publish.text.summary-\(\.$content)") {
|
||||||
|
\.$account
|
||||||
|
\.$visibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func perform() async throws -> some IntentResult & ShowsSnippetView & ReturnsValue<String> {
|
||||||
|
if let client = account?.client, !client.server.isEmpty {
|
||||||
|
let data: StatusData = .init(
|
||||||
|
status: self.content,
|
||||||
|
visibility: self.visibility
|
||||||
|
)
|
||||||
|
|
||||||
|
// posting requires v1
|
||||||
|
if let res = try? await client.post(endpoint: Statuses.postStatus(json: data), forceVersion: .v1), res.statusCode == 200 {
|
||||||
|
return .result(
|
||||||
|
value: self.content,
|
||||||
|
view: Self.StatusSuccess(acc: account!, json: data)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await .result(value: "", view: IssueView())
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct IssueView: View {
|
||||||
|
var body: some View {
|
||||||
|
Label("intent.publish.any.issue", systemImage: "exclamationmark.triangle.fill")
|
||||||
|
.foregroundStyle(Color.red)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
|
||||||
|
.padding()
|
||||||
|
.background(Color.black)
|
||||||
|
.clipShape(Capsule())
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct StatusSuccess: View {
|
||||||
|
var acc: AccountEntity
|
||||||
|
var json: StatusData
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: 7.5) {
|
||||||
|
Text("@\(acc.username)")
|
||||||
|
.foregroundStyle(Color.white)
|
||||||
|
.bold()
|
||||||
|
|
||||||
|
Text(json.status)
|
||||||
|
.foregroundStyle(Color.white)
|
||||||
|
.lineLimit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.padding(.vertical)
|
||||||
|
.padding(.horizontal, 25)
|
||||||
|
.background(Color.black)
|
||||||
|
.clipShape(Capsule())
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue