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
7 changes: 5 additions & 2 deletions app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn run() {
note::catch_panics();
let app = tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
panel::show(app);
welcome::reopened(app);
}))
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_shell::init())
Expand Down Expand Up @@ -79,7 +79,10 @@ pub fn run() {
} => api.prevent_exit(),
tauri::RunEvent::Exit => panel::quit(app),
#[cfg(target_os = "macos")]
tauri::RunEvent::Reopen { .. } => panel::show(app),
tauri::RunEvent::Reopen {
has_visible_windows: false,
..
} => welcome::reopened(app),
_ => {}
});
}
Expand Down
34 changes: 28 additions & 6 deletions app/src-tauri/src/welcome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub const LABEL: &str = "welcome";
pub enum Greeting {
Tour { former: bool },
News { versions: Vec<String> },
Keys,
Nothing,
}

Expand Down Expand Up @@ -137,7 +138,7 @@ pub fn open<R: Runtime>(app: &AppHandle<R>) {
Ok(window) => {
let handle = app.clone();
window.on_window_event(move |event| {
if matches!(event, tauri::WindowEvent::Destroyed) {
if matches!(event, tauri::WindowEvent::Destroyed) && !only_keys(&handle) {
mark(&handle);
}
});
Expand All @@ -147,6 +148,31 @@ pub fn open<R: Runtime>(app: &AppHandle<R>) {
}
}

pub fn remembers(greeting: Option<&Greeting>) -> bool {
!matches!(greeting, Some(Greeting::Keys))
}

fn only_keys<R: Runtime>(app: &AppHandle<R>) -> bool {
app.try_state::<Now>()
.and_then(|state| state.0.lock().ok().map(|held| !remembers(held.as_ref())))
.unwrap_or(false)
}

fn hold<R: Runtime>(app: &AppHandle<R>, greeting: Greeting) {
if let Some(state) = app.try_state::<Now>()
&& let Ok(mut held) = state.0.lock()
{
*held = Some(greeting);
}
}

pub fn reopened<R: Runtime>(app: &AppHandle<R>) {
if app.get_webview_window(LABEL).is_none() {
hold(app, Greeting::Keys);
}
open(app);
}

#[tauri::command]
pub fn greeting(state: tauri::State<'_, Now>) -> Greeting {
state
Expand All @@ -160,11 +186,7 @@ pub fn greeting(state: tauri::State<'_, Now>) -> Greeting {
#[tauri::command]
pub async fn tour(app: AppHandle) {
let wanted = Greeting::Tour { former: false };
if let Some(state) = app.try_state::<Now>()
&& let Ok(mut held) = state.0.lock()
{
*held = Some(wanted.clone());
}
hold(&app, wanted.clone());
if app.get_webview_window(LABEL).is_some() {
let _ = app.emit_to(LABEL, "greeting", wanted);
}
Expand Down
20 changes: 19 additions & 1 deletion app/src-tauri/src/welcome_test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Greeting, behind, decide, told};
use super::{Greeting, behind, decide, remembers, told};

fn said(all: &[&str]) -> Vec<String> {
all.iter().map(|one| (*one).to_owned()).collect()
Expand Down Expand Up @@ -138,3 +138,21 @@ fn a_build_made_by_hand_never_marks_a_published_version_as_seen() {
"the real release still tells what it brings after a build made by hand"
);
}

#[test]
fn opening_it_again_asks_the_window_for_the_shortcut_alone() {
assert_eq!(
serde_json::to_value(Greeting::Keys).expect("it serializes"),
serde_json::json!({ "kind": "keys" })
);
}

#[test]
fn closing_the_shortcut_alone_leaves_the_welcome_as_it_was() {
assert!(!remembers(Some(&Greeting::Keys)));
assert!(remembers(Some(&Greeting::Tour { former: false })));
assert!(remembers(Some(&Greeting::News {
versions: vec!["3.0.0".to_owned()]
})));
assert!(remembers(None));
}
2 changes: 2 additions & 0 deletions app/src/locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const ES = {
welcomeTryAgain: "Probar de nuevo",
welcomeStep: "Paso {one}",
welcomeKeysOver: "1 · El atajo",
welcomeKeysAloneOver: "El atajo",
welcomeKeysTitle: "Pruébalo ahora",
welcomeKeysWhy: "Desde cualquier app, esta combinación abre tu historial.",
welcomeKeysWaiting: "Esperando que lo pruebes…",
Expand Down Expand Up @@ -479,6 +480,7 @@ const EN: Record<keyof Said, string> = {
welcomeTryAgain: "Try again",
welcomeStep: "Step {one}",
welcomeKeysOver: "1 · The shortcut",
welcomeKeysAloneOver: "The shortcut",
welcomeKeysTitle: "Try it now",
welcomeKeysWhy: "From any app, this combination opens your history.",
welcomeKeysWaiting: "Waiting for you to try it…",
Expand Down
17 changes: 17 additions & 0 deletions app/src/tests/tour.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,20 @@ describe("las novedades tras una actualización", () => {
expect(theWindow.close).toHaveBeenCalled();
});
});

describe("abrir la app cuando ya está corriendo", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("enseña solo el atajo, sin el recorrido, y se cierra con un botón", async () => {
asked.greeting = { kind: "keys" };
asked.locale = "en";
render(<Welcome />);

expect(await screen.findByText("The shortcut")).toBeInTheDocument();
expect(screen.queryByText("1 · The shortcut")).toBeNull();
await press("Close");
expect(theWindow.close).toHaveBeenCalled();
});
});
24 changes: 15 additions & 9 deletions app/src/ui/Welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const RELEASES = "https://github.com/rgdevment/CopyPaste/releases";
export type Greeting =
| { kind: "tour"; former: boolean }
| { kind: "news"; versions: string[] }
| { kind: "keys" }
| { kind: "nothing" };

type Step = "former" | "hello" | "trust" | "keys" | "where" | "use";
Expand Down Expand Up @@ -92,6 +93,9 @@ export default function Welcome() {
<Tour kept={kept} change={change} former={greeting.former} />
)}
{kept && greeting?.kind === "news" && <News versions={greeting.versions} />}
{kept && greeting?.kind === "keys" && (
<TryIt kept={kept} change={change} mac={onMac()} dots={null} next={leave} alone />
)}
</>
);
}
Expand Down Expand Up @@ -244,14 +248,18 @@ function TryIt({
mac,
dots,
next,
alone = false,
}: {
kept: Kept;
change: (what: Partial<Kept>) => Promise<void>;
mac: boolean;
dots: ReactNode;
next: () => void;
alone?: boolean;
}) {
const { keys, spare, recheck } = useKeys(kept.shortcut);
const over = alone ? t("welcomeKeysAloneOver") : t("welcomeKeysOver");
const skip = alone ? t("chromeClose") : t("welcomeSkipStep");
const [tried, setTried] = useState(false);

useEffect(() => {
Expand All @@ -266,9 +274,9 @@ function TryIt({

if (taken) {
return (
<Screen dots={dots} left={<Quiet says={t("welcomeSkipStep")} onPress={next} />} right={null}>
<Screen dots={dots} left={<Quiet says={skip} onPress={next} />} right={null}>
<Heading
over={t("welcomeKeysOver")}
over={over}
title={t("welcomeKeysTaken")}
said={fill(spare.length > 0 ? "welcomeKeysTakenWhy" : "welcomeKeysTakenAlone", shown)}
/>
Expand Down Expand Up @@ -297,16 +305,14 @@ function TryIt({
tried ? (
<Quiet says={t("welcomeTryAgain")} onPress={() => setTried(false)} />
) : (
<Quiet says={t("welcomeSkipStep")} onPress={next} />
<Quiet says={skip} onPress={next} />
)
}
right={tried ? <Strong says={t("welcomeNext")} onPress={next} /> : null}
right={
tried ? <Strong says={alone ? t("welcomeDone") : t("welcomeNext")} onPress={next} /> : null
}
>
<Heading
over={t("welcomeKeysOver")}
title={t("welcomeKeysTitle")}
said={t("welcomeKeysWhy")}
/>
<Heading over={over} title={t("welcomeKeysTitle")} said={t("welcomeKeysWhy")} />
<div className="welcome-caps">
{caps(kept.shortcut, mac).map((one, index) => (
<span key={one} className="welcome-cap-pair">
Expand Down
Loading