mirror of
https://github.com/Pandipipas/scoreko-dev.git
synced 2026-06-06 03:32:06 +00:00
fix(players): improve Challonge auth UX and manual token flow
This commit is contained in:
@@ -128,6 +128,8 @@ const oauthLoading = ref(false);
|
|||||||
const challongeOauthLoading = ref(false);
|
const challongeOauthLoading = ref(false);
|
||||||
const isManualTokenDialogOpen = ref(false);
|
const isManualTokenDialogOpen = ref(false);
|
||||||
const manualTokenDraft = ref('');
|
const manualTokenDraft = ref('');
|
||||||
|
const isChallongeManualTokenDialogOpen = ref(false);
|
||||||
|
const challongeManualTokenDraft = ref('');
|
||||||
const oauthSessionId = ref('');
|
const oauthSessionId = ref('');
|
||||||
const challongeOauthSessionId = ref('');
|
const challongeOauthSessionId = ref('');
|
||||||
let oauthPollingTimer: ReturnType<typeof setInterval> | null = null;
|
let oauthPollingTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
@@ -143,6 +145,7 @@ const selectedChallongePlayerIds = ref<string[]>([]);
|
|||||||
const challongeImportDialogOpen = ref(false);
|
const challongeImportDialogOpen = ref(false);
|
||||||
const loadingChallongeTournamentPlayers = ref(false);
|
const loadingChallongeTournamentPlayers = ref(false);
|
||||||
const importingChallongeTournament = ref<ChallongeTournament | null>(null);
|
const importingChallongeTournament = ref<ChallongeTournament | null>(null);
|
||||||
|
const hasValidatedChallongeToken = ref(false);
|
||||||
|
|
||||||
interface OAuthSessionResponse {
|
interface OAuthSessionResponse {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
@@ -161,6 +164,7 @@ watch(startGGToken, (value) => {
|
|||||||
|
|
||||||
watch(challongeToken, (value) => {
|
watch(challongeToken, (value) => {
|
||||||
localStorage.setItem(CHALLONGE_TOKEN_STORAGE_KEY, value);
|
localStorage.setItem(CHALLONGE_TOKEN_STORAGE_KEY, value);
|
||||||
|
hasValidatedChallongeToken.value = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const persistTemporaryStartGGPlayers = () => {
|
const persistTemporaryStartGGPlayers = () => {
|
||||||
@@ -463,6 +467,8 @@ const selectedChallongeTournamentOption = computed(() =>
|
|||||||
const canImportSelectedChallongeTournament = computed(() => Boolean(selectedChallongeTournamentOption.value));
|
const canImportSelectedChallongeTournament = computed(() => Boolean(selectedChallongeTournamentOption.value));
|
||||||
const hasChallongeTokenConfigured = computed(() => Boolean(challongeToken.value.trim()));
|
const hasChallongeTokenConfigured = computed(() => Boolean(challongeToken.value.trim()));
|
||||||
|
|
||||||
|
const challongeConnectionLabel = computed(() => (hasValidatedChallongeToken.value ? 'Connected' : 'Token set'));
|
||||||
|
|
||||||
const filterChallongeTournaments = (value: string, update: (callback: () => void) => void) => {
|
const filterChallongeTournaments = (value: string, update: (callback: () => void) => void) => {
|
||||||
update(() => {
|
update(() => {
|
||||||
const needle = value.toLowerCase().trim();
|
const needle = value.toLowerCase().trim();
|
||||||
@@ -491,18 +497,41 @@ const loadChallongeRecentTournaments = async () => {
|
|||||||
const tournaments = await sendNodeCGMessage<ChallongeTournament[]>('challonge:fetchRecentTournaments', {
|
const tournaments = await sendNodeCGMessage<ChallongeTournament[]>('challonge:fetchRecentTournaments', {
|
||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
|
hasValidatedChallongeToken.value = true;
|
||||||
challongeRecentTournaments.value = tournaments;
|
challongeRecentTournaments.value = tournaments;
|
||||||
if (!tournaments.length) {
|
if (!tournaments.length) {
|
||||||
challongeTournamentsError.value = 'There are no recent tournaments for this account.';
|
challongeTournamentsError.value = 'There are no recent tournaments for this account.';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
challongeTournamentsError.value = error instanceof Error ? error.message : 'Could not load tournaments.';
|
hasValidatedChallongeToken.value = false;
|
||||||
|
const message = error instanceof Error ? error.message : 'Could not load tournaments.';
|
||||||
|
challongeTournamentsError.value = message.includes('401')
|
||||||
|
? 'Challonge rejected the token (401 Unauthorized). Verify OAuth callback/client credentials or paste a valid personal API token.'
|
||||||
|
: message;
|
||||||
challongeRecentTournaments.value = [];
|
challongeRecentTournaments.value = [];
|
||||||
} finally {
|
} finally {
|
||||||
challongeLoadingTournaments.value = false;
|
challongeLoadingTournaments.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openChallongeManualTokenDialog = () => {
|
||||||
|
challongeManualTokenDraft.value = challongeToken.value;
|
||||||
|
isChallongeManualTokenDialogOpen.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveChallongeManualToken = () => {
|
||||||
|
challongeToken.value = challongeManualTokenDraft.value.trim();
|
||||||
|
|
||||||
|
if (!challongeToken.value) {
|
||||||
|
challongeRecentTournaments.value = [];
|
||||||
|
selectedChallongeTournamentSlug.value = '';
|
||||||
|
challongeTournamentInput.value = '';
|
||||||
|
challongeTournamentsError.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
isChallongeManualTokenDialogOpen.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
const openChallongeImportDialog = async (tournament: ChallongeTournament) => {
|
const openChallongeImportDialog = async (tournament: ChallongeTournament) => {
|
||||||
importingChallongeTournament.value = tournament;
|
importingChallongeTournament.value = tournament;
|
||||||
challongeImportDialogOpen.value = true;
|
challongeImportDialogOpen.value = true;
|
||||||
@@ -932,9 +961,19 @@ onBeforeUnmount(() => {
|
|||||||
<QBtn
|
<QBtn
|
||||||
v-else
|
v-else
|
||||||
outline
|
outline
|
||||||
color="positive"
|
:color="hasValidatedChallongeToken ? 'positive' : 'warning'"
|
||||||
icon="check_circle"
|
icon="check_circle"
|
||||||
label="Connected"
|
:label="challongeConnectionLabel"
|
||||||
|
@click="openChallongeManualTokenDialog"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<QBtn
|
||||||
|
outline
|
||||||
|
color="white"
|
||||||
|
icon="vpn_key"
|
||||||
|
label="Use personal API"
|
||||||
|
@click="openChallongeManualTokenDialog"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1147,6 +1186,49 @@ onBeforeUnmount(() => {
|
|||||||
</QCard>
|
</QCard>
|
||||||
</QDialog>
|
</QDialog>
|
||||||
|
|
||||||
|
<QDialog v-model="isChallongeManualTokenDialogOpen">
|
||||||
|
<QCard class="players-dialog">
|
||||||
|
<QCardSection>
|
||||||
|
<div class="text-h6">
|
||||||
|
Personal Challonge API
|
||||||
|
</div>
|
||||||
|
</QCardSection>
|
||||||
|
<QSeparator />
|
||||||
|
<QCardSection>
|
||||||
|
<div class="text-body2 q-mb-sm">
|
||||||
|
If OAuth fails, paste a personal Challonge API token.
|
||||||
|
</div>
|
||||||
|
<QInput
|
||||||
|
v-model="challongeManualTokenDraft"
|
||||||
|
label="Paste your personal Challonge token"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
<QSeparator />
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
label="Cancel"
|
||||||
|
color="secondary"
|
||||||
|
@click="isChallongeManualTokenDialogOpen = false"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
color="negative"
|
||||||
|
label="Delete token"
|
||||||
|
@click="challongeManualTokenDraft = ''; saveChallongeManualToken()"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
label="Save token"
|
||||||
|
@click="saveChallongeManualToken"
|
||||||
|
/>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
|
||||||
<QDialog v-model="isDialogOpen">
|
<QDialog v-model="isDialogOpen">
|
||||||
<QCard class="players-dialog">
|
<QCard class="players-dialog">
|
||||||
<QCardSection>
|
<QCardSection>
|
||||||
|
|||||||
Reference in New Issue
Block a user