我们不再积极开发此应用程序的功能。 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
启动一个运行 shell 的容器,例如。用于运行测试。
确保您有 Node 8 和npm
5 或更高版本。建议您使用nvm管理 Node 安装。
npm ci
安装依赖项。
npm start
在本地构建并运行该站点。
npm ci --legacy-peer-deps
来绕过此问题。请参阅问题 6155 了解更多详细信息。
根/
被重定向到 www.zooniverse.org,因为该前端应用程序不再用于主页。将浏览器指向子路径以查看本地运行的应用程序。
打开您选择的网络浏览器并转到https://localhost:3735/lab
如果您想通过 Panoptes API登录并查看经过身份验证的页面,则需要设置并使用https://local.zooniverse.org:3735/lab
而不是使用 localhost:3735。否则,您将遇到 CORS 错误。 (您需要将主机名添加到您的主机文件中,指向本地。说明位于我们的 Stackoverflow 上。)
故障排除:网络浏览器阻止本地网站
问题:当尝试查看localhost:3735或local.zooniverse.org:3735 时,我的网络浏览器会阻止我并显示警告屏幕。
错误示例:Chrome 104 上的“您的连接不是私有的/NET::ERR_CERT_AUTHORITY_INVALID”; Firefox 103 上的“警告:未来存在潜在安全风险”; Safari 15.4 上“此连接不是私有的”。
原因:本地 Web 服务器正在运行 HTTPS,并且使用自签名证书。现代网络浏览器认为这些证书非常不可信,并且可能是中间人攻击的指标。
解决方案:
thisisunsafe
) 可暂时绕过警告;或者可以使用以下环境变量配置该应用程序:
NODE_ENV
- 设置代码的环境,并确定是否对捆绑代码应用任何生产优化,以及应用哪组默认值,例如 API 主机 url、Talk 主机 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 将由 Jenkins 上演,作为 CI 流程的一部分。 CI 完成后,您的更改应在 https://pr-{PR-Number}.pfe-preview.zooniverse.org 上暂存。詹金斯有时会在完成构建之前超时。如果 PR 构建失败,请使用 Jenkins 的链接(来自您的 PR)登录并尝试重新启动构建。
要使用生产数据进行测试,您可以将env=production
添加到开发 URL,例如localhost:3735/projects?env=production
。请注意,它会在每次页面刷新时被删除。
所有好东西都在./app中。从./app/main.cjsx开始
我们根据 AirBnB 风格指南的修改版本来检查 JavaScript 代码。请使用此存储库根目录下的 .eslintrc 文件,通过 eslint 检查您的更改。如果您有任何疑问,请随时在 GitHub 上询问我们。
编辑时,请尽力遵循项目已使用的样式和架构约定。代码库很大,并且风格在开发过程中不断演变。查看 Zooniverse/front-end-monorepo 以了解我们组织组件的约定。
尝试使用npm ci
更新您的依赖项。并阅读警告,它们应该告诉您是否使用了错误版本的 Node 或 npm 或者是否缺少任何依赖项。如果您使用docker-compose
构建和测试站点,则 Node 版本不应遇到任何问题,但docker-compose build
将使用新的npm ci
构建新映像。
如果您编写一个新组件,请编写一个测试。每个组件都应该有自己的.spec.js
文件。测试运行器是 Mocha,Enzyme 可用于测试 React 组件。编译包含带有模板字符串的 ES6 导入语句的 CoffeeScript 文件时,Mocha 会抛出错误( Illegal import declaration
)。将这些导入转换为require
语句。您可以使用npm test
运行测试。
部署由 Github Action 处理。
打开拉取请求时,会触发 Github 操作以部署到分支暂存位置。 Blob 存储位置取决于拉取请求编号,例如https://pr-5926.pfe-preview.zooniverse.org
。
推送到 master 时,会触发 Github Action 来部署到https://master.pfe-preview.zooniverse.org
上找到的 master staging。
生产部署由production-release
标签指向的提交更新触发。该标签应通过聊天操作进行更新,然后运行 Github Action,构建文件并将其上传到我们的云提供商(位于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
:对于每次 mousedown/touchstart,直到isComplete
返回 true,返回标记的值。
initMove
:对于每个 mousemove/touchmove,返回标记的新值。
initRelease
:对于每个 mouseup/touchend,返回标记的新值。
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 包含在组件中,否则将其保存在单独的文件中,每个组件一个。对于给定的组件,为该组件选择一个唯一的顶级类名,并在其下嵌套子类。在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
如果它只需要为真)。
原生 ES6 类是首选,因为React.createClass()
已被弃用,但是,如果现有组件依赖 mixin,则考虑使用createReactClass()
。
Mixins 已被弃用,并且不受本机类支持,因此不要在新组件中使用它们。
使用反引号将 ES6 组件导入到 CoffeeScript 组件中:
`import NewComponent from './new-component'`
ESLint 配置文件设置在存储库的根目录中,供您与文本编辑器一起使用来检查 ES6 并使用 Airbnb 的 React 风格指南。
编写本机类与使用createReactClass()
指南
请参阅panoptes-client库:https://www.npmjs.com/package/panoptes-client。
注释值的格式取决于用于生成它的任务。
single:所选答案的索引。
multiple:所选答案的索引数组(按照选择的顺序)。
绘图:一组绘图工具标记(其说明如下)。
调查:一系列作为对象的标识。每个识别一个choice
(识别的动物的 ID)和answers
,一个对象。 answers
中的每个键都是问题的 ID。如果该问题允许多个答案,则该值将是答案 ID 的数组,否则只是一个答案 ID。
裁剪:包含裁剪区域的x
、 y
、 width
和height
的对象。
文本:一个字符串。
组合:注释的子数组。
dropdown:对象数组,其中字符串value
指相应问题的答案,布尔option
表示答案位于选项列表中。
所有坐标均相对于图像的左上角。
所有标记都有一个tool
,它是用于制作标记的工具的索引(例如workflow.tasks.T0.tools[0]
)。
所有标记都包含一个frame
,它是标记所在的主题帧(例如subject.locations[0]
)的索引。
如果为工具定义了details
任务,则其标记将具有子分类的details
数组(每个子分类都有一个value
,遵循上面的描述)。
图纸标注值如下:
点: x
和y
坐标。
line:起点 ( x1
, y1
) 和终点 ( x2
, y2
) 坐标。
多边形:对象数组,每个对象包含顶点的x
和y
坐标。如果用户未显式关闭该标记,则auto_closed
为true
。
矩形:矩形左上角的x
、 y
坐标及其width
和height
。
圆:圆心的x
和y
坐标及其半径r
。
ellipse:椭圆中心的x
和y
坐标、半径rx
和ry
以及rx
相对于 x 轴的angle
(以度为单位)(从 3:00 开始逆时针)。
贝塞尔曲线:与多边形相同,但每个奇数索引点都是二次贝塞尔曲线控制点的坐标。
列:最左边的x
像素和列选择的width
。
感谢BrowserStack支持开源并允许我们在多个平台上测试这个项目。