なぜ作ったか
週1回の買い物時にできるだけ必要なものは買い揃えたい。
そのために何を作るか決める必要があるが、大体スーパーについてから考えてしまうのである程度自動化したい。
メイン機能
準備したリストの中からランダムな重複なしリストを取得するアプリケーション
工夫点
- 1アイテムだけチェンジする機能 -> 最近作った、材料がないなどでチェンジするための機能。全部チェンジするとなかなか理想の結果が得られないことがある。
- 結果をLINEに出力するボタン(
SHARE BY LINE
) -> 結果の永続化、シェア - 入力したリストはブラウザに保存 -> 入力時間削減
利用技術
- 言語: TypeScript
- フレームワーク: Next.js
- UI: MUI
- CD: github actionsでGithub Pagesにdeploy
ソースコードと主要部分の実装
リポジトリ: https://github.com/yoshikipom/dinnerslot-web
ページは index.tsx
のみ。
ページ内でListInput.tsx
(リスト入力) と Slot.tsx
(結果生成) を呼び出し、タブで出し分ける。
$ tree src
src
├── components
│ ├── Footer.tsx
│ ├── LineShareButton.tsx
│ ├── ListInput.tsx
│ └── Slot.tsx
├── model
│ └── Food.ts
└── pages
├── _app.tsx
└── index.tsx
結果生成
- 入力されたリストをコピー
- ランダムに並び替え
- 欲しい数だけ前から切り取る
Slot.tsx
const slot = () => {
const copiedFoodList = foodList.concat();
for (let i = copiedFoodList.length - 1; i > 0; i--) {
const r = Math.floor(Math.random() * (i + 1));
const tmp = copiedFoodList[i];
copiedFoodList[i] = copiedFoodList[r];
copiedFoodList[r] = tmp;
}
updateResult(copiedFoodList.slice(0, Number(count)));
}
一点チェンジ
- 入力されたリストをコピー
- 選ばれているアイテムを除去
- 候補がなければ終了
- ランダムで一点選択
- 上書き
Slot.tsx
const slotOneItem = (index: number) => {
const copiedFoodList: Food[] = foodList.concat();
const remainingList: Food[] = copiedFoodList.filter((food) => !results.includes(food))
if (remainingList.length == 0) {
return;
}
const r = Math.floor(Math.random() * (remainingList.length));
results[index] = remainingList[r];
updateResult(results.slice(0, Number(count)));
}
LINE Shareボタン
- 「LINE URLスキーム」を利用
- 値はpropsで渡す
LineShareButton.tsx
const LineShareButton = (props: any) => {
return (
<Button sx={{ mx: 2, my: 2 }} variant="outlined" href={`https://line.me/R/share?text=${encodeURI(props.message)}`}>
Share by LINE
</Button>
);
};
入力リスト保存
- localStorage利用
- 取得は初回呼び出しのみ
- 書き込みは入力(
foodListInputRaw
)が変更が変更するたび
ListInput.tsx
useEffect(() => {
const foodListInputRaw = window.localStorage.getItem(STORAGE_KEY);
if (foodListInputRaw !== null) {
setFoodListInputRaw(foodListInputRaw);
}
}, []);
useEffect(() => {
const strList = foodListInputRaw.split('\n').filter(val => val);
let index = 1;
const newFoodList: Food[] = strList.map(name => ({ id: index++, name }));
setFoodList(newFoodList);
window.localStorage.setItem(STORAGE_KEY, foodListInputRaw);
}, [foodListInputRaw, setFoodList]);
感想
localStorage、LINEシェアは初めて試したが簡単に使えてよかった。
技術の復習としてバックエンドも作りたかったが、要件的に不要になってしまい残念。
自分のほしいアプリケーションを作ると勉強したい技術が使えるとは限らないので、勉強目的なら勉強用のアプリケーションを作った方が無難そう。