Show toast when failing to open external link

- toast contains url
- can be copied or clicked
This commit is contained in:
Mattias Granlund 2024-02-13 18:30:24 +02:00
parent 80dab85ae2
commit 232ea42256
7 changed files with 39 additions and 10 deletions

View File

@ -7,11 +7,11 @@
import TimeAgo from '$lib/components/TimeAgo.svelte'; import TimeAgo from '$lib/components/TimeAgo.svelte';
import { draggable } from '$lib/dragging/draggable'; import { draggable } from '$lib/dragging/draggable';
import { draggableCommit, nonDraggable } from '$lib/dragging/draggables'; import { draggableCommit, nonDraggable } from '$lib/dragging/draggables';
import { openExternalUrl } from '$lib/utils/url';
import { filesToFileTree } from '$lib/vbranches/filetree'; import { filesToFileTree } from '$lib/vbranches/filetree';
import { Ownership } from '$lib/vbranches/ownership'; import { Ownership } from '$lib/vbranches/ownership';
import { listRemoteCommitFiles } from '$lib/vbranches/remoteCommits'; import { listRemoteCommitFiles } from '$lib/vbranches/remoteCommits';
import { LocalFile, RemoteCommit, Commit, RemoteFile } from '$lib/vbranches/types'; import { LocalFile, RemoteCommit, Commit, RemoteFile } from '$lib/vbranches/types';
import { open } from '@tauri-apps/api/shell';
import { writable, type Writable } from 'svelte/store'; import { writable, type Writable } from 'svelte/store';
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
@ -122,7 +122,7 @@
kind="outlined" kind="outlined"
icon="open-link" icon="open-link"
on:click={() => { on:click={() => {
if (commitUrl) open(commitUrl); if (commitUrl) openExternalUrl(commitUrl);
}}>Open commit</Button }}>Open commit</Button
> >
</div> </div>

View File

@ -52,9 +52,7 @@
{#if title} {#if title}
<div class="info-message__title text-base-13 text-semibold">{title}</div> <div class="info-message__title text-base-13 text-semibold">{title}</div>
{/if} {/if}
<div class="info-message__text text-base-body-12"> <div class="info-message__text text-base-body-12"><slot /></div>
<slot />
</div>
</div> </div>
{#if primary || secondary} {#if primary || secondary}
<div class="info-message__actions"> <div class="info-message__actions">
@ -88,6 +86,9 @@
flex-direction: column; flex-direction: column;
gap: var(--space-12); gap: var(--space-12);
} }
.info-message__text {
word-break: break-all;
}
.info-message__content { .info-message__content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Icon from '$lib/components/Icon.svelte'; import Icon from '$lib/components/Icon.svelte';
import { open } from '@tauri-apps/api/shell'; import { openExternalUrl } from '$lib/utils/url';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
let classes = ''; let classes = '';
@ -32,9 +32,9 @@
class:disabled class:disabled
on:click={(e) => { on:click={(e) => {
if (href && isExternal) { if (href && isExternal) {
open(href);
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
openExternalUrl(href);
} }
}} }}
> >

View File

@ -4,7 +4,14 @@
<script lang="ts"> <script lang="ts">
import InfoMessage from '$lib/components/InfoMessage.svelte'; import InfoMessage from '$lib/components/InfoMessage.svelte';
import { dismissToast, toastStore } from '$lib/notifications/toasts'; import { dismissToast, toastStore } from '$lib/notifications/toasts';
import { marked } from 'marked';
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
var renderer = new marked.Renderer();
renderer.link = function (href, title, text) {
if (!title) title = text;
return '<a target="_blank" href="' + href + '" title="' + title + '">' + text + '</a>';
};
</script> </script>
<div class="toast-controller"> <div class="toast-controller">
@ -15,7 +22,7 @@
style={toast.style ?? 'neutral'} style={toast.style ?? 'neutral'}
secondary="Dismiss" secondary="Dismiss"
on:secondary={() => dismissToast(toast.id)} on:secondary={() => dismissToast(toast.id)}
shadow>{toast.message}</InfoMessage shadow>{@html marked.parse(toast.message, { renderer })}</InfoMessage
> >
</div> </div>
{/each} {/each}

View File

@ -3,7 +3,7 @@ import { resetSentry, setSentryUser } from '$lib/analytics/sentry';
import { getCloudApiClient, type User } from '$lib/backend/cloud'; import { getCloudApiClient, type User } from '$lib/backend/cloud';
import { invoke } from '$lib/backend/ipc'; import { invoke } from '$lib/backend/ipc';
import { sleep } from '$lib/utils/sleep'; import { sleep } from '$lib/utils/sleep';
import { open } from '@tauri-apps/api/shell'; import { openExternalUrl } from '$lib/utils/url';
import { BehaviorSubject, Observable, Subject, merge, shareReplay } from 'rxjs'; import { BehaviorSubject, Observable, Subject, merge, shareReplay } from 'rxjs';
export class UserService { export class UserService {
@ -49,7 +49,7 @@ export class UserService {
this.loading$.next(true); this.loading$.next(true);
try { try {
const token = await this.cloud.login.token.create(); const token = await this.cloud.login.token.create();
open(token.url); openExternalUrl(token.url);
// Assumed min time for login flow // Assumed min time for login flow
await sleep(4000); await sleep(4000);

View File

@ -0,0 +1,19 @@
import { showToast } from '$lib/notifications/toasts';
import { posthog } from 'posthog-js';
export function openExternalUrl(href: string) {
try {
open(href);
} catch (e) {
if (typeof e == 'string' || e instanceof String) {
// TODO: Remove if/when we've resolved all external URL problems.
posthog.capture('Link Error', { href, message: e });
const message = 'Failed to open link in external browser: <br />' + href;
showToast({ title: 'External URL error', message, style: 'error' });
}
// Rethrowing for sentry and posthog
throw e;
}
}

View File

@ -2,6 +2,7 @@
import '../styles/main.postcss'; import '../styles/main.postcss';
import ShareIssueModal from '$lib/components/ShareIssueModal.svelte'; import ShareIssueModal from '$lib/components/ShareIssueModal.svelte';
import ToastController from '$lib/notifications/ToastController.svelte';
import { SETTINGS_CONTEXT, loadUserSettings } from '$lib/settings/userSettings'; import { SETTINGS_CONTEXT, loadUserSettings } from '$lib/settings/userSettings';
import * as events from '$lib/utils/events'; import * as events from '$lib/utils/events';
import * as hotkeys from '$lib/utils/hotkeys'; import * as hotkeys from '$lib/utils/hotkeys';
@ -49,3 +50,4 @@
</div> </div>
<Toaster /> <Toaster />
<ShareIssueModal bind:this={shareIssueModal} user={$user$} {cloud} /> <ShareIssueModal bind:this={shareIssueModal} user={$user$} {cloud} />
<ToastController />