iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
Modern Web

30天學習Tauri系列 第 27

27.實作Tauri Todo - 從API上獲取天氣資訊

  • 分享至 

  • xImage
  •  

獲取API

我們使用中央氣象局的氣象開放資料平台

我們先註冊後獲取API授權碼
https://ithelp.ithome.com.tw/upload/images/20221012/20108931oLDi4tC2zi.png

先來看一下API Docs

我們這邊要使用現在天氣觀測
https://ithelp.ithome.com.tw/upload/images/20221012/20108931NSIcBIzjFE.png

測試並觀察
https://ithelp.ithome.com.tw/upload/images/20221012/20108931qpUilemdFy.png

https://ithelp.ithome.com.tw/upload/images/20221012/20108931FCnv59jccS.png

加入允許清單

打開tauri.conf.json加入

{
  "tauri": {
    "allowlist": {
      "http": {
        "all": true, 
        "request": true ,
        "scope": ["https://opendata.cwb.gov.tw/api/*"]
      }
    }
  }
}

https://ithelp.ithome.com.tw/upload/images/20221012/20108931CSuW7qk6bA.png

加入fetch

打開todo\src\pages\index.tsx並加入

//
import { fetch } from '@tauri-apps/api/http';

//
cont url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0003-001?Authorization=CWB-DB6D5CC3-504F-4A00-ADFC-3730E9A2C95D&stationId=466920&elementName=TEMP&parameterName=CITY";

const response = await fetch(url, {
  method: 'GET',
  timeout: 30,
});


//
      <div>
        <h1>Temp is : {temp}</h1>
      </div>

https://ithelp.ithome.com.tw/upload/images/20221012/20108931gXavxziQRA.png

新增todo\src\pages\index.tsx

import { useEffect, useState } from "react";
import { invoke } from '@tauri-apps/api/tauri';
import { message, confirm, ask } from '@tauri-apps/api/dialog';
import { sendNotification } from "@tauri-apps/api/notification";
import { writeTextFile, BaseDirectory, readTextFile } from '@tauri-apps/api/fs';
import { unregister, register } from '@tauri-apps/api/globalShortcut';
import { fetch } from '@tauri-apps/api/http';

const itemStyle = {
  padding: '8px',
};

const buttons = [
  {
    value: 10, display: "10s",
  },
  {
    value: 1800, display: "30m",
  }
];

interface Todo {
  id: number,
  title: string,
  date: string,
  done: boolean
}

function App() {
  
  const [todos, setTodos] = useState<Todo[]>([]);
  const [todo, setTodo] = useState<Todo>({id: -1, title: "", date: "None", done: false});
  // Timer
  const [time, setTime] = useState(0);
  const [timerStart, setTimerStart] = useState(false);
  // weather
  const [temp, setTemp] = useState();

  // Weather
  const url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0003-001?Authorization=CWB-DB6D5CC3-504F-4A00-ADFC-3730E9A2C95D&stationId=466920&elementName=TEMP&parameterName=CITY";
  const handleGetWeather = async () => {
    const response = await fetch(url, {
      method: 'GET'
    });
    let data: any = response.data;
    setTemp(data.records.location[0].weatherElement[0].elementValue);
  }
  useEffect(() => {
    handleGetWeather();
  }, []);


  // Shorcut
  const handleShortcutWithImport = async () => {

    await register('Control+Shift+P', () => {
      console.log('Shortcut triggered');
      handleImportFile();
    });
  }

  useEffect(() => {
    handleShortcutWithImport();
  },[]);

  // Export
  const handleExportFile = async () => {
    const separator = ',';
    const keys = Object.keys(todos[0]);
    const csvContent =
      keys.join(separator) +
      '\n' +
      todos.map(row => {
        return keys.map(k => {
          let cell = row[k] === null || row[k] === undefined ? '' : row[k];
          cell = cell instanceof Date
            ? cell.toLocaleString()
            : cell.toString().replace(/"/g, '""');
          if (cell.search(/("|,|\n)/g) >= 0) {
            cell = `"${cell}"`;
          }
          return cell;
        }).join(separator);
      }).join('\n');
    console.log(csvContent);

    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
    let text = await blob.text();
    await writeTextFile('todos.csv', text, { dir: BaseDirectory.Download });
    await message('Export todos!!', 'Export done!!');
  }

  // Import
  const handleImportFile = async () => {
    const csv = await readTextFile('todos.csv', { dir: BaseDirectory.Download });
    var lines=csv.split("\n");

    let result = [];
  
    var headers=lines[0].split(",");
  
    for(var i=1;i<lines.length;i++){
  
        var obj = {};
        var currentline=lines[i].split(",");
  
        for(var j=0;j<headers.length;j++){
            obj[headers[j]] = currentline[j];
        }
  
        result.push(obj);
    }

    result.forEach(async (element: Todo)  =>  {
      await invoke("new_todo", { title: element.title, date: element.date });
    });

    await message('Import all todos!!', 'Import done!!');
  }

  // Pomodoro
  useEffect(() => {
    const hanleInterval = setInterval(() => {
      if(timerStart) {
        if(time > 0) {
          setTime(time -1);
        } else if (time == 0) {
          sendNotification({
            title: `Time up!!`,
            body: `Done Pomodoro`,
          });
          clearInterval(hanleInterval);
        }
      }
    }, 1000);
    return () => clearInterval(hanleInterval);
  }, [timerStart, time]);

  const toggleTimer = () => {
    setTimerStart(!timerStart);
  }

  const triggerResetDialog = async () => {
    let shouldReset = await ask("Reset?", {
      title: "Pomodoro Timer",
      type: "warning",
    })
    if(shouldReset) {
      setTime(900);
      setTimerStart(false);
    }
  }
  
  // Todo
  useEffect(()=> {
    fetchAllTodo();
  }, [todos]);

  const fetchAllTodo = async () => {
    setTodos(await invoke("get_todos"));
  }

  const handleTitleChange = (e: React.FormEvent<HTMLInputElement>) => {
    let value = e.currentTarget.value;
    setTodo({...todo, title: value});
  }

  const handleTodoAdd = async () => {
    let date = Date.now().toString();
    await invoke("new_todo", { title: todo.title, date: date });
    setTodo({...todo, title: ""});
  }

  const handleTodoDelete = async (id: number) => {
    console.log(id);
    const confirmed = await confirm('This action cannot be reverted. Are you sure?', { title: 'Tauri', type: 'warning' });
    if(confirmed) {
      await invoke("delete_todo", { id: id});
    }
    setTodo({...todo, title: ""});
  }

  const handleChecked = async (index: number) => {
    await invoke("done_todo", {id : todos[index].id, msg: todos[index].title});
    if(!todos[index].done) {
      await message("Done", { title: 'Todo' });
      await handleDoneNotification();
    }
  }

  const handleDoneNotification = async () => {
    await sendNotification({
      title: `Todo is done`,
      body: `Great, you did it!!!!!!!!!`,
    });
  }

  return (
    <div className="container">
      <div>
        <h1>Temp is : {temp}</h1>
      </div>
      <div>
        <h1>
          {`${
              Math.floor(time / 60) < 10
                ? `0${Math.floor(time / 60)}`
                : `${Math.floor(time / 60)}`
            }:${time % 60 < 10 ? `0${time % 60}` : time % 60}`}
        </h1>
      </div>
      <div className="row" style={{margin: 10}}>
        <button onClick={toggleTimer}>
          {!timerStart ? "Start" : "Pause"}
        </button>
        <button onClick={triggerResetDialog}>
          Reset Timer
        </button>
      </div>
      <div>
        {buttons.map(({value, display}) => (
          <button type="button" onClick={() => {
            setTimerStart(false);
            setTime(value);
          }}>
            {display}
          </button>
        ))}
      </div>

      <h1>Todo List</h1>
      
      <div className="row">
        <input
          id="greet-input"
          onChange={(e) => handleTitleChange(e)}
          placeholder="Enter a task..."
          value={todo.title}
        />
        <button type="button" onClick={() => handleTodoAdd()}>
          Add
        </button>
        
      </div>
      <hr />
      {todos.map((todo, index) => {
        return (
          <div className="row" key={index}>
            <div style={itemStyle}></div>
            <span 
              onClick={() => handleChecked(index)}
              style={{
                textDecoration: todos[index].done && 'line-through',
              }}
            >
              {todo.title}
            </span>
            <button type="button" onClick={() => handleTodoDelete(todo.id)}>
              Delete
            </button>
          </div>
        );
      })}

      {/* export */}
      <hr/>

      <div className="row">
        <div>
          <button type="button" onClick={() => handleExportFile()}>
            Export
          </button>
        </div>

        <div>
          <button type="button" onClick={() => handleImportFile()}>
            Import
          </button>
        </div>

      </div>

    </div>
  );
}

export default App;

上一篇
26.Tauri http
下一篇
28.實作Tauri Todo - 完善Tauri
系列文
30天學習Tauri30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言