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
26 changes: 20 additions & 6 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2217,12 +2217,26 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
}
}
#[cfg(target_os = "macos")]
WindowEvent::Focused(focused) if *focused => {
if let Ok(window_id) = CapWindowId::from_str(label)
&& window_id.activates_dock()
{
app.set_activation_policy(tauri::ActivationPolicy::Regular)
.ok();
WindowEvent::Focused(focused) => {
let window_id = CapWindowId::from_str(label);

if matches!(window_id, Ok(CapWindowId::Upgrade)) {
for (label, window) in app.webview_windows() {
if let Ok(id) = CapWindowId::from_str(&label)
&& matches!(id, CapWindowId::TargetSelectOverlay { .. })
{
let _ = window.hide();
}
}
}

if *focused {
if let Ok(window_id) = window_id
&& window_id.activates_dock()
{
app.set_activation_policy(tauri::ActivationPolicy::Regular)
.ok();
}
}
}
WindowEvent::DragDrop(tauri::DragDropEvent::Drop { paths, .. }) => {
Expand Down
88 changes: 60 additions & 28 deletions apps/desktop/src/routes/(window-chrome)/(main).tsx
Original file line number Diff line number Diff line change
Expand Up @@ -486,36 +486,68 @@ function Page() {
Instant Mode
</SignInButton>
) : (
<Button
disabled={toggleRecording.isPending}
variant="blue"
size="md"
onClick={() => toggleRecording.mutate()}
class="flex flex-grow justify-center items-center"
>
{isRecording() ? (
"Stop Recording"
) : (
<Tooltip
childClass="w-full flex"
placement="top"
content={
<>
{rawOptions.mode === "instant" ? (
<IconCapInstant
class={cx(
"size-[0.8rem] mr-1.5",
toggleRecording.isPending ? "opacity-50" : "opacity-100",
)}
/>
) : (
<IconCapFilmCut
class={cx(
"size-[0.8rem] mr-2 -mt-[1.5px]",
toggleRecording.isPending ? "opacity-50" : "opacity-100",
)}
/>
)}
Start Recording
Instant Mode recordings are limited
<br /> to 5 mins,{" "}
<button
class="underline"
onClick={() => commands.showWindow("Upgrade")}
>
Upgrade to Pro
</button>
</>
)}
</Button>
}
openDelay={0}
closeDelay={0}
disabled={
!(
rawOptions.mode === "instant" &&
auth.data?.plan?.upgraded === false
)
}
>
<Button
disabled={toggleRecording.isPending}
variant="blue"
size="md"
onClick={() => toggleRecording.mutate()}
class="flex flex-grow justify-center items-center"
>
{isRecording() ? (
"Stop Recording"
) : (
<>
{rawOptions.mode === "instant" ? (
<IconCapInstant
class={cx(
"size-[0.8rem] mr-1.5",
toggleRecording.isPending
? "opacity-50"
: "opacity-100",
)}
/>
) : (
<IconCapFilmCut
class={cx(
"size-[0.8rem] mr-2 -mt-[1.5px]",
toggleRecording.isPending
? "opacity-50"
: "opacity-100",
)}
/>
)}
{rawOptions.mode === "instant" &&
auth.data?.plan?.upgraded === false
? "Start 5 min recording"
: "Start recording"}
</>
)}
</Button>
</Tooltip>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function CameraSelect(props: {
<div class="flex flex-col gap-[0.25rem] items-stretch text-[--text-primary]">
<button
disabled={!!currentRecording.data || props.disabled}
class="cursor-default flex flex-row gap-2 items-center px-2 w-full h-9 rounded-lg transition-colors bg-gray-3 disabled:text-gray-11 KSelect"
class="flex flex-row gap-2 items-center px-2 w-full h-9 rounded-lg transition-colors cursor-default disabled:opacity-70 bg-gray-3 disabled:text-gray-11 KSelect"
onClick={() => {
Promise.all([
CheckMenuItem.new({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default function MicrophoneSelect(props: {
<div class="flex flex-col gap-[0.25rem] items-stretch text-[--text-primary]">
<button
disabled={!!currentRecording.data || props.disabled}
class="z-10 relative cursor-default flex flex-row gap-2 items-center px-2 w-full h-9 rounded-lg overflow-hidden transition-colors bg-gray-3 disabled:text-gray-11 KSelect"
class="flex overflow-hidden relative z-10 flex-row gap-2 items-center px-2 w-full h-9 rounded-lg transition-colors cursor-default disabled:opacity-70 bg-gray-3 disabled:text-gray-11 KSelect"
onClick={() => {
Promise.all([
CheckMenuItem.new({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function SystemAudio() {
setOptions({ captureSystemAudio: !rawOptions.captureSystemAudio });
}}
disabled={!!currentRecording.data}
class="curosr-default flex flex-row gap-2 items-center px-2 w-full h-9 rounded-lg transition-colors bg-gray-3 disabled:text-gray-11 KSelect"
class="flex flex-row gap-2 items-center px-2 w-full h-9 rounded-lg transition-colors curosr-default disabled:opacity-70 bg-gray-3 disabled:text-gray-11 KSelect"
>
<IconPhMonitorBold class="text-gray-10 size-4" />
<p class="flex-1 text-sm text-left truncate">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ function TargetTypeButton(
selected: boolean;
Component: Component<ComponentProps<"svg">>;
name: string;
disabled?: boolean;
} & ComponentProps<"div">,
) {
return (
Expand All @@ -16,6 +17,7 @@ function TargetTypeButton(
props.selected
? "bg-gray-3 text-white ring-blue-9 ring-1"
: "ring-transparent ring-0",
props.disabled ? "opacity-70 pointer-events-none" : "",
)}
>
<props.Component
Expand Down
28 changes: 19 additions & 9 deletions apps/desktop/src/routes/(window-chrome)/new-main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { generalSettingsStore } from "~/store";
import { createSignInMutation } from "~/utils/auth";
import {
createCameraMutation,
createCurrentRecordingQuery,
createLicenseQuery,
listAudioDevices,
listScreens,
Expand Down Expand Up @@ -112,6 +113,8 @@ function createUpdateCheck() {

function Page() {
const { rawOptions, setOptions } = useRecordingOptions();
const currentRecording = createCurrentRecordingQuery();
const isRecording = () => !!currentRecording.data;

createUpdateCheck();

Expand Down Expand Up @@ -389,27 +392,34 @@ function Page() {
<TargetTypeButton
selected={rawOptions.targetMode === "display"}
Component={IconMdiMonitor}
onClick={() =>
disabled={isRecording()}
onClick={() => {
//if recording early return
if (isRecording()) return;
setOptions("targetMode", (v) =>
v === "display" ? null : "display",
)
}
);
}}
name="Display"
/>
<TargetTypeButton
selected={rawOptions.targetMode === "window"}
Component={IconLucideAppWindowMac}
onClick={() =>
setOptions("targetMode", (v) => (v === "window" ? null : "window"))
}
disabled={isRecording()}
onClick={() => {
if (isRecording()) return;
setOptions("targetMode", (v) => (v === "window" ? null : "window"));
}}
name="Window"
/>
<TargetTypeButton
selected={rawOptions.targetMode === "area"}
Component={IconMaterialSymbolsScreenshotFrame2Rounded}
onClick={() =>
setOptions("targetMode", (v) => (v === "area" ? null : "area"))
}
disabled={isRecording()}
onClick={() => {
if (isRecording()) return;
setOptions("targetMode", (v) => (v === "area" ? null : "area"));
}}
name="Area"
/>
</div>
Expand Down
39 changes: 37 additions & 2 deletions apps/desktop/src/routes/in-progress-recording.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
createEffect,
createMemo,
createSignal,
onCleanup,
Show,
} from "solid-js";
import { createStore, produce } from "solid-js/store";
import createPresence from "solid-presence";
import { authStore } from "~/store";
import { createTauriEventListener } from "~/utils/createEventListener";
import {
createCurrentRecordingQuery,
Expand All @@ -33,6 +33,8 @@ declare global {
}
}

const MAX_RECORDING_FOR_FREE = 5 * 60 * 1000;

export default function () {
const [state, setState] = createSignal<State>(
window.COUNTDOWN === 0
Expand All @@ -47,6 +49,7 @@ export default function () {
const [time, setTime] = createSignal(Date.now());
const currentRecording = createCurrentRecordingQuery();
const optionsQuery = createOptionsQuery();
const auth = authStore.createQuery();

const audioLevel = createAudioInputLevel();

Expand Down Expand Up @@ -156,6 +159,33 @@ export default function () {
return t;
};

const isMaxRecordingLimitEnabled = () => {
// Only enforce the limit on instant mode.
// We enforce it on studio mode when exporting.
return (
optionsQuery.rawOptions.mode === "instant" &&
// If the data is loaded and the user is not upgraded
auth.data?.plan?.upgraded === false
);
};

let aborted = false;
createEffect(() => {
if (
isMaxRecordingLimitEnabled() &&
adjustedTime() > MAX_RECORDING_FOR_FREE &&
!aborted
) {
aborted = true;
stopRecording.mutate();
}
});

const remainingRecordingTime = () => {
if (MAX_RECORDING_FOR_FREE < adjustedTime()) return 0;
return MAX_RECORDING_FOR_FREE - adjustedTime();
};

const [countdownRef, setCountdownRef] = createSignal<HTMLDivElement | null>(
null,
);
Expand Down Expand Up @@ -196,7 +226,12 @@ export default function () {
>
<IconCapStopCircle />
<span class="font-[500] text-[0.875rem] tabular-nums">
{formatTime(adjustedTime() / 1000)}
<Show
when={isMaxRecordingLimitEnabled()}
fallback={formatTime(adjustedTime() / 1000)}
>
{formatTime(remainingRecordingTime() / 1000)}
</Show>
</span>
</button>

Expand Down
Loading
Loading