このアプリの機能は現在積極的に開発されていません。バグ修正、翻訳、コンテンツ更新については PR を受け付けます。 https://github.com/zooniverse/front-end-monorepo/ で積極的な機能開発が行われています。
Node.js やその他の依存関係をインストールする必要がないように、Docker と Docker Compose を使用してすべてを実行できます。
docker-compose build
ローカルの Docker イメージをビルドし、 npm ci
を実行します。 package.json
の依存関係を変更するたびにこれを実行します。
docker-compose up
ポート 3735 をリッスンする開発 Web サーバーを起動します。
docker-compose down
開発サーバーを停止します。
docker-compose run --rm shell
シェルを実行しているコンテナを開始します。テストの実行用。
Node 8 とnpm
5 以降を使用していることを確認してください。 Node のインストールはnvmを使用して管理することをお勧めします。
npm ci
依存関係をインストールします。
npm start
サイトをローカルで構築して実行します。
npm ci --legacy-peer-deps
を実行することで、この問題を回避できます。詳細については、第 6155 号を参照してください。
このフロントエンド アプリはホームページでは使用されなくなったため、ルート/
は www.zooniverse.org にリダイレクトされます。ブラウザーでサブパスを指定すると、このアプリがローカルで実行されていることが表示されます。
選択した Web ブラウザを開いて、 https://localhost:3735/lab
に移動します。
Panoptes API 経由でログインし、認証されたページを表示したい場合は、 localhost:3735 を使用する代わりにhttps://local.zooniverse.org:3735/lab
設定して使用する必要があります。そうしないと、CORS エラーが発生します。 (ローカルを指すホスト名をホスト ファイルに追加する必要があります。手順は Stackoverflow にあります。)
トラブルシューティング: Web ブラウザがローカル Web サイトをブロックする
問題: localhost:3735またはlocal.zooniverse.org:3735 を表示しようとすると、 Web ブラウザーが停止し、警告画面が表示されます。
エラーの例: Chrome 104 では「接続はプライベートではありません / NET::ERR_CERT_AUTHORITY_INVALID」。 Firefox 103 では「警告: 潜在的なセキュリティリスクが発生します」。 Safari 15.4 で「この接続はプライベートではありません」と表示されます。
原因: ローカル Web サーバーは HTTPS を実行しており、自己署名証明書を使用しています。最近の Web ブラウザでは、これらの証明書は非常に信頼できず、中間者攻撃の兆候である可能性があると見なされます。
解決策:
thisisunsafe
) を入力します。またはアプリは次の環境変数を使用して構成できます。
NODE_ENV
- コードの環境を設定し、バンドルされたコードに本番環境の最適化を適用するかどうか、および API ホスト URL、トーク ホスト URL などに適用するデフォルトのセットを決定します。PANOPTES_API_APPLICATION
- Panoptes API に認証リクエストを行うときに使用するアプリケーション ID を設定します。デフォルトはNODE_ENV
によって設定されます。PANOPTES_API_HOST
- Panoptes API のインスタンスの URL を設定します。デフォルトはNODE_ENV
によって設定されます。STAT_HOST
- 統計 API のインスタンスの URL を設定します。デフォルトはNODE_ENV
によって設定されます。SUGAR_HOST
- Sugar API のインスタンスの URL を設定します。デフォルトはNODE_ENV
によって設定されます。TALK_HOST
- Talk API のインスタンスの URL を設定します。デフォルトはNODE_ENV
によって設定されます。 package.json
scripts
ブロック内のコマンドによって設定されます。それらをオーバーライドするには、 package.json
変更する必要があります。NODE_ENV
環境変数によって設定されたデフォルトを確認するには、panoptes-javascript-client のconfig.js
参照してください。Zooniverse 組織内からの新しい GitHub PR は、CI プロセスの一部として Jenkins によってステージングされます。 CI が完了したら、変更は https://pr-{PR-Number}.pfe-preview.zooniverse.org にステージングされる必要があります。 Jenkins はビルドが完了する前にタイムアウトになることがあります。 PR ビルドが失敗した場合は、(PR からの) Jenkins へのリンクを使用してログインし、ビルドを再開してみてください。
実稼働データを使用してテストする場合は、 env=production
開発 URL に追加できます (例: localhost:3735/projects?env=production
。ページが更新されるたびに削除されることに注意してください。
優れた機能はすべて./appにあります。 ./app/main.cjsxから開始します
AirBnB スタイルガイドの修正版に対して JavaScript コードをリントします。このリポジトリのルートにある .eslintrc ファイルを使用して、eslint で変更を lint してください。ご質問がございましたら、GitHub でお気軽にお問い合わせください。
編集中は、プロジェクトですでに使用されているスタイルとアーキテクチャの規則に最大限従うようにしてください。コードベースは大きく、開発中にスタイルが進化しました。コンポーネントを整理するための規則を理解するには、zooniverse/front-end-monorepo を見てください。
npm ci
試して依存関係を更新してください。警告を読むと、間違ったバージョンの Node または npm を使用しているか、依存関係が欠落しているかどうかがわかります。 docker-compose
使用してサイトを構築およびテストする場合、Node バージョンで問題が発生することはありませんが、 docker-compose build
新しいnpm ci
を使用して新しいイメージを構築します。
新しいコンポーネントを作成する場合は、テストを作成します。各コンポーネントには独自の.spec.js
ファイルが必要です。テスト ランナーは Mocha で、React コンポーネントのテストには Enzyme を使用できます。 Mocha は、テンプレート文字列を含む ES6 import ステートメントを含む Coffeescript ファイルをコンパイルするときにエラー ( Illegal import declaration
) をスローします。これらのインポートをrequire
ステートメントに変換します。 npm test
使用してテストを実行できます。
デプロイメントは Github Action によって処理されます。
プル リクエストを開くと、Github アクションがトリガーされ、ブランチのステージング場所にデプロイされます。 BLOB ストレージの場所は、プル リクエスト番号 (例: https://pr-5926.pfe-preview.zooniverse.org
によって異なります。
マスターにプッシュすると、 https://master.pfe-preview.zooniverse.org
にあるマスター ステージングにデプロイする Github アクションがトリガーされます。
運用環境のデプロイメントはproduction-release
タグがポイントするコミットの更新によってトリガーされます。このタグはチャット操作を通じて更新する必要があります。その後、Github アクションが実行され、ファイルをビルドしてhttps://www.zooniverse.org
にあるクラウド プロバイダーにアップロードされます。リポジトリに対する適切な権限がある場合、実稼働デプロイメントは必要に応じてアクション タブでアドホックに実行できますが、これは緊急の場合にのみ実行してください。
分類子に関連するすべてのこと。
コレクション関連のコンポーネント。
その他の汎用の再利用可能なコンポーネント。
アプリレベルのレイアウトがここに含まれます。メイン サイトのヘッダー、メイン サイトのフッター、またはメイン サイト コンテンツのレイアウトに影響を与える場合は、ここに存在します。
コンポーネント間で再利用される個々の機能とデータ。
ここにアプリの大部分が存在します。理想的には、各ルートは、データをフェッチし、ユーザーがそのデータに対して実行できるアクションを処理するページ コンポーネントを指します。そのページ コンポーネントはそのデータを使用してダム コンポーネントで UI をレンダリングし、必要に応じてアクションを渡します。
元々は、実際にはどこにも再利用されない分離されたコンポーネントを保持することを目的としていました。これらはおそらく実際に使用される場所に近いものです。
件名ビュー (TODOC: これはトーク/コレクションとどのような関係がありますか?)
トーク関連のコンポーネント。
ここのファイルは、ビルド中に出力ディレクトリにコピーされます。
各タスク コンポーネント クラスには、いくつかの静的コンポーネントが必要です。
Summary
: タスクの注釈の分類後の概要を表示します。
Editor
: プロジェクト ビルダーでワークフロー タスクを編集するために使用されるコンポーネント。
タスクをタスク領域の外でレンダリングする必要がある場合は、分類インターフェイスの残りの部分にいくつかのフックを使用できます。
BeforeSubject
: タスク中に件名画像の前に表示される HTML コンテンツ。
InsideSubject
: タスク中に主題画像の上に表示される SVG コンテンツ。
AfterSubject
タスク中に件名画像の後に表示される HTML コンテンツ。
これらのフックにはPersist
というプレフィックスを付けることができます。これにより、フックがタスクとともに表示され、ユーザーが次のタスクに移った後も保持されます。
Persist{Before,After}Task
同様に機能しますが、タスク領域が対象です。タスク領域には非永続フックは不要です。
各コンポーネントには、いくつかの静的メソッドも必要です。
getDefaultTask
: ユーザーがプロジェクト ビルダーのワークフローにタスクを追加するときにデフォルトとして使用されるタスクの説明を返します。
getTaskText
: タスクを指定すると、タスクの基本的なテキスト説明 (質問タスクの質問、描画タスクの指示など) を返します。
getDefaultAnnotation
: 分類子がタスクを開始するときに生成されるアノテーション
isAnnotationComplete
: タスクと注釈を指定すると、分類子によってユーザーが次のタスクに進むことができるかどうかが決まります。
testAnnotationQuality
: ユーザーのアノテーションと、同じタスクに対する既知の良好な「ゴールドスタンダード」アノテーションを指定すると、ユーザーのアノテーションが標準にどれだけ近いかを示す 0 (完全に間違っている) から 1 (完全に正しい) までの数値が返されます。
タスクが変更された場合は、更新されたタスクを使用してthis.props.onChange
必ず呼び出してください。
MarkInitializer
コンポーネントから呼び出されるいくつかの静的メソッドは、ユーザーの最初のマーク作成アクション中にマークの値を制御します。
defaultValues
: マークのデフォルトの一部。
initStart
: isComplete
true を返すまで、マウスダウン/タッチスタートごとに、マークの値を返します。
initMove
: マウス移動/タッチ移動ごとに、マークの新しい値を返します。
initRelease
: マウスアップ/タッチエンドごとに、マークの新しい値を返します。
isComplete
: マークは完了していますか?一部のマークでは、イニシャライザーが制御を放棄する前に複数の対話が必要になります。
initValid
: マークが無効な場合 (幅または高さがゼロの四角形など)、マークは自動的に破棄されます。
いくつかのヘルパー コンポーネントは、選択/無効状態を処理し、サブタスク ポップアップをレンダリングするDrawingToolRoot
と、描画ツールの一貫したコントロールをレンダリングするDeleteButton
およびDragHandle
です。また、マーク全体をドラッグした後に呼び出す必要があるdeleteIfOutOfBounds
関数もあります。
React では、配列内の各コンポーネントに兄弟固有のkey
が必要です。 ID を持たないもの (注釈、回答) の配列をレンダリングする場合、ランダムな_key
プロパティが存在しない場合はそれを提供します。アンダースコアの接頭辞が付いているプロパティが永続化されていないことを確認してください。これは、 JSONAPIClient.Model
クラスを使用すると自動的に行われます。
< ul >
{ for item in things
item . _key ?= Math . random ()
< li key = { item . _key }>{ item . label }</ li >}
</ ul >
いくつかありますニース非同期値をサポートする残念な(今にして思えば) コンポーネント。これらは@props.children
として関数を受け取ります。これは少し馬臭いように見えますが、かなりうまく機能します。リクエストされたデータのほとんどはローカルにキャッシュされるため、通常は安全ですが、同じリクエストが連続して複数回行われていることに気付いた場合は、冗長な呼び出しを探すのに適した場所です。以下は、プロジェクトが変更されたときに再レンダリングする例です。これにより、プロジェクトの所有者がチェックされます。
< ChangeListener target = { @props . project }>{ =>
< PromiseRenderer promise = { @props . project . get ( ' owners ' )}>{([owner]) =>
if owner is @props . user
< p > This project is yours.</ p >
else
< p > This project belongs to { owner . display_name }.</ p >
}</ PromiseRenderer >
}</ ChangeListener >
ChangeListener
またはPromiseRenderer
を使用して新しいコードを作成しないでください。
妥当な場合は、作業中のコード内のChangeListener
とPromiseRenderer
インスタンスをコンポーネントの状態に置き換えます。これはより冗長ですが、より読みやすく、将来的にはサーバー上でのレンダリングに近づくことになります。
コンポーネントの機能に必要なCSS はコンポーネントにインラインで含めます。それ以外の場合は、コンポーネントごとに 1 つずつ別のファイルに保存します。特定のコンポーネントについて、そのコンポーネントの一意のトップレベル クラス名を選択し、その下に子クラスをネストします。共通の基本スタイルと変数をcommon.stylに保持します。スタイラスの書式設定: コロンあり、セミコロンなし、中括弧なし。 @extends
最上位、プロパティ (アルファベット順)、子孫セレクターの順に続きます。可能な限り、明示的なメディア クエリに対しては、 display: flex
およびflex-wrap: wrap
を使用することを優先します。
CSS が非常に巨大になったので、整理のために BEM を試しています。
// <special-button.styl>
.special-button
background : red
color : white
.special-button__icon
width : 1 em ;
// <special-container.styl>
.special-container
margin : 1 em 1 vw
.special-container__button
border : 1 px solid
Coffeescript から ES6 に移行しています。これは、ES6 で新しいコンポーネントを作成するか、既存のコンポーネントを書き直すことによって段階的に行うことができます。いくつかの注意点について言及する必要があります。
存在演算子は ES6 には存在しません。 null
と明示的に比較するか、単に真実である必要がある場合は!!thing
使用します。
React.createClass()
非推奨であるため、ネイティブ ES6 クラスが推奨されますが、既存のコンポーネントがミックスインに依存している場合は、 createReactClass()
の使用を検討してください。
ミックスインは非推奨になり、ネイティブ クラスではサポートされないため、新しいコンポーネントでは使用しないでください。
バッククォートを使用して、ES6 コンポーネントを Coffeescript コンポーネントにインポートします。
`import NewComponent from './new-component'`
ESLint 設定ファイルはリポジトリのルートにセットアップされており、テキスト エディタで ES6 と Airbnb の React スタイル ガイドの両方を lint するために使用できます。
ネイティブ クラスの作成とcreateReactClass()
の使用に関するガイド
panoptes-clientライブラリを参照してください: https://www.npmjs.com/package/panoptes-client。
注釈の値の形式は、その生成に使用されるタスクによって異なります。
single:選択した回答のインデックス。
multiple:選択された回答のインデックスの配列 (選択された順序)。
drawing:描画ツール マークの配列 (説明は以下に続きます)。
調査:オブジェクトとしての識別の配列。それぞれの識別はchoice
(識別された動物の ID) とanswers
、オブジェクトです。 answers
内の各キーは質問の ID です。その質問で複数の回答が許可されている場合、値は回答 ID の配列になり、それ以外の場合は単一の回答 ID になります。
Crop: トリミングされた領域のx
、 y
、 width
、およびheight
を含むオブジェクト。
テキスト:文字列。
combo:注釈のサブ配列。
ドロップダウン:文字列value
対応する質問への回答を参照し、ブール値option
回答がオプションのリストに含まれていることを示すオブジェクトの配列。
すべての座標は画像の左上を基準としています。
すべてのマークには、マークの作成に使用されるツール (例: workflow.tasks.T0.tools[0]
) のインデックスであるtool
があります。
すべてのマークには、マークが付けられた対象フレーム (例: subject.locations[0]
) のインデックスであるframe
が含まれます。
ツールに対してdetails
タスクが定義されている場合、そのマークにはサブ分類のdetails
配列が含まれます (上記の説明に従って、それぞれにvalue
が付きます)。
図面の注釈値は次のとおりです。
point: x
座標とy
座標。
line:開始 ( x1
、 y1
) と終了 ( x2
、 y2
) の座標。
ポリゴン:オブジェクトの配列。各オブジェクトには頂点のx
座標とy
座標が含まれます。マークがユーザーによって明示的に閉じられなかった場合、 auto_closed
はtrue
になります。
長方形:長方形の左上の点のx
、 y
座標とそのwidth
とheight
。
Circle:円の中心のx
座標とy
座標、およびその半径r
。
ellipse:楕円の中心のx
座標とy
座標、その半径rx
とry
、および x 軸に対するrx
のangle
(度単位) (3:00 から反時計回り)。
bezier:ポリゴンと同じですが、奇数のインデックスが付いたすべての点が 2 次ベジェ曲線の制御点の座標になります。
列:列選択の左端のx
ピクセルとwidth
。
オープンソースをサポートし、複数のプラットフォームでこのプロジェクトをテストできるようにしてくれた BrowserStack に感謝します。