|
|
@@ -4,7 +4,7 @@ import WebKit
|
|
|
|
|
|
// MARK: - Public SwiftUI view
|
|
|
|
|
|
-/// Tiered Gmail desktop widget: compact unread + compose, inbox preview, or full embedded Gmail.
|
|
|
+/// Tiered Gmail desktop widget: compact unread + compose, or full embedded Gmail.
|
|
|
struct GmailDesktopWidgetView: View {
|
|
|
let app: LauncherApp
|
|
|
let mode: GmailWidgetMode
|
|
|
@@ -18,8 +18,6 @@ struct GmailDesktopWidgetView: View {
|
|
|
switch mode {
|
|
|
case .compact:
|
|
|
compactBody
|
|
|
- case .inboxPreview:
|
|
|
- inboxPreviewBody
|
|
|
case .interactive:
|
|
|
interactiveBody
|
|
|
}
|
|
|
@@ -88,126 +86,6 @@ struct GmailDesktopWidgetView: View {
|
|
|
.opacity(isPreview ? 0.85 : 1)
|
|
|
}
|
|
|
|
|
|
- // MARK: - Medium (inbox preview)
|
|
|
-
|
|
|
- private struct PreviewRow: Identifiable {
|
|
|
- let id: String
|
|
|
- let sender: String
|
|
|
- let subject: String
|
|
|
- let time: String
|
|
|
- let searchQuery: String
|
|
|
- }
|
|
|
-
|
|
|
- private static let previewRows: [PreviewRow] = [
|
|
|
- PreviewRow(id: "1", sender: "Alex Chen", subject: "Q4 roadmap — draft review", time: "9:42a", searchQuery: "from:alex"),
|
|
|
- PreviewRow(id: "2", sender: "Google Alerts", subject: "News: your weekly digest", time: "8:10a", searchQuery: "from:googlealerts"),
|
|
|
- PreviewRow(id: "3", sender: "HR Benefits", subject: "Open enrollment ends Friday", time: "Yesterday", searchQuery: "from:hr"),
|
|
|
- ]
|
|
|
-
|
|
|
- private var inboxPreviewBody: some View {
|
|
|
- ZStack(alignment: .topLeading) {
|
|
|
- gmailGradientBackground
|
|
|
- .frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
- ScrollView {
|
|
|
- VStack(alignment: .leading, spacing: 10) {
|
|
|
- HStack(spacing: 8) {
|
|
|
- AppIconView(app: app, size: 32, showAppBackground: false, iconPaddingFactor: 0.08)
|
|
|
- Text("Inbox preview")
|
|
|
- .font(.system(size: 14, weight: .bold))
|
|
|
- .foregroundStyle(.white.opacity(0.95))
|
|
|
- Spacer(minLength: 0)
|
|
|
- Text("Sample")
|
|
|
- .font(.system(size: 10, weight: .bold))
|
|
|
- .foregroundStyle(.white.opacity(0.55))
|
|
|
- .padding(.horizontal, 8)
|
|
|
- .padding(.vertical, 4)
|
|
|
- .background(Capsule().fill(Color.white.opacity(0.12)))
|
|
|
- }
|
|
|
-
|
|
|
- compactButton(title: "Open Mail", systemImage: "envelope.open", emphasized: false) {
|
|
|
- openMailInBrowser()
|
|
|
- }
|
|
|
-
|
|
|
- Text("Recent messages")
|
|
|
- .font(.system(size: 11, weight: .semibold))
|
|
|
- .foregroundStyle(.white.opacity(0.62))
|
|
|
-
|
|
|
- VStack(spacing: 8) {
|
|
|
- ForEach(Self.previewRows) { row in
|
|
|
- previewRowView(row)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- .frame(maxWidth: .infinity, alignment: .leading)
|
|
|
- .padding(.bottom, 4)
|
|
|
- }
|
|
|
- .padding(14)
|
|
|
- .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
|
|
- }
|
|
|
- .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
|
|
- .disabled(isPreview)
|
|
|
- .opacity(isPreview ? 0.85 : 1)
|
|
|
- }
|
|
|
-
|
|
|
- private func previewRowView(_ row: PreviewRow) -> some View {
|
|
|
- VStack(alignment: .leading, spacing: 8) {
|
|
|
- HStack(alignment: .top, spacing: 8) {
|
|
|
- VStack(alignment: .leading, spacing: 3) {
|
|
|
- Text(row.sender)
|
|
|
- .font(.system(size: 12, weight: .bold))
|
|
|
- .foregroundStyle(.white.opacity(0.95))
|
|
|
- .lineLimit(1)
|
|
|
- Text(row.subject)
|
|
|
- .font(.system(size: 11, weight: .medium))
|
|
|
- .foregroundStyle(.white.opacity(0.78))
|
|
|
- .lineLimit(2)
|
|
|
- }
|
|
|
- Spacer(minLength: 0)
|
|
|
- Text(row.time)
|
|
|
- .font(.system(size: 10, weight: .semibold))
|
|
|
- .foregroundStyle(.white.opacity(0.5))
|
|
|
- }
|
|
|
- HStack(spacing: 8) {
|
|
|
- previewActionButton(title: "Reply", systemImage: "arrowshape.turn.up.left") {
|
|
|
- openComposeReply(subject: row.subject)
|
|
|
- }
|
|
|
- previewActionButton(title: "Archive", systemImage: "archivebox") {
|
|
|
- openGmailSearch(query: row.searchQuery)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- .padding(10)
|
|
|
- .background(
|
|
|
- RoundedRectangle(cornerRadius: 12, style: .continuous)
|
|
|
- .fill(Color.black.opacity(0.28))
|
|
|
- )
|
|
|
- .overlay(
|
|
|
- RoundedRectangle(cornerRadius: 12, style: .continuous)
|
|
|
- .stroke(Color.white.opacity(0.1), lineWidth: 1)
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- private func previewActionButton(title: String, systemImage: String, action: @escaping () -> Void) -> some View {
|
|
|
- Button(action: {
|
|
|
- NSApp.activate(ignoringOtherApps: true)
|
|
|
- action()
|
|
|
- }) {
|
|
|
- HStack(spacing: 5) {
|
|
|
- Image(systemName: systemImage)
|
|
|
- .font(.system(size: 10, weight: .semibold))
|
|
|
- Text(title)
|
|
|
- .font(.system(size: 10, weight: .bold))
|
|
|
- }
|
|
|
- .foregroundStyle(.white.opacity(0.92))
|
|
|
- .padding(.horizontal, 10)
|
|
|
- .padding(.vertical, 6)
|
|
|
- .background(Capsule().fill(Color.white.opacity(0.14)))
|
|
|
- }
|
|
|
- .buttonStyle(.plain)
|
|
|
- .disabled(isPreview)
|
|
|
- .allowsHitTesting(!isPreview)
|
|
|
- }
|
|
|
-
|
|
|
// MARK: - Large (interactive)
|
|
|
|
|
|
@State private var searchText = ""
|
|
|
@@ -465,29 +343,6 @@ struct GmailDesktopWidgetView: View {
|
|
|
InAppBrowserWindowManager.shared.open(url: url, title: app.name)
|
|
|
}
|
|
|
|
|
|
- private func openComposeReply(subject: String) {
|
|
|
- guard !isPreview else { return }
|
|
|
- var c = URLComponents()
|
|
|
- c.scheme = "https"
|
|
|
- c.host = "mail.google.com"
|
|
|
- c.path = "/mail/u/\(gmailAccountSlot)/"
|
|
|
- c.queryItems = [
|
|
|
- URLQueryItem(name: "view", value: "cm"),
|
|
|
- URLQueryItem(name: "fs", value: "1"),
|
|
|
- URLQueryItem(name: "su", value: "Re: \(subject)"),
|
|
|
- ]
|
|
|
- guard let url = c.url else { return }
|
|
|
- InAppBrowserWindowManager.shared.open(url: url, title: app.name)
|
|
|
- }
|
|
|
-
|
|
|
- private func openGmailSearch(query: String) {
|
|
|
- guard !isPreview else { return }
|
|
|
- let encoded = query.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlFragmentAllowed) ?? query
|
|
|
- let path = "/mail/u/{{u}}/#search/\(encoded)"
|
|
|
- let url = WidgetDeepLinkURL.resolved(base: baseURL, actionPath: path, accountSlot: gmailAccountSlot)
|
|
|
- InAppBrowserWindowManager.shared.open(url: url, title: app.name)
|
|
|
- }
|
|
|
-
|
|
|
private func navigateToMailPath(_ path: String) {
|
|
|
let url = WidgetDeepLinkURL.resolved(base: baseURL, actionPath: path, accountSlot: gmailAccountSlot)
|
|
|
DispatchQueue.main.async {
|