From ad6fb66e87299b7c617da7f36ea44893ddbed9be Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 5 Jun 2026 11:15:33 -0700 Subject: [PATCH 1/2] When adding a new X account, show a modal asking about archive-only mode --- src/renderer/src/i18n/locales/en.json | 11 +- src/renderer/src/modals/XAccountModeModal.vue | 100 ++++++++++++++++++ src/renderer/src/views/PlatformView.vue | 17 --- src/renderer/src/views/x/XView.vue | 44 ++++++-- 4 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 src/renderer/src/modals/XAccountModeModal.vue diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index d7ee5136..c1325058 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -123,6 +123,14 @@ "failedToRegisterNewDevice": "Failed to register new device. Please try again later." } }, + "modals": { + "xAccountMode": { + "title": "Do you still have access to your X account?", + "description": "If you can't login to your X account (you deleted it, or you were permanently suspended), but you have an X archive, add this account in Archive Only mode. You won't be able to delete data in this mode, but you can migrate your data to Bluesky.", + "loginButton": "I Can Login to X", + "archiveOnlyButton": "I Can't Login, Use Archive Only Mode" + } + }, "errorReport": { "title": "Submit an error report", "errorsOccurred": "{count} errors occured", @@ -346,8 +354,7 @@ "platform": { "u2fNotice": "If you use a U2F security key (like a Yubikey) for 2FA, press it when you see a white screen.", "u2fNoticeFacebook": "If you use a U2F security key (like a Yubikey) for 2FA, press it when prompted.", - "readMore": "Read more", - "importArchiveOnly": "Import Archive Only (for deleted accounts with an archive)" + "readMore": "Read more" }, "finished": { "viewBlueskyProfile": "View Bluesky Profile", diff --git a/src/renderer/src/modals/XAccountModeModal.vue b/src/renderer/src/modals/XAccountModeModal.vue new file mode 100644 index 00000000..d0d864cd --- /dev/null +++ b/src/renderer/src/modals/XAccountModeModal.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/renderer/src/views/PlatformView.vue b/src/renderer/src/views/PlatformView.vue index c9c4ef7d..bd884614 100644 --- a/src/renderer/src/views/PlatformView.vue +++ b/src/renderer/src/views/PlatformView.vue @@ -76,9 +76,6 @@ const emit = defineEmits<{ finishedRunAgainClicked: []; updateUserPremium: []; - // Archive only (conditional on feature flag) - archiveOnlyClicked: []; - // Job control events onPause: []; onResume: []; @@ -181,20 +178,6 @@ const currentJobsLength = computed(() => props.currentJobs.length); >.

- -
- - - -
- diff --git a/src/renderer/src/views/x/XView.vue b/src/renderer/src/views/x/XView.vue index 3b09d765..381edbb3 100644 --- a/src/renderer/src/views/x/XView.vue +++ b/src/renderer/src/views/x/XView.vue @@ -5,7 +5,7 @@ * Thin wrapper around PlatformView that handles X-specific logic: * - XViewModel instantiation * - X-specific state (rateLimitInfo, failure states, mediaPath) - * - X-specific methods (archiveOnlyClicked, startJobs, etc.) + * - X-specific methods (startJobs, etc.) * - X-specific event handlers */ @@ -49,6 +49,7 @@ import { usePlatformView } from "../../composables/usePlatformView"; import { getPlatformConfig } from "../../config/platforms"; import PlatformView from "../PlatformView.vue"; import XProgressComponent from "./components/XProgressComponent.vue"; +import XAccountModeModal from "../../modals/XAccountModeModal.vue"; // Get the global emitter const vueInstance = getCurrentInstance(); @@ -68,6 +69,7 @@ const failureStateIndexTweets_FailedToRetryAfterRateLimit = ref(false); const failureStateIndexLikes_FailedToRetryAfterRateLimit = ref(false); const rateLimitInfo = ref(null); const mediaPath = ref(""); +const showAccountModeModal = ref(false); // The X view model const model = ref(new XViewModel(props.account, emitter)); @@ -141,11 +143,6 @@ watch( ); // X-specific methods -const archiveOnlyClicked = async () => { - model.value.cancelWaitForURL = true; - await setState(State.WizardArchiveOnly.toString()); -}; - const onAutomationErrorRetry = async () => { console.log("Retrying automation after error"); @@ -322,6 +319,25 @@ const debugModeDisable = async () => { model.value.state = State.WizardPrestart; }; +// Account mode modal handlers +const handleAccountModeModalLogin = async () => { + showAccountModeModal.value = false; + model.value.state = State.Login; + await startStateLoop(); +}; + +const handleAccountModeModalArchiveOnly = async () => { + showAccountModeModal.value = false; + await window.electron.X.initArchiveOnlyMode(props.account.id); + await updateAccount(); + model.value.state = State.WizardArchiveOnly; + await startStateLoop(); +}; + +const handleAccountModeModalHide = () => { + showAccountModeModal.value = false; +}; + // Lifecycle onMounted(async () => { setupAuthListeners(); @@ -356,7 +372,12 @@ onMounted(async () => { localStorage.removeItem(`account-${props.account.id}-state`); } - await startStateLoop(); + // Check if we need to show the account mode modal (new account with no username) + if (!props.account.xAccount.username && !savedState) { + showAccountModeModal.value = true; + } else { + await startStateLoop(); + } } } else { console.error("Webview component not found"); @@ -415,7 +436,6 @@ onUnmounted(async () => { @start-jobs-just-save="startJobsJustSave" @finished-run-again-clicked="finishedRunAgainClicked" @update-user-premium="updateUserPremium" - @archive-only-clicked="archiveOnlyClicked" @on-pause="model.pause()" @on-resume="model.resume()" @on-cancel="emit('onRefreshClicked')" @@ -456,4 +476,12 @@ onUnmounted(async () => { + + + From 52cd327bdfd97f41d99fc26e594f5174c91da42d Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 5 Jun 2026 11:20:09 -0700 Subject: [PATCH 2/2] While showing the X account mode modal, hide the background --- src/renderer/src/views/x/XView.vue | 41 +++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/renderer/src/views/x/XView.vue b/src/renderer/src/views/x/XView.vue index 381edbb3..0f5080fb 100644 --- a/src/renderer/src/views/x/XView.vue +++ b/src/renderer/src/views/x/XView.vue @@ -322,12 +322,34 @@ const debugModeDisable = async () => { // Account mode modal handlers const handleAccountModeModalLogin = async () => { showAccountModeModal.value = false; + await nextTick(); // Wait for PlatformView to render + + // Initialize the platform view now that it's rendered + if ( + platformViewRef.value?.webviewComponent !== null && + platformViewRef.value?.webviewComponent !== undefined + ) { + const webview = platformViewRef.value.webviewComponent as WebviewTag; + await initializePlatformView(webview); + } + model.value.state = State.Login; await startStateLoop(); }; const handleAccountModeModalArchiveOnly = async () => { showAccountModeModal.value = false; + await nextTick(); // Wait for PlatformView to render + + // Initialize the platform view now that it's rendered + if ( + platformViewRef.value?.webviewComponent !== null && + platformViewRef.value?.webviewComponent !== undefined + ) { + const webview = platformViewRef.value.webviewComponent as WebviewTag; + await initializePlatformView(webview); + } + await window.electron.X.initArchiveOnlyMode(props.account.id); await updateAccount(); model.value.state = State.WizardArchiveOnly; @@ -348,7 +370,16 @@ onMounted(async () => { // Wait for child components to mount await nextTick(); - if ( + // Check if we need to show the account mode modal (new account with no username) + const shouldShowModal = + props.account.xAccount !== null && + !props.account.xAccount.username && + !localStorage.getItem(`account-${props.account.id}-state`); + + if (shouldShowModal) { + showAccountModeModal.value = true; + // Don't initialize platform view yet - it will be done in the modal handlers + } else if ( platformViewRef.value?.webviewComponent !== null && platformViewRef.value?.webviewComponent !== undefined ) { @@ -372,12 +403,7 @@ onMounted(async () => { localStorage.removeItem(`account-${props.account.id}-state`); } - // Check if we need to show the account mode modal (new account with no username) - if (!props.account.xAccount.username && !savedState) { - showAccountModeModal.value = true; - } else { - await startStateLoop(); - } + await startStateLoop(); } } else { console.error("Webview component not found"); @@ -406,6 +432,7 @@ onUnmounted(async () => {