Merge pull request #9 from Arduinum/mvp1

Mvp1
This commit is contained in:
Arduinum
2026-03-12 20:22:57 +03:00
committed by GitHub
26 changed files with 228 additions and 126 deletions

View File

@@ -4,7 +4,7 @@ export default defineConfig({
e2e: {
baseUrl: 'http://localhost:5173',
supportFile: 'tests/e2e/support/e2e.js',
specPattern: 'tests/e2e/specs/**/*.cy.{js,jsx}',
specPattern: 'tests/e2e/specs/**/*.cy.{js,jsx,ts,tsx}',
videosFolder: 'tests/e2e/videos',
screenshotsFolder: 'tests/e2e/screenshots',
setupNodeEvents(on, config) {

View File

@@ -24,7 +24,7 @@
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -5,5 +5,5 @@
</template>
<script setup>
import { IonApp, IonRouterOutlet } from '@ionic/vue'
import { IonApp, IonRouterOutlet } from '@ionic/vue'
</script>

View File

@@ -1,7 +1,7 @@
import { appLocalDataDir } from '@tauri-apps/api/path';
export async function getDB_URL() {
// Функция верёнт путь до бд
/** Возвращает URL подключения к SQLite */
export async function getDB_URL(): Promise<string> {
const appDir = await appLocalDataDir();
return `sqlite:${appDir}/timer.db`;

View File

@@ -1,10 +0,0 @@
import { getDb } from "../initDb";
import { SELECT_TIMERS } from "../dml/timerDML";
export async function getTimers() {
// Функция для получения списка таймеров
const db = await getDb();
return await db.select(SELECT_TIMERS);
}

View File

@@ -0,0 +1,10 @@
import { getDb } from "@/db/initDb";
import { SELECT_TIMERS } from "@/db/dml/timerDML";
import { TimersRow } from "@/types/timerType"
/** Получает список таймеров */
export async function getTimers(): Promise<TimersRow[]> {
const db = await getDb();
return await db.select<TimersRow[]>(SELECT_TIMERS);
}

View File

@@ -1,13 +1,12 @@
import Database from '@tauri-apps/plugin-sql';
import { CREATE_TIMER } from './ddl/timerDDL';
import { getDB_URL } from '../config';
import { seedDb } from './seed';
import { CREATE_TIMER } from '@/db/ddl/timerDDL';
import { getDB_URL } from '@/config';
import { seedDb } from '@/db/seed';
let dbPromise = null;
export async function getDb() {
// Функция для получения подключения к базе данных
let dbPromise: Promise<Database> | null = null;
/** Получает подключение к бд */
export async function getDb(): Promise<Database> {
if (!dbPromise) {
dbPromise = (async () => {
try {
@@ -22,5 +21,5 @@ export async function getDb() {
}
})();
}
return await dbPromise; // await для консистентности
return await dbPromise;
}

View File

@@ -1,11 +0,0 @@
import { INSERT_SEED_DB, COUNT_TIMERS } from './dml/timerDML'
export async function seedDb(db) {
// Функция для заполнения бд данными
const count = await db.select(COUNT_TIMERS);
if (count[0].cnt === 0) {
await db.execute(INSERT_SEED_DB);
}
}

14
client/src/db/seed.ts Normal file
View File

@@ -0,0 +1,14 @@
import Database from '@tauri-apps/plugin-sql';
import { INSERT_SEED_DB, COUNT_TIMERS } from '@/db/dml/timerDML'
import { CountRow } from '@/types/timerType'
/** Заполняет бд данными (демо таймерами) */
export async function seedDb(db: Database) {
const count = await db.select<CountRow[]>(COUNT_TIMERS);
const cnt = count[0]?.cnt ?? 0;
if (cnt === 0) {
await db.execute(INSERT_SEED_DB);
}
}

View File

@@ -1,6 +1,6 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router';
import App from '@/App.vue'
import router from '@/router';
import { IonicVue } from '@ionic/vue';
@@ -21,7 +21,7 @@ import '@ionic/vue/css/flex-utils.css';
import '@ionic/vue/css/display.css';
/* Theme variables */
import './theme/variables.css';
import '@/theme/variables.css';
const app = createApp(App)
.use(IonicVue)

View File

@@ -1,7 +1,8 @@
import { createRouter, createWebHistory } from '@ionic/vue-router';
import TimersList from '../views/TimersList/TimersList.vue';
import type { RouteRecordRaw } from 'vue-router';
import TimersList from '@/views/TimersList/TimersList.vue';
const routes = [
const routes: RouteRecordRaw[] = [
{
path: '/',
redirect: '/timers'

View File

@@ -0,0 +1,8 @@
export type TimersRow = {
id: number;
title: string;
pomodoro_time: number;
count_pomodoro: number;
};
export type CountRow = { cnt: number };

View File

@@ -1,66 +0,0 @@
import { IonIcon } from '@ionic/vue';
import {
timerOutline,
chevronUp,
chevronDown,
playCircleOutline,
pencilOutline,
trashOutline
} from 'ionicons/icons';
import { ref, onMounted } from 'vue';
import { getTimers } from '../../db/crud/timerCrud';
export default {
name: 'TimersList',
components: {
IonIcon,
},
setup() {
const timers = ref([]);
const loading = ref(true);
const error = ref(null);
const expandedId = ref(null);
const loadTimers = async () => {
loading.value = true;
error.value = null;
try {
const result = await getTimers();
timers.value = result;
} catch (err) {
error.value = err.message || 'Ошибка загрузки таймеров';
} finally {
loading.value = false;
}
};
const toggleExpand = (id) => {
expandedId.value = expandedId.value === id ? null : id;
};
onMounted(() => {
loadTimers();
});
return {
// состояние
timers,
loading,
error,
expandedId,
// методы
toggleExpand,
// иконки
timerOutline,
chevronUp,
chevronDown,
playCircleOutline,
pencilOutline,
trashOutline,
};
},
};

View File

@@ -0,0 +1,60 @@
import { defineComponent, onMounted, ref } from 'vue'
import { IonIcon } from '@ionic/vue'
import {
timerOutline,
chevronUp,
chevronDown,
playCircleOutline,
pencilOutline,
trashOutline,
} from 'ionicons/icons'
import { getTimers } from '@/db/crud/timerCrud'
import type { TimersRow } from '@/types/timerType'
export default defineComponent({
name: 'TimersList',
components: { IonIcon },
setup() {
const timers = ref<TimersRow[]>([])
const loading = ref<boolean>(true)
const error = ref<string | null>(null)
const expandedId = ref<number | null>(null)
const loadTimers = async (): Promise<void> => {
loading.value = true
error.value = null
try {
const result = await getTimers()
timers.value = result
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Ошибка загрузки таймеров'
} finally {
loading.value = false
}
}
const toggleExpand = (id: number): void => {
expandedId.value = expandedId.value === id ? null : id
}
onMounted(loadTimers)
return {
timers,
loading,
error,
expandedId,
toggleExpand,
timerOutline,
chevronUp,
chevronDown,
playCircleOutline,
pencilOutline,
trashOutline,
}
},
})

View File

@@ -1,4 +1,4 @@
<!-- TimersList.vue -->
<template src="./TimersList.html"></template>
<script src="./TimersList.js"></script>
<style src="./TimersList.css" scoped></style>
<template src="@/views/TimersList/TimersList.html"></template>
<script lang="ts" src="@/views/TimersList/TimersList.ts"></script>
<style src="@/views/TimersList/TimersList.css" scoped></style>

1
client/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

11
client/tsconfig.json Normal file
View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"module": "ESNext",
"target": "ES2020",
"moduleResolution": "Bundler"
}
}

View File

@@ -81,19 +81,6 @@ version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "app"
version = "0.1.0"
dependencies = [
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-log",
"tauri-plugin-sql",
]
[[package]]
name = "arrayvec"
version = "0.7.6"
@@ -1849,6 +1836,19 @@ dependencies = [
"serde_json",
]
[[package]]
name = "kawai-focus"
version = "0.1.0"
dependencies = [
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-log",
"tauri-plugin-sql",
]
[[package]]
name = "keyboard-types"
version = "0.7.0"

View File

@@ -1,5 +1,5 @@
[package]
name = "app"
name = "kawai-focus"
version = "0.1.0"
description = "Kawai-Focus - приложение для фокусировки внимания на основе таймера Pomodoro."
authors = ["Arduinum"]
@@ -11,7 +11,7 @@ rust-version = "1.77.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "app_lib"
name = "kawai_focus"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>io.github.arduinum.KawaiFocus</id>
<name>Kawai-Focus</name>
<summary>Focus timer for working sessions</summary>
<description>
<p>Kawai-Focus is a focus-training app based on the Pomodoro timer.</p>
</description>
<developer id="io.github.arduinum">
<name>Kaddo Eugene</name>
</developer>
<metadata_license>FSFAP</metadata_license>
<project_license>MIT</project_license>
<launchable type="desktop-id">io.github.arduinum.KawaiFocus.desktop</launchable>
<url type="homepage">https://github.com/Arduinum/kawai-focus-v2</url>
</component>

View File

@@ -0,0 +1,44 @@
#[cfg(target_os = "linux")]
pub fn apply_linux_cli_flags() {
let disable_webkit_compositing_mode = std::env::args().any(|a| a == "--webkit-disable-compositing-mode");
if disable_webkit_compositing_mode {
unsafe { std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1"); }
}
let webkit_disable_dmabuf_renderer = std::env::args().any(|a| a == "--webkit-disable-dmabuf-renderer");
if webkit_disable_dmabuf_renderer {
unsafe { std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); }
}
let nv_disable_explicit_sync = std::env::args().any(|a| a == "--nv-disable-explicit-sync");
if nv_disable_explicit_sync {
unsafe { std::env::set_var("__NV_DISABLE_EXPLICIT_SYNC", "1"); }
}
}
#[cfg(target_os = "linux")]
pub fn print_help() {
println!(
r#"Kawai Focus — Pomodoro таймер
Использование:
kawai-focus [FLAGS]
Флаги (Linux):
--webkit-disable-compositing-mode
Отключает WebKit compositing mode.
Помогает при графических артефактах на некоторых GPU.
--webkit-disable-dmabuf-renderer
Отключает DMA-BUF renderer WebKit.
Полезно при проблемах с Wayland и с отсутствием GBM.
--nv-disable-explicit-sync
Отключает explicit sync драйвера NVIDIA.
Иногда исправляет: зависания интерфейса, проблемы с отсутствием GBM.
--help
Показать эту справку и выйти.
"#);
}

View File

@@ -1,5 +1,16 @@
mod flags;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
#[cfg(target_os = "linux")]
if std::env::args().any(|a| a == "--help" || a == "-h") {
flags::print_help();
std::process::exit(0);
}
#[cfg(target_os = "linux")]
flags::apply_linux_cli_flags();
tauri::Builder::default()
.plugin(tauri_plugin_sql::Builder::default().build())
.run(tauri::generate_context!())

View File

@@ -3,5 +3,5 @@
fn main() {
println!("Запуск приложения Tauri...");
app_lib::run();
kawai_focus::run();
}

View File

@@ -2,7 +2,7 @@
"$schema": "https://schema.tauri.app/config/2",
"productName": "Kawai-Focus",
"version": "0.1.0",
"identifier": "com.kawai.focus",
"identifier": "io.github.arduinum.KawaiFocus",
"build": {
"frontendDist": "../../client/dist",
"devUrl": "http://localhost:8100",
@@ -29,7 +29,15 @@
},
"bundle": {
"active": true,
"targets": "all",
"targets": ["deb", "rpm"],
"linux": {
"deb": {
"files": {
"/usr/share/metainfo/io.github.arduinum.KawaiFocus.metainfo.xml": "assets/io.github.arduinum.KawaiFocus.metainfo.xml"
}
}
},
"shortDescription": "Kawai-Focus - приложение для фокусировки внимания на основе таймера Pomodoro.",
"icon": [
"icons/32x32.png",
"icons/128x128.png",