Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/renderer/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
100 changes: 100 additions & 0 deletions src/renderer/src/modals/XAccountModeModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";
import Modal from "bootstrap/js/dist/modal";

const emit = defineEmits<{
hide: [];
chooseLogin: [];
chooseArchiveOnly: [];
}>();

const hide = () => {
emit("hide");
};

const xAccountModeModal = ref<HTMLElement | null>(null);
let modalInstance: Modal | null = null;

const loginClicked = () => {
emit("chooseLogin");
if (modalInstance) {
modalInstance.hide();
}
};

const archiveOnlyClicked = () => {
emit("chooseArchiveOnly");
if (modalInstance) {
modalInstance.hide();
}
};

onMounted(async () => {
const modalElement = xAccountModeModal.value;
if (modalElement) {
modalInstance = new Modal(modalElement, {
backdrop: "static",
keyboard: false,
});
modalInstance.show();

// The 'hidden.bs.modal' event is triggered when the modal is hidden
modalElement.addEventListener("hidden.bs.modal", () => {
hide();
});
}
});

onUnmounted(() => {
if (xAccountModeModal.value && modalInstance) {
xAccountModeModal.value.removeEventListener("hidden.bs.modal", hide);
}
});
</script>

<template>
<div
id="xAccountModeModal"
ref="xAccountModeModal"
class="modal fade"
role="dialog"
aria-labelledby="xAccountModeModalLabel"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
{{ $t("modals.xAccountMode.title") }}
</h4>
</div>
<div class="modal-body">
<p>{{ $t("modals.xAccountMode.description") }}</p>

<div class="d-grid gap-3 mt-4">
<button class="btn btn-primary btn-lg" @click="loginClicked">
{{ $t("modals.xAccountMode.loginButton") }}
</button>
<button
class="btn btn-secondary btn-lg"
@click="archiveOnlyClicked"
>
{{ $t("modals.xAccountMode.archiveOnlyButton") }}
</button>
</div>
</div>
</div>
</div>
</div>
</template>

<style scoped>
.modal-body p {
margin-bottom: 0.75rem;
}

.btn-lg {
padding: 0.75rem 1.5rem;
font-size: 1rem;
}
</style>
17 changes: 0 additions & 17 deletions src/renderer/src/views/PlatformView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ const emit = defineEmits<{
finishedRunAgainClicked: [];
updateUserPremium: [];

// Archive only (conditional on feature flag)
archiveOnlyClicked: [];

// Job control events
onPause: [];
onResume: [];
Expand Down Expand Up @@ -181,20 +178,6 @@ const currentJobsLength = computed(() => props.currentJobs.length);
><span v-if="config.urls.u2fDocs">.</span>
</p>

<!-- Archive only option (conditional on feature flag) -->
<div
v-if="
config.features.hasArchiveOnly && modelState == PlatformStates.Login
"
class="text-center ms-2 mt-2 mb-4"
>
<slot name="archive-only-button">
<button class="btn btn-secondary" @click="emit('archiveOnlyClicked')">
{{ t("platform.importArchiveOnly") }}
</button>
</slot>
</div>

<AutomationNotice v-bind="automationNoticeProps" />
</template>

Expand Down
71 changes: 63 additions & 8 deletions src/renderer/src/views/x/XView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

Expand Down Expand Up @@ -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();
Expand All @@ -68,6 +69,7 @@ const failureStateIndexTweets_FailedToRetryAfterRateLimit = ref(false);
const failureStateIndexLikes_FailedToRetryAfterRateLimit = ref(false);
const rateLimitInfo = ref<XRateLimitInfo | null>(null);
const mediaPath = ref("");
const showAccountModeModal = ref(false);

// The X view model
const model = ref<XViewModel>(new XViewModel(props.account, emitter));
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -322,6 +319,47 @@ const debugModeDisable = async () => {
model.value.state = State.WizardPrestart;
};

// 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;
await startStateLoop();
};

const handleAccountModeModalHide = () => {
showAccountModeModal.value = false;
};

// Lifecycle
onMounted(async () => {
setupAuthListeners();
Expand All @@ -332,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
) {
Expand Down Expand Up @@ -385,6 +432,7 @@ onUnmounted(async () => {

<template>
<PlatformView
v-if="!showAccountModeModal"
ref="platformViewRef"
:account="account"
:config="config"
Expand Down Expand Up @@ -415,7 +463,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')"
Expand Down Expand Up @@ -456,4 +503,12 @@ onUnmounted(async () => {
</div>
</template>
</PlatformView>

<!-- Account mode modal for new X accounts -->
<XAccountModeModal
v-if="showAccountModeModal"
@choose-login="handleAccountModeModalLogin"
@choose-archive-only="handleAccountModeModalArchiveOnly"
@hide="handleAccountModeModalHide"
/>
</template>
Loading