@reverbia/sdk 1.0.0-next.20251219092050 → 1.0.0-next.20251219154503
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/index.cjs +760 -27
- package/dist/react/index.d.mts +347 -8
- package/dist/react/index.d.ts +347 -8
- package/dist/react/index.mjs +760 -27
- package/package.json +1 -1
package/dist/react/index.mjs
CHANGED
|
@@ -5575,7 +5575,7 @@ function useGoogleDriveBackup(options) {
|
|
|
5575
5575
|
};
|
|
5576
5576
|
}
|
|
5577
5577
|
|
|
5578
|
-
// src/react/
|
|
5578
|
+
// src/react/useICloudAuth.ts
|
|
5579
5579
|
import {
|
|
5580
5580
|
createContext as createContext3,
|
|
5581
5581
|
createElement as createElement3,
|
|
@@ -5584,20 +5584,593 @@ import {
|
|
|
5584
5584
|
useEffect as useEffect8,
|
|
5585
5585
|
useState as useState12
|
|
5586
5586
|
} from "react";
|
|
5587
|
-
|
|
5587
|
+
|
|
5588
|
+
// src/lib/backup/icloud/api.ts
|
|
5589
|
+
var CLOUDKIT_JS_URL = "https://cdn.apple-cloudkit.com/ck/2/cloudkit.js";
|
|
5590
|
+
var DEFAULT_BACKUP_FOLDER2 = "conversations";
|
|
5591
|
+
var DEFAULT_CONTAINER_ID = "iCloud.Memoryless";
|
|
5592
|
+
var RECORD_TYPE = "ConversationBackup";
|
|
5593
|
+
var cloudKitLoadPromise = null;
|
|
5594
|
+
function isCloudKitAvailable() {
|
|
5595
|
+
return typeof window !== "undefined" && !!window.CloudKit;
|
|
5596
|
+
}
|
|
5597
|
+
async function loadCloudKit() {
|
|
5598
|
+
if (typeof window === "undefined") {
|
|
5599
|
+
throw new Error("CloudKit JS can only be loaded in browser environment");
|
|
5600
|
+
}
|
|
5601
|
+
if (window.CloudKit) {
|
|
5602
|
+
return;
|
|
5603
|
+
}
|
|
5604
|
+
if (cloudKitLoadPromise) {
|
|
5605
|
+
return cloudKitLoadPromise;
|
|
5606
|
+
}
|
|
5607
|
+
cloudKitLoadPromise = new Promise((resolve, reject) => {
|
|
5608
|
+
const script = document.createElement("script");
|
|
5609
|
+
script.src = CLOUDKIT_JS_URL;
|
|
5610
|
+
script.async = true;
|
|
5611
|
+
script.onload = () => {
|
|
5612
|
+
if (window.CloudKit) {
|
|
5613
|
+
resolve();
|
|
5614
|
+
} else {
|
|
5615
|
+
reject(new Error("CloudKit JS loaded but CloudKit object not found"));
|
|
5616
|
+
}
|
|
5617
|
+
};
|
|
5618
|
+
script.onerror = () => {
|
|
5619
|
+
cloudKitLoadPromise = null;
|
|
5620
|
+
reject(new Error("Failed to load CloudKit JS"));
|
|
5621
|
+
};
|
|
5622
|
+
document.head.appendChild(script);
|
|
5623
|
+
});
|
|
5624
|
+
return cloudKitLoadPromise;
|
|
5625
|
+
}
|
|
5626
|
+
async function ensureCloudKitLoaded() {
|
|
5627
|
+
if (!isCloudKitAvailable()) {
|
|
5628
|
+
await loadCloudKit();
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
async function configureCloudKit(config) {
|
|
5632
|
+
await ensureCloudKitLoaded();
|
|
5633
|
+
ensureAuthElements();
|
|
5634
|
+
window.CloudKit.configure({
|
|
5635
|
+
containers: [
|
|
5636
|
+
{
|
|
5637
|
+
containerIdentifier: config.containerIdentifier,
|
|
5638
|
+
apiTokenAuth: {
|
|
5639
|
+
apiToken: config.apiToken,
|
|
5640
|
+
persist: true,
|
|
5641
|
+
signInButton: {
|
|
5642
|
+
id: "apple-sign-in-button",
|
|
5643
|
+
theme: "black"
|
|
5644
|
+
},
|
|
5645
|
+
signOutButton: {
|
|
5646
|
+
id: "apple-sign-out-button",
|
|
5647
|
+
theme: "black"
|
|
5648
|
+
}
|
|
5649
|
+
},
|
|
5650
|
+
environment: config.environment
|
|
5651
|
+
}
|
|
5652
|
+
]
|
|
5653
|
+
});
|
|
5654
|
+
}
|
|
5655
|
+
async function getContainer() {
|
|
5656
|
+
await ensureCloudKitLoaded();
|
|
5657
|
+
return window.CloudKit.getDefaultContainer();
|
|
5658
|
+
}
|
|
5659
|
+
function ensureAuthElements() {
|
|
5660
|
+
let signInButton = document.getElementById("apple-sign-in-button");
|
|
5661
|
+
let signOutButton = document.getElementById("apple-sign-out-button");
|
|
5662
|
+
if (!signInButton) {
|
|
5663
|
+
signInButton = document.createElement("div");
|
|
5664
|
+
signInButton.id = "apple-sign-in-button";
|
|
5665
|
+
signInButton.style.position = "fixed";
|
|
5666
|
+
signInButton.style.top = "-9999px";
|
|
5667
|
+
signInButton.style.left = "-9999px";
|
|
5668
|
+
document.body.appendChild(signInButton);
|
|
5669
|
+
}
|
|
5670
|
+
if (!signOutButton) {
|
|
5671
|
+
signOutButton = document.createElement("div");
|
|
5672
|
+
signOutButton.id = "apple-sign-out-button";
|
|
5673
|
+
signOutButton.style.position = "fixed";
|
|
5674
|
+
signOutButton.style.top = "-9999px";
|
|
5675
|
+
signOutButton.style.left = "-9999px";
|
|
5676
|
+
document.body.appendChild(signOutButton);
|
|
5677
|
+
}
|
|
5678
|
+
return { signIn: signInButton, signOut: signOutButton };
|
|
5679
|
+
}
|
|
5680
|
+
async function authenticateICloud() {
|
|
5681
|
+
const container = await getContainer();
|
|
5682
|
+
ensureAuthElements();
|
|
5683
|
+
return container.setUpAuth();
|
|
5684
|
+
}
|
|
5685
|
+
async function requestICloudSignIn() {
|
|
5686
|
+
const container = await getContainer();
|
|
5687
|
+
const { signIn } = ensureAuthElements();
|
|
5688
|
+
const existingUser = await container.setUpAuth();
|
|
5689
|
+
if (existingUser) {
|
|
5690
|
+
return existingUser;
|
|
5691
|
+
}
|
|
5692
|
+
console.log("[CloudKit] Sign-in container innerHTML:", signIn.innerHTML);
|
|
5693
|
+
console.log("[CloudKit] Sign-in container children:", signIn.children.length);
|
|
5694
|
+
const appleButton = signIn.querySelector("a, button, [role='button'], div[id*='apple']");
|
|
5695
|
+
console.log("[CloudKit] Found button element:", appleButton);
|
|
5696
|
+
if (appleButton) {
|
|
5697
|
+
console.log("[CloudKit] Clicking button...");
|
|
5698
|
+
appleButton.click();
|
|
5699
|
+
} else {
|
|
5700
|
+
const anyClickable = signIn.firstElementChild;
|
|
5701
|
+
if (anyClickable) {
|
|
5702
|
+
console.log("[CloudKit] Clicking first child element:", anyClickable);
|
|
5703
|
+
anyClickable.click();
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
return container.whenUserSignsIn();
|
|
5707
|
+
}
|
|
5708
|
+
async function uploadFileToICloud(filename, content) {
|
|
5709
|
+
const container = await getContainer();
|
|
5710
|
+
const database = container.privateCloudDatabase;
|
|
5711
|
+
const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
5712
|
+
const arrayBuffer = await content.arrayBuffer();
|
|
5713
|
+
const base64Data = btoa(
|
|
5714
|
+
String.fromCharCode(...new Uint8Array(arrayBuffer))
|
|
5715
|
+
);
|
|
5716
|
+
const record = {
|
|
5717
|
+
recordType: RECORD_TYPE,
|
|
5718
|
+
recordName,
|
|
5719
|
+
fields: {
|
|
5720
|
+
filename: { value: filename },
|
|
5721
|
+
data: { value: base64Data },
|
|
5722
|
+
size: { value: content.size },
|
|
5723
|
+
contentType: { value: content.type || "application/json" }
|
|
5724
|
+
}
|
|
5725
|
+
};
|
|
5726
|
+
const response = await database.saveRecords(record);
|
|
5727
|
+
if (!response.records || response.records.length === 0) {
|
|
5728
|
+
throw new Error("Failed to upload file to iCloud");
|
|
5729
|
+
}
|
|
5730
|
+
const savedRecord = response.records[0];
|
|
5731
|
+
return {
|
|
5732
|
+
recordName: savedRecord.recordName,
|
|
5733
|
+
filename,
|
|
5734
|
+
modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
|
|
5735
|
+
size: content.size
|
|
5736
|
+
};
|
|
5737
|
+
}
|
|
5738
|
+
async function listICloudFiles() {
|
|
5739
|
+
const container = await getContainer();
|
|
5740
|
+
const database = container.privateCloudDatabase;
|
|
5741
|
+
const query = {
|
|
5742
|
+
recordType: RECORD_TYPE
|
|
5743
|
+
// Note: Sorting requires SORTABLE index on the field in CloudKit Dashboard
|
|
5744
|
+
// For now, we skip sorting and sort client-side after fetching
|
|
5745
|
+
};
|
|
5746
|
+
const allRecords = [];
|
|
5747
|
+
let response = await database.performQuery(query);
|
|
5748
|
+
if (response.records) {
|
|
5749
|
+
allRecords.push(...response.records);
|
|
5750
|
+
}
|
|
5751
|
+
while (response.continuationMarker) {
|
|
5752
|
+
break;
|
|
5753
|
+
}
|
|
5754
|
+
const files = allRecords.map((record) => ({
|
|
5755
|
+
recordName: record.recordName,
|
|
5756
|
+
filename: record.fields.filename?.value ?? "",
|
|
5757
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5758
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5759
|
+
}));
|
|
5760
|
+
return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
5761
|
+
}
|
|
5762
|
+
async function downloadICloudFile(recordName) {
|
|
5763
|
+
const container = await getContainer();
|
|
5764
|
+
const database = container.privateCloudDatabase;
|
|
5765
|
+
const response = await database.fetchRecords([{ recordName }], {
|
|
5766
|
+
desiredKeys: ["filename", "data", "contentType"]
|
|
5767
|
+
});
|
|
5768
|
+
if (!response.records || response.records.length === 0) {
|
|
5769
|
+
throw new Error(`File not found: ${recordName}`);
|
|
5770
|
+
}
|
|
5771
|
+
const record = response.records[0];
|
|
5772
|
+
const dataField = record.fields.data?.value;
|
|
5773
|
+
if (!dataField) {
|
|
5774
|
+
throw new Error("No data in record");
|
|
5775
|
+
}
|
|
5776
|
+
if (typeof dataField === "string") {
|
|
5777
|
+
const binaryString = atob(dataField);
|
|
5778
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
5779
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
5780
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
5781
|
+
}
|
|
5782
|
+
return new Blob([bytes], { type: "application/json" });
|
|
5783
|
+
}
|
|
5784
|
+
if (typeof dataField === "object" && "downloadURL" in dataField) {
|
|
5785
|
+
const fetchResponse = await fetch(
|
|
5786
|
+
dataField.downloadURL
|
|
5787
|
+
);
|
|
5788
|
+
if (!fetchResponse.ok) {
|
|
5789
|
+
throw new Error(`Failed to download from iCloud: ${fetchResponse.status}`);
|
|
5790
|
+
}
|
|
5791
|
+
return fetchResponse.blob();
|
|
5792
|
+
}
|
|
5793
|
+
throw new Error("Unknown data format in iCloud record");
|
|
5794
|
+
}
|
|
5795
|
+
async function findICloudFile(filename) {
|
|
5796
|
+
const container = await getContainer();
|
|
5797
|
+
const database = container.privateCloudDatabase;
|
|
5798
|
+
const query = {
|
|
5799
|
+
recordType: RECORD_TYPE,
|
|
5800
|
+
filterBy: [
|
|
5801
|
+
{
|
|
5802
|
+
fieldName: "filename",
|
|
5803
|
+
comparator: "EQUALS",
|
|
5804
|
+
fieldValue: { value: filename }
|
|
5805
|
+
}
|
|
5806
|
+
]
|
|
5807
|
+
};
|
|
5808
|
+
const response = await database.performQuery(query);
|
|
5809
|
+
if (!response.records || response.records.length === 0) {
|
|
5810
|
+
return null;
|
|
5811
|
+
}
|
|
5812
|
+
const record = response.records[0];
|
|
5813
|
+
return {
|
|
5814
|
+
recordName: record.recordName,
|
|
5815
|
+
filename: record.fields.filename?.value ?? "",
|
|
5816
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5817
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5818
|
+
};
|
|
5819
|
+
}
|
|
5820
|
+
|
|
5821
|
+
// src/react/useICloudAuth.ts
|
|
5822
|
+
var ICloudAuthContext = createContext3(null);
|
|
5823
|
+
function ICloudAuthProvider({
|
|
5824
|
+
apiToken,
|
|
5825
|
+
containerIdentifier = DEFAULT_CONTAINER_ID,
|
|
5826
|
+
environment = "production",
|
|
5827
|
+
children
|
|
5828
|
+
}) {
|
|
5829
|
+
const [isAuthenticated, setIsAuthenticated] = useState12(false);
|
|
5830
|
+
const [userRecordName, setUserRecordName] = useState12(null);
|
|
5831
|
+
const [isAvailable, setIsAvailable] = useState12(false);
|
|
5832
|
+
const [isConfigured, setIsConfigured] = useState12(false);
|
|
5833
|
+
const [isLoading, setIsLoading] = useState12(false);
|
|
5834
|
+
useEffect8(() => {
|
|
5835
|
+
if (!apiToken || typeof window === "undefined") {
|
|
5836
|
+
return;
|
|
5837
|
+
}
|
|
5838
|
+
const initCloudKit = async () => {
|
|
5839
|
+
setIsLoading(true);
|
|
5840
|
+
try {
|
|
5841
|
+
await loadCloudKit();
|
|
5842
|
+
setIsAvailable(true);
|
|
5843
|
+
const config = {
|
|
5844
|
+
containerIdentifier,
|
|
5845
|
+
apiToken,
|
|
5846
|
+
environment
|
|
5847
|
+
};
|
|
5848
|
+
await configureCloudKit(config);
|
|
5849
|
+
setIsConfigured(true);
|
|
5850
|
+
try {
|
|
5851
|
+
const userIdentity = await authenticateICloud();
|
|
5852
|
+
if (userIdentity) {
|
|
5853
|
+
setIsAuthenticated(true);
|
|
5854
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5855
|
+
}
|
|
5856
|
+
} catch {
|
|
5857
|
+
}
|
|
5858
|
+
} catch {
|
|
5859
|
+
setIsAvailable(false);
|
|
5860
|
+
setIsConfigured(false);
|
|
5861
|
+
} finally {
|
|
5862
|
+
setIsLoading(false);
|
|
5863
|
+
}
|
|
5864
|
+
};
|
|
5865
|
+
initCloudKit();
|
|
5866
|
+
}, [apiToken, containerIdentifier, environment]);
|
|
5867
|
+
const requestAccess = useCallback14(async () => {
|
|
5868
|
+
if (!isConfigured) {
|
|
5869
|
+
throw new Error("iCloud is not configured");
|
|
5870
|
+
}
|
|
5871
|
+
if (isAuthenticated) {
|
|
5872
|
+
return;
|
|
5873
|
+
}
|
|
5874
|
+
try {
|
|
5875
|
+
const userIdentity = await requestICloudSignIn();
|
|
5876
|
+
setIsAuthenticated(true);
|
|
5877
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5878
|
+
} catch (err) {
|
|
5879
|
+
throw new Error(
|
|
5880
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
5881
|
+
);
|
|
5882
|
+
}
|
|
5883
|
+
}, [isAuthenticated, isConfigured]);
|
|
5884
|
+
const logout = useCallback14(() => {
|
|
5885
|
+
setIsAuthenticated(false);
|
|
5886
|
+
setUserRecordName(null);
|
|
5887
|
+
}, []);
|
|
5888
|
+
return createElement3(
|
|
5889
|
+
ICloudAuthContext.Provider,
|
|
5890
|
+
{
|
|
5891
|
+
value: {
|
|
5892
|
+
isAuthenticated,
|
|
5893
|
+
isConfigured,
|
|
5894
|
+
isAvailable,
|
|
5895
|
+
userRecordName,
|
|
5896
|
+
requestAccess,
|
|
5897
|
+
logout
|
|
5898
|
+
}
|
|
5899
|
+
},
|
|
5900
|
+
children
|
|
5901
|
+
);
|
|
5902
|
+
}
|
|
5903
|
+
function useICloudAuth() {
|
|
5904
|
+
const context = useContext3(ICloudAuthContext);
|
|
5905
|
+
if (!context) {
|
|
5906
|
+
throw new Error("useICloudAuth must be used within ICloudAuthProvider");
|
|
5907
|
+
}
|
|
5908
|
+
return context;
|
|
5909
|
+
}
|
|
5910
|
+
function hasICloudCredentials() {
|
|
5911
|
+
return isCloudKitAvailable();
|
|
5912
|
+
}
|
|
5913
|
+
function clearICloudAuth() {
|
|
5914
|
+
}
|
|
5915
|
+
|
|
5916
|
+
// src/react/useICloudBackup.ts
|
|
5917
|
+
import { useCallback as useCallback15, useMemo as useMemo6 } from "react";
|
|
5918
|
+
|
|
5919
|
+
// src/lib/backup/icloud/backup.ts
|
|
5920
|
+
var isAuthError3 = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
|
|
5921
|
+
async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
|
|
5922
|
+
try {
|
|
5923
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5924
|
+
const filename = `${conversationId}.json`;
|
|
5925
|
+
const existingFile = await findICloudFile(filename);
|
|
5926
|
+
if (existingFile) {
|
|
5927
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
5928
|
+
const conversationsCollection = database.get("conversations");
|
|
5929
|
+
const records = await conversationsCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
5930
|
+
if (records.length > 0) {
|
|
5931
|
+
const conversation = conversationToStored(records[0]);
|
|
5932
|
+
const localUpdated = conversation.updatedAt.getTime();
|
|
5933
|
+
const remoteModified = existingFile.modifiedAt.getTime();
|
|
5934
|
+
if (localUpdated <= remoteModified) {
|
|
5935
|
+
return "skipped";
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
const exportResult = await deps.exportConversation(
|
|
5940
|
+
conversationId,
|
|
5941
|
+
userAddress
|
|
5942
|
+
);
|
|
5943
|
+
if (!exportResult.success || !exportResult.blob) {
|
|
5944
|
+
return "failed";
|
|
5945
|
+
}
|
|
5946
|
+
await uploadFileToICloud(filename, exportResult.blob);
|
|
5947
|
+
return "uploaded";
|
|
5948
|
+
} catch (err) {
|
|
5949
|
+
if (isAuthError3(err) && !_retried) {
|
|
5950
|
+
try {
|
|
5951
|
+
await deps.requestICloudAccess();
|
|
5952
|
+
return pushConversationToICloud(
|
|
5953
|
+
database,
|
|
5954
|
+
conversationId,
|
|
5955
|
+
userAddress,
|
|
5956
|
+
deps,
|
|
5957
|
+
true
|
|
5958
|
+
);
|
|
5959
|
+
} catch {
|
|
5960
|
+
return "failed";
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
return "failed";
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5966
|
+
async function performICloudExport(database, userAddress, deps, onProgress) {
|
|
5967
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5968
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
5969
|
+
const conversationsCollection = database.get("conversations");
|
|
5970
|
+
const records = await conversationsCollection.query(Q4.where("is_deleted", false)).fetch();
|
|
5971
|
+
const conversations = records.map(conversationToStored);
|
|
5972
|
+
const total = conversations.length;
|
|
5973
|
+
if (total === 0) {
|
|
5974
|
+
return { success: true, uploaded: 0, skipped: 0, total: 0 };
|
|
5975
|
+
}
|
|
5976
|
+
let uploaded = 0;
|
|
5977
|
+
let skipped = 0;
|
|
5978
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
5979
|
+
const conv = conversations[i];
|
|
5980
|
+
onProgress?.(i + 1, total);
|
|
5981
|
+
const result = await pushConversationToICloud(
|
|
5982
|
+
database,
|
|
5983
|
+
conv.conversationId,
|
|
5984
|
+
userAddress,
|
|
5985
|
+
deps
|
|
5986
|
+
);
|
|
5987
|
+
if (result === "uploaded") uploaded++;
|
|
5988
|
+
if (result === "skipped") skipped++;
|
|
5989
|
+
}
|
|
5990
|
+
return { success: true, uploaded, skipped, total };
|
|
5991
|
+
}
|
|
5992
|
+
async function performICloudImport(userAddress, deps, onProgress) {
|
|
5993
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5994
|
+
const remoteFiles = await listICloudFiles();
|
|
5995
|
+
if (remoteFiles.length === 0) {
|
|
5996
|
+
return {
|
|
5997
|
+
success: false,
|
|
5998
|
+
restored: 0,
|
|
5999
|
+
failed: 0,
|
|
6000
|
+
total: 0,
|
|
6001
|
+
noBackupsFound: true
|
|
6002
|
+
};
|
|
6003
|
+
}
|
|
6004
|
+
const jsonFiles = remoteFiles.filter(
|
|
6005
|
+
(file) => file.filename.endsWith(".json")
|
|
6006
|
+
);
|
|
6007
|
+
const total = jsonFiles.length;
|
|
6008
|
+
let restored = 0;
|
|
6009
|
+
let failed = 0;
|
|
6010
|
+
for (let i = 0; i < jsonFiles.length; i++) {
|
|
6011
|
+
const file = jsonFiles[i];
|
|
6012
|
+
onProgress?.(i + 1, total);
|
|
6013
|
+
try {
|
|
6014
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6015
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6016
|
+
if (result.success) {
|
|
6017
|
+
restored++;
|
|
6018
|
+
} else {
|
|
6019
|
+
failed++;
|
|
6020
|
+
}
|
|
6021
|
+
} catch (err) {
|
|
6022
|
+
if (isAuthError3(err)) {
|
|
6023
|
+
try {
|
|
6024
|
+
await deps.requestICloudAccess();
|
|
6025
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6026
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6027
|
+
if (result.success) {
|
|
6028
|
+
restored++;
|
|
6029
|
+
} else {
|
|
6030
|
+
failed++;
|
|
6031
|
+
}
|
|
6032
|
+
} catch {
|
|
6033
|
+
failed++;
|
|
6034
|
+
}
|
|
6035
|
+
} else {
|
|
6036
|
+
failed++;
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
}
|
|
6040
|
+
return { success: true, restored, failed, total };
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
// src/react/useICloudBackup.ts
|
|
6044
|
+
function useICloudBackup(options) {
|
|
6045
|
+
const {
|
|
6046
|
+
database,
|
|
6047
|
+
userAddress,
|
|
6048
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6049
|
+
exportConversation,
|
|
6050
|
+
importConversation
|
|
6051
|
+
} = options;
|
|
6052
|
+
const {
|
|
6053
|
+
isAuthenticated,
|
|
6054
|
+
isConfigured,
|
|
6055
|
+
isAvailable,
|
|
6056
|
+
requestAccess
|
|
6057
|
+
} = useICloudAuth();
|
|
6058
|
+
const deps = useMemo6(
|
|
6059
|
+
() => ({
|
|
6060
|
+
requestICloudAccess: requestAccess,
|
|
6061
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6062
|
+
exportConversation,
|
|
6063
|
+
importConversation
|
|
6064
|
+
}),
|
|
6065
|
+
[requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6066
|
+
);
|
|
6067
|
+
const ensureAuthenticated = useCallback15(async () => {
|
|
6068
|
+
if (isAuthenticated) return true;
|
|
6069
|
+
try {
|
|
6070
|
+
await requestAccess();
|
|
6071
|
+
return true;
|
|
6072
|
+
} catch {
|
|
6073
|
+
return false;
|
|
6074
|
+
}
|
|
6075
|
+
}, [isAuthenticated, requestAccess]);
|
|
6076
|
+
const backup = useCallback15(
|
|
6077
|
+
async (backupOptions) => {
|
|
6078
|
+
if (!userAddress) {
|
|
6079
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6080
|
+
}
|
|
6081
|
+
if (!isAvailable) {
|
|
6082
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6083
|
+
}
|
|
6084
|
+
if (!isConfigured) {
|
|
6085
|
+
return { error: "iCloud is not configured" };
|
|
6086
|
+
}
|
|
6087
|
+
const authenticated = await ensureAuthenticated();
|
|
6088
|
+
if (!authenticated) {
|
|
6089
|
+
return { error: "iCloud access denied" };
|
|
6090
|
+
}
|
|
6091
|
+
try {
|
|
6092
|
+
return await performICloudExport(
|
|
6093
|
+
database,
|
|
6094
|
+
userAddress,
|
|
6095
|
+
deps,
|
|
6096
|
+
backupOptions?.onProgress
|
|
6097
|
+
);
|
|
6098
|
+
} catch (err) {
|
|
6099
|
+
return {
|
|
6100
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6101
|
+
};
|
|
6102
|
+
}
|
|
6103
|
+
},
|
|
6104
|
+
[database, userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6105
|
+
);
|
|
6106
|
+
const restore = useCallback15(
|
|
6107
|
+
async (restoreOptions) => {
|
|
6108
|
+
if (!userAddress) {
|
|
6109
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6110
|
+
}
|
|
6111
|
+
if (!isAvailable) {
|
|
6112
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6113
|
+
}
|
|
6114
|
+
if (!isConfigured) {
|
|
6115
|
+
return { error: "iCloud is not configured" };
|
|
6116
|
+
}
|
|
6117
|
+
const authenticated = await ensureAuthenticated();
|
|
6118
|
+
if (!authenticated) {
|
|
6119
|
+
return { error: "iCloud access denied" };
|
|
6120
|
+
}
|
|
6121
|
+
try {
|
|
6122
|
+
return await performICloudImport(
|
|
6123
|
+
userAddress,
|
|
6124
|
+
deps,
|
|
6125
|
+
restoreOptions?.onProgress
|
|
6126
|
+
);
|
|
6127
|
+
} catch (err) {
|
|
6128
|
+
return {
|
|
6129
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
},
|
|
6133
|
+
[userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6134
|
+
);
|
|
6135
|
+
return {
|
|
6136
|
+
backup,
|
|
6137
|
+
restore,
|
|
6138
|
+
isConfigured,
|
|
6139
|
+
isAuthenticated,
|
|
6140
|
+
isAvailable
|
|
6141
|
+
};
|
|
6142
|
+
}
|
|
6143
|
+
|
|
6144
|
+
// src/react/useBackupAuth.ts
|
|
6145
|
+
import {
|
|
6146
|
+
createContext as createContext4,
|
|
6147
|
+
createElement as createElement4,
|
|
6148
|
+
useCallback as useCallback16,
|
|
6149
|
+
useContext as useContext4,
|
|
6150
|
+
useEffect as useEffect9,
|
|
6151
|
+
useState as useState13
|
|
6152
|
+
} from "react";
|
|
6153
|
+
var BackupAuthContext = createContext4(null);
|
|
5588
6154
|
function BackupAuthProvider({
|
|
5589
6155
|
dropboxAppKey,
|
|
5590
6156
|
dropboxCallbackPath = "/auth/dropbox/callback",
|
|
5591
6157
|
googleClientId,
|
|
5592
6158
|
googleCallbackPath = "/auth/google/callback",
|
|
6159
|
+
icloudApiToken,
|
|
6160
|
+
icloudContainerIdentifier = DEFAULT_CONTAINER_ID,
|
|
6161
|
+
icloudEnvironment = "production",
|
|
5593
6162
|
apiClient,
|
|
5594
6163
|
children
|
|
5595
6164
|
}) {
|
|
5596
|
-
const [dropboxToken, setDropboxToken] =
|
|
6165
|
+
const [dropboxToken, setDropboxToken] = useState13(null);
|
|
5597
6166
|
const isDropboxConfigured = !!dropboxAppKey;
|
|
5598
|
-
const [googleToken, setGoogleToken] =
|
|
6167
|
+
const [googleToken, setGoogleToken] = useState13(null);
|
|
5599
6168
|
const isGoogleConfigured = !!googleClientId;
|
|
5600
|
-
|
|
6169
|
+
const [icloudAuthenticated, setIcloudAuthenticated] = useState13(false);
|
|
6170
|
+
const [icloudUserRecordName, setIcloudUserRecordName] = useState13(null);
|
|
6171
|
+
const [isIcloudAvailable, setIsIcloudAvailable] = useState13(false);
|
|
6172
|
+
const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
|
|
6173
|
+
useEffect9(() => {
|
|
5601
6174
|
const checkStoredTokens = async () => {
|
|
5602
6175
|
if (hasDropboxCredentials()) {
|
|
5603
6176
|
const token = await getDropboxAccessToken(apiClient);
|
|
@@ -5614,7 +6187,35 @@ function BackupAuthProvider({
|
|
|
5614
6187
|
};
|
|
5615
6188
|
checkStoredTokens();
|
|
5616
6189
|
}, [apiClient]);
|
|
5617
|
-
|
|
6190
|
+
useEffect9(() => {
|
|
6191
|
+
if (!icloudApiToken || typeof window === "undefined") {
|
|
6192
|
+
return;
|
|
6193
|
+
}
|
|
6194
|
+
const initCloudKit = async () => {
|
|
6195
|
+
try {
|
|
6196
|
+
await loadCloudKit();
|
|
6197
|
+
setIsIcloudAvailable(true);
|
|
6198
|
+
const config = {
|
|
6199
|
+
containerIdentifier: icloudContainerIdentifier,
|
|
6200
|
+
apiToken: icloudApiToken,
|
|
6201
|
+
environment: icloudEnvironment
|
|
6202
|
+
};
|
|
6203
|
+
await configureCloudKit(config);
|
|
6204
|
+
try {
|
|
6205
|
+
const userIdentity = await authenticateICloud();
|
|
6206
|
+
if (userIdentity) {
|
|
6207
|
+
setIcloudAuthenticated(true);
|
|
6208
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6209
|
+
}
|
|
6210
|
+
} catch {
|
|
6211
|
+
}
|
|
6212
|
+
} catch {
|
|
6213
|
+
setIsIcloudAvailable(false);
|
|
6214
|
+
}
|
|
6215
|
+
};
|
|
6216
|
+
initCloudKit();
|
|
6217
|
+
}, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
|
|
6218
|
+
useEffect9(() => {
|
|
5618
6219
|
if (!isDropboxConfigured) return;
|
|
5619
6220
|
const handleCallback = async () => {
|
|
5620
6221
|
if (isDropboxCallback()) {
|
|
@@ -5629,7 +6230,7 @@ function BackupAuthProvider({
|
|
|
5629
6230
|
};
|
|
5630
6231
|
handleCallback();
|
|
5631
6232
|
}, [dropboxCallbackPath, isDropboxConfigured, apiClient]);
|
|
5632
|
-
|
|
6233
|
+
useEffect9(() => {
|
|
5633
6234
|
if (!isGoogleConfigured) return;
|
|
5634
6235
|
const handleCallback = async () => {
|
|
5635
6236
|
if (isGoogleDriveCallback()) {
|
|
@@ -5644,14 +6245,14 @@ function BackupAuthProvider({
|
|
|
5644
6245
|
};
|
|
5645
6246
|
handleCallback();
|
|
5646
6247
|
}, [googleCallbackPath, isGoogleConfigured, apiClient]);
|
|
5647
|
-
const refreshDropboxTokenFn =
|
|
6248
|
+
const refreshDropboxTokenFn = useCallback16(async () => {
|
|
5648
6249
|
const token = await getDropboxAccessToken(apiClient);
|
|
5649
6250
|
if (token) {
|
|
5650
6251
|
setDropboxToken(token);
|
|
5651
6252
|
}
|
|
5652
6253
|
return token;
|
|
5653
6254
|
}, [apiClient]);
|
|
5654
|
-
const requestDropboxAccess =
|
|
6255
|
+
const requestDropboxAccess = useCallback16(async () => {
|
|
5655
6256
|
if (!isDropboxConfigured || !dropboxAppKey) {
|
|
5656
6257
|
throw new Error("Dropbox is not configured");
|
|
5657
6258
|
}
|
|
@@ -5671,18 +6272,18 @@ function BackupAuthProvider({
|
|
|
5671
6272
|
isDropboxConfigured,
|
|
5672
6273
|
apiClient
|
|
5673
6274
|
]);
|
|
5674
|
-
const logoutDropbox =
|
|
6275
|
+
const logoutDropbox = useCallback16(async () => {
|
|
5675
6276
|
await revokeDropboxToken(apiClient);
|
|
5676
6277
|
setDropboxToken(null);
|
|
5677
6278
|
}, [apiClient]);
|
|
5678
|
-
const refreshGoogleTokenFn =
|
|
6279
|
+
const refreshGoogleTokenFn = useCallback16(async () => {
|
|
5679
6280
|
const token = await getGoogleDriveAccessToken(apiClient);
|
|
5680
6281
|
if (token) {
|
|
5681
6282
|
setGoogleToken(token);
|
|
5682
6283
|
}
|
|
5683
6284
|
return token;
|
|
5684
6285
|
}, [apiClient]);
|
|
5685
|
-
const requestGoogleAccess =
|
|
6286
|
+
const requestGoogleAccess = useCallback16(async () => {
|
|
5686
6287
|
if (!isGoogleConfigured || !googleClientId) {
|
|
5687
6288
|
throw new Error("Google Drive is not configured");
|
|
5688
6289
|
}
|
|
@@ -5702,16 +6303,51 @@ function BackupAuthProvider({
|
|
|
5702
6303
|
isGoogleConfigured,
|
|
5703
6304
|
apiClient
|
|
5704
6305
|
]);
|
|
5705
|
-
const logoutGoogle =
|
|
6306
|
+
const logoutGoogle = useCallback16(async () => {
|
|
5706
6307
|
await revokeGoogleDriveToken(apiClient);
|
|
5707
6308
|
setGoogleToken(null);
|
|
5708
6309
|
}, [apiClient]);
|
|
5709
|
-
const
|
|
6310
|
+
const refreshIcloudTokenFn = useCallback16(async () => {
|
|
6311
|
+
try {
|
|
6312
|
+
const userIdentity = await authenticateICloud();
|
|
6313
|
+
if (userIdentity) {
|
|
6314
|
+
setIcloudAuthenticated(true);
|
|
6315
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6316
|
+
return userIdentity.userRecordName;
|
|
6317
|
+
}
|
|
6318
|
+
} catch {
|
|
6319
|
+
}
|
|
6320
|
+
return null;
|
|
6321
|
+
}, []);
|
|
6322
|
+
const requestIcloudAccess = useCallback16(async () => {
|
|
6323
|
+
if (!isIcloudConfigured) {
|
|
6324
|
+
throw new Error("iCloud is not configured");
|
|
6325
|
+
}
|
|
6326
|
+
if (icloudAuthenticated && icloudUserRecordName) {
|
|
6327
|
+
return icloudUserRecordName;
|
|
6328
|
+
}
|
|
6329
|
+
try {
|
|
6330
|
+
const userIdentity = await requestICloudSignIn();
|
|
6331
|
+
setIcloudAuthenticated(true);
|
|
6332
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6333
|
+
return userIdentity.userRecordName;
|
|
6334
|
+
} catch (err) {
|
|
6335
|
+
throw new Error(
|
|
6336
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
6337
|
+
);
|
|
6338
|
+
}
|
|
6339
|
+
}, [icloudAuthenticated, icloudUserRecordName, isIcloudConfigured]);
|
|
6340
|
+
const logoutIcloud = useCallback16(async () => {
|
|
6341
|
+
setIcloudAuthenticated(false);
|
|
6342
|
+
setIcloudUserRecordName(null);
|
|
6343
|
+
}, []);
|
|
6344
|
+
const logoutAll = useCallback16(async () => {
|
|
5710
6345
|
await Promise.all([
|
|
5711
6346
|
isDropboxConfigured ? logoutDropbox() : Promise.resolve(),
|
|
5712
|
-
isGoogleConfigured ? logoutGoogle() : Promise.resolve()
|
|
6347
|
+
isGoogleConfigured ? logoutGoogle() : Promise.resolve(),
|
|
6348
|
+
isIcloudConfigured ? logoutIcloud() : Promise.resolve()
|
|
5713
6349
|
]);
|
|
5714
|
-
}, [isDropboxConfigured, isGoogleConfigured, logoutDropbox, logoutGoogle]);
|
|
6350
|
+
}, [isDropboxConfigured, isGoogleConfigured, isIcloudConfigured, logoutDropbox, logoutGoogle, logoutIcloud]);
|
|
5715
6351
|
const dropboxState = {
|
|
5716
6352
|
accessToken: dropboxToken,
|
|
5717
6353
|
isAuthenticated: !!dropboxToken,
|
|
@@ -5728,14 +6364,24 @@ function BackupAuthProvider({
|
|
|
5728
6364
|
logout: logoutGoogle,
|
|
5729
6365
|
refreshToken: refreshGoogleTokenFn
|
|
5730
6366
|
};
|
|
5731
|
-
|
|
6367
|
+
const icloudState = {
|
|
6368
|
+
accessToken: icloudUserRecordName,
|
|
6369
|
+
// Use userRecordName as the "token" for iCloud
|
|
6370
|
+
isAuthenticated: icloudAuthenticated,
|
|
6371
|
+
isConfigured: isIcloudConfigured,
|
|
6372
|
+
requestAccess: requestIcloudAccess,
|
|
6373
|
+
logout: logoutIcloud,
|
|
6374
|
+
refreshToken: refreshIcloudTokenFn
|
|
6375
|
+
};
|
|
6376
|
+
return createElement4(
|
|
5732
6377
|
BackupAuthContext.Provider,
|
|
5733
6378
|
{
|
|
5734
6379
|
value: {
|
|
5735
6380
|
dropbox: dropboxState,
|
|
5736
6381
|
googleDrive: googleDriveState,
|
|
5737
|
-
|
|
5738
|
-
|
|
6382
|
+
icloud: icloudState,
|
|
6383
|
+
hasAnyProvider: isDropboxConfigured || isGoogleConfigured || isIcloudConfigured,
|
|
6384
|
+
hasAnyAuthentication: !!dropboxToken || !!googleToken || icloudAuthenticated,
|
|
5739
6385
|
logoutAll
|
|
5740
6386
|
}
|
|
5741
6387
|
},
|
|
@@ -5743,7 +6389,7 @@ function BackupAuthProvider({
|
|
|
5743
6389
|
);
|
|
5744
6390
|
}
|
|
5745
6391
|
function useBackupAuth() {
|
|
5746
|
-
const context =
|
|
6392
|
+
const context = useContext4(BackupAuthContext);
|
|
5747
6393
|
if (!context) {
|
|
5748
6394
|
throw new Error("useBackupAuth must be used within BackupAuthProvider");
|
|
5749
6395
|
}
|
|
@@ -5751,7 +6397,7 @@ function useBackupAuth() {
|
|
|
5751
6397
|
}
|
|
5752
6398
|
|
|
5753
6399
|
// src/react/useBackup.ts
|
|
5754
|
-
import { useCallback as
|
|
6400
|
+
import { useCallback as useCallback17, useMemo as useMemo7 } from "react";
|
|
5755
6401
|
function useBackup(options) {
|
|
5756
6402
|
const {
|
|
5757
6403
|
database,
|
|
@@ -5766,11 +6412,12 @@ function useBackup(options) {
|
|
|
5766
6412
|
const {
|
|
5767
6413
|
dropbox: dropboxAuth,
|
|
5768
6414
|
googleDrive: googleDriveAuth,
|
|
6415
|
+
icloud: icloudAuth,
|
|
5769
6416
|
hasAnyProvider,
|
|
5770
6417
|
hasAnyAuthentication,
|
|
5771
6418
|
logoutAll
|
|
5772
6419
|
} = useBackupAuth();
|
|
5773
|
-
const dropboxDeps =
|
|
6420
|
+
const dropboxDeps = useMemo7(
|
|
5774
6421
|
() => ({
|
|
5775
6422
|
requestDropboxAccess: dropboxAuth.requestAccess,
|
|
5776
6423
|
requestEncryptionKey: requestEncryptionKey2,
|
|
@@ -5779,7 +6426,7 @@ function useBackup(options) {
|
|
|
5779
6426
|
}),
|
|
5780
6427
|
[dropboxAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
5781
6428
|
);
|
|
5782
|
-
const googleDriveDeps =
|
|
6429
|
+
const googleDriveDeps = useMemo7(
|
|
5783
6430
|
() => ({
|
|
5784
6431
|
requestDriveAccess: googleDriveAuth.requestAccess,
|
|
5785
6432
|
requestEncryptionKey: requestEncryptionKey2,
|
|
@@ -5788,7 +6435,18 @@ function useBackup(options) {
|
|
|
5788
6435
|
}),
|
|
5789
6436
|
[googleDriveAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
5790
6437
|
);
|
|
5791
|
-
const
|
|
6438
|
+
const icloudDeps = useMemo7(
|
|
6439
|
+
() => ({
|
|
6440
|
+
requestICloudAccess: async () => {
|
|
6441
|
+
await icloudAuth.requestAccess();
|
|
6442
|
+
},
|
|
6443
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6444
|
+
exportConversation,
|
|
6445
|
+
importConversation
|
|
6446
|
+
}),
|
|
6447
|
+
[icloudAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6448
|
+
);
|
|
6449
|
+
const dropboxBackup = useCallback17(
|
|
5792
6450
|
async (backupOptions) => {
|
|
5793
6451
|
if (!userAddress) {
|
|
5794
6452
|
return { error: "Please sign in to backup to Dropbox" };
|
|
@@ -5818,7 +6476,7 @@ function useBackup(options) {
|
|
|
5818
6476
|
},
|
|
5819
6477
|
[database, userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
5820
6478
|
);
|
|
5821
|
-
const dropboxRestore =
|
|
6479
|
+
const dropboxRestore = useCallback17(
|
|
5822
6480
|
async (restoreOptions) => {
|
|
5823
6481
|
if (!userAddress) {
|
|
5824
6482
|
return { error: "Please sign in to restore from Dropbox" };
|
|
@@ -5847,7 +6505,7 @@ function useBackup(options) {
|
|
|
5847
6505
|
},
|
|
5848
6506
|
[userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
5849
6507
|
);
|
|
5850
|
-
const googleDriveBackup =
|
|
6508
|
+
const googleDriveBackup = useCallback17(
|
|
5851
6509
|
async (backupOptions) => {
|
|
5852
6510
|
if (!userAddress) {
|
|
5853
6511
|
return { error: "Please sign in to backup to Google Drive" };
|
|
@@ -5885,7 +6543,7 @@ function useBackup(options) {
|
|
|
5885
6543
|
googleConversationsFolder
|
|
5886
6544
|
]
|
|
5887
6545
|
);
|
|
5888
|
-
const googleDriveRestore =
|
|
6546
|
+
const googleDriveRestore = useCallback17(
|
|
5889
6547
|
async (restoreOptions) => {
|
|
5890
6548
|
if (!userAddress) {
|
|
5891
6549
|
return { error: "Please sign in to restore from Google Drive" };
|
|
@@ -5937,9 +6595,77 @@ function useBackup(options) {
|
|
|
5937
6595
|
connect: googleDriveAuth.requestAccess,
|
|
5938
6596
|
disconnect: googleDriveAuth.logout
|
|
5939
6597
|
};
|
|
6598
|
+
const icloudBackup = useCallback17(
|
|
6599
|
+
async (backupOptions) => {
|
|
6600
|
+
if (!userAddress) {
|
|
6601
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6602
|
+
}
|
|
6603
|
+
if (!icloudAuth.isConfigured) {
|
|
6604
|
+
return { error: "iCloud is not configured" };
|
|
6605
|
+
}
|
|
6606
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6607
|
+
try {
|
|
6608
|
+
await icloudAuth.requestAccess();
|
|
6609
|
+
} catch {
|
|
6610
|
+
return { error: "iCloud access denied" };
|
|
6611
|
+
}
|
|
6612
|
+
}
|
|
6613
|
+
try {
|
|
6614
|
+
return await performICloudExport(
|
|
6615
|
+
database,
|
|
6616
|
+
userAddress,
|
|
6617
|
+
icloudDeps,
|
|
6618
|
+
backupOptions?.onProgress
|
|
6619
|
+
);
|
|
6620
|
+
} catch (err) {
|
|
6621
|
+
return {
|
|
6622
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6623
|
+
};
|
|
6624
|
+
}
|
|
6625
|
+
},
|
|
6626
|
+
[database, userAddress, icloudAuth, icloudDeps]
|
|
6627
|
+
);
|
|
6628
|
+
const icloudRestore = useCallback17(
|
|
6629
|
+
async (restoreOptions) => {
|
|
6630
|
+
if (!userAddress) {
|
|
6631
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6632
|
+
}
|
|
6633
|
+
if (!icloudAuth.isConfigured) {
|
|
6634
|
+
return { error: "iCloud is not configured" };
|
|
6635
|
+
}
|
|
6636
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6637
|
+
try {
|
|
6638
|
+
await icloudAuth.requestAccess();
|
|
6639
|
+
} catch {
|
|
6640
|
+
return { error: "iCloud access denied" };
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
try {
|
|
6644
|
+
return await performICloudImport(
|
|
6645
|
+
userAddress,
|
|
6646
|
+
icloudDeps,
|
|
6647
|
+
restoreOptions?.onProgress
|
|
6648
|
+
);
|
|
6649
|
+
} catch (err) {
|
|
6650
|
+
return {
|
|
6651
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6652
|
+
};
|
|
6653
|
+
}
|
|
6654
|
+
},
|
|
6655
|
+
[userAddress, icloudAuth, icloudDeps]
|
|
6656
|
+
);
|
|
6657
|
+
const icloudState = {
|
|
6658
|
+
isConfigured: icloudAuth.isConfigured,
|
|
6659
|
+
isAuthenticated: icloudAuth.isAuthenticated,
|
|
6660
|
+
backup: icloudBackup,
|
|
6661
|
+
restore: icloudRestore,
|
|
6662
|
+
connect: icloudAuth.requestAccess,
|
|
6663
|
+
disconnect: icloudAuth.logout
|
|
6664
|
+
};
|
|
5940
6665
|
return {
|
|
5941
6666
|
dropbox: dropboxState,
|
|
5942
6667
|
googleDrive: googleDriveState,
|
|
6668
|
+
icloud: icloudState,
|
|
5943
6669
|
hasAnyProvider,
|
|
5944
6670
|
hasAnyAuthentication,
|
|
5945
6671
|
disconnectAll: logoutAll
|
|
@@ -5948,6 +6674,7 @@ function useBackup(options) {
|
|
|
5948
6674
|
export {
|
|
5949
6675
|
DEFAULT_CONVERSATIONS_FOLDER as BACKUP_DRIVE_CONVERSATIONS_FOLDER,
|
|
5950
6676
|
DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER,
|
|
6677
|
+
DEFAULT_BACKUP_FOLDER2 as BACKUP_ICLOUD_FOLDER,
|
|
5951
6678
|
BackupAuthProvider,
|
|
5952
6679
|
Conversation as ChatConversation,
|
|
5953
6680
|
Message as ChatMessage,
|
|
@@ -5955,15 +6682,18 @@ export {
|
|
|
5955
6682
|
DEFAULT_CONVERSATIONS_FOLDER as DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
|
|
5956
6683
|
DEFAULT_ROOT_FOLDER as DEFAULT_DRIVE_ROOT_FOLDER,
|
|
5957
6684
|
DEFAULT_BACKUP_FOLDER as DEFAULT_DROPBOX_FOLDER,
|
|
6685
|
+
DEFAULT_BACKUP_FOLDER2 as DEFAULT_ICLOUD_BACKUP_FOLDER,
|
|
5958
6686
|
DEFAULT_TOOL_SELECTOR_MODEL,
|
|
5959
6687
|
DropboxAuthProvider,
|
|
5960
6688
|
GoogleDriveAuthProvider,
|
|
6689
|
+
ICloudAuthProvider,
|
|
5961
6690
|
Memory as StoredMemoryModel,
|
|
5962
6691
|
ModelPreference as StoredModelPreferenceModel,
|
|
5963
6692
|
chatStorageMigrations,
|
|
5964
6693
|
chatStorageSchema,
|
|
5965
6694
|
clearToken as clearDropboxToken,
|
|
5966
6695
|
clearGoogleDriveToken,
|
|
6696
|
+
clearICloudAuth,
|
|
5967
6697
|
createMemoryContextSystemMessage,
|
|
5968
6698
|
decryptData,
|
|
5969
6699
|
decryptDataBytes,
|
|
@@ -5978,6 +6708,7 @@ export {
|
|
|
5978
6708
|
hasDropboxCredentials,
|
|
5979
6709
|
hasEncryptionKey,
|
|
5980
6710
|
hasGoogleDriveCredentials,
|
|
6711
|
+
hasICloudCredentials,
|
|
5981
6712
|
memoryStorageSchema,
|
|
5982
6713
|
requestEncryptionKey,
|
|
5983
6714
|
sdkMigrations,
|
|
@@ -5994,6 +6725,8 @@ export {
|
|
|
5994
6725
|
useEncryption,
|
|
5995
6726
|
useGoogleDriveAuth,
|
|
5996
6727
|
useGoogleDriveBackup,
|
|
6728
|
+
useICloudAuth,
|
|
6729
|
+
useICloudBackup,
|
|
5997
6730
|
useImageGeneration,
|
|
5998
6731
|
useMemoryStorage,
|
|
5999
6732
|
useModels,
|