Skip to content

Commit

Permalink
Merge pull request #87 from nomandhoni-cs/develop
Browse files Browse the repository at this point in the history
Added Workday setup for v1.9.0 and update toast
  • Loading branch information
nomandhoni-cs authored Nov 18, 2024
2 parents 3b528a5 + f44bf3e commit 71e04ae
Show file tree
Hide file tree
Showing 30 changed files with 1,263 additions and 240 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "blink-eye",
"private": true,
"version": "1.8.1",
"version": "1.9.0",
"type": "module",
"displayName": "Blink Eye",
"categories": ["Other"],
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "Blink-Eye"
version = "1.8.1"
version = "1.9.0"
description = "A minimalist eye care reminder app for Windows, macOS, and Linux."
authors = ["Noman Dhoni"]
edition = "2021"
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2.0.6",
"productName": "Blink Eye",
"version": "1.8.1",
"version": "1.9.0",
"identifier": "com.blinkeye.app",
"build": {
"beforeDevCommand": "bun run dev",
Expand Down
13 changes: 11 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "./App.css";
import { useAutoStart } from "./hooks/useAutoStart";
import { ErrorDisplay } from "./components/ErrorDisplay";
import { LoadingSpinner } from "./components/LoadingSpinner";
import Workday from "./components/window/Workday";

// Lazy load route components
const Dashboard = lazy(() => import("./components/window/Dashboard"));
Expand Down Expand Up @@ -59,6 +60,14 @@ function App() {
</Suspense>
}
/>
<Route
path="reminderthemes"
element={
<Suspense fallback={<LoadingSpinner />}>
<ReminderStyles />
</Suspense>
}
/>
<Route
path="usagetime"
element={
Expand All @@ -68,10 +77,10 @@ function App() {
}
/>
<Route
path="reminderthemes"
path="workday"
element={
<Suspense fallback={<LoadingSpinner />}>
<ReminderStyles />
<Workday />
</Suspense>
}
/>
Expand Down
84 changes: 84 additions & 0 deletions src/components/ConfigDataLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect } from "react";
import { BaseDirectory } from "@tauri-apps/api/path";
import { exists } from "@tauri-apps/plugin-fs";
import Database from "@tauri-apps/plugin-sql";
import { load } from "@tauri-apps/plugin-store";

const ConfigDataLoader: React.FC = () => {
const defaultWorkday = {
Monday: { start: "09:00", end: "17:00" },
Tuesday: { start: "09:00", end: "17:00" },
Wednesday: { start: "09:00", end: "17:00" },
Thursday: { start: "09:00", end: "17:00" },
Friday: { start: "09:00", end: "17:00" },
Saturday: null,
Sunday: null,
};

useEffect(() => {
const setupDatabase = async () => {
// Check if the database file exists
const dbExists = await exists("appconfig.db", {
baseDir: BaseDirectory.AppData,
});

// Initialize the database

if (!dbExists) {
const db = await Database.load("sqlite:appconfig.db");
console.log("Database does not exist. Initializing...");

// Create the tables
await db.execute(`
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT
);
`);

// Insert default values
await db.execute(`INSERT INTO config (key, value) VALUES (?, ?);`, [
"blinkEyeWorkday",
JSON.stringify(defaultWorkday),
]);
await db.execute(`INSERT INTO config (key, value) VALUES (?, ?);`, [
"isWorkdayEnabled",
"false",
]);
await db.execute(`INSERT INTO config (key, value) VALUES (?, ?);`, [
"isUpdateAvailable",
"false",
]);
await db.execute(`INSERT INTO config (key, value) VALUES (?, ?);`, [
"usingStrictMode",
"false",
]);
console.log("Database initialized with default configuration.");
} else {
console.log("Database already exists. No action needed.");
}

const storeExists = await exists("store.json", {
baseDir: BaseDirectory.AppData,
});
if (!storeExists) {
const store = await load("store.json", { autoSave: false });
await store.set("blinkEyeReminderDuration", 20);
await store.set("blinkEyeReminderInterval", 20);
await store.set(
"blinkEyeReminderScreenText",
"Look 20 feet away to protect your eyes."
);
await store.save();
} else {
console.log("Store already exists. No action needed.");
}
};

setupDatabase();
}, []);

return null; // This component does not render anything
};

export default ConfigDataLoader;
145 changes: 145 additions & 0 deletions src/components/ReminderHandler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { useEffect, useState } from "react";
import { WebviewWindow } from "@tauri-apps/api/webviewWindow";
import Database from "@tauri-apps/plugin-sql";
import { usePremiumFeatures } from "../contexts/PremiumFeaturesContext";
import { load } from "@tauri-apps/plugin-store";
import { useTrigger } from "../contexts/TriggerReRender";

// Define the type for workday configuration
type Workday = { [day: string]: { start: string; end: string } } | null;

const ReminderHandler = () => {
const { trigger } = useTrigger(); // Use the trigger value from the context
const [interval, setInterval] = useState<number>(20);
const [workday, setWorkday] = useState<Workday>(null);
const [isWorkdayEnabled, setIsWorkdayEnabled] = useState<boolean>(false);
const { canAccessPremiumFeatures } = usePremiumFeatures();

// Function to open the reminder window
const openReminderWindow = () => {
console.log("Opening reminder window...");
const webview = new WebviewWindow("ReminderWindow", {
url: "/reminder",
fullscreen: true,
alwaysOnTop: true,
title: "Take A Break Reminder - Blink Eye",
skipTaskbar: true,
});

webview.once("tauri://created", () => {
console.log("Reminder window created");
});

webview.once("tauri://error", (e) => {
console.error("Error creating reminder window:", e);
});
};

// Fetch settings when `trigger` changes
useEffect(() => {
const fetchSettings = async () => {
console.log("Fetching settings due to trigger:", trigger);
const db = await Database.load("sqlite:appconfig.db");
const store = await load("store.json", { autoSave: false });

// Load the reminder interval from storage
const storedInterval = await store.get<number>(
"blinkEyeReminderInterval"
);
if (storedInterval) setInterval(storedInterval);

// Fetch workday setup from the database
type ConfigResult = { value: string };
const workdayData = (await db.select(
"SELECT value FROM config WHERE key = ?",
["blinkEyeWorkday"]
)) as ConfigResult[];
if (workdayData.length > 0 && workdayData[0].value) {
try {
const parsedWorkday = JSON.parse(workdayData[0].value) as Workday;
setWorkday(parsedWorkday);
} catch (error) {
console.error("Failed to parse workday data:", error);
}
}

// Fetch whether workday is enabled
const isEnabledData = (await db.select(
"SELECT value FROM config WHERE key = ?",
["isWorkdayEnabled"]
)) as ConfigResult[];

if (isEnabledData.length > 0 && isEnabledData[0].value) {
setIsWorkdayEnabled(isEnabledData[0].value === "true");
}
console.log(workdayData, "workdayData");
console.log(storedInterval, "storedInterval");
console.log(isWorkdayEnabled, "isWorkdayEnabled");
};

fetchSettings();
}, [trigger]); // Refetch settings when the trigger updates

// Handle interval-based reminder logic
useEffect(() => {
console.log("Initializing reminder logic");
let timer: number | null = null;

const startReminder = () => {
console.log("Starting reminder timer with interval:", interval);
timer = window.setInterval(() => {
openReminderWindow();
}, interval * 60 * 1000); // The interval value will be non-null
};

const checkWorkdayAndStartTimer = () => {
const now = new Date();
const day = now.toLocaleString("en-US", { weekday: "long" });
const todayWorkday = workday?.[day];

if (canAccessPremiumFeatures && isWorkdayEnabled) {
if (todayWorkday) {
const [startHour, startMinute] = todayWorkday.start
.split(":")
.map(Number);
const [endHour, endMinute] = todayWorkday.end.split(":").map(Number);

const startTime = new Date();
startTime.setHours(startHour, startMinute, 0, 0);

const endTime = new Date();
endTime.setHours(endHour, endMinute, 0, 0);

console.log("Start time:", startTime);
console.log("End time:", endTime);
console.log("Now:", now);

if (now >= startTime && now <= endTime) {
console.log("Within workday window. Starting reminder.");
startReminder();
} else {
console.log("Outside workday window. Reminder not started.");
}
} else {
console.log("No workday setup for today. Reminder skipped.");
}
} else {
console.log("Non-premium user or workday disabled. Starting reminder.");
startReminder();
}
};

// Start reminder logic if necessary
if (workday || !canAccessPremiumFeatures) {
checkWorkdayAndStartTimer();
}

return () => {
if (timer !== null) window.clearInterval(timer); // Cleanup previous timers
};
}, [workday, isWorkdayEnabled, canAccessPremiumFeatures, interval]);

return null;
};

export default ReminderHandler;
75 changes: 75 additions & 0 deletions src/components/StrictModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useState, useEffect } from "react";
import { Label } from "./ui/label";
import { Switch } from "./ui/switch";
import Database from "@tauri-apps/plugin-sql";

interface ConfigRow {
value: string;
}

const StrictModeToggle = () => {
const [isStrictModeEnabled, setIsStrictModeEnabled] = useState(false);

useEffect(() => {
const initializeStrictMode = async () => {
try {
// Load initial configuration store
const db = await Database.load("sqlite:appconfig.db");

// Retrieve the 'usingStrictMode' value from the config table
const result: ConfigRow[] = await db.select(
"SELECT value FROM config WHERE key = 'usingStrictMode';"
);

if (result.length > 0) {
setIsStrictModeEnabled(result[0].value === "true");
}
} catch (error) {
console.error("Failed to initialize strict mode:", error);
}
};
initializeStrictMode();
}, []);

const handleCheckboxChange = async (checked: boolean) => {
try {
const db = await Database.load("sqlite:appconfig.db");

// Use an `INSERT OR REPLACE` or `UPDATE` query
await db.execute(
`
INSERT INTO config (key, value) VALUES ('usingStrictMode', ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value;
`,
[checked ? "true" : "false"]
);

setIsStrictModeEnabled(checked);
} catch (error) {
console.error("Failed to update strict mode status:", error);
}
};

return (
<div className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<Label
htmlFor="strictmode"
className="font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Use Strict Mode
</Label>
<p className="text-sm text-muted-foreground">
This will hide the 'Skip this time' button to force follow the break.
</p>
</div>
<Switch
id="strictmode"
checked={isStrictModeEnabled}
onCheckedChange={handleCheckboxChange}
/>
</div>
);
};

export default StrictModeToggle;
2 changes: 1 addition & 1 deletion src/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const items = [
},
{
title: "Workday Setup",
url: "/soon",
url: "/workday",
icon: Calendar,
isPremiumFeature: true,
},
Expand Down
Loading

0 comments on commit 71e04ae

Please sign in to comment.