@@ -87,6 +87,11 @@ import { getAuth, onAuthStateChanged } from "firebase/auth";
8787import { mapGetters } from " vuex" ;
8888import ResultCard from " @/components/OmikujiResult" ;
8989import OmikujiScene from " @/components/OmikujiScene" ;
90+ import {
91+ saveOmikujiState ,
92+ loadOmikujiState ,
93+ formatOmikujiRemaining ,
94+ } from " @/utils/omikujiCooldown" ;
9095
9196function resolveCurrentUser (auth ) {
9297 return new Promise ((resolve ) => {
@@ -110,6 +115,24 @@ export default {
110115 };
111116 },
112117 async mounted () {
118+ // 覚えているものがあれば、通信を待たずにその場で出す。ここで待たせると
119+ // 「押してから待たされた末に引けないと分かる」になる。
120+ // サーバーの応答が来たら上書きされる(正はあくまでサーバー)。
121+ const saved = loadOmikujiState (
122+ this .user && this .user .github_id ,
123+ Date .now ()
124+ );
125+ if (saved) {
126+ this .result = saved .result ;
127+ if (saved .remainingSeconds > 0 ) {
128+ this .remaining = saved .remainingSeconds ;
129+ this .state = " cooldown" ;
130+ this .startTimer ();
131+ } else if (saved .result ) {
132+ // 引ける。前回の結果を添えてボタンを出す。
133+ this .state = " available" ;
134+ }
135+ }
113136 await this .fetchStatus ();
114137 },
115138 beforeDestroy () {
@@ -129,7 +152,11 @@ export default {
129152 },
130153 // 引かずに現在の状態(引ける/クールダウン+前回結果)を取得する。
131154 async fetchStatus () {
132- this .state = " loading" ;
155+ // 既に出せるものがあるなら、確認中もそれを出したままにする。
156+ // ローディングに戻すと、せっかく即表示した意味が無くなる。
157+ if (this .state !== " cooldown" && this .state !== " available" ) {
158+ this .state = " loading" ;
159+ }
133160 const token = await this .getToken ();
134161 if (! token) return ;
135162 try {
@@ -182,13 +209,32 @@ export default {
182209 this .remaining = this ._pendingRemaining || 0 ;
183210 this .pendingResult = null ;
184211 this .state = " cooldown" ;
212+ saveOmikujiState (
213+ this .user && this .user .github_id ,
214+ this .remaining ,
215+ this .result ,
216+ Date .now ()
217+ );
185218 this .startTimer ();
186219 } else {
187220 // 演出が結果より先に終わった(スキップ等)→ サーバー状態を取り直す
188221 this .fetchStatus ();
189222 }
190223 },
191224 applyResponse (d ) {
225+ // 次に引ける時刻と前回の結果を覚えておく(次に開いたとき待たせないため)。
226+ if (
227+ d .status === " success" ||
228+ d .status === " cooldown" ||
229+ d .status === " available"
230+ ) {
231+ saveOmikujiState (
232+ this .user && this .user .github_id ,
233+ d .remaining_seconds || 0 ,
234+ d .result || this .result ,
235+ Date .now ()
236+ );
237+ }
192238 if (d .status === " success" ) {
193239 this .result = d .result ;
194240 this .remaining = d .remaining_seconds || 0 ;
@@ -212,28 +258,37 @@ export default {
212258 },
213259 startTimer () {
214260 if (this .timerId ) clearInterval (this .timerId );
261+ // 残り秒を1秒ずつ引くのではなく、明ける時刻を決めて毎回そこから計算する。
262+ // タブが背面にあると setInterval は間引かれる(止まることもある)ため、
263+ // 引き算だと戻ってきたときに実際より長い残り時間を表示してしまい、
264+ // 「引けるのに引けないと見える」という直したかった症状が再発する。
265+ const deadline = Date .now () + this .remaining * 1000 ;
215266 this .timerId = setInterval (() => {
216- this .remaining -= 1 ;
267+ this .remaining = Math .max (
268+ 0 ,
269+ Math .ceil ((deadline - Date .now ()) / 1000 )
270+ );
217271 if (this .remaining <= 0 ) {
218272 clearInterval (this .timerId );
219273 this .timerId = null ;
220274 this .remaining = 0 ;
221275 // 引ける状態へ。前回結果は残しつつボタンを出す。
222276 this .state = " available" ;
277+ saveOmikujiState (
278+ this .user && this .user .github_id ,
279+ 0 ,
280+ this .result ,
281+ Date .now ()
282+ );
223283 }
224284 }, 1000 );
225285 },
226286 },
227287 computed: {
228288 ... mapGetters ([" user" ]),
229289 remainingText () {
230- const s = Math .max (0 , this .remaining );
231- const h = Math .floor (s / 3600 );
232- const m = Math .floor ((s % 3600 ) / 60 );
233- const sec = s % 60 ;
234- if (h > 0 ) return ` ${ h} 時間${ m} 分` ;
235- if (m > 0 ) return ` ${ m} 分${ sec} 秒` ;
236- return ` ${ sec} 秒` ;
290+ // トップページと同じ表記にする(整形は omikujiCooldown.js に一本化)。
291+ return formatOmikujiRemaining (this .remaining );
237292 },
238293 shareUrl () {
239294 return this .$config .baseUrl ;
0 commit comments