[[ð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>