diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index a0d2973..c8c64cb 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2116,6 +2116,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2988,6 +2998,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", @@ -4257,6 +4268,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.8" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 4011387..8c00f36 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,8 +28,8 @@ hound = "3.4.0" whisper-rs = "0.4.0" rusqlite = "*" samplerate-rs = "0.1.0" -# download model -reqwest = { version = "0.11", features = ["stream"] } +# download model and openai request +reqwest = { version = "0.11", features = ["stream", "multipart", "json"] } tokio = { version = "1", features = ["full"] } futures-util = "0.3.26" # serve audio diff --git a/src-tauri/migrations/001.sql b/src-tauri/migrations/001.sql index 168b89f..50cbc2d 100644 --- a/src-tauri/migrations/001.sql +++ b/src-tauri/migrations/001.sql @@ -22,6 +22,8 @@ CREATE TABLE settings ( ); INSERT INTO settings(setting_name, setting_status) VALUES("speakerLanguage", NULL); INSERT INTO settings(setting_name, setting_status) VALUES("transcriptionAccuracy", "off"); +INSERT INTO settings(setting_name, setting_status) VALUES("settingKey", ""); +INSERT INTO settings(setting_name, setting_status) VALUES("settingLanguage", "日本語"); CREATE TABLE models ( id INTEGER PRIMARY KEY AUTOINCREMENT, model_name TEXT, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e06a01a..33a7bd5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -73,7 +73,6 @@ fn start_command( let mut lock = state.0.lock().unwrap(); let (stop_record_tx, stop_record_rx) = unbounded(); *lock = Some(stop_record_tx); - std::thread::spawn(move || { let record = module::record::Record::new(window.app_handle().clone()); record.start( @@ -107,13 +106,23 @@ fn start_trace_command( *lock = Some(stop_convert_tx); std::thread::spawn(move || { - let mut transcription = module::transcription::Transcription::new( - window.app_handle(), - transcription_accuracy, - speaker_language, - note_id, - ); - transcription.start(stop_convert_rx, true); + if transcription_accuracy.starts_with("online") { + let mut transcription_online = module::transcription_online::TranscriptionOnline::new( + window.app_handle(), + transcription_accuracy, + speaker_language, + note_id, + ); + transcription_online.start(stop_convert_rx, true); + } else { + let mut transcription = module::transcription::Transcription::new( + window.app_handle(), + transcription_accuracy, + speaker_language, + note_id, + ); + transcription.start(stop_convert_rx, true); + } }); } @@ -124,10 +133,7 @@ fn stop_trace_command(state: State<'_, RecordState>, window: tauri::Window) { stop_convert_tx.send(()).unwrap_or_else(|_| { window .app_handle() - .emit_all( - "traceCompletion", - TraceCompletion {}, - ) + .emit_all("traceCompletion", TraceCompletion {}) .unwrap(); }) } diff --git a/src-tauri/src/module/mod.rs b/src-tauri/src/module/mod.rs index 9833013..0bae062 100644 --- a/src-tauri/src/module/mod.rs +++ b/src-tauri/src/module/mod.rs @@ -8,4 +8,5 @@ pub mod record; mod sqlite; mod transcriber; pub mod transcription; +pub mod transcription_online; mod writer; diff --git a/src-tauri/src/module/record.rs b/src-tauri/src/module/record.rs index cd42ac9..8063ad5 100644 --- a/src-tauri/src/module/record.rs +++ b/src-tauri/src/module/record.rs @@ -23,7 +23,8 @@ use crossbeam_channel::{unbounded, Receiver}; use tauri::{api::path::data_dir, AppHandle, Manager}; use super::{ - recognizer::MyRecognizer, sqlite::Sqlite, transcription::Transcription, writer::Writer, + recognizer::MyRecognizer, sqlite::Sqlite, transcription::Transcription, + transcription_online::TranscriptionOnline, writer::Writer, }; pub struct Record { @@ -140,7 +141,6 @@ impl Record { let is_converting = Arc::new(Mutex::new(false)); let (stop_convert_tx, stop_convert_rx) = unbounded(); let is_no_transcription = transcription_accuracy == "off"; - let app_handle = self.app_handle.clone(); thread::spawn(move || loop { match notify_decoding_state_is_finalized_rx.try_recv() { @@ -177,7 +177,6 @@ impl Record { .lock() .unwrap() .replace(Writer::build(&audio_path.to_str().expect("error"), spec)); - if !is_no_transcription && !*is_converting.lock().unwrap() { let is_converting_clone = Arc::clone(&is_converting); let app_handle_clone = app_handle.clone(); @@ -188,13 +187,24 @@ impl Record { let mut lock = is_converting_clone.lock().unwrap(); *lock = true; drop(lock); - let mut transcription = Transcription::new( - app_handle_clone, - transcription_accuracy_clone, - speaker_language_clone, - note_id, - ); - transcription.start(stop_convert_rx_clone, false); + if transcription_accuracy_clone.starts_with("online") { + let mut transcription_online = TranscriptionOnline::new( + app_handle_clone, + transcription_accuracy_clone, + speaker_language_clone, + note_id, + ); + transcription_online.start(stop_convert_rx_clone, false); + } else { + let mut transcription = Transcription::new( + app_handle_clone, + transcription_accuracy_clone, + speaker_language_clone, + note_id, + ); + transcription.start(stop_convert_rx_clone, false); + } + let mut lock = is_converting_clone.lock().unwrap(); *lock = false; drop(lock); diff --git a/src-tauri/src/module/sqlite.rs b/src-tauri/src/module/sqlite.rs index e2ae4fc..211117f 100644 --- a/src-tauri/src/module/sqlite.rs +++ b/src-tauri/src/module/sqlite.rs @@ -73,6 +73,14 @@ impl Sqlite { }); } + pub fn select_whisper_token(&self) -> Result { + return self.conn.query_row( + "SELECT setting_status FROM settings WHERE setting_name = \"settingKey\"", + params![], + |row| Ok(row.get_unwrap(0)), + ); + } + pub fn update_model_vosk_to_whisper( &self, id: u16, diff --git a/src-tauri/src/module/transcriber.rs b/src-tauri/src/module/transcriber.rs index 543ba33..9e3f8b2 100644 --- a/src-tauri/src/module/transcriber.rs +++ b/src-tauri/src/module/transcriber.rs @@ -29,44 +29,47 @@ impl Transcriber { speaker_language: String, transcription_accuracy: String, ) -> FullParams<'static, 'static> { - let mut language = "ja"; - if speaker_language.starts_with("en-us") || speaker_language.starts_with("small-en-us") { - language = "en"; + let language = if speaker_language.starts_with("en-us") + || speaker_language.starts_with("small-en-us") + { + "en" } else if speaker_language.starts_with("cn") || speaker_language.starts_with("small-cn") { - language = "zh"; + "zh" } else if speaker_language.starts_with("small-ko") { - language = "ko"; + "ko" } else if speaker_language.starts_with("fr") || speaker_language.starts_with("small-fr") { - language = "fr"; + "fr" } else if speaker_language.starts_with("de") || speaker_language.starts_with("small-de") { - language = "de"; + "de" } else if speaker_language.starts_with("ru") || speaker_language.starts_with("small-ru") { - language = "ru"; + "ru" } else if speaker_language.starts_with("es") || speaker_language.starts_with("small-es") { - language = "es"; + "es" } else if speaker_language.starts_with("small-pt") { - language = "pt"; + "pt" } else if speaker_language.starts_with("small-tr") { - language = "tr"; + "tr" } else if speaker_language.starts_with("vn") || speaker_language.starts_with("small-vn") { - language = "vi"; + "vi" } else if speaker_language.starts_with("it") || speaker_language.starts_with("small-it") { - language = "it"; + "it" } else if speaker_language.starts_with("small-nl") { - language = "nl"; + "nl" } else if speaker_language.starts_with("small-ca") { - language = "ca"; + "ca" } else if speaker_language.starts_with("uk") || speaker_language.starts_with("small-uk") { - language = "uk"; + "uk" } else if speaker_language.starts_with("small-sv") { - language = "sv"; + "sv" } else if speaker_language.starts_with("hi") || speaker_language.starts_with("small-hi") { - language = "hi"; + "hi" } else if speaker_language.starts_with("small-cs") { - language = "cs"; + "cs" } else if speaker_language.starts_with("small-pl") { - language = "pl"; - } + "pl" + } else { + "ja" + }; let mut params = FullParams::new(SamplingStrategy::BeamSearch { beam_size: 5, patience: 1.0, diff --git a/src-tauri/src/module/transcription_online.rs b/src-tauri/src/module/transcription_online.rs new file mode 100644 index 0000000..4b43b2b --- /dev/null +++ b/src-tauri/src/module/transcription_online.rs @@ -0,0 +1,206 @@ +use tokio::{fs::File, io::AsyncReadExt}; + +use super::sqlite::Sqlite; + +use crossbeam_channel::Receiver; + +use reqwest::{ + header::{HeaderMap, HeaderValue, AUTHORIZATION}, + multipart, Client, +}; +use serde_json::Value; +use tauri::{AppHandle, Manager}; + +#[derive(Debug, Clone, serde::Serialize)] +pub struct TraceCompletion {} + +pub struct TranscriptionOnline { + app_handle: AppHandle, + sqlite: Sqlite, + speaker_language: String, + transcription_accuracy: String, + note_id: u64, + token: String, +} + +impl TranscriptionOnline { + pub fn new( + app_handle: AppHandle, + transcription_accuracy: String, + speaker_language: String, + note_id: u64, + ) -> Self { + let sqlite = Sqlite::new(); + let token = sqlite.select_whisper_token().unwrap(); + Self { + app_handle, + sqlite, + speaker_language, + transcription_accuracy, + note_id, + token, + } + } + + pub fn start(&mut self, stop_convert_rx: Receiver<()>, is_continuous: bool) { + while Self::convert(self).is_ok() { + if is_continuous { + let vosk_speech = self.sqlite.select_vosk(self.note_id); + if vosk_speech.is_err() { + self.app_handle + .clone() + .emit_all("traceCompletion", TraceCompletion {}) + .unwrap(); + break; + } + } + if stop_convert_rx.try_recv().is_ok() { + let vosk_speech = self.sqlite.select_vosk(self.note_id); + if vosk_speech.is_err() { + self.app_handle + .clone() + .emit_all("traceCompletion", TraceCompletion {}) + .unwrap(); + } else { + self.app_handle + .clone() + .emit_all("traceUnCompletion", TraceCompletion {}) + .unwrap(); + } + break; + } + } + } + + #[tokio::main] + async fn request( + speaker_language: String, + file_path: String, + token: String, + is_translate: bool, + ) -> Result> { + let url = if is_translate { + "https://api.openai.com/v1/audio/translations" + } else { + "https://api.openai.com/v1/audio/transcriptions" + }; + + let model = "whisper-1"; + + let client = Client::new(); + + let mut headers = HeaderMap::new(); + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {}", token))?, + ); + let mut file = File::open(file_path).await?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).await?; + + let part_file = multipart::Part::bytes(buffer) + .file_name("test.wav") + .mime_str("audio/wav")?; + + let part_model = multipart::Part::text(model); + let language = if speaker_language.starts_with("en-us") + || speaker_language.starts_with("small-en-us") + { + "en" + } else if speaker_language.starts_with("cn") || speaker_language.starts_with("small-cn") { + "zh" + } else if speaker_language.starts_with("small-ko") { + "ko" + } else if speaker_language.starts_with("fr") || speaker_language.starts_with("small-fr") { + "fr" + } else if speaker_language.starts_with("de") || speaker_language.starts_with("small-de") { + "de" + } else if speaker_language.starts_with("ru") || speaker_language.starts_with("small-ru") { + "ru" + } else if speaker_language.starts_with("es") || speaker_language.starts_with("small-es") { + "es" + } else if speaker_language.starts_with("small-pt") { + "pt" + } else if speaker_language.starts_with("small-tr") { + "tr" + } else if speaker_language.starts_with("vn") || speaker_language.starts_with("small-vn") { + "vi" + } else if speaker_language.starts_with("it") || speaker_language.starts_with("small-it") { + "it" + } else if speaker_language.starts_with("small-nl") { + "nl" + } else if speaker_language.starts_with("small-ca") { + "ca" + } else if speaker_language.starts_with("uk") || speaker_language.starts_with("small-uk") { + "uk" + } else if speaker_language.starts_with("small-sv") { + "sv" + } else if speaker_language.starts_with("hi") || speaker_language.starts_with("small-hi") { + "hi" + } else if speaker_language.starts_with("small-cs") { + "cs" + } else if speaker_language.starts_with("small-pl") { + "pl" + } else { + "ja" + }; + let part_language = multipart::Part::text(language); + + let form = if is_translate { + multipart::Form::new() + .part("file", part_file) + .part("model", part_model) + } else { + multipart::Form::new() + .part("file", part_file) + .part("model", part_model) + .part("language", part_language) + }; + + let response = client + .post(url) + .headers(headers) + .multipart(form) + .send() + .await?; + + println!("Status: {}", response.status()); + let json_response: Value = response.json().await?; + println!("Response: {:?}", json_response); + let response_text = json_response["text"] + .as_str() + .unwrap_or("text field not found"); + + Ok(response_text.to_string()) + } + + fn convert(&mut self) -> Result<(), rusqlite::Error> { + let vosk_speech = self.sqlite.select_vosk(self.note_id); + return vosk_speech.and_then(|speech| { + let result = Self::request( + self.speaker_language.clone(), + speech.wav, + self.token.clone(), + self.transcription_accuracy.ends_with("en"), + ); + + if result.is_ok() { + let updated = self + .sqlite + .update_model_vosk_to_whisper(speech.id, result.unwrap()); + + let updated = updated.unwrap(); + if updated.content != "" { + self.app_handle + .clone() + .emit_all("finalTextConverted", updated) + .unwrap(); + } + } else { + println!("whisper api is temporally failed, so skipping...") + } + + Ok(()) + }); + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 64ead4f..a42616c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "Lycoris", - "version": "0.7.0" + "version": "0.8.0" }, "tauri": { "allowlist": { diff --git a/src/components/molecules/AudioDevice.tsx b/src/components/molecules/AudioDevice.tsx index 26e9cb1..9fcdbff 100644 --- a/src/components/molecules/AudioDevice.tsx +++ b/src/components/molecules/AudioDevice.tsx @@ -15,8 +15,8 @@ const AudioDevices = (): JSX.Element => { } return ( - + {audioDevices?.map((device, i) => ( ))} diff --git a/src/components/molecules/SettingKey.tsx b/src/components/molecules/SettingKey.tsx new file mode 100644 index 0000000..524a1a7 --- /dev/null +++ b/src/components/molecules/SettingKey.tsx @@ -0,0 +1,22 @@ +import { ChangeEvent } from "react"; +import { useRecoilState } from 'recoil'; +import { settingKeyState } from "../../store/atoms/settingKeyState"; + +const SettingKey = (): JSX.Element => { + const [settingKey, setSettingKey] = useRecoilState(settingKeyState) + + const change = (e: ChangeEvent) => { + const settingKey = e.target.value + console.log("test") + setSettingKey(settingKey) + } + + return ( +
+

APIキー

+ +
+ ) +} + +export { SettingKey } diff --git a/src/components/molecules/SpeakerLanguage.tsx b/src/components/molecules/SpeakerLanguage.tsx index b4e92cf..463fdbe 100644 --- a/src/components/molecules/SpeakerLanguage.tsx +++ b/src/components/molecules/SpeakerLanguage.tsx @@ -82,8 +82,8 @@ const SpeakerLanguage = (): JSX.Element => { } return ( - + {downloadedModels?.map((model, i) => ( ))} diff --git a/src/components/molecules/SpeechHistory.tsx b/src/components/molecules/SpeechHistory.tsx index c14f28f..8cc7dad 100644 --- a/src/components/molecules/SpeechHistory.tsx +++ b/src/components/molecules/SpeechHistory.tsx @@ -40,10 +40,10 @@ const SpeechHistory = (props: SpeechHistoryProps): JSX.Element => { if (i > 0 && cal === dayjs.unix(histories[i - 1].created_at_unixtime).format('YYYY-M-D')) { cal = "" } - return (<> + return (
{cal &&
0 ? 'mt-6 ' : '') + 'mb-2'}>[{cal}]
} - - ) + +
) } )} diff --git a/src/components/molecules/TranscriptionAccuracy.tsx b/src/components/molecules/TranscriptionAccuracy.tsx index 8007b72..ad1cef5 100644 --- a/src/components/molecules/TranscriptionAccuracy.tsx +++ b/src/components/molecules/TranscriptionAccuracy.tsx @@ -4,12 +4,14 @@ import { transcriptionAccuracyState } from "../../store/atoms/transcriptionAccur import { modelWhisperDownloadedState } from "../../store/atoms/modelWhisperDownloadedState"; import { recordState } from "../../store/atoms/recordState"; import { speakerLanguageState } from "../../store/atoms/speakerLanguageState"; +import { settingKeyState } from "../../store/atoms/settingKeyState"; const TranscriptionAccuracy = (): JSX.Element => { const downloadedModels = useRecoilValue(modelWhisperDownloadedState) const [transcriptionAccuracy, setTranscriptionAccuracy] = useRecoilState(transcriptionAccuracyState) const isRecording = useRecoilValue(recordState) const speakerLanguage = useRecoilValue(speakerLanguageState) + const settingKey = useRecoilValue(settingKeyState) const change = (e: ChangeEvent) => { const transcriptionAccuracy = e.target.value @@ -36,9 +38,9 @@ const TranscriptionAccuracy = (): JSX.Element => { } return ( - + + {downloadedModels?.reduce((a: string[], c) => { if (speakerLanguage?.startsWith("en-us") || speakerLanguage?.startsWith("small-en-us")) { return [...a, c] @@ -47,6 +49,9 @@ const TranscriptionAccuracy = (): JSX.Element => { }, []).map((model, i) => ( ))} + {settingKey && <> + + {(!speakerLanguage?.startsWith("en-us") && !speakerLanguage?.startsWith("small-en-us")) && ()}} ) } diff --git a/src/components/organisms/NoteFooter.tsx b/src/components/organisms/NoteFooter.tsx index bc969d6..3b2b08f 100644 --- a/src/components/organisms/NoteFooter.tsx +++ b/src/components/organisms/NoteFooter.tsx @@ -3,7 +3,12 @@ import { useRecoilValue, useSetRecoilState } from 'recoil' import { selectedNoteState } from '../../store/atoms/selectedNoteState' import { speechHistoryState } from '../../store/atoms/speechHistoryState' -const NoteFooter = (): JSX.Element => { +type NoteFooterProps = { + titleRef: React.RefObject +} + +const NoteFooter = (props: NoteFooterProps): JSX.Element => { + const { titleRef } = props; const inputEl = useRef(null) const selectedNote = useRecoilValue(selectedNoteState) const setHistories = useSetRecoilState(speechHistoryState(selectedNote!.note_id)) @@ -28,7 +33,7 @@ const NoteFooter = (): JSX.Element => { } } useEffect(() => { - if (inputEl.current) { + if (inputEl.current && (document.activeElement !== titleRef.current)) { inputEl.current.focus(); } }, [selectedNote]); diff --git a/src/components/organisms/NoteMain.tsx b/src/components/organisms/NoteMain.tsx index c48c7ec..f6a4170 100644 --- a/src/components/organisms/NoteMain.tsx +++ b/src/components/organisms/NoteMain.tsx @@ -31,6 +31,7 @@ const NoteMain = (): JSX.Element => { const [editTitle, setEditTitle] = useState(false); const isTracing = useRecoilValue(tracingState); const bottomRef = useRef(null); + const inputEl = useRef(null); useEffect(() => { if (recordingNote === selectedNote!.note_id) { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); @@ -86,7 +87,7 @@ const NoteMain = (): JSX.Element => {

{ e.preventDefault(); setEditTitle(true); }}> {editTitle ? - { if (e.key === "Enter" && e.keyCode === 13) { setEditTitle(false) @@ -124,7 +125,7 @@ const NoteMain = (): JSX.Element => {
{partialText}
- +
) } diff --git a/src/components/organisms/SettingsMain.tsx b/src/components/organisms/SettingsMain.tsx index 883220b..2768183 100644 --- a/src/components/organisms/SettingsMain.tsx +++ b/src/components/organisms/SettingsMain.tsx @@ -4,6 +4,7 @@ import { ModelDownloadVoskButton } from "../molecules/ModelDownloadVoskButton" import { ModelDownloadVoskProgress } from "../molecules/ModelDownloadVoskProgress" import { ModelDownloadWhisperButton } from "../molecules/ModelDownloadWhisperButton" import { ModelDownloadWhisperProgress } from "../molecules/ModelDownloadWhisperProgress" +import { SettingKey } from "../molecules/SettingKey" import { SettingLanguages } from "../molecules/SettingLanguages" const SettingsMain = (): JSX.Element => { @@ -317,7 +318,7 @@ const SettingsMain = (): JSX.Element => { - {/*
+

@@ -327,15 +328,12 @@ const SettingsMain = (): JSX.Element => {

OpenAI社のAPIを利用することで、

-

高速な追っかけ文字起こしが選択可能となります。

+

高速な追っかけ文字起こし・翻訳が選択可能となります。

(APIの利用に関しては、OpenAI社の利用規約を参照ください。)

-
-

APIキー

- -
+
-

*/} +
) } diff --git a/src/store/atoms/settingKeyState.ts b/src/store/atoms/settingKeyState.ts new file mode 100644 index 0000000..b26eded --- /dev/null +++ b/src/store/atoms/settingKeyState.ts @@ -0,0 +1,35 @@ +import { atom, AtomEffect } from 'recoil' +import DB from '../../lib/sqlite'; + +const sqliteEffect: AtomEffect = ({setSelf, onSet, trigger}) => { + const loadPersisted = async () => { + const db = (await DB.getInstance()) + const savedValue = await db.loadSetting("settingKey"); + if (savedValue === null) { + setSelf(""); + } else { + setSelf(savedValue!.setting_status); + } + }; + + if (trigger === 'get') { + loadPersisted(); + } + + onSet(async(newValue, _, isReset:any) => { + const db = await DB.getInstance() + if (isReset) { + await db.updateSetting("settingKey", "") + } else { + await db.updateSetting("settingKey", newValue) + } + }); +}; + +export const settingKeyState = atom({ + key: 'settingKeyState', + default: "", + effects: [ + sqliteEffect, + ] +}) \ No newline at end of file diff --git a/src/store/atoms/settingLanguageState.ts b/src/store/atoms/settingLanguageState.ts index 8b43a65..aeac0bb 100644 --- a/src/store/atoms/settingLanguageState.ts +++ b/src/store/atoms/settingLanguageState.ts @@ -5,7 +5,11 @@ const sqliteEffect: AtomEffect = ({setSelf, onSet, trigger}) => { const loadPersisted = async () => { const db = (await DB.getInstance()) const savedValue = await db.loadSetting("settingLanguage"); - setSelf(savedValue!.setting_status); + if (savedValue === null) { + setSelf("日本語"); + } else { + setSelf(savedValue!.setting_status); + } }; if (trigger === 'get') { diff --git a/src/store/atoms/speakerLanguageState.ts b/src/store/atoms/speakerLanguageState.ts index 794b648..562b23b 100644 --- a/src/store/atoms/speakerLanguageState.ts +++ b/src/store/atoms/speakerLanguageState.ts @@ -7,7 +7,7 @@ const sqliteEffect: AtomEffect = ({setSelf, onSet, trigger}) => { const savedValue = await db.loadSetting("speakerLanguage"); if (savedValue === null) { setSelf(null); - }else{ + } else { setSelf(savedValue.setting_status); } };