-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4186 from tloncorp/promote-11-14
ops: promote 11/14
- Loading branch information
Showing
153 changed files
with
3,938 additions
and
1,591 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import AppIntents | ||
import Intents | ||
|
||
struct ChannelEntity: AppEntity { | ||
struct Group { | ||
let title: String | ||
} | ||
|
||
static var defaultQuery = ChannelQuery() | ||
|
||
var id: String | ||
let title: String | ||
let group: ChannelEntity.Group | ||
|
||
static var typeDisplayRepresentation: TypeDisplayRepresentation { | ||
TypeDisplayRepresentation( | ||
name: "Channel", | ||
numericFormat: "\(placeholder: .int) channels" | ||
) | ||
} | ||
|
||
var displayRepresentation: DisplayRepresentation { | ||
DisplayRepresentation( | ||
title: "\(title)", | ||
subtitle: "\(group.title)", | ||
image: nil | ||
) | ||
} | ||
} | ||
|
||
struct ChannelQuery: EntityStringQuery { | ||
private var database: SQLiteDB? { | ||
SQLiteDB.openApplicationDatabase() | ||
} | ||
|
||
func entities(matching string: String) async throws -> [ChannelEntity] { | ||
guard let database else { return [] } | ||
|
||
let results = database.exec(""" | ||
select | ||
c.id, | ||
c.title, | ||
g.title as group_title | ||
from channels c | ||
join groups g on c.group_id = g.id | ||
where c.title like ? | ||
""", parameters: ["%\(string)%"]) | ||
|
||
database.close() | ||
|
||
return results.map { result in | ||
ChannelEntity( | ||
id: result["id"]!!, | ||
title: result["title"]!!, | ||
group: ChannelEntity.Group(title: result["group_title"]!!) | ||
) | ||
} | ||
} | ||
|
||
func entities(for identifiers: [ChannelEntity.ID]) async throws -> [ChannelEntity] { | ||
guard let database else { return [] } | ||
|
||
let results = database.exec(""" | ||
with query(id) as ( | ||
values \(identifiers.map { _ in "(?)" }.joined(separator: ", ")) | ||
) | ||
select | ||
c.id, | ||
c.title, | ||
g.title as group_title | ||
from query | ||
join channels c on query.id = c.id | ||
join groups g on c.group_id = g.id | ||
""", parameters: identifiers) | ||
database.close() | ||
|
||
return results.map { result in | ||
ChannelEntity( | ||
id: result["id"]!!, | ||
title: result["title"]!!, | ||
group: ChannelEntity.Group(title: result["group_title"]!!) | ||
) | ||
} | ||
} | ||
|
||
func suggestedEntities() async throws -> [ChannelEntity] { | ||
guard let database else { return [] } | ||
|
||
let results = database.exec(""" | ||
select | ||
c.id, | ||
c.title, | ||
g.title as group_title | ||
from channels c | ||
join groups g on c.group_id = g.id | ||
order by max(c.last_viewed_at, c.last_post_at) desc | ||
limit 10 | ||
""") | ||
database.close() | ||
|
||
return results.map { result in | ||
ChannelEntity( | ||
id: result["id"]!!, | ||
title: result["title"]!!, | ||
group: ChannelEntity.Group(title: result["group_title"]!!) | ||
) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import AppIntents | ||
|
||
struct OpenAppIntent: AppIntent { | ||
static var title: LocalizedStringResource = "Open channel" | ||
static var description = IntentDescription("Opens the app to the specified channel.") | ||
static var openAppWhenRun = true | ||
|
||
@Parameter(title: "Channel", description: "The app will open to this channel.") | ||
var channel: ChannelEntity? | ||
|
||
@Parameter(title: "Start draft", description: "True if you want to start a new draft in the channel.") | ||
var startDraft: Bool? | ||
|
||
@Dependency | ||
private var intentNotepad: IntentNotepad | ||
|
||
@MainActor | ||
func perform() async throws -> some IntentResult { | ||
if let channelId = channel?.id { | ||
intentNotepad.action = .openChannel(channelId: channelId, startDraft: startDraft ?? false) | ||
} else { | ||
intentNotepad.action = nil | ||
} | ||
return .result() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import Foundation | ||
import Combine | ||
import AppIntents | ||
|
||
final class IntentNotepad: ObservableObject, @unchecked Sendable { | ||
static let shared = IntentNotepad() | ||
|
||
enum Action { | ||
case openChannel(channelId: String, startDraft: Bool) | ||
} | ||
|
||
@Published var action: Action? | ||
} | ||
|
||
@objc class ShortcutsManager: NSObject { | ||
// reference necessary to retain subscription | ||
private static var subscription: AnyCancellable? | ||
|
||
@objc static func setup() { | ||
AppDependencyManager.shared.add(dependency: IntentNotepad.shared) | ||
|
||
subscription = IntentNotepad.shared | ||
.$action | ||
.sink { action in | ||
switch action { | ||
case let .openChannel(channelId, startDraft): | ||
if let url = URL.deeplinkToOpenChannel(withId: channelId, startDraft: startDraft) { | ||
UIApplication.shared.open(url) | ||
} | ||
|
||
default: break | ||
} | ||
} | ||
} | ||
} | ||
|
||
extension URL { | ||
static var schemeForDeeplinking: String? { | ||
if let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [[String: Any]] { | ||
if let urlSchemes = urlTypes.first?["CFBundleURLSchemes"] as? [String] { | ||
return urlSchemes.first | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
static func deeplinkToOpenChannel(withId channelId: String, startDraft: Bool = false) -> URL? { | ||
guard let scheme = URL.schemeForDeeplinking else { return nil } | ||
var urlComponents = URLComponents() | ||
urlComponents.scheme = scheme | ||
urlComponents.host = "channel" | ||
urlComponents.path = "/open" | ||
var queryItems: [URLQueryItem] = [ | ||
URLQueryItem(name: "id", value: channelId), | ||
] | ||
if startDraft { | ||
queryItems.append(URLQueryItem(name: "startDraft", value: "true")) | ||
} | ||
urlComponents.queryItems = queryItems | ||
return urlComponents.url | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.