Release 1.0.3: refine tablet layouts, pointer selection and account sync cleanup
This commit is contained in:
@@ -607,6 +607,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
DEVELOPMENT_TEAM = Q3JLTAG9PV;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = dev.ayushya.noo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -680,6 +681,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
DEVELOPMENT_TEAM = Q3JLTAG9PV;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = dev.ayushya.noo.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -708,6 +710,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
DEVELOPMENT_TEAM = Q3JLTAG9PV;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = dev.ayushya.noo.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -736,6 +739,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
DEVELOPMENT_TEAM = Q3JLTAG9PV;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = dev.ayushya.noo.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -870,6 +874,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
DEVELOPMENT_TEAM = Q3JLTAG9PV;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = dev.ayushya.noo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -893,6 +898,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
DEVELOPMENT_TEAM = Q3JLTAG9PV;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = dev.ayushya.noo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
||||
@@ -149,6 +149,18 @@ enum NativeServices {
|
||||
// Fire and forget, like Android: the status stream reports the result.
|
||||
Task { _ = await coordinator.resolveConflict(conflict, resolution: resolution, creds: creds) }
|
||||
}
|
||||
case "removeAccountData":
|
||||
guard let accountId = args["accountId"] as? String else {
|
||||
return result(FlutterError(code: "bad_args", message: "Missing accountId", details: nil))
|
||||
}
|
||||
Task {
|
||||
do {
|
||||
try await coordinator.removeAccountData(accountId: accountId)
|
||||
await MainActor.run { result(nil) }
|
||||
} catch {
|
||||
await MainActor.run { result(FlutterError(code: "cleanup_failed", message: error.localizedDescription, details: nil)) }
|
||||
}
|
||||
}
|
||||
case "removeLocalSync":
|
||||
guard let accountId = args["accountId"] as? String, let path = args["path"] as? String else {
|
||||
return result(FlutterError(code: "bad_args", message: "Missing required arguments", details: nil))
|
||||
|
||||
@@ -194,6 +194,16 @@ final class SyncCoordinator {
|
||||
/// removal under the same mutex as runs - a run still holding the old
|
||||
/// state would otherwise write it back over the removal (and a transfer
|
||||
/// finishing after it would leave a file the state doesn't know about).
|
||||
func removeAccountData(accountId: String) async throws {
|
||||
cancel(accountId: accountId, forget: true)
|
||||
try await mutex.withLock {
|
||||
try store.removeAccountData(accountId: accountId)
|
||||
for conflict in bus.snapshot().conflicts where conflict.accountId == accountId {
|
||||
bus.removeConflict(accountId: accountId, fileId: conflict.fileId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeLocalSync(accountId: String, path: String) async {
|
||||
cancelRun(accountId: accountId, invalidateQueued: true)
|
||||
_ = try? await mutex.withLock {
|
||||
|
||||
@@ -108,6 +108,13 @@ final class SyncStore {
|
||||
if changed { _ = write(Array(current).sorted(), to: file(accountId, "missing.json")) }
|
||||
}
|
||||
|
||||
func removeAccountData(accountId: String) throws {
|
||||
for directory in ["sync", "sync-state"] {
|
||||
let url = base.appendingPathComponent(directory).appendingPathComponent(accountId)
|
||||
if fileManager.fileExists(atPath: url.path) { try fileManager.removeItem(at: url) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Removing a path's mirror
|
||||
|
||||
/// Deletes [path]'s local mirror (a file, or a whole folder's worth) and
|
||||
|
||||
@@ -237,6 +237,21 @@ final class SyncEngineTests: XCTestCase {
|
||||
|
||||
// MARK: - The happy path
|
||||
|
||||
func testRemovingAccountDeletesMirrorAndStateButPreservesOtherAccounts() async throws {
|
||||
seedServer()
|
||||
_ = await runner.run(config, force: false)
|
||||
let other = store.syncRoot(accountId: "other").appendingPathComponent("keep.txt")
|
||||
try Data("keep".utf8).write(to: other)
|
||||
let mirrorPath = store.base.appendingPathComponent("sync/acct")
|
||||
let statePath = store.base.appendingPathComponent("sync-state/acct")
|
||||
try store.removeAccountData(accountId: "acct")
|
||||
XCTAssertFalse(FileManager.default.fileExists(atPath: mirrorPath.path))
|
||||
XCTAssertFalse(FileManager.default.fileExists(atPath: statePath.path))
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath: other.path))
|
||||
XCTAssertTrue(try store.loadState(accountId: "acct").isEmpty)
|
||||
try store.removeAccountData(accountId: "acct")
|
||||
}
|
||||
|
||||
func testFirstSyncDownloadsEverythingAndMirrorsFolders() async {
|
||||
seedServer()
|
||||
FakeDav.mkdir("/Docs/empty")
|
||||
|
||||
Reference in New Issue
Block a user