objective categorical abstract machine language personal data server
65
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add import repo button to account page

futurGH 5b6f6e7a fb3a1dc5

+95 -10
-1
bin/main.ml
··· 222 222 [ Dream.logger 223 223 ; Dream.set_secret (Env.jwt_key |> Kleidos.privkey_to_multikey) 224 224 ; Dream.cookie_sessions 225 - ; Dream.livereload 226 225 ; Xrpc.dpop_middleware 227 226 ; Xrpc.cors_middleware ] 228 227 @@ Dream.router
+95 -9
frontend/src/templates/AccountPage.mlx
··· 78 78 let confirmEmailLoading, setConfirmEmailLoading = useState (fun () -> false) in 79 79 let confirmEmailTokenInput, setConfirmEmailTokenInput = useState (fun () -> "") in 80 80 let successMessage, setSuccessMessage = useState (fun () -> success) in 81 + let importLoading, setImportLoading = useState (fun () -> false) in 82 + let importError, setImportError = useState (fun () -> None) in 83 + let importSuccess, setImportSuccess = useState (fun () -> false) in 84 + let fileInputRef : Dom.element Js.nullable React.ref = useRef Js.Nullable.null in 81 85 <div className="w-auto h-full px-4 sm:px-0 pt-16 mx-auto flex flex-col md:flex-row gap-12"> 82 86 <AccountSidebar 83 87 current_user logged_in_users active_page="/account" ··· 499 503 </form> 500 504 <section className="mt-8"> 501 505 <h2 className="text-xl font-serif text-mana-200 mb-1"> 502 - (string "download repository") 506 + (string "my repository") 503 507 </h2> 504 508 <p className="text-mist-100 mb-4"> 505 509 (string 506 - "Export your data to back up or transfer to another PDS." ) 510 + "Export your data to back up or transfer to another PDS, or import from a CAR backup.") 507 511 </p> 508 - <a 509 - href=("/xrpc/com.atproto.sync.getRepo?did=" ^ current_user.did) 510 - download=("repo-" ^ current_user.handle ^ ".car")> 511 - <Button kind=`Primary className="max-w-1/2"> 512 - (string "download") 513 - </Button> 514 - </a> 512 + <div className="flex flex-row gap-x-3"> 513 + <a 514 + href=("/xrpc/com.atproto.sync.getRepo?did=" ^ current_user.did) 515 + download=("repo-" ^ current_user.handle ^ ".car") 516 + className="basis-1/2 grow"> 517 + <Button kind=`Primary> 518 + (string "download") 519 + </Button> 520 + </a> 521 + <ClientOnly fallback=( 522 + <Button kind=`Secondary disabled=true className="basis-1/2 grow"> 523 + (string "import") 524 + </Button> 525 + )> 526 + [%browser_only 527 + (fun () -> 528 + let handleFileChange e = 529 + let files = (Event.Form.target e)##files in 530 + if Js.Array.length files > 0 then begin 531 + let file = Js.Array.unsafe_get files 0 in 532 + setImportLoading (fun _ -> true) ; 533 + setImportError (fun _ -> None) ; 534 + setImportSuccess (fun _ -> false) ; 535 + let _ = 536 + Fetch.fetchWithInit "/xrpc/com.atproto.repo.importRepo" 537 + (Fetch.RequestInit.make ~method_:Post 538 + ~body:(Fetch.BodyInit.makeWithBlob file) 539 + ~headers: 540 + (Fetch.HeadersInit.makeWithArray 541 + [|("Content-Type", "application/vnd.ipld.car")|] ) 542 + () ) 543 + |> Js.Promise.then_ (fun response -> 544 + setImportLoading (fun _ -> false) ; 545 + if Fetch.Response.ok response then begin 546 + setImportSuccess (fun _ -> true) ; 547 + Js.Promise.resolve () 548 + end else begin 549 + setImportError (fun _ -> 550 + Some "Import failed. Please try again." ) ; 551 + Js.Promise.resolve () 552 + end ) 553 + |> Js.Promise.catch (fun _ -> 554 + setImportLoading (fun _ -> false) ; 555 + setImportError (fun _ -> 556 + Some "An error occurred. Please try again." ) ; 557 + Js.Promise.resolve () ) 558 + in 559 + () 560 + end 561 + in 562 + <div className="basis-1/2 grow"> 563 + <input 564 + type_="file" 565 + accept=".car,application/vnd.ipld.car" 566 + ref=(ReactDOM.Ref.domRef fileInputRef) 567 + onChange=handleFileChange 568 + className="hidden" 569 + /> 570 + <Button 571 + kind=`Secondary 572 + disabled=importLoading 573 + onClick=(fun _ -> 574 + match Js.Nullable.toOption fileInputRef.current with 575 + | Some el -> 576 + let input = Webapi.Dom.Element.unsafeAsHtmlElement el in 577 + Webapi.Dom.HtmlElement.click input 578 + | None -> () )> 579 + (string (if importLoading then "importing..." else "import")) 580 + </Button> 581 + </div> )] 582 + </ClientOnly> 583 + </div> 584 + ( match importError with 585 + | Some err -> 586 + <span className="inline-flex items-center text-phoenix-100 text-sm mt-4"> 587 + <CircleAlertIcon className="w-4 h-4 mr-2" /> 588 + (string err) 589 + </span> 590 + | None when importSuccess -> 591 + <span className="inline-flex items-center text-mana-100 text-sm mt-4"> 592 + (string "Repository imported successfully.") 593 + </span> 594 + | None -> 595 + <p className="text-mist-80 text-sm mt-4"> 596 + (string "It is recommended to export a copy of your \ 597 + repository before importing a backup. \ 598 + The import process will overwrite any existing \ 599 + data with the contents of the backup.") 600 + </p> ) 515 601 </section> 516 602 <section className="mt-8"> 517 603 <h2 className="text-xl font-serif text-mana-200 mb-1">