Skip to content

Commit

Permalink
feat: add iOS 18 control widget
Browse files Browse the repository at this point in the history
  • Loading branch information
mudkipme committed Nov 21, 2024
1 parent b48f3e0 commit f9121cf
Show file tree
Hide file tree
Showing 18 changed files with 166 additions and 29 deletions.
18 changes: 15 additions & 3 deletions MoeMemos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
19F31BF728C3E26D000C5207 /* MemosList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F31BF628C3E26D000C5207 /* MemosList.swift */; };
19F31BFB28C3E9B2000C5207 /* Memo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F31BFA28C3E9B2000C5207 /* Memo.swift */; };
19F31BFD28C3ED11000C5207 /* MemoCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F31BFC28C3ED11000C5207 /* MemoCard.swift */; };
19F81CDE2CEDA6DB00728439 /* AppOpenIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F81CDD2CEDA6D500728439 /* AppOpenIntent.swift */; };
19F81CE02CEDAE2800728439 /* QuickMemoWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F81CDF2CEDAE2300728439 /* QuickMemoWidget.swift */; };
19F81CE12CEDAF2400728439 /* AppOpenIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F81CDD2CEDA6D500728439 /* AppOpenIntent.swift */; };
19FC1C7C290DA47C0078A7F2 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19FC1C7B290DA47C0078A7F2 /* Route.swift */; };
19FC1C7E290DA6E90078A7F2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19FC1C7D290DA6E90078A7F2 /* Navigation.swift */; };
31EE785629B83D1B005F62D0 /* LazyImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31EE785529B83D1B005F62D0 /* LazyImageProvider.swift */; };
Expand Down Expand Up @@ -212,6 +215,8 @@
19F31BF628C3E26D000C5207 /* MemosList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemosList.swift; sourceTree = "<group>"; };
19F31BFA28C3E9B2000C5207 /* Memo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Memo.swift; sourceTree = "<group>"; };
19F31BFC28C3ED11000C5207 /* MemoCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoCard.swift; sourceTree = "<group>"; };
19F81CDD2CEDA6D500728439 /* AppOpenIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppOpenIntent.swift; sourceTree = "<group>"; };
19F81CDF2CEDAE2300728439 /* QuickMemoWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickMemoWidget.swift; sourceTree = "<group>"; };
19FC1C7B290DA47C0078A7F2 /* Route.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Route.swift; sourceTree = "<group>"; };
19FC1C7D290DA6E90078A7F2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = "<group>"; };
31EE785529B83D1B005F62D0 /* LazyImageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyImageProvider.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -271,13 +276,14 @@
191339B2291D7288001F2B2E /* MoeMemosWidgets */ = {
isa = PBXGroup;
children = (
191339B8291D7289001F2B2E /* Assets.xcassets */,
191339D3291D7F13001F2B2E /* MoeMemosWidgetsExtension.entitlements */,
191339BA291D7289001F2B2E /* Info.plist */,
191339DB291D8F15001F2B2E /* MemoryWidget.swift */,
191339B5291D7288001F2B2E /* MemosGraphWidget.swift */,
193F04E529CCBA9C0010E3BF /* MoeMemosWidgets.intentdefinition */,
191339B3291D7288001F2B2E /* MoeMemosWidgetsBundle.swift */,
191339D3291D7F13001F2B2E /* MoeMemosWidgetsExtension.entitlements */,
19F81CDF2CEDAE2300728439 /* QuickMemoWidget.swift */,
191339B8291D7289001F2B2E /* Assets.xcassets */,
193F04E529CCBA9C0010E3BF /* MoeMemosWidgets.intentdefinition */,
);
path = MoeMemosWidgets;
sourceTree = "<group>";
Expand Down Expand Up @@ -382,6 +388,7 @@
199009B72CE4F24E00578B04 /* MoeMemosAppIntents */ = {
isa = PBXGroup;
children = (
19F81CDD2CEDA6D500728439 /* AppOpenIntent.swift */,
197123DE2CEBC21700CEEECC /* AccountEntity.swift */,
197123022CEBB96B00CEEECC /* AppShortcuts.swift */,
199009B82CE4F43100578B04 /* SaveMemoIntent.swift */,
Expand Down Expand Up @@ -625,10 +632,12 @@
191339CA291D7675001F2B2E /* Usage.swift in Sources */,
191339D2291D7785001F2B2E /* Date.swift in Sources */,
191339CE291D76BD001F2B2E /* Constants.swift in Sources */,
19F81CE02CEDAE2800728439 /* QuickMemoWidget.swift in Sources */,
191339DC291D8F15001F2B2E /* MemoryWidget.swift in Sources */,
191339CF291D7748001F2B2E /* Heatmap.swift in Sources */,
191339D0291D774B001F2B2E /* HeatmapStat.swift in Sources */,
191339D1291D7765001F2B2E /* Color.swift in Sources */,
19F81CE12CEDAF2400728439 /* AppOpenIntent.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -657,6 +666,7 @@
197123032CEBB96E00CEEECC /* AppShortcuts.swift in Sources */,
19709CFC297F2C1900F6A144 /* MemoInputResourceView.swift in Sources */,
197A4D3329599C300027427E /* MemoCardImageView.swift in Sources */,
19F81CDE2CEDA6DB00728439 /* AppOpenIntent.swift in Sources */,
1919FBE329CF6BDD00FEF570 /* ArchivedMemoCard.swift in Sources */,
193AA8A928CDB27F00FF7EB6 /* ResourceCard.swift in Sources */,
1995E9CE28C46DC9004F2EDB /* Heatmap.swift in Sources */,
Expand Down Expand Up @@ -955,6 +965,7 @@
INFOPLIST_FILE = MoeMemos/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = MoeMemos;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_NSCameraUsageDescription = "Capture photos and attach to memos";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down Expand Up @@ -994,6 +1005,7 @@
INFOPLIST_FILE = MoeMemos/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = MoeMemos;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_NSCameraUsageDescription = "Capture photos and attach to memos";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down
19 changes: 18 additions & 1 deletion MoeMemos/Helpers/Route.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import Models
import Env
import Account

@MainActor
extension Route {
@MainActor @ViewBuilder
@ViewBuilder
func destination() -> some View {
switch self {
case .memos:
Expand All @@ -31,3 +32,19 @@ extension Route {
}
}
}

@MainActor
extension View {
func withSheetDestinations(sheetDestinations: Binding<SheetDestination?>) -> some View {
sheet(item: sheetDestinations) { destination in
switch destination {
case .newMemo:
MemoInput(memo: nil)
case .editMemo(let memo):
MemoInput(memo: memo)
case .addAccount:
AddAccountView()
}
}
}
}
19 changes: 19 additions & 0 deletions MoeMemos/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>me.mudkip.MoeMemos</string>
<key>CFBundleURLSchemes</key>
<array>
<string>moememos</string>
</array>
</dict>
</array>
<key>INIntentsSupported</key>
<array>
<string>AppOpenIntent</string>
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
Expand Down
14 changes: 13 additions & 1 deletion MoeMemos/MoeMemosApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,36 @@ import Account
import Models
import Factory
import AppIntents
import Env

@main
struct MoeMemosApp: App {
@Injected(\.appInfo) private var appInfo
@Injected(\.accountViewModel) private var userState
@Injected(\.accountManager) private var accountManager

@Injected(\.appPath) private var appPath
@State private var memosViewModel = MemosViewModel()

init() {
AppDependencyManager.shared.add(dependency: Container.shared.accountManager())
AppDependencyManager.shared.add(dependency: Container.shared.accountViewModel())
AppDependencyManager.shared.add(dependency: Container.shared.appPath())

AppShortcuts.updateAppShortcutParameters()
}

var body: some Scene {
WindowGroup {
ContentView()
.tint(.green)
.environment(userState)
.environment(accountManager)
.environment(appInfo)
.environment(appPath)
.environment(memosViewModel)
.onOpenURL { url in
print(url)
}
}
}
}
14 changes: 6 additions & 8 deletions MoeMemos/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ import Env
struct ContentView: View {
@Environment(AccountViewModel.self) private var accountViewModel: AccountViewModel
@Environment(AccountManager.self) private var accountManager: AccountManager
@Environment(MemosViewModel.self) private var memosViewModel: MemosViewModel
@Environment(AppPath.self) private var appPath: AppPath
@Injected(\.appInfo) private var appInfo
@State private var selection: Route? = .memos
@State private var memosViewModel = MemosViewModel()
@Environment(\.scenePhase) var scenePhase

var body: some View {
@Bindable var accountViewModel = accountViewModel
@Bindable var appPath = appPath

Navigation(selection: $selection)
.tint(.green)
.environment(memosViewModel)
.environment(appPath)
.onChange(of: scenePhase, initial: true, { _, newValue in
if newValue == .active {
Task {
Expand All @@ -42,10 +43,7 @@ struct ContentView: View {
try? await memosViewModel.loadTags()
}
.modelContext(appInfo.modelContext)
.sheet(isPresented: $accountViewModel.showingAddAccount) {
AddAccountView()
.tint(.green)
}
.withSheetDestinations(sheetDestinations: $appPath.presentedSheet)
}

private func loadCurrentUser() async {
Expand All @@ -55,7 +53,7 @@ struct ContentView: View {
}
try await accountViewModel.reloadUsers()
} catch MoeMemosError.notLogin {
accountViewModel.showingAddAccount = true
appPath.presentedSheet = .addAccount
} catch {
print(error)
}
Expand Down
8 changes: 3 additions & 5 deletions MoeMemos/Views/MemoCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import SwiftUI
import UniformTypeIdentifiers
import MarkdownUI
import Models
import Env

@MainActor
struct MemoCard: View {
let memo: Memo
let defaultMemoVisilibity: MemoVisibility?

@Environment(MemosViewModel.self) private var memosViewModel: MemosViewModel
@State private var showingEdit = false
@Environment(AppPath.self) private var appPath
@State private var showingDeleteConfirmation = false

init(_ memo: Memo, defaultMemoVisibility: MemoVisibility) {
Expand Down Expand Up @@ -61,9 +62,6 @@ struct MemoCard: View {
Label("memo.copy", systemImage: "doc.on.doc")
}
}
.sheet(isPresented: $showingEdit) {
MemoInput(memo: memo)
}
.confirmationDialog("memo.delete.confirm", isPresented: $showingDeleteConfirmation, titleVisibility: .visible) {
Button("memo.action.ok", role: .destructive) {
Task {
Expand Down Expand Up @@ -94,7 +92,7 @@ struct MemoCard: View {
}
}
Button {
showingEdit = true
appPath.presentedSheet = .editMemo(memo)
} label: {
Label("memo.edit", systemImage: "pencil")
}
Expand Down
8 changes: 3 additions & 5 deletions MoeMemos/Views/MemosList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
import SwiftUI
import Account
import Models
import Env

struct MemosList: View {
let tag: Tag?

@State private var searchString = ""
@State private var showingNewPost = false
@Environment(AppPath.self) private var appPath
@Environment(AccountManager.self) private var accountManager: AccountManager
@Environment(AccountViewModel.self) var userState: AccountViewModel
@Environment(MemosViewModel.self) private var memosViewModel: MemosViewModel
Expand All @@ -32,7 +33,7 @@ struct MemosList: View {

if tag == nil {
Button {
showingNewPost = true
appPath.presentedSheet = .newMemo
} label: {
Circle().overlay {
Image(systemName: "plus")
Expand All @@ -53,9 +54,6 @@ struct MemosList: View {
})
.searchable(text: $searchString)
.navigationTitle(tag?.name ?? NSLocalizedString("memo.memos", comment: "Memos"))
.sheet(isPresented: $showingNewPost) {
MemoInput(memo: nil)
}
.onAppear {
filteredMemoList = filterMemoList(memosViewModel.memoList, tag: tag, searchString: searchString)
}
Expand Down
23 changes: 23 additions & 0 deletions MoeMemosAppIntents/AppOpenIntent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// OpenAppIntent.swift
// MoeMemos
//
// Created by Mudkip on 2024/11/20.
//

import AppIntents
import Env

struct AppOpenIntent: AppIntent {
static let title: LocalizedStringResource = "Launch Moe Memos"

static let openAppWhenRun: Bool = true

@Dependency
var appPath: AppPath

func perform() async throws -> some IntentResult {
appPath.presentedSheet = .newMemo
return .result()
}
}
3 changes: 3 additions & 0 deletions MoeMemosWidgets/MoeMemosWidgetsBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ struct MoeMemosWidgetsBundle: WidgetBundle {
var body: some Widget {
MemosGraphWidget()
MemoryWidget()
if #available(iOS 18.0, *) {
QuickMemoWidget()
}
}
}
22 changes: 22 additions & 0 deletions MoeMemosWidgets/QuickMemoWidget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// QuickMemoWidget.swift
// MoeMemos
//
// Created by Mudkip on 2024/11/20.
//

import WidgetKit
import SwiftUI

@available(iOS 18.0, *)
struct QuickMemoWidget: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: "me.mudkip.MoeMemos.QuickMemoWidget") {
ControlWidgetButton(action: AppOpenIntent()) {
Label("Quick Memo", systemImage: "note.text.badge.plus")
}
}
.displayName("Quick Memo")
.description("Write a memo in Moe Memos.")
}
}
3 changes: 2 additions & 1 deletion Packages/Account/Sources/Account/AccountSectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Env
public struct AccountSectionView: View {
@Environment(AccountViewModel.self) private var accountViewModel
@Environment(AccountManager.self) private var accountManager
@Environment(AppPath.self) private var appPath

public init() {}

Expand Down Expand Up @@ -41,7 +42,7 @@ public struct AccountSectionView: View {
}
}
Button {
accountViewModel.showingAddAccount = true
appPath.presentedSheet = .addAccount
} label: {
Label("account.add-account", systemImage: "plus.circle")
}
Expand Down
1 change: 0 additions & 1 deletion Packages/Account/Sources/Account/AccountViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import MemosV0Service
@Observable public final class AccountViewModel: @unchecked Sendable {
private var currentContext: ModelContext
private var accountManager: AccountManager
public var showingAddAccount = false

public init(currentContext: ModelContext, accountManager: AccountManager) {
self.currentContext = currentContext
Expand Down
4 changes: 3 additions & 1 deletion Packages/Account/Sources/Account/MemosAccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import Foundation
import SwiftUI
import Models
import Env

public struct MemosAccountView: View {
@State var user: User? = nil
@State var version: MemosVersion? = nil
private let accountKey: String
@Environment(AccountManager.self) private var accountManager
@Environment(AccountViewModel.self) private var accountViewModel
@Environment(AppPath.self) private var appPath
private var account: Account? { accountManager.accounts.first { $0.key == accountKey } }
@Environment(\.presentationMode) var presentationMode

Expand Down Expand Up @@ -77,7 +79,7 @@ public struct MemosAccountView: View {
presentationMode.wrappedValue.dismiss()

if accountManager.currentAccount == nil {
accountViewModel.showingAddAccount = true
appPath.presentedSheet = .addAccount
}
}
} label: {
Expand Down
Loading

0 comments on commit f9121cf

Please sign in to comment.