generated at
Firestore+Authendication+Reactでなにか作る

試すこと
bulmaでいい感じの見た目を作る
やれたらやる

データ構造
まあ適当なやつで
とはいえ少しはtakker-schedulerを意識するか。
ts
type RecordData = { title: string; // 記録の名前 start: Date; // 記録の開始時刻 end: Date; // 記録の終了時刻 }
まあ、こんなシンプルなやつでいいだろ
document nameは records にするか。
あ、そうだ、日付操作ライブラリをどれにするか決めないと
date-fnsにしよう
typescript対応
シンプル

名前
いい加減決めないと呼ぶのに困りそう
recorder-prototypeでいいや。

UI
まずは試し試しやっていく
sign in画面は作らない
takkerしかlog inできないようにする
2020-11-28 01:52:38 Google loginだと無理みたい
仕方ないので一時的にsign in画面を作っておこう
いや別にmail loginでもいいか
どうしようかな
google login
pros
securityはこっちのほうが上?
cons
アカウントを再作成する羽目になったときが面倒
アカウント作成画面を復活させないといけない
いや普段は非表示にしておいて、アカウントを作成したいときだけbuildし直せばいいのか
それでも面倒だが
maillogin
pros
consoleから手動でaccountを追加でいる
cons
securityが甘い?
emailとpasswordだけ
もしやれたら、stop watchを実装したい
ボタンのon/offでやれるやつ

22:56:33 続きはまた今度

2020-11-28 01:42:09
CodeSandboxでFirebaseを使うのsandboxをforkする
2020-11-29 23:20:07 いつの間にかforkしちゃってたみたい
01:46:06 log in画面を作る
同時に、firebase consoleでaccountの設定をする
今回はgoogle log inにする
takkerだけlog in出来るようにする
01:49:49 mail login以外は、手動でaccountを追加できないみたい
firebase projectは前回と同じのを使う
01:57:28 CodeSandboxでFirebase authendicationを使ってみるのsandboxからlogin 画面のcodeをコピペする
いやFirebaseUI React Componentsを使うんだから、新しいcodeにしないとだめか。
02:00:28 FirebaseUI React Componentsの使い方を調べる
02:14:50 コピペで実装した
02:15:00 accountの状態によってcomponentを切り替える
すでにlog inしている
そのままページを表示
まだlog inしていない
log in pageにredirectする
とすると、全部のcomponentにaccountの状態をcheckする処理をかぶせる必要が出てくるわけか
これはcomponentとして分離したほうがいいな
どっちがいいかな?
同じpage内でcomponentを切り替える
URLが変化しないのはなんか変かも
Routerでlog in用ページに飛ばす
このとき前のページを覚えておいて、log inに成功したらそこに飛ぶ
各ページのcomponentに useAuthState() を置く必要がある
後者にした
2020-11-28 15:34:24
後者だと、認証状態を取得する前にdatabaseにaccessしないといけなくなる?
いや、先に useAuthState で状態を取得しておき、 !user === true のとき即座にLog in pageにredirectするようにすればいい?
hookの宣言の途中で history.push('/Login') を実行して別ページに遷移すると、hookの順番が変わることになりかねないか?
即座にcomponentをunmountしちゃうから別に構わない?
ここにReact Routerを使ったredirectの方法が載っている
これ読んでから考えるか。
02:46:11 FirebaseUI React Componentsでerrorが出た
なんでだ?
module errorが出るなら、それはIDEで検出できるはず
libraryの問題か?
代わりに素のfirebase APIを使って実装してみる
02:58:32 domainの許可が足りなかった
preview用URLのdomainも入れないとだめみたい
02:59:50 invalidになっちゃった
account作っていないから当然か
いや、account連携の場合は、accountがなければ自動的に作成されるらしい
じゃあ何がいけないんだろう?
03:02:36 signInWithPopup に変えて実行してみる
今度は成功した
signInWithRedirect の使い方が良くなかったのか?
別なタブを開いても問題なし
log in状態は維持されていた
03:10:10 collectionのデータ型を整えた
日付型にはFirebase.firestore.Timestampを使った
03:11:22 firestoreのsecurity ruleを設定する
03:26:37 log in状態でもaccessできなくなった
collection (firestore)にaccessしようとしているのが原因
collection('records').doc(user.uid) にaccessしないといけない
2020-11-29 17:22:21
認証状態を見てredirectするcomponent AuthGuard を作る
認証されていたら、 children に認証情報を渡してrenderする
どうやって認証情報を渡す?
これが現実的っぽい?
これが参考になりそう
Reduxを使っている
実現したいこと
認証情報がないと絶対にrenderingできないcomponentを作りたい
これをしないと、各component内部に認証が切れたときの処理を書かないといけなくなる
Redux使わずにうまいこと渡すには
useAuthState() を各componentで呼び出す
log in済みであることを保証できない
認証が必要なページにいる間に認証が切れたときのredirectが面倒
useCollectionData を呼び出す前にredirectすればいいけど、そうするとhookの順番がわかってしまう
やっぱりPropsから入れ込むしかない
templateに型を渡す
これが妥当か?
これは無理そう
userAuthState() useContext() に変わっただけ
個別のcomponentで認証状態をチェックする
できるけど、バカバカしくない?
認証がない時点で最初から呼べないようにしたい
認証されていなかったら、 Login pageにredirectする
Reference
20:03:51 higher-order componentのような仕組みを使って認証ガードを作ってみる
描画したいcomponentを受け取り、内部でそいつに認証情報を渡して描画する
20:33:21 useAuthStateの型が何故か死んでいるので、 [firebase.User,boolean,firebase.auth.Error,] を指定しておく
21:04:14 力技で実装した
AuthGuard.tsx
import * as React from 'react'; import { Redirect, Route, RouteProps } from 'react-router-dom'; import { useAuthState } from 'react-firebase-hooks/auth'; import { authentication } from '../config/firebase'; import firebase from 'firebase'; type AuthData = { userId: string; }; interface Props extends Omit<RouteProps, 'component' | 'children'> { Component: React.FC<AuthData>; } export function AuthGuard({ Component, ...props }: Props) { const [user, initialising, authError]: [ firebase.User, boolean, firebase.auth.Error, ] = useAuthState(authentication); if (!user) { return <Redirect to="/login" />; } return ( <Route {...props}> <Component userId={user.uid} /> </Route> ); }
こういう事して良いのかどうかは全くわからない
T extends AuthData にしたかったが何故かerrorが出るのでやめた
21:04:43 そういえばfirestoreとauthを使ったappがcodesandboxにあったな
みてみよ
21:06:41 ……なんかすっげー変なことしてる
やはりすぐに読めるような代物ではなかった
21:22:37 なんかloginできなくなった?
loginするaccountを選択した後、popupが閉じるまでだいぶ時間がかかる
閉じられた後何も起きない
多分errorになっている
でもerrorCode出てこないな
21:25:40 domainを追加していなかった
新しいsandboxのpreview windowのdomain
追加して試してみる
done21:26:55 成功
やっぱりdomainを追加していないだけだった
さっき時間かかっていたのは、codesandbox.ioを経由していたから?
逆につながっていたのが気になる
21:34:40 firestoreを開けない
firestoreのsecurity ruleがおかしい?
これなら動く
rule
service cloud.firestore { match /databases/{database}/documents { match /records/{documents=**} { allow read, write: if request.auth != null; } } }
これデータ構造に関わるな
やりたいこと
元の認証をクリアすれば、その配下の全てのdataにaccessできるようにしたい
その場合、これはデータ一つ一つに認証情報を入れているので使えない
てことは、collectionを入れ子にするしかないな
/users/{userId}/records/{document=**} というかんじ
21:58:04 認証成功!
/users/{userId}/records/{document=**} にした
22:45:32 /users/{userId} の下にfieldsとしてuser情報入れられるじゃん!
typescript側の接続方法
ts
const [values, loading, error] = useCollectionData<UserData>( firestore.collection('users').doc(userId).collection('records'), { idField: 'uid' }, );
method chainすればいい
22:01:26 きちんと複数タブ間で同期できている
一方でlog outするともう一方のタブも即座にLog in画面にうつる
22:04:54 dataを追加してみた
userId は完全に文字列だけで判別されるみたい
誰が作ったとかのメタデータは存在しない
Firebase.firestore.TimestampをJSXに読み込めない?
変換する必要があるっぽい?
単純にJSXがDate型等Object型を直接表示する術を持っていないだけだった
とりあえず toDate().toLocaleString() を挟んでおいた
22:11:49 いい感じのところまで来たぞ!!!takkertakkertakker
22:33:31 Detail.tsx を表示できるようにしてみた
更新処理は動くようにはしていない
データ型を別のfileに分離させた
mobileからも動作する。

2020-11-30 20:54:24
やること
Hooks Injection patternを適用したい
useCollectionDataが複数の場所に分散してきたので
23:18:34 やる
23:42:06 これAuthGuardも疎結合にしないとだめか
抽象的な認証情報を渡す
API固有のdataを渡せない
genericを使う
どう指定するんだ?
23:47:25 まあprototypeだし、今そこまで考えなくていいだろ。
適当にやろ
AuthData をexportして直に依存させるようにした
00:08:54 React.createContext<>にdefault valueを渡さないといけないみたい
Hooks Injection patternの記事は型指定がいい加減だったみたい
いい加減どころか文法がおかしい
2020-12-01 00:19:09 適当にダミーの関数を作って渡した
00:18:57 useRecords を使うようにした
返り値は [] ではなく {} で指定する
記録の追加ボタンを入れる
まずcreateいらないので消す
追加ボタンをどのcomponentに入れるか考える
CodePenを使ってReact.jsを学ぶ練習logでつかったTodo appをベースにする
15:54:18 入力欄が潰れてしまっていたので、min-width: 30pxを指定しておいた
記録開始と記録終了ボタンに変えたい
記録中は、下手にdatabaseに登録せずに、component内で持たせた方が良さそう
記録中に削除されたら困る
記録終了後に登録するようにする
20:50:01 記録開始からの経過時間を表示するようにした
useTimer を作った
21:21:07 実際の記録とtimerで使う記録とで開始時刻を別なDateで使っていたのでややズレが生じていた
同じDateを使うように修正した
21:21:51 だんだん開発への意欲が上がってきたぞtakker
一定のしきい値を越えて、開発しやすくなった気がする
少しの修正を積み重ねてどんどんUXを向上させる段階に突入した
21:04:45 記録開始後に、記録名を入力できるようにした
ふつうの <input> にした
まだややレイアウトがおかしいが、入力できるので十分
00:09:59 記録を終了すると即座に次の記録が開始されるようにした
改善点:最後に記録した時刻からのtimerにしたい
21:05:46 開始時刻の早い順に並ぶようにする
方法
firestore側で並び替えておく
orderBy をuseRecordsの実装のなかに挟めば実現できそう
本当は並べ方を外部から指定できるようにしたほうが良いんだろうけど、まあprototypeだし、細かいことは気にしない。
動くことが一番の目標
できた
react tableで並び替える
記録をinteractiveに編集できるようにする
<input> でまずは実装してみる
その後contenteditableを使えないかどうか試す
react-contenteditableをつかう?
00:33:18 先にdataの更新追加を行うhookを作っておこう
00:52:07 更新関数を作った
useUpdator で認証情報を受け取り、記録を更新する関数を返す
2020-12-01 13:12:45 これ困った
cellの中で更新関数を呼び出したい
しかしcellはどのdocumentを更新すれば良いのか知ることが出来ない
13:23:40 どうするか
rowIndexとcolumnIdから元のrecordを特定する関数を作る
pagenationなどを使うと表示上のindexと実際のdataのindexがずれてしまうが、問題なく対処できそう
ずれずに指定できるみたい
何らかの方法でdocIdをCellに渡す
14:08:13 できた
何故か書き込みエラーが生じるのでなんでかと思ったが、引数を間違えていただけだった
fail const { setRecord } = useUpdator(userId);
pass const { setRecord } = useUpdator({userId});
間違えないように、 userId ではなく authData でやりとりするようにした
認証情報の中身を知っている必要はない
14:24:40 可変長<input type="text">の値が更新されるようにした
useEffect を使うことで、初期値の変更に応じて表示値を変えられるようにした
14:30:45 EditableCellを列ごとに使い分けたいな
14:37:53 どうやら Columns<T>[] Cell propertyに指定すれば行けるっぽい
useTable() から注入するoption引数はそのまま使える
15:14:01 React Hook Formを使わずにvalidationする
あれはFormを前提とした作りになっていて、使いづらい
onBlur でcheckするだけで簡単に作れた
15:33:54 値が変化したときだけ onBlur でdatabase更新するようにした
onChange も消した。可変長<input type="text">のcomponentでやっているのでいらない
21:19:17 秒まで表示するようにした
21:58:25 使用時間を表示するようにした
formatDurationだと長すぎるので、独自にformat関数を作った
01:07:36 React Tableを入れる
01:44:38 うーん、かなりめんどくさい
自前でtable作ったほうが良さそう?
border を全部0pxにしたらいい感じの見た目になった
15:17:31 div をcontenteditableにした方が見た目は良かったかも
input だと若干文字が小さい
2020-12-01 12:46:25 時刻だけ表示するようにした
年月日は隠した
12:46:50 date-fnsを使う
流石に生のDateで日付操作するのつらい
記録の選択・削除機能の実装
選択行を削除が難しい
外部に選択行の情報を送ると何故か無限ループに陥ってしまう
回避策
自前で選択処理を実装する
これしかなさそう
22:44:32 Table側にstateを用意した
22:46:47 Tableには番号だけ持たせよう
Idで選択したかどうかを保持するのは重い
Objectを比較しないといけない
RecordId の内部実装に依存してしまっている
選択範囲が必要になったときに、 rows からIdを照会して渡す
23:17:05 できた
実行負荷をへらすために rows useEffect()の依存配列から取り外した
その影響で、選択範囲が削除後に残ってしまっている
これは今後の課題だな
もっとまともなCRUDの実装方法があるはず
23:23:05 window.confrimを使って削除確認画面を出すようにした
List componentに削除ボタンをもたせる
これはやりたくないな
Nav barの実装
別途componentにしよう
<NavBar> でいいや
22:26:01 調べた
22:28:58 作ってる
hamburger menu貼り付けたらlint errorでた
22:37:08 Dashboard.tsx の一番上の div に入れたら何も表示されなくなった
入れる場所が指定されているのか?
入れ直したら直った
単にdeployの反映にエラーがあっただけか
22:55:32 なぜかbuttonが表示されない?
logoがないせいか?
中に文字を入れていないだけだった
23:02:45 javascriptでmenuに移動するようにしないといけないみたい
面倒だな
hamburger menu使うのやめよう
23:16:52 とりあえずそれっぽいの出来た
20:19:26 見にくいので上部に固定した
23:54:09 やめた
上の部品が隠れてしまう
calendar表示の導入
できた
2020-12-02 00:12:24
vercelでdeployしてみた
00:25:03 失敗。
typescriptのversionが低いみたい
最新版に書き換えてdeployしてみる

#2020-12-02 00:12:27
#2020-12-01 00:19:11
#2020-11-29 19:03:58
#2020-11-28 01:42:03