From 577063d520cb54d7acd796848d7c19e912ce54b5 Mon Sep 17 00:00:00 2001 From: HoshinoSuzumi Date: Wed, 17 Jul 2024 20:39:34 +0800 Subject: [PATCH] feat: hr indicator, adjust color theme --- components.d.ts | 3 ++ src-tauri/src/main.rs | 42 +++++++++++++++-- src-tauri/tauri.conf.json | 22 +++++++-- src/App.vue | 6 +-- src/assets/css/style.css | 13 ++--- src/components/DrawerContainer.vue | 47 +++++++++++++++---- src/components/PageContainer.vue | 2 +- .../icons/TablerActivityHeartbeat.vue | 10 ++++ .../icons/TablerCaretDownFilled.vue | 10 ++++ src/components/icons/TablerCaretUpFilled.vue | 10 ++++ src/pages/charts.vue | 12 ++--- src/pages/index.vue | 3 +- src/pages/settings.vue | 12 ++--- src/pages/streaming-plugins.vue | 8 ++-- src/pages/widgets.vue | 12 ++--- src/stores/index.ts | 14 +++++- tailwind.config.ts | 16 +++++++ 17 files changed, 185 insertions(+), 57 deletions(-) create mode 100644 src/components/icons/TablerActivityHeartbeat.vue create mode 100644 src/components/icons/TablerCaretDownFilled.vue create mode 100644 src/components/icons/TablerCaretUpFilled.vue diff --git a/components.d.ts b/components.d.ts index 7f67bd5..5abaf80 100644 --- a/components.d.ts +++ b/components.d.ts @@ -18,10 +18,13 @@ declare module 'vue' { SystemUiconsSignalFull: typeof import('./src/components/icons/SystemUiconsSignalFull.vue')['default'] SystemUiconsSignalLow: typeof import('./src/components/icons/SystemUiconsSignalLow.vue')['default'] SystemUiconsSignalMedium: typeof import('./src/components/icons/SystemUiconsSignalMedium.vue')['default'] + TablerActivityHeartbeat: typeof import('./src/components/icons/TablerActivityHeartbeat.vue')['default'] TablerBluetooth: typeof import('./src/components/icons/TablerBluetooth.vue')['default'] TablerBluetoothConnected: typeof import('./src/components/icons/TablerBluetoothConnected.vue')['default'] TablerBluetoothOff: typeof import('./src/components/icons/TablerBluetoothOff.vue')['default'] TablerBluetoothX: typeof import('./src/components/icons/TablerBluetoothX.vue')['default'] + TablerCaretDownFilled: typeof import('./src/components/icons/TablerCaretDownFilled.vue')['default'] + TablerCaretUpFilled: typeof import('./src/components/icons/TablerCaretUpFilled.vue')['default'] TablerDeviceWatch: typeof import('./src/components/icons/TablerDeviceWatch.vue')['default'] TablerReload: typeof import('./src/components/icons/TablerReload.vue')['default'] } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 3d2c472..778520e 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,10 +2,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use btleplug::api::bleuuid::uuid_from_u16; -use btleplug::api::CentralEvent::{ - DeviceConnected, DeviceDisconnected, DeviceDiscovered, DeviceUpdated, - ManufacturerDataAdvertisement, ServiceDataAdvertisement, ServicesAdvertisement, -}; +use btleplug::api::CentralEvent::{DeviceDisconnected, DeviceDiscovered, DeviceUpdated}; use btleplug::api::{BDAddr, Central, Manager as _, Peripheral as _, ScanFilter}; use btleplug::platform::{Adapter, Manager as BtleManager, Peripheral, PeripheralId}; use futures::StreamExt; @@ -88,6 +85,7 @@ impl BleConnection { { return Err("Peripheral does not have the required service".into()); } + self.set_peripheral(Some(peripheral)).await; let peripheral = self.peripheral.lock().await; @@ -110,6 +108,40 @@ impl BleConnection { .rssi .unwrap(), }; + + let service = peripheral + .as_ref() + .unwrap() + .services() + .into_iter() + .find(|s| s.uuid == uuid_from_u16(0x180D)) + .unwrap(); + let characteristic = service + .characteristics + .into_iter() + .find(|c| c.uuid == uuid_from_u16(0x2A37)) + .unwrap(); + + let peripheral = peripheral.clone(); + peripheral + .as_ref() + .unwrap() + .subscribe(&characteristic) + .await?; + let app_clone = app.clone(); + + tokio::spawn(async move { + let mut notification_stream = + peripheral.as_ref().unwrap().notifications().await.unwrap(); + while let Some(notification) = notification_stream.next().await { + if notification.uuid == uuid_from_u16(0x2A37) { + let value = notification.value; + let heart_rate = value[1] as u16; + app_clone.emit_all("heart-rate", heart_rate).unwrap(); + } + } + }); + app.emit_all("device-connected", device).unwrap(); Ok(()) } @@ -127,7 +159,7 @@ impl BleConnection { let app_handle = app.clone(); // Clone the AppHandle to move into the tokio::spawn closure let mut event_stream = central.as_ref().unwrap().events().await.unwrap(); - let mut self_clone = self.clone(); // Clone the BleConnection to move into the tokio::spawn closure + let self_clone = self.clone(); // Clone the BleConnection to move into the tokio::spawn closure tokio::spawn(async move { while let Some(event) = event_stream.next().await { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 13614a2..c929920 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -6,8 +6,8 @@ "distDir": "../dist" }, "package": { - "productName": "heartbeat-cat", - "version": "0.0.1" + "productName": "HBCat", + "version": "1.0.1" }, "tauri": { "allowlist": { @@ -37,7 +37,8 @@ "minWidth": 800, "minHeight": 500, "width": 800, - "height": 500 + "height": 500, + "userAgent": "Heartbeat Cat" } ], "security": { @@ -47,9 +48,20 @@ "active": true, "targets": "all", "identifier": "ga.bh8.heartbeat-cat", + "publisher": "TimothyYin", + "copyright": "2024 TimothyYin", + "category": "Utility", + "shortDescription": "HBCat", + "longDescription": "Catch your heartbeat", "icon": [ "icons/icon.ico" - ] + ], + "windows": { + "nsis": { + "installerIcon": "icons/icon.ico", + "installMode": "both" + } + } } } -} +} \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 063cec8..0bd3eaf 100644 --- a/src/App.vue +++ b/src/App.vue @@ -7,10 +7,6 @@ const store = useBrcatStore(); invoke("register_central_events"); -listen("heart-rate", (hr) => { - console.log('Heart Rate', hr); -}) - listen("scan-list-update", (event) => { console.log('scan-list-update', event.payload); }) @@ -52,7 +48,7 @@ listen("device-disconnected", (event) => { --app-background: #f8f8f8; --title-bar-background: #e4e4e4; - --title-bar-color: #F25E86; + --title-bar-color: var(--primary-color); --title-bar-height: 25px; --drawer-bar-background: #eeeeee; diff --git a/src/assets/css/style.css b/src/assets/css/style.css index a2aaed7..7965745 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -71,7 +71,6 @@ } .btn { - border: none; outline: none; padding: 6px 12px; min-width: 30px; @@ -79,7 +78,6 @@ cursor: pointer; text-transform: uppercase; color: #fff; - background-color: #F25E86; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; @@ -90,22 +88,19 @@ -moz-transition: all .3s ease; -ms-transition: all .3s ease; -o-transition: all .3s ease; - @apply text-xs; + @apply text-xs bg-primary border-0; } .btn.outline { - background-color: transparent; - color: #F25E86; - border: 1px solid #F25E86; - box-shadow: none; + @apply text-primary bg-transparent border border-primary shadow-none; } .btn:hover { - box-shadow: 2px 2px 6px 1px rgb(0 0 0 / 20%); + box-shadow: 2px 2px 6px 1px rgb(0 0 0 / .1); } .btn:active { - box-shadow: 2px 2px 6px 1px rgb(0 0 0 / 20%), 2px 2px 6px 1px rgb(0 0 0 / 20%) inset; + box-shadow: 2px 2px 6px 1px rgb(0 0 0 / .1), 2px 2px 6px 1px rgb(0 0 0 / .1) inset; } .btn:disabled { diff --git a/src/components/DrawerContainer.vue b/src/components/DrawerContainer.vue index 6e74dbb..bce4324 100644 --- a/src/components/DrawerContainer.vue +++ b/src/components/DrawerContainer.vue @@ -21,12 +21,33 @@ const navList = ref([ {{ item.title }} -
- - - - {{ store.is_connected ? store.connected_device?.name : '未连接' }} - +
+ +
+ + + {{ store.current_heart_rate || '--' }} + + + + + {{ '--' }} + + + + {{ '--' }} + + +
+
+
+ + + + {{ store.is_connected ? store.connected_device?.name : '未连接' }} + +
@@ -36,6 +57,16 @@ const navList = ref([ + diff --git a/src/pages/index.vue b/src/pages/index.vue index 767874c..5e5c6ae 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -57,8 +57,7 @@ onMounted(() => { 正在扫描蓝牙设备
-
+
正在连接到设备
diff --git a/src/pages/settings.vue b/src/pages/settings.vue index 46dc4ab..e122cf2 100644 --- a/src/pages/settings.vue +++ b/src/pages/settings.vue @@ -2,11 +2,11 @@ - + diff --git a/src/pages/streaming-plugins.vue b/src/pages/streaming-plugins.vue index 5813795..c76a3c0 100644 --- a/src/pages/streaming-plugins.vue +++ b/src/pages/streaming-plugins.vue @@ -3,9 +3,11 @@ + diff --git a/src/stores/index.ts b/src/stores/index.ts index ae77f75..8048fb1 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -1,6 +1,7 @@ import { defineStore } from "pinia"; -import { ref, watchEffect } from "vue"; +import { onMounted, ref, watchEffect } from "vue"; import { invoke } from "@tauri-apps/api/tauri"; +import { listen } from "@tauri-apps/api/event"; import { useThrottleFn } from "@vueuse/core"; export interface Device { @@ -17,6 +18,16 @@ export const useBrcatStore = defineStore("brcat", () => { const is_scanning = ref(false); + const current_heart_rate = ref(0); + + onMounted(async () => { + const unlisten = await listen("heart-rate", (heart_rate) => { + current_heart_rate.value = heart_rate.payload as Number; + }); + + return unlisten; + }) + setInterval(async () => { is_connected.value = await invoke("is_connected"); }, 500); @@ -64,6 +75,7 @@ export const useBrcatStore = defineStore("brcat", () => { } return { + current_heart_rate, is_connected, is_scanning, connected_device, diff --git a/tailwind.config.ts b/tailwind.config.ts index fc6e630..7287f21 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -4,6 +4,22 @@ export default { content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], theme: { extend: { + colors: { + primary: { + "50": "#fef1f6", + "100": "#fee5ef", + "200": "#ffcbe1", + "300": "#ffa1c6", + "400": "#ff6ea3", + "500": "#fa3a7b", + "600": "#ea1854", + "700": "#cc0a3c", + "800": "#a80c32", + "900": "#8c0f2d", + "950": "#560116", + DEFAULT: "#ff6ea3", + }, + }, fontSize: { "2xs": [ "0.625rem",