-
知り合いGSシリーズのファンが、X(旧Twitter)上で 他のファンと交流しています
-
GSシリーズのある男子を好きな人は、他にどの男子が好きか気になるか... 言い換えれば、推しの組み合わせは皆同じではなさそう
-
GSシリーズファン同士がSNSなどでやり取りをしていると自然と 察するものがあるらしく、推しの組み合わせには何か傾向が有りそう
-
公式の人気投票では、推しの組み合わせについての情報が得られない... 何か可視化できる情報収集/結果表示の道具が欲しい
-
GSシリーズファンの交流を助ける機能も持ったWebアプリケーション として開発できないだろうか?
-
本当のところサーバ維持費くらい稼ぎたいのですが、 稼ぎ方が分からない&隣で見ていてGSシリーズが面白いので、 先ずは既に借りているサーバ内で動く範囲でやってみます
- 次に攻略するキャラに悩んだ時のアイディアを出せるかも
- キャラの属性(先輩、先生、他校生 等...)を記録しておくことで、
推しキャラのより大きな傾向をつかんでみたい気もします
- 属性は公式に決まっているわけでない&唯一無二に近いものもあるので、 認識違いなどの火種になるのは怖いかも... (王子、勉強、芸術...とかはアリかも)
- 「現実逃避ボタン」で認識したくない結果を非表示に出来たら精神的によさそうです (「そんな...私の推しキャラがこのキャラと一緒に表示されるなんて...」)
ユーザプロファイルにプレイ状況を表示出来たら面白そうですが、 どのキャラがプレイ中で、実況で、プレイ済みか選択させるのは どこでおこなうべきでしょうか?
- ユーザ画面
- GS1~4のどれをプレイ済みか位はここで記録できそう ですが、キャラクターそれぞれについて全て選択するのは難しそう
- 思ったのですが、GSシリーズを実際に持っていて 推しキャラをまだプレイしていない状況なんてそうそうあるでしょうか? (真っ先に攻略しちゃいそう)
- こっちの方がユーザの手間が少ないので有力候補
- 推し登録時
- 次に思いつくもっともらしいタイミングはここ。 ただ、毎回プレイ済みかどうか入れるのってどうなのでしょう? 最新の結果から分かる部分は予め自動入力することも可能ですが...
- GS1,3 で登場するバーのマスター、益田さん
この部分は、新しい検討の方が上に来ます
今の設定で動いているのは逆に不思議なくらいかも...?
AUTH_URL
環境変数を設定next.config.js
でbasePathを設定 ← この時点でbasepath-redundant といった警告あり- middlewareのmatcherには'/', と'/profile'を設定(後者のみでよいはず)
- 認証用エンドポイントはedgeランタイムでないとなぜか動かない
これらを修正していってどうなるか様子を見たい
どうやらリバースプロキシが存在しているため、 外から認証ページにアクセスするためのURLと、 Auth.jsがURL関連のパースを行うためのベースとなるURLとが 食い違っているのかも...
リバースプロキシ下で正しく使うのはやはり結構難しそう
AUTH_URL
は通常と異なる認証URLを使うときに必要そう、 設定しないとtwitter oauthに渡す認証先URLが http://test.local/api/auth...になって、Twitter OAuth認証画面でエラー
現在のFreeプランだと、ポストへのアクセスまでできてしまう (そこまでいらない...)
また、X(Twitter)認証を行えるアプリがこの一つに限定されてしまう 多分他にはもう作ることはないが...
もしかすると自前でハッシュ化されたパスワードを保存しておくべきかも...
OAuth認証で取得できる情報は何を使うかによって異なるので、
src/auth.ts
の設定でdebug: true
としてその内容を確認し
(本当に色々表示されるので気を付けた方が良いくらい)、
それに合わせてmodule augumentationでProfile, JWT, Session型を拡張して、
jwt
, session
関数を適宜定義して内容を取得するとよさそう
当初は分析結果を全部まとめてStatic Renderingして返すことを 考えていましたが、先ずは表示したい分析結果をsearchParamsに入れておき、 サーバ側でDynamic Renderingしてもよいかもしれないです。
やはりユーザ1人1人のアクセスでDB集計をやり直すのは ちょっと違う...? 新規登録時にはrevalidatePathをして集計をやり直し、 更新時には即時のDB集計を行わず、定期的な処理とするのは どうでしょうか
→ export const revalidate = 60 * 5;
で実現できそう?
でもキャラ毎のパスをstaticにするのは何か違う気もします。 とはいえdynamicなパスを設定するとstatic renderingにはならない様な?
→ generateStaticParams()
を使う手が有るらしいです。
これはビルド時にパラメータを列挙して自動で複数のdynamic pathを
準備する機能の様に思われます。
キャラごとの集計結果をgenerateStaticParams()に持たせておいて...
revalidateと組み合わせられるのでしょうか?
→ できそうな感覚があります
[x] OK、できました!
ユーザが後から推しを変更したり、 コミュニティの異なる新規ユーザ群が登録を行ったりすることで 時系列で傾向が変化していく様子を追いかけられたら楽しそう。
複数の推しの組み合わせはいつも同時に登録されると仮定して (具体的には一つのinsert文でいつもまとめて登録するようにして)、 twitterIDとtimestampを組みにして全ての新規追加・更新を同じテーブルに 記録してみます。
SELECT
twitter_id,
MAX(voted_time) as voted_time,
charaName,
level,
FROM
Votes
WHERE
voted_time < $specified_time
GROUP BY twitter_id
;
...みたいなSQL文で時系列分析も出来るような気がする
erDiagram
Characters {
number series PK "登場シリーズ番号"
number sort PK "公式サイト等での紹介順"
varchar charaName "キャラ名"
}
Votes {
varchar twitterId PK "X(Twitter)アカウントID"
timestamp votedTime PK "投票時間"
varchar charaName PK "キャラ名"
number level "推しレベル"
}
Characters ||--o{ Votes : charaName
VotesのtwitterID, voteTimeをprimaryKeyにするつもりだったが、 同じtwitterID, voteTimeを持ったVotes複数個の組で 推しの組み合わせを表現することができない → characterNameもprimaryKeyに入れることで対処できるかも
select
*
from
Votes as t1
where
voted_time = (
select
max(voted_time)
from
Votes as t2
where
t1.twitter_id = t2.twitter_id
)
;
...でできそう
select
*
from
Votes as t1
where
'柊夜ノ介' in (
select
character_name
from
Votes as t2
where
t1.twitter_id = t2.twitter_id
and
t1.voted_time = t2.voted_time
)
;
...でできそう
select
*
from
Votes as t1
where
'柊夜ノ介' in (
select
character_name
from
Votes as t2
where
t1.twitter_id = t2.twitter_id
and
t1.voted_time = t2.voted_time
)
and
voted_time = (
select
max(voted_time)
from
Votes as t3
where
t1.twitter_id = t3.twitter_id
);
...でできそう
select
*
from
Votes as t1
where
'柊夜ノ介' in (
select
character_name
from
Votes as t2
where
t1.twitter_id = t2.twitter_id
and
t1.voted_time = t2.voted_time
)
and
voted_time = (
select
max(voted_time)
from
Votes as t3
where
t1.twitter_id = t3.twitter_id
)
and
character_name <> '柊夜ノ介'
;
...でできそう
select
*
from
Votes as t1
where
exists (
select
character_name
from
Votes as t2
where
t1.twitter_id = t2.twitter_id
and
t1.voted_time = t2.voted_time
and
t2.character_name = '柊夜ノ介'
)
and
voted_time = (
select
max(voted_time)
from
Votes as t3
where
t1.twitter_id = t3.twitter_id
)
and
character_name <> '柊夜ノ介'
;
...でできそう
select
character_name,
count(*) as count
from
Votes as t1
where
exists (
select
character_name
from
Votes as t2
where
t1.twitter_id = t2.twitter_id
and
t1.voted_time = t2.voted_time
and
t2.character_name = '柊夜ノ介'
)
and
voted_time = (
select
max(voted_time)
from
Votes as t3
where
t1.twitter_id = t3.twitter_id
)
and
character_name <> '柊夜ノ介'
group by
character_name
;
...でできそう
[x] OK、できました!
絵に出来たらもっと良いかもなのですが...
以前のPlantUMLの図
@startuml
skinparam handwritten true
skinparam linetype ortho
actor あなた as user
participant 投票データ as database
participant 分析結果 as analysis
participant "X (Twitter)" as twitter
actor 誰か as stranger
user -> twitter: 1.アクセスを許可してもらえると
user <-- twitter: 2.ユーザ名, ユーザ番号 を\n 教えてもらえます
note over user
ようこそ"ユーザ名"さん!
end note
|||
user -> database: 3.推しキャラ投票時、\n ユーザ番号も記録します(自動)\n (ユーザ__名__は記録__しません__)
|||
user -> database: 4.あなたが使うとき、\n あなたのユーザ番号は分かるので...
user <-- database: 5.過去のあなたの投票を\n 確認できます!
|||
database -> analysis: 集計・分析
user o<- analysis: 6.投票データ全体の分析結果は\n誰でも閲覧できます
analysis ->o stranger
|||
database x<- stranger: 投票データは開発者以外は閲覧出来ません
database x<- stranger: 開発者も含め他の人は\nあなたのユーザ番号を知らないので\n誰が投票したか分かりません
twitter x<- stranger: あなたの\nアカウントも\n分かりません
@enduml
sequenceDiagram
actor user as あなた
participant database as 投票データ
participant analysis as 分析結果
participant twitter as X(Twitter)
actor stranger as 誰か
Note over user,stranger : データの使用方法について
user ->> twitter : 1. アクセスを許可してもらえると
twitter -->> user : 2. ユーザ名、ユーザ番号を教えてもらえます
Note over user : ようこそ"ユーザ名"さん!
user ->> database : 3. 押しキャラ投票時、<br/>ユーザ番号も記録します<br/>(ユーザ名は記録しません)
user ->> database : 4. あなたが使うとき、<br/>あなたのユーザ番号は分かるので...
database -->> user : 5. 過去のあなたの投票を確認できます!
database ->> analysis : 集計・分析
analysis ->> user : 6. 投票データの分析結果の<br/>閲覧ができます
analysis ->> stranger : あなた以外の人も<br/>分析結果は閲覧可能です
Note over user,stranger : その他のアクセス方法への対策
stranger -x database : 投票データは開発者しか閲覧できません
stranger -x database : 開発者も含め、あなた以外の人は<br/>あなたのユーザ番号を知りませんので、<br/>誰がなんの投票をしたかは分かりません
stranger -x twitter : あなたのアカウントを<br/>特定することは出来ません
意外と難しい...プレイ情報のうち、投票日時以前でも最も新しいものの プレイ情報を取得する必要がります
過去の最新の投票結果から初期状態を構成して、
- キャラのガーデンへの追加、退場
- おおよその推し順の変更
...ができる画面を作りたいですが、どうしましょうか
- ガーデンに入れているキャラとそのおおよその推し順をstateで保持します
- キャラの追加Select(TopCharacterSelectを流用)では 既に追加されているキャラ名を追加済みとしたうえでdisabledにします
- 推し順は左右で表現、左が高くて右が低いです。 同じ推し順なら最初に設定した方が上に表示されます
どうやって推し順を保持しましょうか...
最初に試している、先ずレベルごとに並び替える→同じレベルの中で 先に登録された順に並び替える...というのはDOM構造が変わるので、 恐らく順位変更ボタンを押すと急に位置が変わる様に見えるのでは...
それはあんまりかっこよくない気がします。
位置をスムーズに(アニメーションさせながら)変化させるためには、 表示位置の調整にDOM構造を用いてはいけない気がします。
→Framer Motionというライブラリが有るらしいです... →色々試しましたが、コンポーネントがアンマウントされる &DOMツリー構造が変化する場合には上手くいかなかったので、 relative要素内に放り込むことにし、素のCSSアニメーションを 用いることにしました。
もしかしたらトップレベルのコンポーネントがgridレイアウトなどで高さを指定しないとカオスになるかも...
現在は入れ子になったコンポーネントが独自に高さ指定を行っているため どの部分の数字を変えたら思った通りの結果になるのかわからない...
コンポーネントのサイズ指定は外から行うべきでしょうか...
controlled componentの投票用フォームもform要素に囲まれているのは ちょっと不安になるかも...使い方を間違っていないでしょうか
基本的にh-fullを指定しつつ、その中身のサイズ配分は 子コンポーネントまかせ、というのが良いかも...