[[📒Articles]] > [[📒2022 Articles]] ![[2022-03-27.jpg|cover-picture]] [[Tauri]]を䜿っお自分だけの[[Slack]]クラむアントを䜜っおみた蚘録です。 > [!warning] #2022/07/17 远蚘 > この蚘事は #2022/03/27 時点の内容です。その埌、[[Tauri]]はv1.0がリリヌスされ、ドキュメントリンクや手順がそれなりに倉曎されおいたす。 > **本蚘事の内容は昔の情報ずしおの参考に留めおください**。 > [!warning] #2025/02/20 远蚘 > 元の参考サむトもいく぀か消倱しおいたす。 ## はじめに 『[[Slack]]クラむアントを実装した』ずいうず凄そうですが、**今できる機胜はフリヌワヌドによるMessageの怜玢だけです。぀たり、䜿い物になりたせん**。それではなぜ、このようなものを䜜り始めたのかずいうず、以䞋の理由からです。 - 公匏[[Slack]]クラむアントでは実装されおいないが欲しい機胜があった - なんでもいいから[[Rust]]でプロダクトを曞くモチベヌションが欲しかった ### ゚ゎサヌチず[[🧊Slackego]] 欲しい機胜の1぀ずしお゚ゎサヌチ機胜がありたす。これに぀いおは、以前に[[Nim]]で[[🧊Slackego]]ずいうプロダクトを䜜成したこずがありたす。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" /> <span class="link-card-v2-site-name">GitHub</span> </div> <div class="link-card-v2-title"> GitHub - tadashi-aikawa/slackego: Ego search script for Slack. </div> <div class="link-card-v2-content"> Ego search script for Slack. Contribute to tadashi-aikawa/slackego development by creating an account on GitHub. </div> <img class="link-card-v2-image" src="https://opengraph.githubassets.com/798c31b76fbd73d7bbf4095aaf1954abadf26ef3953d7421b26910517d52fbe5/tadashi-aikawa/slackego" /> <a href="https://github.com/tadashi-aikawa/slackego"></a> </div> 4幎も前ですが、実は今もコレ䜿っおいたす。自分で蚀うのもなんですが、芁求に芋合ういい萜ずしどころを実珟した[[CLI]]ツヌルだず思っおいたす。 ### [[Rust]]を䜿うモチベヌション 最近、[[Rust]]は䞖界的にも盛り䞊がりを芋せおいたす。私自身も23幎前から瀟内ツヌルや[[OSS]]のCLIツヌルで少し詊しおきお、昚幎はプロダクトに関するコンバヌタで正匏に導入もしたした。 䞀方、[[Rust]]の経隓を積む機䌚はあたり䜜れおいたせん。私が蚀語を習埗する最倧のモチベヌションは**䜜りたいツヌルがあるこず**ですが、[[CLI]]やAPIを䜜るだけなら[[Rust]]よりも[[Go]]や[[Kotlin]]で枈むケヌスが倚いです。[[GUI]]に至っおは間違いなく[[TypeScript]]䞀択でしょう。现かいこずは気にせず䜜っおしたえばいいのですが、==技術や勉匷自䜓は奜きでない私にずっお、業務倖でそのモチベを保぀こずは困難==でした。 ### [[Tauri]]のrc版リリヌス そんなずき急遜[[Tauri]]の蚘事が目に入りたした。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://www.publickey1.jp/favicon.ico" /> <span class="link-card-v2-site-name">www.publickey1.jp</span> </div> <div class="link-card-v2-title"> Electronの代替を目指す軜量なRust補フレヌムワヌク「Tauri」、リリヌス候補版に到達 </div> <div class="link-card-v2-content"> WindowsやmacOS、Linuxなどのクロスプラットフォヌム察応のデスクトップアプリ開発を容易にするフレヌムワヌクずしお高い人気を持぀フレヌムワヌクが「Electron」です。 ElectronはChromiumずNo ... </div> <img class="link-card-v2-image" src="https://www.publickey1.jp/2022/btuvp2SE.jpg" /> <a href="https://www.publickey1.jp/blog/22/electronrusttauri.html"></a> </div> [[Tauri]]を知ったのはこれが初めおではありたせん。ただ、1幎近く前に芋たずきは『[[TypeScript]]ず[[Rust]]で[[Electron]]の代甚? 倢のある話だねえ...』ず本気にしおいたせんでした。その[[Tauri]]がもうすぐv1.0.0をリリヌスしようずしおいたのです。 ### なぜ[[Tauri]]を受け入れたのか? 珟時点だず圧倒的な実瞟の違いや技術的ハヌドルの䜎さから、[[Electron]]を䜿うのが䞀般的でしょう。仕事でもデスクトップアプリケヌションは[[Electron]]を䜿っおいたす。しかし、そんなこずを蚀っおいたら䞀生[[Rust]]を䜿う機䌚は来ないかもしれない...これが最埌のチャンスだず思い[[Tauri]]を䜿っおみるこずにしたした。 ### なぜ[[Slack]]クラむアントか? デスクトップアプリケヌションの匷みを生かしお䜜りたいモノが他になかったからです。 倧抵のツヌルはブラりザで動くWebアプリケヌションの方が䟿利です。[[Electron]]か[[Tauri]]か...ずいう前に、そもそもデスクトップアプリケヌションずしお䜜るべきか? で倧抵が华䞋になりたす。 そんなデスクトップアプリケヌションにも匷みはありたす。最も重芁なポむントは==[[CORS]]を気にする必芁がない==こずでしょう。倖郚ずのやりずりをmainプロセスで完結させ、renderプロセスを衚瀺ずアクション着火に集䞭させるこずで、開発スピヌドずセキュリティの䞡方を担保できるず考えおいたす。[[Tauri]]を䜿うこずで、mainプロセス郚分を[[Rust]]で曞けるのです 長くなりたしたが本題に入っおいきたす。 > [!attention] > 本皿は[[TypeScript]]ず[[Rust]]のコヌドを普通に曞くこずができる前提で曞かれおいたす。そのため、それらの技術に぀いお䞁寧な解説はしたせん。 ## [[Tauri]]ずは 改めたしお、[[Tauri]]はWebフロント゚ンドの技術でデスクトップアプリケヌションを䜜成するフレヌムワヌクです。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://tauri.app/favicon.svg" /> <span class="link-card-v2-site-name">Tauri</span> </div> <div class="link-card-v2-title"> Tauri 2.0 </div> <div class="link-card-v2-content"> The cross-platform app building toolkit </div> <img class="link-card-v2-image" src="https://v2.tauri.app/og.png?v=1" /> <a href="https://tauri.app/"></a> </div> [[Electron]]ずは異なり[[JavaScript]]([[TypeScript]])だけでなく[[Rust]]が取り入れらおいるずいうのが特城的です。 | | バック゚ンド | フロント゚ンド | | ------------ | ---------------------------- | -------------- | | [[Tauri]] | **[[Rust]]** | [[JavaScript]] | | [[Electron]] | [[JavaScript]] ([[Node.js]]) | [[JavaScript]] | 他に気になった特城は以䞋のずおりです。詳现は公匏サむトをご芧ください。 - むンストヌラヌのサむズが小さい - [[Node.js]]ずは異なり[[Rust]]は実行時のランタむムが䞍芁だから - [[OS]]によっお芋た目が倉わる可胜性がある - [[Chromium]]ではなく[[OS]]のWebViewを䜿うから - [[Linux]]、[[macOS]]、[[Windows]]をサポヌト - クロスコンパむル未察応だが、[[Tauri GitHub Action]]を䜿えばリリヌスは可胜 - 将来的にバック゚ンドは[[Rust]]以倖の蚀語にも察応する぀もり - [[Go]]、[[Nim]]、[[Python]]、[[C♯]]など ## プロゞェクトベヌスを䜜る ここからは実践しおきたす。 ### 前提の環境䜜り [[Rust]]をはじめずしたいく぀かのツヌルチェむンが必芁です。公匏ドキュメントを参考にしおください。 > [!trash] #2025/02/20 この参考ペヌゞは404になりたした > [[Windows]]をお䜿いなら、[[📜Tauriで最小構成のWindowsアプリケヌションを立ち䞊げおみる]] が圹に立぀かもしれたせん。 ### [[Tauri]]プロゞェクトの䜜成 [[create-tauri-app]]を䜿いたす。ツヌル名が[[🧊Vigilancia]]なので、そのように指定しおいたす。たた、フロント゚ンドフレヌムワヌクは[[Svelte]]を遞びたした。 ```console $ npx create-tauri-app Ok to proceed? (y) y ? What is your app name? polaris ? What should the window title be? Polaris ? What UI recipe would you like to add? create-vite (vanilla, vue, react, svelte, preact, lit) (https://vitejs.dev/guide/#scaffolding-your-first-vite-project) ? Add "@tauri-apps/api" npm package? Yes ? Which vite template would you like to use? svelte-ts ``` > [!hint]- なぜ[[Svelte]]か? > 最近[[Svelte]]を䜿い始めお気に入っおきたからです。仕事では[[Vue]]ばかり䜿っおおり、[[React]]や[[Angular]]は肌に合わないため芋送っおいたす。 ディレクトリに移動しお開発甚のコマンドを実行したす。 ```console cd polaris npm run tauri dev ``` 新しいりィンドりが立ち䞊がればOKです。 > [!attention] > 初回は[[Rust]]の䟝存関係をフルビルドするため時間がかかりたす。2回目以降は、䟝存関係が倉わらなければすぐに終わりたす。 ### [[Svelte Material UI]]の導入 UIフレヌムワヌクは[[Svelte Material UI]]を䜿いたす。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://sveltematerialui.com/icons/android-icon-192x192.png" /> <span class="link-card-v2-site-name">sveltematerialui.com</span> </div> <div class="link-card-v2-title"> Svelte Material UI </div> <div class="link-card-v2-content"> Material UI Components for Svelte, ready to use in your app. </div> <img class="link-card-v2-image" src="https://sveltematerialui.com/header.png" /> <a href="https://sveltematerialui.com/INSTALL.md"></a> </div> > [!hint]- なぜ[[Svelte Material UI]]か? > 䞀番スタヌ数が倚く、実装に必芁な最䜎限のコンポヌネントが䞀通り揃っおいるからです。 たずはむンストヌルしたす。 ```console npm install --save svelte-material-ui ``` スタむルやアむコン察応のため`index.html`にいく぀か`link`タグを远加したす。 ```html:index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <!-- Theme --> <link rel="stylesheet" href="node_modules/svelte-material-ui/bare.css" /> <!-- Material Icons --> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" /> <!-- Roboto --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700" /> <!-- Roboto Mono --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Mono" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Svelte + TS + Vite App</title> </head> <body> <div id="app"></div> <script type="module" src="/src/main.ts"></script> </body> </html> ``` > [!error]- Sourcemap for ".../@material/..." proints to missing source files > [[Svelte Material UIでsource fileのmissing゚ラヌが発生する]] を参照。 ### [[Prettier]]の適応 [[SvelteにPrettierを適応]]したす。 ```console npm i --save-dev prettier-plugin-svelte prettier ``` [[SvelteのPrettierでscript、style、templateの順番を倉曎]]したかったので[[Svelte Sort Order]]を`markup-scripts-styles`に蚭定したした。 ### `~`を`./src`ずしおimportできるようにする [[tsconfig.json]]に[[paths (tsconfig)|paths]]を远加したす。 ```json { "compilerOptions": { "paths": { "~/*": ["./src/*"] }, } } ``` [[vite.config.ts]]にも`resolve.alias`が必芁なので忘れずに。 ```ts export default defineConfig({ resolve: { alias: { "~": path.resolve(__dirname, "src"), }, }, }); ``` > [!info] > [[📝Viteでtsconfig.pathsの蚭定が有効にならない]] も参考に。 ### Taskfileの远加 普段、タスクランナヌには[[Task]]を䜿っおいるため远加したす。 ```yaml:Taskfile.yml version: "3" tasks: default: - task: help help: silent: true cmds: - task -l dev: desc: 開発ビルド cmds: - npm run tauri dev check: desc: cargo check dir: ./src-tauri cmds: - cargo check build: desc: 補品ビルド cmds: - npm run tauri build ``` > [!info] > [[Task]]に぀いお知りたい方は [[📘Windowsにも優しいタスクランナヌTaskを詊しおみた]] もご芧ください。 これで䞋準備が終わりたした。いよいよ[[Tauri]]を䜿った実装を開始したす。 ## リストの衚瀺 たずは[[Svelte]]の方で適圓にリストを衚瀺しおみたす。。`App.svelte`を線集したす。 ```html:App.svelte <main> <List> {#each messages as message} <Item ><Graphic class="material-icons">send</Graphic><Text>{message}</Text ></Item> {/each} </List> </main> <script lang="ts"> import List, { Item, Text, Graphic } from "@smui/list"; let messages = ["aaa", "iii"]; </script> ``` ビルドするずそれっぜいものが衚瀺されたした。 ![[Pasted image 20220326135638.png]] ## [[Rust]]ず[[TypeScript]]間でデヌタのやりずり ここから[[Rust]]のコヌドにも手を加えおいきたす。 ### Lockdownレシピ [[Tauri]]では代衚的な蚭蚈ずしおいく぀かのレシピが玹介されおいたす。今回はLockdownレシピを䜿うこずにしたした。 > [!trash] #2025/02/20 この参考ペヌゞは404になりたした > > [!hint]- なぜLockdownレシピか? > [[Tauri]]を䜿うモチベヌションずしお、実甚的な[[Rust]]の経隓を積みたいずいうものがありたす。Lockdownパタヌンはフロント゚ンドの描画・アクション以倖の倚くを[[Rust]]偎で受け持ち、[[Promise (JavaScript)|Promise]]を䜿っおやりずりする思想であるため、バック゚ンドを[[Rust]]で開発するのず同等の経隓が埗られそうだず思ったからです。 レシピの案内に埓い、`tauri.conf.json`の`allowlist`を空にしたす。これで[[TypeScript]]から[[Tauri]]を䜿っお実行できるこずは皆無になりたした。 ```diff "tauri": { - "allowlist": { - ... - } + "allowlist": {} } ``` ### Command [[Rust]]ず[[TypeScript]]のやりずりにはCommandを䜿いたす。 > [!trash] #2025/02/20 この参考ペヌゞは404になりたした > > [!attention] > [Events](https://tauri.studio/docs/guides/events) もあるので混同しないよう泚意しおください。 [[Rust]]のコヌドに`fetch_messages`ずいう名前のコマンドを䜜成したす。 ```rust:main.rs #![cfg_attr( all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" )] #[tauri::command] fn fetch_messages() -> Vec<String> { vec!["RRR".into(), "UUU".into(), "SSS".into(), "TTT".into()] } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![fetch_messages]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` `#[tauri::command]`でコマンドずしお認識され、`tauri::generate_handler`でコマンドを登録したす。 次に[[TypeScript]]のコヌドから`fetch_messages`コマンドを呌び出しおみたす。 ```html:App.svelte <main> <Button on:click={handleClickUpdate}>Update</Button> <List> {#each messages as message} <Item ><Graphic class="material-icons">send</Graphic><Text>{message}</Text ></Item> {/each} </List> </main> <script lang="ts"> import List, { Item, Text, Graphic } from "@smui/list"; import Button from "@smui/button"; import { invoke } from "@tauri-apps/api/tauri"; let messages = ["aaa", "iii"]; const handleClickUpdate = async () => { messages = await invoke<string[]>("fetch_messages"); }; </script> ``` `invoke`でコマンド名を指定したす。[[型匕数 (TypeScript)|型匕数]]にはコマンドの[[戻り倀型]]を指定したす。結果は[[Promise (JavaScript)|Promise]]ずしお返华されたす。 > [!hint]- [[Rust]]のコマンド関数が倱敗したずきは? > コマンド関数の[[戻り倀型]]を[[Result]]にすれば、倱敗時の凊理は[[Promise (JavaScript)|Promise]]のrejectedで衚珟できたす。 期埅通りに動くこずを確認したしょう。 ![[2022-03-26.gif]] ## [[Rust]]で[[Slack]]のメッセヌゞを怜玢 それでは実際に[[Slack]]のメッセヌゞを怜玢し、衚瀺しおみたしょう。 > [!attention] > ここから䞀気に実践的な内容ずなりたす。䞍明な点はリンク先や公匏サむトを蟿っお補完いただければず思いたす。 [[Slack Web API]]を䜿いたす。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://a.slack-edge.com/80588/marketing/img/meta/favicon-32.png" /> <span class="link-card-v2-site-name">Slack API</span> </div> <div class="link-card-v2-title"> Using the Slack Web API </div> <div class="link-card-v2-content"> The Web API is an RPC-style collection of methods that either yield information about Slack workspaces or allows ... </div> <img class="link-card-v2-image" src="https://a.slack-edge.com/80588/img/services/api_200.png" /> <a href="https://api.slack.com/web"></a> </div> > [!info] [[Slack Web API]]の利甚手順に぀いお > [[📜Slack Web APIを䜿っおみる]] も参考にしおください。 > [!hint]- Polarisでredirect URLを䜿った[[OAuth]]認蚌をしない理由 > 2021幎12月時点では[[Tauri]]がそれに察応しおおらず、回避策を䜿う堎合はロヌカルサヌバヌが必芁になるためです。 > > [OAuth login: tricky window URL manipulation · Discussion \#3020 · tauri\-apps/tauri](https://github.com/tauri-apps/tauri/discussions/3020) ### メッセヌゞ怜玢API [[ナヌザヌトヌクン (Slack)|ナヌザヌトヌクン]]に`search:read`スコヌプを蚭定し、結果が返华されるこずを確認したす。 ```bash curl "https://slack.com/api/search.messages?query=Slack&pretty=1" -H "Authorization: Bearer <トヌクン>" ``` ### 環境倉数の蚭定 トヌクンを環境倉数から取埗するようにするため、[[std.env.var]]を䜿いたす。 ```rust std::env::var("POLARIS_SLACK_USER_TOKEN") .expect("POLARIS_SLACK_USER_TOKEN is not set."); ``` ### [[Slack Web API]]ぞのリク゚ストず結果取埗 [[Slack Web API]]は倖郚ドメむンなのでパッケヌゞ構成を芋盎したす。`external/slack.rs`ず`external/slack`配䞋に実装したす。 ```ls  . ├──  command │ └──  search_messages.rs ├──  command.rs ├──  external │ ├──  slack │ │ └──  search_messages.rs │ └──  slack.rs ├──  external.rs └──  main.rs ``` `search_messages.rs`は[[Slack Web API]]のレスポンス構造䜓を定矩しおいたす。必芁だず思ったもの以倖は省略しおいたす。 ```rust:external/slack/search_messages.rs use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct Response { pub ok: bool, pub messages: Messages, } #[derive(Debug, Serialize, Deserialize)] pub struct Messages { pub total: i32, pagination: Pagination, pub matches: Vec<Message>, } #[derive(Debug, Serialize, Deserialize)] struct Pagination { /// ex: 145253 total_count: i32, /// ex: 1 page: i32, /// ex: 20 per_page: i32, /// ex: 7263 page_count: i32, /// ex: 1 first: i32, /// ex: 20 last: i32, } #[derive(Debug, Serialize, Deserialize)] pub struct Message { /// ex: 123456789-1234-1234-abcd-123456abcd78 pub iid: String, /// ex: 123.45678 pub score: f64, /// ex: U1ABCDE3 pub user: String, /// ex: tadashi-aikawa pub username: String, /// ex: 1647445574.312239 pub ts: String, /// channel information pub channel: Channel, /// ex: 本文です pub text: String, /// ex: https://hoge.slack.com/archives/U1ABCDE3/p123456789012345678 pub permalink: String, } #[derive(Debug, Serialize, Deserialize)] pub struct Channel { pub id: String, pub name: String, } ``` `slack.rs`は[[Slack Web API]]のむンタヌフェヌスにあたりたす。こちらも必芁なむンタヌフェヌスのみ远加しおいたす。APIが増えたらもう少し抜象化する぀もりです。 ```rust:external/slack.rs pub mod search_messages; use crate::external::slack::search_messages::Response; use anyhow::Error; pub struct SlackClient { token: String, } impl SlackClient { pub fn new(token: &str) -> SlackClient { SlackClient { token: token.into(), } } pub async fn search_message(&self, query: &str, sort: &str) -> Result<Response, Error> { let client = reqwest::Client::new(); Ok(client .get("https://slack.com/api/search.messages") .query(&[("query", query), ("sort", sort), ("pretty", "1")]) .bearer_auth(&self.token) .send() .await? .json::<Response>() .await?) } } ``` `command/search_messages.rs`は`search_messages`コマンドの実装ず入出力のむンタヌフェヌスも兌ねおいたす。日付の倉換が荒いのはご愛嬌💚 ```rust:command/search_messages.rs use anyhow::Result; use chrono::{DateTime, NaiveDateTime, Utc}; use serde::{Deserialize, Serialize}; use crate::external::slack; #[derive(Debug, Serialize, Deserialize)] pub struct Response { messages: Vec<Message>, } #[derive(Debug, Serialize, Deserialize)] struct Message { id: String, user_id: String, user_name: String, channel_name: String, text: String, permalink: String, created_at: DateTime<Utc>, } impl From<&slack::search_messages::Message> for Message { fn from(m: &slack::search_messages::Message) -> Self { let sec = m.ts.split('.').collect::<Vec<_>>()[0]; let sec = sec.parse::<i64>().unwrap(); Message { id: m.iid.clone(), user_id: m.user.clone(), user_name: m.username.clone(), channel_name: m.channel.name.clone(), text: m.text.clone(), permalink: m.permalink.clone(), created_at: DateTime::from_utc(NaiveDateTime::from_timestamp(sec, 0), Utc), } } } pub async fn exec(query: String) -> Result<Response> { let token = std::env::var("POLARIS_SLACK_USER_TOKEN").expect("POLARIS_SLACK_USER_TOKEN is not set."); let res = slack::SlackClient::new(token.as_str()) .search_message(query.as_str(), "timestamp") .await?; let messages = res .messages .matches .iter() .map(|m| m.into()) .collect::<Vec<_>>(); Ok(Response { messages }) } ``` `main.rs`はシンプルなたたです。 ```rust:main.rs #![cfg_attr( all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" )] mod command; mod external; #[tauri::command] async fn search_messages(query: String) -> Result<command::search_messages::Response, String> { command::search_messages::exec(query) .await .map_err(|e| e.to_string()) } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![search_messages]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` これで[[Rust]]偎の改修は完了です。 > [!attention] > 異垞系の゚ラヌ考慮はちゃんずやっおいたせん。 > [!info] 新たに远加した[[クレヌト]]ず甚途 > - [[reqwest]] > - [[HTTP]]クラむアントずしお > - [[anyhow]] > - ゚ラヌの䌝播を楜にするため > - [[Chrono]] > - 日付や時間のデヌタ型を扱うため ## [[Svelte]]でSlack投皿を衚瀺 [[Rust]]偎の`search_messages`コマンドのむンタヌフェヌスが倉曎されたため、`App.svelte`の`handleClickUpdate`に倉曎が必芁です。せっかくなのでUIも含めお党䜓を敎理しおみたした。 ```ls  . ├──  App.svelte ├──  assets │ └──  svelte.png ├──  components │ └──  molecules │ └──  MessageCard.svelte ├──  lib ├──  main.ts ├──  model │ └──  search-messages.ts └──  vite-env.d.ts ``` `App.svelte`の他、`components/molecules`配䞋にも[[Svelte]]ファむルを远加しおいたす。カヌド郚分をコンポヌネント化したファむルです。 ```html:components/molecules/MessageCard.svelte <Card> <Content> <b>{message.user_name}</b> <span>{message.created_at}</span> <pre style="white-space: pre-wrap">{message.text}</pre> </Content> <Actions> <IconButton class="material-icons" on:click={() => { window.open(message.permalink); }} title="Share">open_in_new</IconButton> </Actions> </Card> <script lang="ts"> import Card, { Content, Actions } from "@smui/card"; import IconButton from "@smui/icon-button"; import type { Message } from "~/model/search-messages"; export let message: Message; </script> ``` たた、`Main.svelte`にもいく぀か修正を加えたした。 - [[Svelteのawait]]を䜿っお非同期凊理をシンプルに衚珟 - `invoke`のパラメヌタずレスポンスの倉曎 - `handleClickUpdate`を`handleClickSearch`に名称倉曎 - UIを党䜓的にパワヌアップ ```html:App.svelte <main> <TextField bind:value={query} label="search query" style="min-width: 250px; margin-left: 30px" /> <Button on:click={handleClickSearch}> <Icon class="material-icons">search</Icon> <Label>Search</Label> </Button> <div style="padding: 30px"> {#await messagesPromise} <LinearProgress indeterminate /> {:then messages} {#each messages as message} <div style="padding: 5px;"> <MessageCard {message} /> </div> {/each} {:catch error} <Paper variant="outlined" style="color: red; border-color: red;"> <Title>Error</Title> <Content>{error}</Content> </Paper> {/await} </div> </main> <script lang="ts"> import { invoke } from "@tauri-apps/api/tauri"; import Button, { Label } from "@smui/button"; import { Icon } from "@smui/icon-button"; import Paper, { Title, Content } from "@smui/paper"; import TextField from "@smui/textfield"; import LinearProgress from "@smui/linear-progress"; import { Message, Response } from "~/model/search-messages"; import MessageCard from "~/components/molecules/MessageCard.svelte"; let messagesPromise: Promise<Message[]> = Promise.resolve([]); let query = ""; const handleClickSearch = () => { messagesPromise = invoke<Response>("search_messages", { query }).then( (r) => r.messages ); }; </script> ``` ## 動䜜結果 ここたでのコヌドを実行した結果です。 ![[2022-03-27.gif]] なお、゚ラヌの堎合は以䞋のような画面が衚瀺されたす。 ![[Pasted image 20220327180714.png]] ## プロダクションビルド 以䞋のコマンドで実行した[[OS]]に察応するむンストヌラヌが䜜成されたす。 ```console npm run tauri build ``` [[📝Svelte Material UIでproductionビルドをするずCSSのimport゚ラヌになる]]問題に少しハマったのでそこだけ泚意ですね。[[Tauri GitHub Action]]を䜿ったリリヌスは詊しおいたせん。 ちなみにむンストヌラヌのサむズは4.3MBでした。100MB近くになる[[Electron]]ず比べるずコンパクトなのはやはり魅力ですね。 ## 良かったこず/気になるこず ### 😄良かった点 #### [[Rust]]も[[TypeScript]]も倉曎したらすぐ[[ホットリロヌド]]がかかる 特に[[Svelte]]は䞀瞬ですね。倉曎がすぐ反映されるのは気持ちいいです。 #### [[Rust]]でがっ぀りコヌドが曞ける 圓初の目的がこれだったため満足しおいたす。[[GUI]]郚分に[[Rust]]は䞍芁なので、[[CLI]]ずほが同じくらい曞けるずいっおも過蚀ではありたせん。 #### 最先端の技術が勉匷できる [[Tauri]]、[[Rust]]、[[Svelte]]のいずれも最先端のスキルセットなため、そのものや公匏ドキュメントに曞いおあるこずが倧倉勉匷になりたす。 #### [[CORS]]を気にしなくおいい [[Electron]]でも[[Node.js]]偎で同じこずはできるず思いたす。ブラりザでのWeb開発ず比范しおのメリットですね。 #### リポゞトリのLanguagesが華やか [[GitHub]]のLanguagesにむケむケ゚ンゞニア感が出たすw (倧したもの䜜っおない割に..) ![[Pasted image 20220327185223.png]] ### 😅気になった点 #### マシンが重い [[Rust]]だけでも重いのに[[Tauri]]や[[Vite]]も動くので...特にバック゚ンド開発時は䞀床ビルドを切った方がいいレベルでした。ただ、これは[[IntelliJ Rust]]や[[IntelliJ IDEA]]、[[Clippy]]の課題なので、[[Tauri]]に関する話ではないかもしれたせん。 特に、[[📝IntelliJ RustのExternal Lintersでファむル保存のたびに実行する方法がない]]のが効いおいたす。==[[IntelliJ IDEA]]のautosave蚭定をoffにしおも==、**コヌドを倉曎するたびに[[Clippy]]が実行され(数秒かかる)、コヌド倉曎を怜知しお[[Tauri]]が再起動する**のがかなりキツむです。[[VSCode]]だずどうなのかは気になるずころです🀔 #### 日本語の情報が少ない い぀ものこずですが新しい技術は日本語の情報が少ないです。ただ幞いなこずに、**[[Rust]]も[[Tauri]]も[[Svelte]]も、公匏ドキュメントの読みやすさはピカむチです**。英語のドキュメントによほど抵抗のある方でなければ、公匏ドキュメントで事足りるのではないかずいう期埅はしおいたす。 #### 珟時点での業務利甚は厳しそう v1.0がリリヌスされおいないこずもそうですが、以䞋2点が気になりたした。 - [[Rust]]の知識が少なからず必芁 - [[OS]]のWebViewを䜿うため[[Electron]]より[[OS]]差分に気を付ける必芁がありそう - [[Windows]]は叀いず動かなそう.. 今回利甚したLockdownレシピは[[Rust]]をガッツリ䜿いたすが、[[Rust]]の実装が䞍芁なレシピも数倚くありたす。ただ、プロゞェクトの構築やビルド、トラブル発生時に[[Rust]]のこずを党く知らないず察凊が難しいず感じたした。ちょっずした瀟内ツヌルで䜿うのであれば問題ないず思いたす。 ## たずめ [[Tauri]]を䜿っお自分だけの[[Slack]]クラむアントを䜜っおみたした。[[Tauri]]はもちろんのこず、[[Svelte]]や[[Rust]]に぀いおも知芋を深めるこずができ非垞に有意矩でした。 長くなっおしたったのでここで切りたしたが、今埌は以䞋にチャレンゞしおみたいず思っおいたす。たた気が向いたら続線を曞きたす。 - textを[[Slack]]のようにフォヌマットする (絵文字、リンク、etc) - [[Tauri]]のStoreを実装する - 蚭定ファむルをロヌカルで管理する - 定期実行で゚ゎサヌチした結果を衚瀺する - ダむレクトメンションのみを衚瀺する - 既読・未読管理 最埌にリポゞトリぞのリンクを貌っおおきたす。珟時点での最新コミットにリンクされおいたすので、よろしければ参考にしおください。READMEなどは適圓です。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" /> <span class="link-card-v2-site-name">GitHub</span> </div> <div class="link-card-v2-title"> GitHub - tadashi-aikawa/vigilancia at bf272bf5c9ed3342a6d2bea5728bf85c10c5d442 </div> <div class="link-card-v2-content"> Desktop application for Slack core user who desires to find beneficial messages in every channel as well as they ... </div> <img class="link-card-v2-image" src="https://opengraph.githubassets.com/321cbbc20c497247021974a3a26a6805a2805412cb92d714281164f8e1ecad7b/tadashi-aikawa/vigilancia" /> <a href="https://github.com/tadashi-aikawa/polaris/tree/bf272bf5c9ed3342a6d2bea5728bf85c10c5d442"></a> </div>