Skip to content
Closed
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
43 changes: 43 additions & 0 deletions apps/desktop/src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,49 @@ pub async fn set_recently_opened_sessions<R: tauri::Runtime>(
app.set_recently_opened_sessions(v)
}


#[tauri::command]
#[specta::specta]
pub async fn get_app_open_count<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<u32, String> {
app.get_app_open_count()
}

#[tauri::command]
#[specta::specta]
pub async fn set_app_open_count<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
v: u32,
) -> Result<(), String> {
app.set_app_open_count(v)
}

#[tauri::command]
#[specta::specta]
pub async fn increment_app_open_count<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<u32, String> {
app.increment_app_open_count()
}

#[tauri::command]
#[specta::specta]
pub async fn get_survey_dismissed<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
) -> Result<bool, String> {
app.get_survey_dismissed()
}

#[tauri::command]
#[specta::specta]
pub async fn set_survey_dismissed<R: tauri::Runtime>(
app: tauri::AppHandle<R>,
v: bool,
) -> Result<(), String> {
app.set_survey_dismissed(v)
}

#[tauri::command]
#[specta::specta]
pub async fn get_char_v1p1_preview<R: tauri::Runtime>(
Expand Down
55 changes: 55 additions & 0 deletions apps/desktop/src-tauri/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ pub trait AppExt<R: tauri::Runtime> {
fn get_recently_opened_sessions(&self) -> Result<Option<String>, String>;
fn set_recently_opened_sessions(&self, v: String) -> Result<(), String>;

fn get_app_open_count(&self) -> Result<u32, String>;
fn set_app_open_count(&self, v: u32) -> Result<(), String>;
fn increment_app_open_count(&self) -> Result<u32, String>;

fn get_survey_dismissed(&self) -> Result<bool, String>;
fn set_survey_dismissed(&self, v: bool) -> Result<(), String>;

fn get_char_v1p1_preview(&self) -> Result<bool, String>;
fn set_char_v1p1_preview(&self, v: bool) -> Result<(), String>;
}
Expand Down Expand Up @@ -115,6 +122,54 @@ impl<R: tauri::Runtime, T: tauri::Manager<R>> AppExt<R> for T {
store.save().map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn get_app_open_count(&self) -> Result<u32, String> {
let store = self.desktop_store()?;
store
.get(StoreKey::AppOpenCount)
.map(|opt: Option<u32>| opt.unwrap_or(0))
.map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn set_app_open_count(&self, v: u32) -> Result<(), String> {
let store = self.desktop_store()?;
store
.set(StoreKey::AppOpenCount, v)
.map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn increment_app_open_count(&self) -> Result<u32, String> {
let current = self.get_app_open_count()?;
let new_count = current + 1;
let store = self.desktop_store()?;
store
.set(StoreKey::AppOpenCount, new_count)
.map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())?;
Ok(new_count)
}

#[tracing::instrument(skip_all)]
fn get_survey_dismissed(&self) -> Result<bool, String> {
let store = self.desktop_store()?;
store
.get(StoreKey::SurveyDismissed)
.map(|opt| opt.unwrap_or(false))
.map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn set_survey_dismissed(&self, v: bool) -> Result<(), String> {
let store = self.desktop_store()?;
store
.set(StoreKey::SurveyDismissed, v)
.map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())
}

#[tracing::instrument(skip_all)]
fn get_char_v1p1_preview(&self) -> Result<bool, String> {
if cfg!(feature = "new") {
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ fn make_specta_builder<R: tauri::Runtime>() -> tauri_specta::Builder<R> {
commands::set_pinned_tabs::<tauri::Wry>,
commands::get_recently_opened_sessions::<tauri::Wry>,
commands::set_recently_opened_sessions::<tauri::Wry>,
commands::get_app_open_count::<tauri::Wry>,
commands::set_app_open_count::<tauri::Wry>,
commands::increment_app_open_count::<tauri::Wry>,
commands::get_survey_dismissed::<tauri::Wry>,
commands::set_survey_dismissed::<tauri::Wry>,
commands::get_char_v1p1_preview::<tauri::Wry>,
commands::set_char_v1p1_preview::<tauri::Wry>,
])
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src-tauri/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub enum StoreKey {
TinybaseValues,
PinnedTabs,
RecentlyOpenedSessions,
AppOpenCount,
SurveyDismissed,
CharV1p1Preview,
}

Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src/services/event-listeners.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getOrCreateSessionForEventId,
} from "~/store/tinybase/store/sessions";
import { useTabs } from "~/store/zustand/tabs";
import { SurveyModal } from "~/survey/survey-modal";

function useUpdaterEvents() {
const openNew = useTabs((state) => state.openNew);
Expand Down Expand Up @@ -145,5 +146,5 @@ export function EventListeners() {
useUpdaterEvents();
useNotificationEvents();

return null;
return <SurveyModal />;
}
85 changes: 85 additions & 0 deletions apps/desktop/src/sidebar/devtool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { cn } from "@hypr/utils";
import { getLatestVersion } from "~/changelog";
import * as main from "~/store/tinybase/store/main";
import { useTabs } from "~/store/zustand/tabs";
import { SurveyModal } from "~/survey/survey-modal";
import { commands } from "~/types/tauri.gen";

export function DevtoolView() {
Expand All @@ -17,6 +18,7 @@ export function DevtoolView() {
<div className="flex flex-1 flex-col gap-2 overflow-y-auto px-1 py-2">
<NavigationCard />
<ToastsCard />
<SurveyCard />
<CountdownTestCard />
<ErrorTestCard />
</div>
Expand Down Expand Up @@ -208,6 +210,89 @@ function ToastsCard() {
);
}

function SurveyCard() {
const [surveyOpen, setSurveyOpen] = useState(false);
const [appOpenCount, setAppOpenCount] = useState<number | null>(null);
const [surveyDismissed, setSurveyDismissed] = useState<boolean | null>(null);

const refreshState = useCallback(async () => {
const countResult = await commands.getAppOpenCount();
if (countResult.status === "ok") {
setAppOpenCount(countResult.data);
}
const dismissedResult = await commands.getSurveyDismissed();
if (dismissedResult.status === "ok") {
setSurveyDismissed(dismissedResult.data);
}
}, []);

const handleResetAppOpenCount = useCallback(async () => {
await commands.setAppOpenCount(0);
await refreshState();
}, [refreshState]);

const handleResetSurvey = useCallback(async () => {
await commands.setSurveyDismissed(false);
await refreshState();
}, [refreshState]);

const handleTriggerSurvey = useCallback(() => {
setSurveyOpen(true);
}, []);

const btnClass = cn([
"w-full rounded-md px-2.5 py-1.5",
"text-left text-xs font-medium",
"border border-neutral-200 text-neutral-700",
"cursor-pointer transition-colors",
"hover:border-neutral-300 hover:bg-neutral-50",
]);

return (
<DevtoolCard title="Survey">
<div className="flex flex-col gap-1.5">
<div className="text-xs text-neutral-500">
<div>
Open count: {appOpenCount ?? "—"} | Dismissed:{" "}
{surveyDismissed === null ? "—" : surveyDismissed ? "yes" : "no"}
</div>
</div>
<button
type="button"
onClick={() => void refreshState()}
className={btnClass}
>
Refresh State
</button>
<button
type="button"
onClick={() => void handleResetAppOpenCount()}
className={btnClass}
>
Reset App Open Count
</button>
<button
type="button"
onClick={() => void handleResetSurvey()}
className={btnClass}
>
Reset Survey Dismissed
</button>
<button
type="button"
onClick={handleTriggerSurvey}
className={btnClass}
>
Show Survey Modal
</button>
</div>
{surveyOpen && (
<SurveyModal forceOpen onClose={() => setSurveyOpen(false)} />
)}
</DevtoolCard>
);
}

function CountdownTestCard() {
const store = main.UI.useStore(main.STORE_ID) as main.Store | undefined;
const { user_id } = main.UI.useValues(main.STORE_ID);
Expand Down
Loading
Loading