├── .babelrc ├── .dockerignore ├── .eslintignore ├── .eslintrc.js ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── 01_bug_report.md │ ├── 02_feature_request.md │ ├── 03_security_vulnerability.md │ ├── 04_documentation_issue.md │ ├── 05_question.md │ ├── 06_discussion.md │ └── 07_support.md ├── PULL_REQUEST_TEMPLATE.md └── labels │ ├── bsd.png │ ├── bug.png │ ├── can't reproduce.png │ ├── code.png │ ├── critical.png │ ├── design.png │ ├── discussion.png │ ├── docker.png │ ├── documentation.png │ ├── duplicate.png │ ├── enhancement.png │ ├── feature.png │ ├── help wanted.png │ ├── in progress.png │ ├── invalid.png │ ├── labels.md │ ├── linux.png │ ├── macOS.png │ ├── more info needed.png │ ├── not a bug.png │ ├── on hold.png │ ├── optimization.png │ ├── performance.png │ ├── question.png │ ├── security.png │ ├── test.png │ ├── waiting feedback.png │ ├── watchlist.png │ ├── windows.png │ └── wontfix.png ├── .gitignore ├── .jsdoc.json ├── .prettierrc ├── .travis.yml ├── ABOUT.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── client ├── .eslintrc.js ├── config │ ├── .eslintrc.js │ ├── env.js │ ├── jest │ │ ├── cssTransform.js │ │ └── fileTransform.js │ ├── paths.js │ ├── polyfills.js │ ├── webpack.config.dev.js │ ├── webpack.config.prod.js │ └── webpackDevServer.config.js ├── scripts │ ├── .eslintrc.js │ ├── build.js │ ├── deprecated-warning.js │ ├── start.js │ ├── svg-react-component-generator.js │ ├── test.js │ └── typed-css-modules-loader.js └── src │ ├── fonts │ ├── LICENSE.txt │ ├── Roboto-500 │ │ ├── Roboto-500.eot │ │ ├── Roboto-500.svg │ │ ├── Roboto-500.ttf │ │ ├── Roboto-500.woff │ │ └── Roboto-500.woff2 │ ├── Roboto-700 │ │ ├── Roboto-700.eot │ │ ├── Roboto-700.svg │ │ ├── Roboto-700.ttf │ │ ├── Roboto-700.woff │ │ └── Roboto-700.woff2 │ ├── Roboto-700italic │ │ ├── Roboto-700italic.eot │ │ ├── Roboto-700italic.svg │ │ ├── Roboto-700italic.ttf │ │ ├── Roboto-700italic.woff │ │ └── Roboto-700italic.woff2 │ ├── Roboto-italic │ │ ├── Roboto-italic.eot │ │ ├── Roboto-italic.svg │ │ ├── Roboto-italic.ttf │ │ ├── Roboto-italic.woff │ │ └── Roboto-italic.woff2 │ └── Roboto-regular │ │ ├── Roboto-regular.eot │ │ ├── Roboto-regular.svg │ │ ├── Roboto-regular.ttf │ │ ├── Roboto-regular.woff │ │ └── Roboto-regular.woff2 │ ├── images │ ├── flags │ │ ├── ad.png │ │ ├── ae.png │ │ ├── af.png │ │ ├── ag.png │ │ ├── al.png │ │ ├── am.png │ │ ├── ao.png │ │ ├── ar.png │ │ ├── at.png │ │ ├── au.png │ │ ├── az.png │ │ ├── ba.png │ │ ├── bb.png │ │ ├── bd.png │ │ ├── be.png │ │ ├── bf.png │ │ ├── bg.png │ │ ├── bh.png │ │ ├── bi.png │ │ ├── bj.png │ │ ├── bn.png │ │ ├── bo.png │ │ ├── br.png │ │ ├── bs.png │ │ ├── bt.png │ │ ├── bw.png │ │ ├── by.png │ │ ├── bz.png │ │ ├── ca.png │ │ ├── cd.png │ │ ├── cf.png │ │ ├── cg.png │ │ ├── ch.png │ │ ├── ci.png │ │ ├── cl.png │ │ ├── cm.png │ │ ├── cn.png │ │ ├── co.png │ │ ├── cr.png │ │ ├── cu.png │ │ ├── cv.png │ │ ├── cw.png │ │ ├── cy.png │ │ ├── cz.png │ │ ├── de.png │ │ ├── dj.png │ │ ├── dk.png │ │ ├── dm.png │ │ ├── do.png │ │ ├── dz.png │ │ ├── ec.png │ │ ├── ee.png │ │ ├── eg.png │ │ ├── eh.png │ │ ├── er.png │ │ ├── es.png │ │ ├── et.png │ │ ├── fi.png │ │ ├── fj.png │ │ ├── fm.png │ │ ├── fr.png │ │ ├── ga.png │ │ ├── gb.png │ │ ├── gd.png │ │ ├── ge.png │ │ ├── gh.png │ │ ├── gm.png │ │ ├── gn.png │ │ ├── gq.png │ │ ├── gr.png │ │ ├── gt.png │ │ ├── gw.png │ │ ├── gy.png │ │ ├── hk.png │ │ ├── hn.png │ │ ├── hr.png │ │ ├── ht.png │ │ ├── hu.png │ │ ├── id.png │ │ ├── ie.png │ │ ├── il.png │ │ ├── in.png │ │ ├── iq.png │ │ ├── ir.png │ │ ├── is.png │ │ ├── it.png │ │ ├── je.png │ │ ├── jm.png │ │ ├── jo.png │ │ ├── jp.png │ │ ├── ke.png │ │ ├── kg.png │ │ ├── kh.png │ │ ├── ki.png │ │ ├── km.png │ │ ├── kn.png │ │ ├── kp.png │ │ ├── kr.png │ │ ├── ks.png │ │ ├── kw.png │ │ ├── kz.png │ │ ├── la.png │ │ ├── lb.png │ │ ├── lc.png │ │ ├── li.png │ │ ├── lk.png │ │ ├── lr.png │ │ ├── ls.png │ │ ├── lt.png │ │ ├── lu.png │ │ ├── lv.png │ │ ├── ly.png │ │ ├── ma.png │ │ ├── mc.png │ │ ├── md.png │ │ ├── me.png │ │ ├── mg.png │ │ ├── mh.png │ │ ├── mk.png │ │ ├── ml.png │ │ ├── mm.png │ │ ├── mn.png │ │ ├── mr.png │ │ ├── mt.png │ │ ├── mu.png │ │ ├── mv.png │ │ ├── mw.png │ │ ├── mx.png │ │ ├── my.png │ │ ├── mz.png │ │ ├── na.png │ │ ├── ne.png │ │ ├── ng.png │ │ ├── ni.png │ │ ├── nl.png │ │ ├── no.png │ │ ├── np.png │ │ ├── nr.png │ │ ├── nz.png │ │ ├── om.png │ │ ├── pa.png │ │ ├── pe.png │ │ ├── pg.png │ │ ├── ph.png │ │ ├── pk.png │ │ ├── pl.png │ │ ├── pt.png │ │ ├── pw.png │ │ ├── py.png │ │ ├── qa.png │ │ ├── ro.png │ │ ├── rs.png │ │ ├── ru.png │ │ ├── rw.png │ │ ├── sa.png │ │ ├── sb.png │ │ ├── sc.png │ │ ├── sd.png │ │ ├── se.png │ │ ├── sg.png │ │ ├── si.png │ │ ├── sk.png │ │ ├── sl.png │ │ ├── sm.png │ │ ├── sn.png │ │ ├── so.png │ │ ├── sr.png │ │ ├── st.png │ │ ├── sv.png │ │ ├── sy.png │ │ ├── sz.png │ │ ├── td.png │ │ ├── tg.png │ │ ├── th.png │ │ ├── tj.png │ │ ├── tl.png │ │ ├── tm.png │ │ ├── tn.png │ │ ├── to.png │ │ ├── tr.png │ │ ├── tt.png │ │ ├── tv.png │ │ ├── tw.png │ │ ├── tz.png │ │ ├── ua.png │ │ ├── ug.png │ │ ├── us.png │ │ ├── uy.png │ │ ├── uz.png │ │ ├── va.png │ │ ├── vc.png │ │ ├── ve.png │ │ ├── vn.png │ │ ├── vu.png │ │ ├── ws.png │ │ ├── ye.png │ │ ├── za.png │ │ ├── zm.png │ │ └── zw.png │ └── flood.svg │ ├── index.html │ ├── javascript │ ├── actions │ │ ├── AuthActions.js │ │ ├── ClientActions.js │ │ ├── FloodActions.js │ │ ├── SettingsActions.js │ │ ├── TorrentActions.js │ │ └── UIActions.js │ ├── app.tsx │ ├── components │ │ ├── AppWrapper.js │ │ ├── alerts │ │ │ ├── Alert.js │ │ │ └── Alerts.js │ │ ├── auth │ │ │ └── AuthForm.js │ │ ├── general │ │ │ ├── Badge.js │ │ │ ├── ClientConnectionInterruption.js │ │ │ ├── CustomScrollbars.js │ │ │ ├── Duration.js │ │ │ ├── GlobalContextMenuMountPoint.js │ │ │ ├── Icon.scss │ │ │ ├── Icon.scss.d.ts │ │ │ ├── Icon.tsx │ │ │ ├── ListViewport.js │ │ │ ├── LoadingIndicator.js │ │ │ ├── NavigationList.js │ │ │ ├── Portal.js │ │ │ ├── ProgressBar.js │ │ │ ├── Ratio.js │ │ │ ├── RtorrentConnectionTypeSelection.js │ │ │ ├── Size.js │ │ │ ├── SortableList.js │ │ │ ├── SortableListItem.js │ │ │ ├── SortableListItemDragLayer.js │ │ │ ├── Tooltip.js │ │ │ ├── WindowTitle.js │ │ │ ├── filesystem │ │ │ │ ├── DirectoryFileList.js │ │ │ │ ├── DirectoryTree.js │ │ │ │ ├── DirectoryTreeNode.js │ │ │ │ ├── FilesystemBrowser.js │ │ │ │ ├── PriorityMeter.js │ │ │ │ └── TorrentDestination.js │ │ │ └── form-elements │ │ │ │ ├── Dropdown.js │ │ │ │ └── TextboxRepeater.js │ │ ├── icons │ │ │ ├── Active.js │ │ │ ├── Add.js │ │ │ ├── AddMini.js │ │ │ ├── All.js │ │ │ ├── ArrowIcon.js │ │ │ ├── BaseIcon.js │ │ │ ├── CalendarCreatedIcon.js │ │ │ ├── CalendarIcon.js │ │ │ ├── Checkmark.js │ │ │ ├── ChevronLeftIcon.js │ │ │ ├── ChevronRightIcon.js │ │ │ ├── CircleCheckmarkIcon.js │ │ │ ├── CircleExclamationIcon.js │ │ │ ├── CircleIcon.js │ │ │ ├── ClipboardIcon.js │ │ │ ├── ClockIcon.js │ │ │ ├── Close.js │ │ │ ├── CommentIcon.js │ │ │ ├── Completed.js │ │ │ ├── DetailNotAvailableIcon.js │ │ │ ├── Disk.js │ │ │ ├── DiskIcon.js │ │ │ ├── DotsMini.js │ │ │ ├── Download.js │ │ │ ├── DownloadSmall.js │ │ │ ├── DownloadThickIcon.js │ │ │ ├── ETA.js │ │ │ ├── Edit.js │ │ │ ├── ErrorIcon.js │ │ │ ├── FeedIcon.js │ │ │ ├── File.js │ │ │ ├── Files.js │ │ │ ├── FolderClosedOutlined.js │ │ │ ├── FolderClosedSolid.js │ │ │ ├── FolderOpenOutlined.js │ │ │ ├── FolderOpenSolid.js │ │ │ ├── HashIcon.js │ │ │ ├── Inactive.js │ │ │ ├── InfinityIcon.js │ │ │ ├── InformationIcon.js │ │ │ ├── Limits.js │ │ │ ├── LoadingIndicatorDots.js │ │ │ ├── LockIcon.js │ │ │ ├── Logout.js │ │ │ ├── NotificationIcon.js │ │ │ ├── PeersIcon.js │ │ │ ├── RadarIcon.js │ │ │ ├── RadioDot.js │ │ │ ├── Ratio.js │ │ │ ├── RatioIcon.js │ │ │ ├── Remove.js │ │ │ ├── RemoveMini.js │ │ │ ├── Search.js │ │ │ ├── SeedsIcon.js │ │ │ ├── SettingsIcon.js │ │ │ ├── SpinnerIcon.js │ │ │ ├── StartIcon.js │ │ │ ├── StopIcon.js │ │ │ ├── TrackerMessageIcon.js │ │ │ ├── Upload.js │ │ │ └── UploadThickIcon.js │ │ ├── layout │ │ │ ├── ApplicationContent.js │ │ │ ├── ApplicationPanel.js │ │ │ └── ApplicationView.js │ │ ├── modals │ │ │ ├── Modal.js │ │ │ ├── ModalActions.js │ │ │ ├── ModalFormSectionHeader.js │ │ │ ├── ModalTabs.js │ │ │ ├── Modals.js │ │ │ ├── add-torrents-modal │ │ │ │ ├── AddTorrentsActions.js │ │ │ │ ├── AddTorrentsByFile.js │ │ │ │ ├── AddTorrentsByURL.js │ │ │ │ └── AddTorrentsModal.js │ │ │ ├── confirm-modal │ │ │ │ └── ConfirmModal.js │ │ │ ├── feeds-modal │ │ │ │ ├── DownloadRulesTab.js │ │ │ │ ├── FeedsModal.js │ │ │ │ └── FeedsTab.js │ │ │ ├── move-torrents-modal │ │ │ │ └── MoveTorrentsModal.js │ │ │ ├── remove-torrents-modal │ │ │ │ └── RemoveTorrentsModal.js │ │ │ ├── set-tags-modal │ │ │ │ └── SetTagsModal.js │ │ │ ├── settings-modal │ │ │ │ ├── AboutTab.js │ │ │ │ ├── AuthTab.js │ │ │ │ ├── BandwidthTab.js │ │ │ │ ├── ConnectivityTab.js │ │ │ │ ├── DiskUsageTab.js │ │ │ │ ├── ResourcesTab.js │ │ │ │ ├── SettingsModal.js │ │ │ │ ├── SettingsTab.js │ │ │ │ ├── UITab.js │ │ │ │ └── UITabSortableDetailColumns.js │ │ │ └── torrent-details-modal │ │ │ │ ├── NavigationList.js │ │ │ │ ├── TorrentDetailsModal.js │ │ │ │ ├── TorrentFiles.js │ │ │ │ ├── TorrentGeneralInfo.js │ │ │ │ ├── TorrentHeading.js │ │ │ │ ├── TorrentMediainfo.js │ │ │ │ ├── TorrentPeers.js │ │ │ │ └── TorrentTrackers.js │ │ ├── sidebar │ │ │ ├── DiskUsage.js │ │ │ ├── FeedsButton.js │ │ │ ├── LogoutButton.js │ │ │ ├── NotificationsButton.js │ │ │ ├── SearchTorrents.js │ │ │ ├── SettingsButton.js │ │ │ ├── Sidebar.js │ │ │ ├── SidebarActions.js │ │ │ ├── SidebarFilter.js │ │ │ ├── SidebarItem.js │ │ │ ├── SpeedLimitDropdown.js │ │ │ ├── StatusFilters.js │ │ │ ├── TagFilters.js │ │ │ ├── TrackerFilters.js │ │ │ ├── TransferData.js │ │ │ ├── TransferRateDetails.js │ │ │ └── TransferRateGraph.js │ │ ├── torrent-list │ │ │ ├── Action.js │ │ │ ├── ActionBar.js │ │ │ ├── SortDropdown.js │ │ │ ├── TableHeading.js │ │ │ ├── Torrent.js │ │ │ ├── TorrentDetail.js │ │ │ └── TorrentList.js │ │ └── views │ │ │ ├── Login.js │ │ │ ├── Register.js │ │ │ └── TorrentClientOverview.js │ ├── constants │ │ ├── ActionTypes.js │ │ ├── Alerts.js │ │ ├── EventTypes.js │ │ ├── Languages.js │ │ ├── PriorityLevels.js │ │ └── TorrentProperties.js │ ├── dispatcher │ │ └── AppDispatcher.js │ ├── i18n │ │ ├── de.js │ │ ├── en.js │ │ ├── es.js │ │ ├── fr.js │ │ ├── ko.js │ │ ├── languages.ts │ │ ├── nl.js │ │ └── zh.js │ ├── stores │ │ ├── AlertStore.js │ │ ├── AuthStore.js │ │ ├── BaseStore.js │ │ ├── ClientStatusStore.js │ │ ├── ConfigStore.js │ │ ├── DiskUsageStore.js │ │ ├── FeedMonitorStore.js │ │ ├── NotificationStore.js │ │ ├── SettingsStore.js │ │ ├── TorrentFilterStore.js │ │ ├── TorrentStore.js │ │ ├── TransferDataStore.js │ │ └── UIStore.js │ └── util │ │ ├── connectStores.tsx │ │ ├── filterTorrents.js │ │ ├── history.js │ │ ├── searchTorrents.js │ │ ├── selectTorrents.js │ │ ├── size.js │ │ ├── sortTorrents.js │ │ ├── torrentStatusClasses.js │ │ ├── torrentStatusIcons.js │ │ └── validators.js │ ├── public │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── manifest.json │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ └── safari-pinned-tab.svg │ └── sass │ ├── base │ ├── _animations.scss │ ├── _font-families.scss │ ├── _layout.scss │ ├── _main.scss │ └── _typography.scss │ ├── components │ ├── _action-bar.scss │ ├── _alerts.scss │ ├── _app-wrapper.scss │ ├── _attached-panel.scss │ ├── _badge.scss │ ├── _base-menu.scss │ ├── _client-stats.scss │ ├── _connection-status.scss │ ├── _dependency-list.scss │ ├── _directory-tree.scss │ ├── _dropdown.scss │ ├── _dropzone.scss │ ├── _duration.scss │ ├── _filesystem.scss │ ├── _floating-action.scss │ ├── _icons.scss │ ├── _interactive-list.scss │ ├── _loading-indicator.scss │ ├── _mediainfo.scss │ ├── _modals.scss │ ├── _notifications.scss │ ├── _peers-list.scss │ ├── _priority-meter.scss │ ├── _progress-bar.scss │ ├── _scrollbars.scss │ ├── _search.scss │ ├── _sidebar-filter.scss │ ├── _sidebar.scss │ ├── _sort-dropdown.scss │ ├── _sortable-list.scss │ ├── _table.scss │ ├── _tags.scss │ ├── _textbox-repeater.scss │ ├── _toolbar.scss │ ├── _tooltip.scss │ ├── _torrent-details-panel.scss │ ├── _torrent.scss │ ├── _torrents.scss │ └── _transfer-data.scss │ ├── style.scss │ ├── style.scss.d.ts │ ├── tools │ ├── _colors.scss │ ├── _reset.scss │ └── _variables.scss │ └── views │ ├── _feeds.scss │ └── _login.scss ├── config.docker.js ├── config.template.js ├── custom.d.ts ├── flood.png ├── package-lock.json ├── package.json ├── scripts └── prettier.js ├── server ├── .eslintrc.js ├── app.js ├── bin │ ├── enforce-prerequisites.js │ ├── migrations │ │ ├── fix-is-admin-flag.js │ │ ├── per-user-rtorrent-instances.js │ │ └── run.js │ ├── start.js │ └── web-server.js ├── config │ └── passport.js ├── constants │ ├── clientGatewayServiceEvents.js │ ├── diskUsageServiceEvents.js │ ├── fileListPropMap.js │ ├── historyServiceEvents.js │ ├── notificationServiceEvents.js │ ├── taxonomyServiceEvents.js │ ├── torrentListPropMap.js │ ├── torrentServiceEvents.js │ └── transferSummaryPropMap.js ├── middleware │ ├── appendUserServices.js │ ├── booleanCoerce.js │ ├── clientActivityStream.js │ ├── eventStream.js │ └── requireAdmin.js ├── models │ ├── ClientRequest.js │ ├── Feed.js │ ├── Filesystem.js │ ├── HistoryEra.js │ ├── ServerEvent.js │ ├── TemporaryStorage.js │ ├── Users.js │ ├── client.js │ └── settings.js ├── routes │ ├── api.js │ ├── auth.js │ └── client.js ├── services │ ├── BaseService.js │ ├── clientGatewayService.js │ ├── clientRequestManager.js │ ├── diskUsageService.js │ ├── feedService.js │ ├── historyService.js │ ├── index.js │ ├── notificationService.js │ ├── taxonomyService.js │ └── torrentService.js ├── util │ ├── ajaxUtil.js │ ├── clientResponseUtil.js │ ├── mediainfo.js │ ├── methodCallUtil.js │ ├── minifyUtil.js │ ├── numberUtils.js │ ├── rTorrentDeserializer.js │ ├── rTorrentPropMap.js │ └── scgiUtil.js └── views │ ├── error.pug │ └── layout.pug ├── shared ├── constants │ ├── clientSettingsMap.js │ ├── diffActionTypes.js │ ├── historySnapshotTypes.js │ ├── serverEventTypes.js │ ├── torrentFilePropsMap.js │ ├── torrentPeerPropsMap.js │ ├── torrentStatusMap.js │ └── torrentTrackerPropsMap.js └── util │ ├── formatUtil.js │ ├── objectUtil.js │ ├── regEx.js │ └── stringUtil.js └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env", 4 | "@babel/typescript", 5 | "@babel/react" 6 | ], 7 | "plugins": [ 8 | "@babel/plugin-proposal-class-properties", 9 | "@babel/proposal-object-rest-spread" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore files not needed for building and running to keep image size low. 2 | .git/ 3 | node_modules/ 4 | 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /server/assets 3 | **/*.d.ts 4 | /docs 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['airbnb', 'plugin:import/errors', 'plugin:import/warnings', 'prettier'], 3 | parser: 'babel-eslint', 4 | plugins: ['import'], 5 | rules: { 6 | 'arrow-parens': 0, 7 | 'class-methods-use-this': 0, 8 | 'consistent-return': 0, 9 | 'implicit-arrow-linebreak': 0, 10 | 'import/no-extraneous-dependencies': 0, 11 | 'import/prefer-default-export': 0, 12 | 'max-len': [ 13 | 'error', 14 | { 15 | code: 120, 16 | ignoreRegExpLiterals: true, 17 | ignoreStrings: true, 18 | ignoreTemplateLiterals: true, 19 | ignoreUrls: true, 20 | }, 21 | ], 22 | 'no-console': 0, 23 | 'no-param-reassign': 0, 24 | 'no-plusplus': 0, 25 | 'no-underscore-dangle': [2, {allow: ['_id']}], 26 | 'no-unused-vars': [0, { "argsIgnorePattern": "^_" }], 27 | 28 | 'object-curly-newline': 0, 29 | 'object-curly-spacing': 0, 30 | 'prefer-destructuring': [ 31 | 2, 32 | { 33 | array: false, 34 | object: true, 35 | }, 36 | { 37 | enforceForRenamedProperties: false, 38 | }, 39 | ], 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02_feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "💡 Feature Request" 3 | about: "Suggest an idea for this project" 4 | --- 5 | Type: Feature Request 6 | 7 | - [ ] If you want to contribute to the project please review the [contributing guidelines](https://github.com/Flood-UI/flood/blob/master/.github/CONTRIBUTING.md). 8 | 9 | ## Summary 10 | 11 | 12 | 13 | ## Idea of implementation 14 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/03_security_vulnerability.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🔒 Security Vulnerability" 3 | about: "Report a security vulnerability in flood" 4 | --- 5 | PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW. 6 | 7 | If you discover a security vulnerability within flood, please send an e-mail to jfurrow (me@johnfurrow.com) or noraj (cybersecurity@tutamail.com). 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/04_documentation_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "📚 Documentation Issue" 3 | about: 'Report an issue or missing part in the documentation' 4 | --- 5 | Type: Documentation Issue 6 | 7 | ## Summary 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/05_question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "❓ Question" 3 | about: "Ask your questions here" 4 | --- 5 | Type: Question 6 | 7 | ## Question 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/06_discussion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🎙️ Discussion" 3 | about: "Start a discussion here" 4 | --- 5 | Type: Discussion 6 | 7 | ## Discussion 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/07_support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🆘 Support" 3 | about: "Ask for help on Discord" 4 | --- 5 | If you need support, ask on Discord https://discord.gg/Z7yR5Uf 6 | -------------------------------------------------------------------------------- /.github/labels/bsd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/bsd.png -------------------------------------------------------------------------------- /.github/labels/bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/bug.png -------------------------------------------------------------------------------- /.github/labels/can't reproduce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/can't reproduce.png -------------------------------------------------------------------------------- /.github/labels/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/code.png -------------------------------------------------------------------------------- /.github/labels/critical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/critical.png -------------------------------------------------------------------------------- /.github/labels/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/design.png -------------------------------------------------------------------------------- /.github/labels/discussion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/discussion.png -------------------------------------------------------------------------------- /.github/labels/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/docker.png -------------------------------------------------------------------------------- /.github/labels/documentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/documentation.png -------------------------------------------------------------------------------- /.github/labels/duplicate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/duplicate.png -------------------------------------------------------------------------------- /.github/labels/enhancement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/enhancement.png -------------------------------------------------------------------------------- /.github/labels/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/feature.png -------------------------------------------------------------------------------- /.github/labels/help wanted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/help wanted.png -------------------------------------------------------------------------------- /.github/labels/in progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/in progress.png -------------------------------------------------------------------------------- /.github/labels/invalid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/invalid.png -------------------------------------------------------------------------------- /.github/labels/labels.md: -------------------------------------------------------------------------------- 1 | # Labels 2 | 3 | Category | Label(s) | Color(s) 4 | --- | --- | --- 5 | Platform | ![](bsd.png) ![](docker.png) ![](linux.png) ![](macOS.png) ![](windows.png) | #BFD4F2 6 | Problems | ![](bug.png) ![](security.png) | #EE3F46 7 | Severity | ![](critical.png) | #B60205 8 | Type | ![](code.png) ![](design.png) ![](documentation.png) ![](test.png) | #FFC274 9 | Feedback | ![](discussion.png) ![](question.png) | #CC317C 10 | Improvements | ![](enhancement.png) ![](optimization.png) ![](performance.png) | #5EBEFF 11 | Help | ![](help%20wanted.png) | #76C3A9 12 | Additions | ![](feature.png) | #90C954 13 | Pending | ![](can't%20reproduce.png) ![](in%20progress.png) ![](more%20info%20needed.png) ![](waiting%20feedback.png) ![](watchlist.png) | #FBCA04 14 | Inactive | ![](duplicate.png) ![](invalid.png) ![](not%20a%20bug.png) ![](on%20hold.png) ![](wontfix.png) | #D2DAE1 15 | 16 | Note: in order to take a sharp screenshot of labels with Firefox: Right click the label => Inspect element => Right click the element on the inspector => Screenshot Node 17 | -------------------------------------------------------------------------------- /.github/labels/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/linux.png -------------------------------------------------------------------------------- /.github/labels/macOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/macOS.png -------------------------------------------------------------------------------- /.github/labels/more info needed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/more info needed.png -------------------------------------------------------------------------------- /.github/labels/not a bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/not a bug.png -------------------------------------------------------------------------------- /.github/labels/on hold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/on hold.png -------------------------------------------------------------------------------- /.github/labels/optimization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/optimization.png -------------------------------------------------------------------------------- /.github/labels/performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/performance.png -------------------------------------------------------------------------------- /.github/labels/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/question.png -------------------------------------------------------------------------------- /.github/labels/security.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/security.png -------------------------------------------------------------------------------- /.github/labels/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/test.png -------------------------------------------------------------------------------- /.github/labels/waiting feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/waiting feedback.png -------------------------------------------------------------------------------- /.github/labels/watchlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/watchlist.png -------------------------------------------------------------------------------- /.github/labels/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/windows.png -------------------------------------------------------------------------------- /.github/labels/wontfix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/.github/labels/wontfix.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Dependency directories 15 | node_modules/ 16 | 17 | # Optional npm cache directory 18 | .npm 19 | 20 | # Optional eslint cache 21 | .eslintcache 22 | 23 | # Optional REPL history 24 | .node_repl_history 25 | 26 | # Output of 'npm pack' 27 | *.tgz 28 | 29 | # Yarn Integrity file 30 | .yarn-integrity 31 | 32 | # dotenv environment variables file 33 | .env 34 | 35 | # macOS custom attributes 36 | .DS_Store 37 | 38 | # VisualStudio 39 | .vscode 40 | 41 | # Intellij 42 | .idea 43 | 44 | # Personal flood config 45 | config.js 46 | 47 | # Flood server 48 | server/db 49 | server/assets 50 | server/temp 51 | 52 | # Vim temp files 53 | *.swp 54 | 55 | # JSDoc output 56 | docs/ 57 | out/ 58 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc"] 5 | }, 6 | "source": { 7 | "include": ["client", "server", "shared", "package.json", "README.md"], 8 | "includePattern": ".js$", 9 | "excludePattern": "(node_modules/|docs)" 10 | }, 11 | "plugins": [ 12 | "plugins/markdown" 13 | ], 14 | "templates": { 15 | "cleverLinks": false, 16 | "monospaceLinks": true, 17 | "useLongnameInNav": false, 18 | "showInheritedInNav": true 19 | }, 20 | "opts": { 21 | "destination": "./docs/", 22 | "encoding": "utf8", 23 | "private": true, 24 | "recurse": true, 25 | "template": "./node_modules/minami" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "jsxBracketSameLine": true, 4 | "printWidth": 120, 5 | "singleQuote": true, 6 | "trailingComma": "all" 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: node_js 3 | node_js: 4 | - '10' 5 | - '12' 6 | - '13' 7 | matrix: 8 | fast_finish: true 9 | allow_failures: 10 | - node_js: 10 11 | before_script: 12 | - cp config.template.js config.js 13 | script: 14 | - npm run check-source-formatting 15 | - npm run lint 16 | - npm run check-types 17 | - npm run test 18 | - npm run build 19 | -------------------------------------------------------------------------------- /ABOUT.md: -------------------------------------------------------------------------------- 1 | # Flood 2 | 3 | [![Travis CI build status badge](https://img.shields.io/travis/Flood-UI/flood/master.svg?style=flat-square)](https://travis-ci.org/Flood-UI/flood) [![Discord server badge](https://img.shields.io/discord/418267176873623553.svg?style=flat-square)](https://discord.gg/Z7yR5Uf) 4 | 5 | Flood is a monitoring service for [rTorrent](https://github.com/rakshasa/rtorrent). It's a Node.js service that communicates with rTorrent instances and serves a decent web UI for administration. It's a work-in-progress. 6 | 7 | #### Feedback 8 | 9 | If you have a specific issue or bug, please file a GitHub issue. Please join the [Flood Discord server](https://discord.gg/Z7yR5Uf) to discuss feature requests and implementation details. 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG NODE_IMAGE=node:12.2-alpine 2 | ARG WORKDIR=/usr/src/app/ 3 | 4 | FROM ${NODE_IMAGE} as nodebuild 5 | ARG WORKDIR 6 | 7 | WORKDIR $WORKDIR 8 | 9 | # Generate node_modules 10 | COPY package.json \ 11 | package-lock.json \ 12 | .babelrc \ 13 | .eslintrc.js \ 14 | .eslintignore \ 15 | .prettierrc \ 16 | ABOUT.md \ 17 | $WORKDIR 18 | RUN apk add --no-cache --virtual=build-dependencies \ 19 | python build-base && \ 20 | npm install && \ 21 | apk del --purge build-dependencies 22 | 23 | # Build static assets and remove devDependencies. 24 | COPY client ./client 25 | COPY server ./server 26 | COPY shared ./shared 27 | COPY scripts ./scripts 28 | COPY config.docker.js ./config.js 29 | RUN npm run build && \ 30 | npm prune --production 31 | 32 | # Now get the clean image without any dependencies and copy compiled app 33 | FROM ${NODE_IMAGE} as flood 34 | ARG WORKDIR 35 | 36 | WORKDIR $WORKDIR 37 | 38 | # Install runtime dependencies. 39 | RUN apk --no-cache add \ 40 | mediainfo 41 | 42 | COPY --from=nodebuild $WORKDIR $WORKDIR 43 | 44 | # Hints for consumers of the container. 45 | EXPOSE 3000 46 | VOLUME ["/data"] 47 | 48 | # Start application. 49 | CMD [ "npm", "start" ] 50 | -------------------------------------------------------------------------------- /client/config/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: 0, 4 | node: 1, 5 | }, 6 | rules: { 7 | 'no-console': 0, 8 | 'global-require': 0, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /client/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | // This is a custom Jest transformer turning style imports into empty objects. 2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 3 | 4 | module.exports = { 5 | process() { 6 | return 'module.exports = {};'; 7 | }, 8 | getCacheKey() { 9 | // The output is always the same. 10 | return 'cssTransform'; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /client/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // This is a custom Jest transformer turning file imports into filenames. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /client/config/paths.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const userConfig = require('../../config'); 4 | 5 | // Make sure any symlinks in the project folder are resolved: 6 | // https://github.com/facebookincubator/create-react-app/issues/637 7 | const appDirectory = fs.realpathSync(process.cwd()); 8 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 9 | const ensureSlash = (questionablePath, needsSlash) => { 10 | const hasSlash = questionablePath.endsWith('/'); 11 | if (hasSlash && !needsSlash) { 12 | return questionablePath.substr(questionablePath, questionablePath.length - 1); 13 | } 14 | if (!hasSlash && needsSlash) { 15 | return `${questionablePath}/`; 16 | } 17 | return questionablePath; 18 | }; 19 | 20 | module.exports = { 21 | appBuild: resolveApp('server/assets'), 22 | appPublic: resolveApp('client/src/public/'), 23 | appHtml: resolveApp('client/src/index.html'), 24 | appIndex: resolveApp('client/src/javascript/app.tsx'), 25 | appPackageJson: resolveApp('package.json'), 26 | appSrc: resolveApp('./'), 27 | clientSrc: resolveApp('client/src'), 28 | testsSetup: resolveApp('tests/setupTests.js'), 29 | appNodeModules: resolveApp('node_modules'), 30 | servedPath: ensureSlash(userConfig.baseURI || '/', true), 31 | }; 32 | -------------------------------------------------------------------------------- /client/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // Object.assign() is commonly used with React. 10 | // It will use the native implementation if it's present and isn't buggy. 11 | Object.assign = require('object-assign'); 12 | -------------------------------------------------------------------------------- /client/scripts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: 0, 4 | node: 1, 5 | }, 6 | rules: { 7 | 'no-console': 0, 8 | 'global-require': 0, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /client/scripts/test.js: -------------------------------------------------------------------------------- 1 | console.log('There are no tests yet.'); 2 | process.exit(0); 3 | 4 | // Do this as the first thing so that any code reading it knows the right env. 5 | process.env.BABEL_ENV = 'test'; 6 | process.env.NODE_ENV = 'test'; 7 | process.env.BASE_URI = ''; 8 | 9 | // Makes the script crash on unhandled rejections instead of silently 10 | // ignoring them. In the future, promise rejections that are not handled will 11 | // terminate the Node.js process with a non-zero exit code. 12 | process.on('unhandledRejection', err => { 13 | throw err; 14 | }); 15 | 16 | // Ensure environment variables are read. 17 | require('../config/env'); 18 | 19 | const jest = require('jest'); 20 | 21 | const argv = process.argv.slice(2); 22 | 23 | // Watch unless on CI or in coverage mode 24 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 25 | argv.push('--watch'); 26 | } 27 | 28 | jest.run(argv); 29 | -------------------------------------------------------------------------------- /client/scripts/typed-css-modules-loader.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const DtsCreator = require('typed-css-modules'); 3 | const prettier = require('../../scripts/prettier'); 4 | 5 | const creator = new DtsCreator(); 6 | 7 | module.exports = async function moduleLoader(source, map) { 8 | if (this.cacheable) { 9 | this.cacheable(); 10 | } 11 | 12 | try { 13 | const callback = this.async(); 14 | const dtsContent = await creator.create(this.resourcePath, source); 15 | 16 | await dtsContent.writeFile(); 17 | await prettier.formatFile(dtsContent.outputFilePath, dtsContent.outputFilePath); 18 | 19 | return callback(null, source, map); 20 | } catch (error) { 21 | console.log(chalk.red(chalk.red('CSS module type generation failed.'))); 22 | console.log(error.message); 23 | 24 | if (error.stack != null) { 25 | console.log(chalk.gray(error.stack)); 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /client/src/fonts/Roboto-500/Roboto-500.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-500/Roboto-500.eot -------------------------------------------------------------------------------- /client/src/fonts/Roboto-500/Roboto-500.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-500/Roboto-500.ttf -------------------------------------------------------------------------------- /client/src/fonts/Roboto-500/Roboto-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-500/Roboto-500.woff -------------------------------------------------------------------------------- /client/src/fonts/Roboto-500/Roboto-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-500/Roboto-500.woff2 -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700/Roboto-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700/Roboto-700.eot -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700/Roboto-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700/Roboto-700.ttf -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700/Roboto-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700/Roboto-700.woff -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700/Roboto-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700/Roboto-700.woff2 -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700italic/Roboto-700italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700italic/Roboto-700italic.eot -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700italic/Roboto-700italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700italic/Roboto-700italic.ttf -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700italic/Roboto-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700italic/Roboto-700italic.woff -------------------------------------------------------------------------------- /client/src/fonts/Roboto-700italic/Roboto-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-700italic/Roboto-700italic.woff2 -------------------------------------------------------------------------------- /client/src/fonts/Roboto-italic/Roboto-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-italic/Roboto-italic.eot -------------------------------------------------------------------------------- /client/src/fonts/Roboto-italic/Roboto-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-italic/Roboto-italic.ttf -------------------------------------------------------------------------------- /client/src/fonts/Roboto-italic/Roboto-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-italic/Roboto-italic.woff -------------------------------------------------------------------------------- /client/src/fonts/Roboto-italic/Roboto-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-italic/Roboto-italic.woff2 -------------------------------------------------------------------------------- /client/src/fonts/Roboto-regular/Roboto-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-regular/Roboto-regular.eot -------------------------------------------------------------------------------- /client/src/fonts/Roboto-regular/Roboto-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-regular/Roboto-regular.ttf -------------------------------------------------------------------------------- /client/src/fonts/Roboto-regular/Roboto-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-regular/Roboto-regular.woff -------------------------------------------------------------------------------- /client/src/fonts/Roboto-regular/Roboto-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/fonts/Roboto-regular/Roboto-regular.woff2 -------------------------------------------------------------------------------- /client/src/images/flags/ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ad.png -------------------------------------------------------------------------------- /client/src/images/flags/ae.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ae.png -------------------------------------------------------------------------------- /client/src/images/flags/af.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/af.png -------------------------------------------------------------------------------- /client/src/images/flags/ag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ag.png -------------------------------------------------------------------------------- /client/src/images/flags/al.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/al.png -------------------------------------------------------------------------------- /client/src/images/flags/am.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/am.png -------------------------------------------------------------------------------- /client/src/images/flags/ao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ao.png -------------------------------------------------------------------------------- /client/src/images/flags/ar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ar.png -------------------------------------------------------------------------------- /client/src/images/flags/at.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/at.png -------------------------------------------------------------------------------- /client/src/images/flags/au.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/au.png -------------------------------------------------------------------------------- /client/src/images/flags/az.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/az.png -------------------------------------------------------------------------------- /client/src/images/flags/ba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ba.png -------------------------------------------------------------------------------- /client/src/images/flags/bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bb.png -------------------------------------------------------------------------------- /client/src/images/flags/bd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bd.png -------------------------------------------------------------------------------- /client/src/images/flags/be.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/be.png -------------------------------------------------------------------------------- /client/src/images/flags/bf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bf.png -------------------------------------------------------------------------------- /client/src/images/flags/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bg.png -------------------------------------------------------------------------------- /client/src/images/flags/bh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bh.png -------------------------------------------------------------------------------- /client/src/images/flags/bi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bi.png -------------------------------------------------------------------------------- /client/src/images/flags/bj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bj.png -------------------------------------------------------------------------------- /client/src/images/flags/bn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bn.png -------------------------------------------------------------------------------- /client/src/images/flags/bo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bo.png -------------------------------------------------------------------------------- /client/src/images/flags/br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/br.png -------------------------------------------------------------------------------- /client/src/images/flags/bs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bs.png -------------------------------------------------------------------------------- /client/src/images/flags/bt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bt.png -------------------------------------------------------------------------------- /client/src/images/flags/bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bw.png -------------------------------------------------------------------------------- /client/src/images/flags/by.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/by.png -------------------------------------------------------------------------------- /client/src/images/flags/bz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/bz.png -------------------------------------------------------------------------------- /client/src/images/flags/ca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ca.png -------------------------------------------------------------------------------- /client/src/images/flags/cd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cd.png -------------------------------------------------------------------------------- /client/src/images/flags/cf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cf.png -------------------------------------------------------------------------------- /client/src/images/flags/cg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cg.png -------------------------------------------------------------------------------- /client/src/images/flags/ch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ch.png -------------------------------------------------------------------------------- /client/src/images/flags/ci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ci.png -------------------------------------------------------------------------------- /client/src/images/flags/cl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cl.png -------------------------------------------------------------------------------- /client/src/images/flags/cm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cm.png -------------------------------------------------------------------------------- /client/src/images/flags/cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cn.png -------------------------------------------------------------------------------- /client/src/images/flags/co.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/co.png -------------------------------------------------------------------------------- /client/src/images/flags/cr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cr.png -------------------------------------------------------------------------------- /client/src/images/flags/cu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cu.png -------------------------------------------------------------------------------- /client/src/images/flags/cv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cv.png -------------------------------------------------------------------------------- /client/src/images/flags/cw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cw.png -------------------------------------------------------------------------------- /client/src/images/flags/cy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cy.png -------------------------------------------------------------------------------- /client/src/images/flags/cz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/cz.png -------------------------------------------------------------------------------- /client/src/images/flags/de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/de.png -------------------------------------------------------------------------------- /client/src/images/flags/dj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/dj.png -------------------------------------------------------------------------------- /client/src/images/flags/dk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/dk.png -------------------------------------------------------------------------------- /client/src/images/flags/dm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/dm.png -------------------------------------------------------------------------------- /client/src/images/flags/do.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/do.png -------------------------------------------------------------------------------- /client/src/images/flags/dz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/dz.png -------------------------------------------------------------------------------- /client/src/images/flags/ec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ec.png -------------------------------------------------------------------------------- /client/src/images/flags/ee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ee.png -------------------------------------------------------------------------------- /client/src/images/flags/eg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/eg.png -------------------------------------------------------------------------------- /client/src/images/flags/eh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/eh.png -------------------------------------------------------------------------------- /client/src/images/flags/er.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/er.png -------------------------------------------------------------------------------- /client/src/images/flags/es.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/es.png -------------------------------------------------------------------------------- /client/src/images/flags/et.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/et.png -------------------------------------------------------------------------------- /client/src/images/flags/fi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/fi.png -------------------------------------------------------------------------------- /client/src/images/flags/fj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/fj.png -------------------------------------------------------------------------------- /client/src/images/flags/fm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/fm.png -------------------------------------------------------------------------------- /client/src/images/flags/fr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/fr.png -------------------------------------------------------------------------------- /client/src/images/flags/ga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ga.png -------------------------------------------------------------------------------- /client/src/images/flags/gb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gb.png -------------------------------------------------------------------------------- /client/src/images/flags/gd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gd.png -------------------------------------------------------------------------------- /client/src/images/flags/ge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ge.png -------------------------------------------------------------------------------- /client/src/images/flags/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gh.png -------------------------------------------------------------------------------- /client/src/images/flags/gm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gm.png -------------------------------------------------------------------------------- /client/src/images/flags/gn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gn.png -------------------------------------------------------------------------------- /client/src/images/flags/gq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gq.png -------------------------------------------------------------------------------- /client/src/images/flags/gr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gr.png -------------------------------------------------------------------------------- /client/src/images/flags/gt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gt.png -------------------------------------------------------------------------------- /client/src/images/flags/gw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gw.png -------------------------------------------------------------------------------- /client/src/images/flags/gy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/gy.png -------------------------------------------------------------------------------- /client/src/images/flags/hk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/hk.png -------------------------------------------------------------------------------- /client/src/images/flags/hn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/hn.png -------------------------------------------------------------------------------- /client/src/images/flags/hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/hr.png -------------------------------------------------------------------------------- /client/src/images/flags/ht.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ht.png -------------------------------------------------------------------------------- /client/src/images/flags/hu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/hu.png -------------------------------------------------------------------------------- /client/src/images/flags/id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/id.png -------------------------------------------------------------------------------- /client/src/images/flags/ie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ie.png -------------------------------------------------------------------------------- /client/src/images/flags/il.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/il.png -------------------------------------------------------------------------------- /client/src/images/flags/in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/in.png -------------------------------------------------------------------------------- /client/src/images/flags/iq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/iq.png -------------------------------------------------------------------------------- /client/src/images/flags/ir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ir.png -------------------------------------------------------------------------------- /client/src/images/flags/is.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/is.png -------------------------------------------------------------------------------- /client/src/images/flags/it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/it.png -------------------------------------------------------------------------------- /client/src/images/flags/je.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/je.png -------------------------------------------------------------------------------- /client/src/images/flags/jm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/jm.png -------------------------------------------------------------------------------- /client/src/images/flags/jo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/jo.png -------------------------------------------------------------------------------- /client/src/images/flags/jp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/jp.png -------------------------------------------------------------------------------- /client/src/images/flags/ke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ke.png -------------------------------------------------------------------------------- /client/src/images/flags/kg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/kg.png -------------------------------------------------------------------------------- /client/src/images/flags/kh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/kh.png -------------------------------------------------------------------------------- /client/src/images/flags/ki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ki.png -------------------------------------------------------------------------------- /client/src/images/flags/km.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/km.png -------------------------------------------------------------------------------- /client/src/images/flags/kn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/kn.png -------------------------------------------------------------------------------- /client/src/images/flags/kp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/kp.png -------------------------------------------------------------------------------- /client/src/images/flags/kr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/kr.png -------------------------------------------------------------------------------- /client/src/images/flags/ks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ks.png -------------------------------------------------------------------------------- /client/src/images/flags/kw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/kw.png -------------------------------------------------------------------------------- /client/src/images/flags/kz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/kz.png -------------------------------------------------------------------------------- /client/src/images/flags/la.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/la.png -------------------------------------------------------------------------------- /client/src/images/flags/lb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/lb.png -------------------------------------------------------------------------------- /client/src/images/flags/lc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/lc.png -------------------------------------------------------------------------------- /client/src/images/flags/li.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/li.png -------------------------------------------------------------------------------- /client/src/images/flags/lk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/lk.png -------------------------------------------------------------------------------- /client/src/images/flags/lr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/lr.png -------------------------------------------------------------------------------- /client/src/images/flags/ls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ls.png -------------------------------------------------------------------------------- /client/src/images/flags/lt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/lt.png -------------------------------------------------------------------------------- /client/src/images/flags/lu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/lu.png -------------------------------------------------------------------------------- /client/src/images/flags/lv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/lv.png -------------------------------------------------------------------------------- /client/src/images/flags/ly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ly.png -------------------------------------------------------------------------------- /client/src/images/flags/ma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ma.png -------------------------------------------------------------------------------- /client/src/images/flags/mc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mc.png -------------------------------------------------------------------------------- /client/src/images/flags/md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/md.png -------------------------------------------------------------------------------- /client/src/images/flags/me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/me.png -------------------------------------------------------------------------------- /client/src/images/flags/mg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mg.png -------------------------------------------------------------------------------- /client/src/images/flags/mh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mh.png -------------------------------------------------------------------------------- /client/src/images/flags/mk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mk.png -------------------------------------------------------------------------------- /client/src/images/flags/ml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ml.png -------------------------------------------------------------------------------- /client/src/images/flags/mm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mm.png -------------------------------------------------------------------------------- /client/src/images/flags/mn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mn.png -------------------------------------------------------------------------------- /client/src/images/flags/mr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mr.png -------------------------------------------------------------------------------- /client/src/images/flags/mt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mt.png -------------------------------------------------------------------------------- /client/src/images/flags/mu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mu.png -------------------------------------------------------------------------------- /client/src/images/flags/mv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mv.png -------------------------------------------------------------------------------- /client/src/images/flags/mw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mw.png -------------------------------------------------------------------------------- /client/src/images/flags/mx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mx.png -------------------------------------------------------------------------------- /client/src/images/flags/my.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/my.png -------------------------------------------------------------------------------- /client/src/images/flags/mz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/mz.png -------------------------------------------------------------------------------- /client/src/images/flags/na.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/na.png -------------------------------------------------------------------------------- /client/src/images/flags/ne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ne.png -------------------------------------------------------------------------------- /client/src/images/flags/ng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ng.png -------------------------------------------------------------------------------- /client/src/images/flags/ni.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ni.png -------------------------------------------------------------------------------- /client/src/images/flags/nl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/nl.png -------------------------------------------------------------------------------- /client/src/images/flags/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/no.png -------------------------------------------------------------------------------- /client/src/images/flags/np.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/np.png -------------------------------------------------------------------------------- /client/src/images/flags/nr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/nr.png -------------------------------------------------------------------------------- /client/src/images/flags/nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/nz.png -------------------------------------------------------------------------------- /client/src/images/flags/om.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/om.png -------------------------------------------------------------------------------- /client/src/images/flags/pa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/pa.png -------------------------------------------------------------------------------- /client/src/images/flags/pe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/pe.png -------------------------------------------------------------------------------- /client/src/images/flags/pg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/pg.png -------------------------------------------------------------------------------- /client/src/images/flags/ph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ph.png -------------------------------------------------------------------------------- /client/src/images/flags/pk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/pk.png -------------------------------------------------------------------------------- /client/src/images/flags/pl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/pl.png -------------------------------------------------------------------------------- /client/src/images/flags/pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/pt.png -------------------------------------------------------------------------------- /client/src/images/flags/pw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/pw.png -------------------------------------------------------------------------------- /client/src/images/flags/py.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/py.png -------------------------------------------------------------------------------- /client/src/images/flags/qa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/qa.png -------------------------------------------------------------------------------- /client/src/images/flags/ro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ro.png -------------------------------------------------------------------------------- /client/src/images/flags/rs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/rs.png -------------------------------------------------------------------------------- /client/src/images/flags/ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ru.png -------------------------------------------------------------------------------- /client/src/images/flags/rw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/rw.png -------------------------------------------------------------------------------- /client/src/images/flags/sa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sa.png -------------------------------------------------------------------------------- /client/src/images/flags/sb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sb.png -------------------------------------------------------------------------------- /client/src/images/flags/sc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sc.png -------------------------------------------------------------------------------- /client/src/images/flags/sd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sd.png -------------------------------------------------------------------------------- /client/src/images/flags/se.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/se.png -------------------------------------------------------------------------------- /client/src/images/flags/sg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sg.png -------------------------------------------------------------------------------- /client/src/images/flags/si.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/si.png -------------------------------------------------------------------------------- /client/src/images/flags/sk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sk.png -------------------------------------------------------------------------------- /client/src/images/flags/sl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sl.png -------------------------------------------------------------------------------- /client/src/images/flags/sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sm.png -------------------------------------------------------------------------------- /client/src/images/flags/sn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sn.png -------------------------------------------------------------------------------- /client/src/images/flags/so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/so.png -------------------------------------------------------------------------------- /client/src/images/flags/sr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sr.png -------------------------------------------------------------------------------- /client/src/images/flags/st.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/st.png -------------------------------------------------------------------------------- /client/src/images/flags/sv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sv.png -------------------------------------------------------------------------------- /client/src/images/flags/sy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sy.png -------------------------------------------------------------------------------- /client/src/images/flags/sz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/sz.png -------------------------------------------------------------------------------- /client/src/images/flags/td.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/td.png -------------------------------------------------------------------------------- /client/src/images/flags/tg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tg.png -------------------------------------------------------------------------------- /client/src/images/flags/th.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/th.png -------------------------------------------------------------------------------- /client/src/images/flags/tj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tj.png -------------------------------------------------------------------------------- /client/src/images/flags/tl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tl.png -------------------------------------------------------------------------------- /client/src/images/flags/tm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tm.png -------------------------------------------------------------------------------- /client/src/images/flags/tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tn.png -------------------------------------------------------------------------------- /client/src/images/flags/to.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/to.png -------------------------------------------------------------------------------- /client/src/images/flags/tr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tr.png -------------------------------------------------------------------------------- /client/src/images/flags/tt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tt.png -------------------------------------------------------------------------------- /client/src/images/flags/tv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tv.png -------------------------------------------------------------------------------- /client/src/images/flags/tw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tw.png -------------------------------------------------------------------------------- /client/src/images/flags/tz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/tz.png -------------------------------------------------------------------------------- /client/src/images/flags/ua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ua.png -------------------------------------------------------------------------------- /client/src/images/flags/ug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ug.png -------------------------------------------------------------------------------- /client/src/images/flags/us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/us.png -------------------------------------------------------------------------------- /client/src/images/flags/uy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/uy.png -------------------------------------------------------------------------------- /client/src/images/flags/uz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/uz.png -------------------------------------------------------------------------------- /client/src/images/flags/va.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/va.png -------------------------------------------------------------------------------- /client/src/images/flags/vc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/vc.png -------------------------------------------------------------------------------- /client/src/images/flags/ve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ve.png -------------------------------------------------------------------------------- /client/src/images/flags/vn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/vn.png -------------------------------------------------------------------------------- /client/src/images/flags/vu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/vu.png -------------------------------------------------------------------------------- /client/src/images/flags/ws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ws.png -------------------------------------------------------------------------------- /client/src/images/flags/ye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/ye.png -------------------------------------------------------------------------------- /client/src/images/flags/za.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/za.png -------------------------------------------------------------------------------- /client/src/images/flags/zm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/zm.png -------------------------------------------------------------------------------- /client/src/images/flags/zw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/images/flags/zw.png -------------------------------------------------------------------------------- /client/src/images/flood.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Flood 10 | 11 | 12 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /client/src/javascript/components/alerts/Alert.js: -------------------------------------------------------------------------------- 1 | import {FormattedMessage} from 'react-intl'; 2 | import classnames from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | import React from 'react'; 5 | 6 | import Alerts from '../../constants/Alerts'; 7 | import CircleCheckmarkIcon from '../icons/CircleCheckmarkIcon'; 8 | import CircleExclamationIcon from '../icons/CircleExclamationIcon'; 9 | 10 | export default class Alert extends React.Component { 11 | static propTypes = { 12 | count: PropTypes.number, 13 | id: PropTypes.string, 14 | }; 15 | 16 | static defaultProps = { 17 | count: 0, 18 | type: 'success', 19 | }; 20 | 21 | render() { 22 | let icon = ; 23 | const alertClasses = classnames('alert', { 24 | 'is-success': this.props.type === 'success', 25 | 'is-error': this.props.type === 'error', 26 | }); 27 | 28 | if (this.props.type === 'error') { 29 | icon = ; 30 | } 31 | 32 | return ( 33 |
  • 34 | {icon} 35 | 36 | {this.props.count}, 42 | }} 43 | /> 44 | 45 |
  • 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/src/javascript/components/alerts/Alerts.js: -------------------------------------------------------------------------------- 1 | import {CSSTransition, TransitionGroup} from 'react-transition-group'; 2 | import React from 'react'; 3 | 4 | import Alert from './Alert'; 5 | import AlertStore from '../../stores/AlertStore'; 6 | import connectStores from '../../util/connectStores'; 7 | import EventTypes from '../../constants/EventTypes'; 8 | 9 | class Alerts extends React.Component { 10 | renderAlerts() { 11 | const {alerts} = this.props; 12 | 13 | if (alerts.length > 0) { 14 | return ( 15 | 16 |
      17 | {this.props.alerts.map(alert => ( 18 | 19 | ))} 20 |
    21 |
    22 | ); 23 | } 24 | 25 | return null; 26 | } 27 | 28 | render() { 29 | return {this.renderAlerts()}; 30 | } 31 | } 32 | 33 | const ConnectedAlerts = connectStores(Alerts, () => { 34 | return [ 35 | { 36 | store: AlertStore, 37 | event: EventTypes.ALERTS_CHANGE, 38 | getValue: ({store}) => { 39 | return { 40 | alerts: store.getAlerts(), 41 | }; 42 | }, 43 | }, 44 | ]; 45 | }); 46 | 47 | export default ConnectedAlerts; 48 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/Badge.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Badge extends React.Component { 4 | render() { 5 | return
    {this.props.children}
    ; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/Icon.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | display: block; 3 | } 4 | 5 | .sizeSmall { 6 | height: 12px; 7 | width: 12px; 8 | } 9 | 10 | .sizeMedium { 11 | height: 16px; 12 | width: 16px; 13 | } 14 | 15 | .fillCurrentColor { 16 | fill: currentColor; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/Icon.scss.d.ts: -------------------------------------------------------------------------------- 1 | declare const styles: { 2 | readonly icon: string; 3 | readonly sizeSmall: string; 4 | readonly sizeMedium: string; 5 | readonly fillCurrentColor: string; 6 | }; 7 | export = styles; 8 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/Icon.tsx: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import * as React from 'react'; 3 | import * as styles from './Icon.scss'; 4 | 5 | enum Fills { 6 | NONE, 7 | CURRENT_COLOR, 8 | } 9 | 10 | enum Sizes { 11 | SMALL, 12 | MEDIUM, 13 | } 14 | 15 | interface Props { 16 | className?: string; 17 | fill: Fills; 18 | glyph: string; 19 | size: Sizes; 20 | } 21 | 22 | export default class Icon extends React.PureComponent { 23 | public static defaultProps = { 24 | fill: Fills.CURRENT_COLOR, 25 | size: Sizes.MEDIUM, 26 | }; 27 | 28 | public static Fills = Fills; 29 | 30 | public static Sizes = Sizes; 31 | 32 | public render(): React.ReactNode { 33 | const {className, fill, glyph, size, ...restProps} = this.props; 34 | 35 | return ( 36 | 43 | 44 | 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/LoadingIndicator.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import React from 'react'; 3 | 4 | export default class LoadingIndicator extends React.Component { 5 | render() { 6 | const classes = classnames('loading-indicator', { 7 | 'is-inverse': this.props.inverse, 8 | }); 9 | 10 | return ( 11 |
    12 |
    13 |
    14 |
    15 |
    16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/Portal.js: -------------------------------------------------------------------------------- 1 | import {IntlProvider} from 'react-intl'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | 6 | import * as i18n from '../../i18n/languages'; 7 | import SettingsStore from '../../stores/SettingsStore'; 8 | 9 | class Portal extends React.Component { 10 | static propTypes = { 11 | children: PropTypes.node, 12 | }; 13 | 14 | static defaultProps = { 15 | children:
    , 16 | }; 17 | 18 | componentDidMount() { 19 | this.nodeEl = document.createElement('div'); 20 | document.body.appendChild(this.nodeEl); 21 | } 22 | 23 | componentWillUnmount() { 24 | ReactDOM.unmountComponentAtNode(this.nodeEl); 25 | document.body.removeChild(this.nodeEl); 26 | } 27 | 28 | render() { 29 | const locale = SettingsStore.getFloodSettings('language'); 30 | if (this.nodeEl == null) return null; 31 | return ReactDOM.createPortal( 32 | // eslint-disable-next-line import/namespace 33 | 34 | {this.props.children} 35 | , 36 | this.nodeEl, 37 | ); 38 | } 39 | } 40 | 41 | export default Portal; 42 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ProgressBar extends React.PureComponent { 4 | render() { 5 | const percent = Math.round(this.props.percent); 6 | const style = {}; 7 | 8 | if (percent !== 100) { 9 | style.width = `${percent}%`; 10 | } 11 | 12 | return ( 13 |
    14 |
    {this.props.icon}
    15 |
    16 |
    17 |
    18 |
    19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/Ratio.js: -------------------------------------------------------------------------------- 1 | import {FormattedNumber} from 'react-intl'; 2 | import React from 'react'; 3 | 4 | export default class Ratio extends React.Component { 5 | render() { 6 | let ratio = this.props.value; 7 | 8 | ratio /= 1000; 9 | let precision = 1; 10 | 11 | if (ratio < 10) { 12 | precision = 2; 13 | } else if (ratio >= 100) { 14 | precision = 0; 15 | } 16 | 17 | ratio = ratio.toFixed(precision); 18 | 19 | return ; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/javascript/components/general/Size.js: -------------------------------------------------------------------------------- 1 | import {FormattedNumber, injectIntl} from 'react-intl'; 2 | import React from 'react'; 3 | 4 | import {compute, getTranslationString} from '../../util/size'; 5 | 6 | class Size extends React.Component { 7 | renderNumber(computedNumber) { 8 | if (Number.isNaN(computedNumber.value)) { 9 | return '—'; 10 | } 11 | 12 | return ; 13 | } 14 | 15 | render() { 16 | const {value, isSpeed, className, precision, intl} = this.props; 17 | const computed = compute(value, precision); 18 | 19 | let translatedUnit = intl.formatMessage({id: getTranslationString(computed.unit)}); 20 | 21 | if (isSpeed) { 22 | translatedUnit = intl.formatMessage( 23 | { 24 | id: 'unit.speed', 25 | defaultMessage: '{baseUnit}/s', 26 | }, 27 | { 28 | baseUnit: translatedUnit, 29 | }, 30 | ); 31 | } 32 | 33 | return ( 34 | 35 | {this.renderNumber(computed)} 36 | {translatedUnit} 37 | 38 | ); 39 | } 40 | } 41 | 42 | Size.defaultProps = { 43 | isSpeed: false, 44 | precision: 2, 45 | }; 46 | 47 | export default injectIntl(Size); 48 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Active.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Active extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Add.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class AddMini extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/AddMini.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class AddMini extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/All.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class All extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/ArrowIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class ArrowIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/BaseIcon.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | export default class BaseIcon extends React.Component { 5 | static propTypes = { 6 | size: PropTypes.string, 7 | viewBox: PropTypes.string, 8 | }; 9 | 10 | static defaultProps = { 11 | className: '', 12 | viewBox: '0 0 60 60', 13 | }; 14 | 15 | getViewBox() { 16 | let {viewBox} = this.props; 17 | 18 | if (this.props.size && this.props.size === 'mini') { 19 | viewBox = '0 0 8 8'; 20 | } 21 | 22 | return viewBox; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/CalendarCreatedIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class CalendarCreatedIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 11 | 12 | 13 | 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/CalendarIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class CalendarIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Checkmark.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Checkmark extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/ChevronLeftIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class ChevronLeftIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/ChevronRightIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class ChevronRightIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/CircleCheckmarkIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class CircleCheckmarkIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 14 | 15 | 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/CircleExclamationIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class CircleExclamationIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 14 | 15 | 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/CircleIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Circle extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/ClipboardIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class ClipboardIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/ClockIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class ClockIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Close.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Close extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/CommentIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class CommentIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Completed.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Completed extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/DetailNotAvailableIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class DetailNotAvailableIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Disk.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Disk extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/DiskIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class DiskIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/DotsMini.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class DotsMini extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Download.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Download extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/DownloadSmall.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class DownloadSmall extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/DownloadThickIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class DownloadThickIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/ETA.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class ETA extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 13 | 17 | 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Edit.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Edit extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/ErrorIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Error extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/FeedIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class FeedIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/File.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class File extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Files.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Files extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 13 | 17 | 21 | 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/FolderClosedOutlined.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class FolderClosedOutline extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/FolderClosedSolid.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class FolderClosedOutline extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/FolderOpenOutlined.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class FolderOpenOutlined extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/FolderOpenSolid.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class FolderOpenSolid extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/HashIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class HashIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Inactive.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Inactive extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/InfinityIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class InfinityIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/InformationIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class InformationIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 13 | 14 | 18 | 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/LoadingIndicatorDots.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class LoadingIndicatorDots extends BaseIcon { 6 | render() { 7 | return ( 8 | 11 | 15 | 19 | 23 | 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/LockIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class LockIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 13 | 14 | 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Logout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Logout extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/NotificationIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class NotificationIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/PeersIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class PeersIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/RadarIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class RadarIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/RadioDot.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class RadioDot extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Ratio.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Ratio extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/RatioIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class RatioIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Remove.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class AddMini extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/RemoveMini.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class RemoveMini extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Search.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Search extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 17 | 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/SeedsIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class SeedsIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/StartIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Start extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/StopIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Stop extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/TrackerMessageIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class TrackerMessageIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/Upload.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class Upload extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/javascript/components/icons/UploadThickIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import BaseIcon from './BaseIcon'; 4 | 5 | export default class UploadThickIcon extends BaseIcon { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/javascript/components/layout/ApplicationContent.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | 4 | class ApplicationContent extends React.Component { 5 | static propTypes = { 6 | children: PropTypes.node, 7 | }; 8 | 9 | render() { 10 | return
    {this.props.children}
    ; 11 | } 12 | } 13 | 14 | export default ApplicationContent; 15 | -------------------------------------------------------------------------------- /client/src/javascript/components/layout/ApplicationPanel.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | 5 | class ApplicationContent extends React.Component { 6 | static propTypes = { 7 | children: PropTypes.node, 8 | className: PropTypes.string, 9 | modifier: PropTypes.string, 10 | }; 11 | 12 | static defaultProps = { 13 | baseClassName: 'application__panel', 14 | }; 15 | 16 | render() { 17 | const classes = classnames(this.props.baseClassName, { 18 | [`${this.props.baseClassName}--${this.props.modifier}`]: this.props.baseClassName, 19 | [this.props.className]: this.props.className, 20 | }); 21 | 22 | return
    {this.props.children}
    ; 23 | } 24 | } 25 | 26 | export default ApplicationContent; 27 | -------------------------------------------------------------------------------- /client/src/javascript/components/layout/ApplicationView.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | 5 | class ApplicationView extends React.Component { 6 | static propTypes = { 7 | children: PropTypes.node, 8 | modifier: PropTypes.string, 9 | }; 10 | 11 | render() { 12 | const classes = classnames('application__view', { 13 | [`application__view--${this.props.modifier}`]: this.props.modifier != null, 14 | }); 15 | 16 | return
    {this.props.children}
    ; 17 | } 18 | } 19 | 20 | export default ApplicationView; 21 | -------------------------------------------------------------------------------- /client/src/javascript/components/modals/ModalFormSectionHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class ModalFormSectionHeader extends React.PureComponent { 4 | render() { 5 | return

    {this.props.children}

    ; 6 | } 7 | } 8 | 9 | export default ModalFormSectionHeader; 10 | -------------------------------------------------------------------------------- /client/src/javascript/components/modals/ModalTabs.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import React from 'react'; 3 | 4 | export default class ModalTabs extends React.Component { 5 | handleTabClick(tab) { 6 | if (this.props.onTabChange) { 7 | this.props.onTabChange(tab); 8 | } 9 | } 10 | 11 | render() { 12 | const tabs = Object.keys(this.props.tabs).map(tabId => { 13 | const currentTab = this.props.tabs[tabId]; 14 | 15 | currentTab.id = tabId; 16 | 17 | const classes = classnames('modal__tab', { 18 | 'is-active': tabId === this.props.activeTabId, 19 | }); 20 | 21 | return ( 22 |
  • 23 | {currentTab.label} 24 |
  • 25 | ); 26 | }); 27 | return
      {tabs}
    ; 28 | } 29 | } 30 | 31 | ModalTabs.defaultProps = { 32 | tabs: [], 33 | }; 34 | -------------------------------------------------------------------------------- /client/src/javascript/components/modals/add-torrents-modal/AddTorrentsModal.js: -------------------------------------------------------------------------------- 1 | import {injectIntl} from 'react-intl'; 2 | import React from 'react'; 3 | 4 | import AddTorrentsByFile from './AddTorrentsByFile'; 5 | import AddTorrentsByURL from './AddTorrentsByURL'; 6 | import Modal from '../Modal'; 7 | import UIActions from '../../../actions/UIActions'; 8 | 9 | class AddTorrents extends React.Component { 10 | dismissModal() { 11 | UIActions.dismissModal(); 12 | } 13 | 14 | render() { 15 | const tabs = { 16 | 'by-url': { 17 | content: AddTorrentsByURL, 18 | label: this.props.intl.formatMessage({ 19 | id: 'torrents.add.tab.url.title', 20 | defaultMessage: 'By URL', 21 | }), 22 | }, 23 | 'by-file': { 24 | content: AddTorrentsByFile, 25 | label: this.props.intl.formatMessage({ 26 | id: 'torrents.add.tab.file.title', 27 | defaultMessage: 'By File', 28 | }), 29 | }, 30 | }; 31 | 32 | return ( 33 | 41 | ); 42 | } 43 | } 44 | 45 | export default injectIntl(AddTorrents); 46 | -------------------------------------------------------------------------------- /client/src/javascript/components/modals/confirm-modal/ConfirmModal.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Modal from '../Modal'; 4 | 5 | export default class ConfirmModal extends React.Component { 6 | getContent() { 7 | return
    {this.props.options.content}
    ; 8 | } 9 | 10 | render() { 11 | return ( 12 | 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/javascript/components/modals/feeds-modal/FeedsModal.js: -------------------------------------------------------------------------------- 1 | import {injectIntl} from 'react-intl'; 2 | import React from 'react'; 3 | 4 | import DownloadRulesTab from './DownloadRulesTab'; 5 | import FeedMonitorStore from '../../../stores/FeedMonitorStore'; 6 | import FeedsTab from './FeedsTab'; 7 | import Modal from '../Modal'; 8 | 9 | class FeedsModal extends React.Component { 10 | componentDidMount() { 11 | FeedMonitorStore.fetchFeedMonitors(); 12 | } 13 | 14 | render() { 15 | const tabs = { 16 | feeds: { 17 | content: FeedsTab, 18 | label: this.props.intl.formatMessage({ 19 | id: 'feeds.tabs.feeds', 20 | defaultMessage: 'Feeds', 21 | }), 22 | }, 23 | downloadRules: { 24 | content: DownloadRulesTab, 25 | label: this.props.intl.formatMessage({ 26 | id: 'feeds.tabs.download.rules', 27 | defaultMessage: 'Download Rules', 28 | }), 29 | }, 30 | }; 31 | 32 | return ( 33 | 43 | ); 44 | } 45 | } 46 | 47 | export default injectIntl(FeedsModal); 48 | -------------------------------------------------------------------------------- /client/src/javascript/components/modals/settings-modal/AboutTab.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React from 'react'; 3 | import ReactMarkdown from 'react-markdown'; 4 | 5 | import AboutMarkdownPath from '../../../../../../ABOUT.md'; 6 | import SettingsTab from './SettingsTab'; 7 | 8 | export default class AboutTab extends SettingsTab { 9 | state = { 10 | about: null, 11 | }; 12 | 13 | componentDidMount() { 14 | axios.get(AboutMarkdownPath).then(response => { 15 | this.setState({about: response.data}); 16 | }); 17 | } 18 | 19 | render() { 20 | return ; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/javascript/components/modals/settings-modal/SettingsTab.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class SettingsTab extends React.Component { 4 | state = {}; 5 | 6 | getFieldValue(fieldName) { 7 | if (this.state[fieldName] == null) { 8 | return this.props.settings[fieldName] || ''; 9 | } 10 | 11 | return this.state[fieldName]; 12 | } 13 | 14 | handleClientSettingFieldChange(fieldName, event) { 15 | let {value} = event.target; 16 | 17 | if (event.target.type === 'checkbox') { 18 | value = event.target.checked ? '1' : '0'; 19 | } 20 | 21 | const nextState = {[fieldName]: value}; 22 | 23 | this.setState(nextState); 24 | this.props.onClientSettingsChange(nextState); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/src/javascript/components/sidebar/LogoutButton.js: -------------------------------------------------------------------------------- 1 | import {defineMessages, injectIntl} from 'react-intl'; 2 | import React from 'react'; 3 | 4 | import AuthActions from '../../actions/AuthActions'; 5 | import Logout from '../icons/Logout'; 6 | import Tooltip from '../general/Tooltip'; 7 | 8 | const MESSAGES = defineMessages({ 9 | logOut: { 10 | id: 'sidebar.button.log.out', 11 | defaultMessage: 'Log Out', 12 | }, 13 | }); 14 | 15 | class LogoutButton extends React.Component { 16 | handleLogoutClick() { 17 | AuthActions.logout().then(() => { 18 | window.location.reload(); 19 | }); 20 | } 21 | 22 | render() { 23 | return ( 24 | 31 | 32 | 33 | ); 34 | } 35 | } 36 | 37 | export default injectIntl(LogoutButton); 38 | -------------------------------------------------------------------------------- /client/src/javascript/components/sidebar/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import ClientStats from './TransferData'; 4 | import CustomScrollbars from '../general/CustomScrollbars'; 5 | import FeedsButton from './FeedsButton'; 6 | import LogoutButton from './LogoutButton'; 7 | import NotificationsButton from './NotificationsButton'; 8 | import SearchTorrents from './SearchTorrents'; 9 | import SettingsButton from './SettingsButton'; 10 | import SidebarActions from './SidebarActions'; 11 | import SpeedLimitDropdown from './SpeedLimitDropdown'; 12 | import StatusFilters from './StatusFilters'; 13 | import TagFilters from './TagFilters'; 14 | import TrackerFilters from './TrackerFilters'; 15 | import DiskUsage from './DiskUsage'; 16 | 17 | const Sidebar = () => { 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | export default Sidebar; 38 | -------------------------------------------------------------------------------- /client/src/javascript/components/sidebar/SidebarActions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class SidebarActions extends React.Component { 4 | render() { 5 | return
    {this.props.children}
    ; 6 | } 7 | } 8 | 9 | export default SidebarActions; 10 | -------------------------------------------------------------------------------- /client/src/javascript/components/sidebar/SidebarFilter.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import {injectIntl} from 'react-intl'; 3 | import React from 'react'; 4 | 5 | import Badge from '../general/Badge'; 6 | 7 | const METHODS_TO_BIND = ['handleClick']; 8 | 9 | class SidebarFilter extends React.Component { 10 | constructor() { 11 | super(); 12 | 13 | METHODS_TO_BIND.forEach(method => { 14 | this[method] = this[method].bind(this); 15 | }); 16 | } 17 | 18 | handleClick() { 19 | this.props.handleClick(this.props.slug); 20 | } 21 | 22 | render() { 23 | const classNames = classnames('sidebar-filter__item', { 24 | 'is-active': this.props.isActive, 25 | }); 26 | let {name} = this.props; 27 | 28 | if (this.props.name === 'all') { 29 | name = this.props.intl.formatMessage({ 30 | id: 'filter.all', 31 | defaultMessage: 'All', 32 | }); 33 | } else if (this.props.name === 'untagged') { 34 | name = this.props.intl.formatMessage({ 35 | id: 'filter.untagged', 36 | defaultMessage: 'Untagged', 37 | }); 38 | } 39 | 40 | return ( 41 |
  • 42 | {this.props.icon} 43 | {name} 44 | {this.props.count} 45 |
  • 46 | ); 47 | } 48 | } 49 | 50 | export default injectIntl(SidebarFilter); 51 | -------------------------------------------------------------------------------- /client/src/javascript/components/sidebar/SidebarItem.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import PropTypes from 'prop-types'; 3 | import React from 'react'; 4 | 5 | class SidebarItem extends React.Component { 6 | static propTypes = { 7 | baseClassName: PropTypes.string, 8 | children: PropTypes.node, 9 | modifier: PropTypes.string, 10 | }; 11 | 12 | static defaultProps = { 13 | baseClassName: 'sidebar__item', 14 | }; 15 | 16 | render() { 17 | const classes = classnames(this.props.baseClassName, { 18 | [`${this.props.baseClassName}--${this.props.modifier}`]: this.props.modifier, 19 | }); 20 | 21 | return
    {this.props.children}
    ; 22 | } 23 | } 24 | 25 | export default SidebarItem; 26 | -------------------------------------------------------------------------------- /client/src/javascript/components/torrent-list/Action.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import React from 'react'; 3 | 4 | import Tooltip from '../general/Tooltip'; 5 | 6 | export default class Action extends React.Component { 7 | render() { 8 | const {clickHandler, icon, label, slug} = this.props; 9 | const classes = classnames('action tooltip__wrapper', { 10 | [`action--${slug}`]: slug != null, 11 | }); 12 | 13 | return ( 14 | 15 | {icon} 16 | {label} 17 | 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/javascript/components/views/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import ApplicationView from '../layout/ApplicationView'; 4 | import AuthForm from '../auth/AuthForm'; 5 | 6 | export default class LoginView extends React.Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/javascript/components/views/Register.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import ApplicationView from '../layout/ApplicationView'; 4 | import AuthForm from '../auth/AuthForm'; 5 | 6 | export default class LoginView extends React.Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/javascript/components/views/TorrentClientOverview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import ActionBar from '../torrent-list/ActionBar'; 4 | import Alerts from '../alerts/Alerts'; 5 | import ApplicationContent from '../layout/ApplicationContent'; 6 | import ApplicationPanel from '../layout/ApplicationPanel'; 7 | import ApplicationView from '../layout/ApplicationView'; 8 | import Modals from '../modals/Modals'; 9 | import Sidebar from '../sidebar/Sidebar'; 10 | import TorrentList from '../torrent-list/TorrentList'; 11 | 12 | export default class TorrentCLientOverview extends React.Component { 13 | render() { 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/javascript/constants/Alerts.js: -------------------------------------------------------------------------------- 1 | const ALERTS = { 2 | 'alert.torrent.add': `Successfully added {countElement} {count, plural, 3 | =1 {torrent} 4 | other {torrents} 5 | }.`, 6 | 'alert.torrent.add.failed': `Failed to add {countElement} {count, plural, 7 | =1 {torrent} 8 | other {torrents} 9 | }.`, 10 | 'alert.torrent.move': `Successfully moved {countElement} {count, plural, 11 | =1 {torrent} 12 | other {torrents} 13 | }.`, 14 | 'alert.torrent.move.failed': `Failed to move {countElement} {count, plural, 15 | =1 {torrent} 16 | other {torrents} 17 | }.`, 18 | 'alert.torrent.remove': `Successfully removed {countElement} {count, plural, 19 | =1 {torrent} 20 | other {torrents} 21 | }.`, 22 | 'alert.torrent.remove.failed': `Failed to remove {countElement} {count, plural, 23 | =1 {torrent} 24 | other {torrents} 25 | }.`, 26 | 'alert.settings.saved': 'Successfully saved settings.', 27 | }; 28 | 29 | export default ALERTS; 30 | -------------------------------------------------------------------------------- /client/src/javascript/constants/Languages.js: -------------------------------------------------------------------------------- 1 | const LANGUAGES = { 2 | en: { 3 | defaultMessage: 'English', 4 | id: 'locale.language.en', 5 | }, 6 | es: { 7 | defaultMessage: 'Spanish', 8 | id: 'locale.language.es', 9 | }, 10 | fr: { 11 | defaultMessage: 'French', 12 | id: 'locale.language.fr', 13 | }, 14 | nl: { 15 | defaultMessage: 'Nederlands', 16 | id: 'locale.language.nl', 17 | }, 18 | ko: { 19 | defaultMessage: 'Korean', 20 | id: 'locale.language.ko', 21 | }, 22 | zh: { 23 | defaultMessage: 'Chinese', 24 | id: 'locale.language.zh', 25 | }, 26 | }; 27 | 28 | export default LANGUAGES; 29 | -------------------------------------------------------------------------------- /client/src/javascript/constants/PriorityLevels.js: -------------------------------------------------------------------------------- 1 | const PRIORITY_LEVELS = { 2 | file: { 3 | 0: 'DONT_DOWNLOAD', 4 | 1: 'NORMAL', 5 | 2: 'HIGH', 6 | }, 7 | torrent: { 8 | 0: 'DONT_DOWNLOAD', 9 | 1: 'LOW', 10 | 2: 'NORMAL', 11 | 3: 'HIGH', 12 | }, 13 | }; 14 | 15 | export default PRIORITY_LEVELS; 16 | -------------------------------------------------------------------------------- /client/src/javascript/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | import {Dispatcher} from 'flux'; 2 | 3 | class FloodDispatcher extends Dispatcher { 4 | dispatchUIAction(action) { 5 | if (action.type == null) { 6 | console.warn('Undefined action.type', action); 7 | } 8 | this.dispatch({source: 'UI_ACTION', action}); 9 | } 10 | 11 | dispatchServerAction(action) { 12 | if (action.type == null) { 13 | console.warn('Undefined action.type', action); 14 | } 15 | this.dispatch({source: 'SERVER_ACTION', action}); 16 | } 17 | } 18 | 19 | const AppDispatcher = new FloodDispatcher(); 20 | 21 | export default AppDispatcher; 22 | -------------------------------------------------------------------------------- /client/src/javascript/i18n/de.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'sidebar.button.settings': 'Einstellungen', 3 | 4 | 'button.save.feed': 'Speichern', 5 | }; 6 | -------------------------------------------------------------------------------- /client/src/javascript/i18n/languages.ts: -------------------------------------------------------------------------------- 1 | import {addLocaleData} from 'react-intl'; 2 | import deLocaleData from 'react-intl/locale-data/de'; 3 | import enLocaleData from 'react-intl/locale-data/en'; 4 | import esLocaleData from 'react-intl/locale-data/es'; 5 | import frLocaleData from 'react-intl/locale-data/fr'; 6 | import koLocaleData from 'react-intl/locale-data/ko'; 7 | import nlLocaleData from 'react-intl/locale-data/nl'; 8 | import zhLocaleData from 'react-intl/locale-data/zh'; 9 | 10 | addLocaleData(deLocaleData); 11 | addLocaleData(enLocaleData); 12 | addLocaleData(esLocaleData); 13 | addLocaleData(frLocaleData); 14 | addLocaleData(koLocaleData); 15 | addLocaleData(nlLocaleData); 16 | addLocaleData(zhLocaleData); 17 | 18 | export {default as de} from './de'; 19 | export {default as en} from './en'; 20 | export {default as es} from './es'; 21 | export {default as fr} from './fr'; 22 | export {default as ko} from './ko'; 23 | export {default as nl} from './nl'; 24 | export {default as zh} from './zh'; 25 | -------------------------------------------------------------------------------- /client/src/javascript/stores/BaseStore.js: -------------------------------------------------------------------------------- 1 | import {EventEmitter} from 'events'; 2 | 3 | export default class BaseStore extends EventEmitter { 4 | constructor(...eventEmitterConfig) { 5 | super(...eventEmitterConfig); 6 | 7 | this.dispatcherID = null; 8 | this.on('uncaughtException', error => { 9 | throw new Error(error); 10 | }); 11 | this.requests = {}; 12 | this.setMaxListeners(20); 13 | } 14 | 15 | beginRequest(id) { 16 | this.requests[id] = true; 17 | } 18 | 19 | isRequestPending(id) { 20 | if (this.requests[id] == null) { 21 | return false; 22 | } 23 | 24 | return true; 25 | } 26 | 27 | listen(event, callback) { 28 | this.on(event, callback); 29 | } 30 | 31 | resolveRequest(id) { 32 | delete this.requests[id]; 33 | } 34 | 35 | unlisten(event, callback) { 36 | this.removeListener(event, callback); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/src/javascript/stores/ClientStatusStore.js: -------------------------------------------------------------------------------- 1 | import ActionTypes from '../constants/ActionTypes'; 2 | import AppDispatcher from '../dispatcher/AppDispatcher'; 3 | import BaseStore from './BaseStore'; 4 | import EventTypes from '../constants/EventTypes'; 5 | 6 | class ClientStatusStoreClass extends BaseStore { 7 | constructor() { 8 | super(); 9 | this.errorCount = 0; 10 | this.isConnected = null; 11 | } 12 | 13 | getIsConnected() { 14 | return this.isConnected === true; 15 | } 16 | 17 | handleConnectivityStatusChange({isConnected}) { 18 | if (this.isConnected !== isConnected) { 19 | this.isConnected = isConnected; 20 | this.emit(EventTypes.CLIENT_CONNECTION_STATUS_CHANGE); 21 | } 22 | } 23 | } 24 | 25 | const ClientStatusStore = new ClientStatusStoreClass(); 26 | 27 | ClientStatusStore.dispatcherID = AppDispatcher.register(payload => { 28 | const {action} = payload; 29 | 30 | switch (action.type) { 31 | case ActionTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE: 32 | ClientStatusStore.handleConnectivityStatusChange(action.data); 33 | break; 34 | default: 35 | break; 36 | } 37 | }); 38 | 39 | export default ClientStatusStore; 40 | -------------------------------------------------------------------------------- /client/src/javascript/stores/ConfigStore.js: -------------------------------------------------------------------------------- 1 | import BaseStore from './BaseStore'; 2 | 3 | class ConfigStoreClass extends BaseStore { 4 | getBaseURI() { 5 | return process.env.BASE_URI; 6 | } 7 | 8 | getPollInterval() { 9 | return process.env.POLL_INTERVAL || 5000; 10 | } 11 | } 12 | 13 | export default new ConfigStoreClass(); 14 | -------------------------------------------------------------------------------- /client/src/javascript/stores/DiskUsageStore.js: -------------------------------------------------------------------------------- 1 | import BaseStore from './BaseStore'; 2 | import ActionTypes from '../constants/ActionTypes'; 3 | import EventTypes from '../constants/EventTypes'; 4 | import AppDispatcher from '../dispatcher/AppDispatcher'; 5 | 6 | class DiskUsageStoreClass extends BaseStore { 7 | constructor() { 8 | super(); 9 | this.disks = []; 10 | } 11 | 12 | setDiskUsage(disks) { 13 | this.disks = disks; 14 | this.emit(EventTypes.DISK_USAGE_CHANGE); 15 | } 16 | 17 | getDiskUsage() { 18 | return this.disks; 19 | } 20 | } 21 | 22 | const DiskUsageStore = new DiskUsageStoreClass(); 23 | 24 | DiskUsageStore.dispatcherID = AppDispatcher.register(payload => { 25 | const {action} = payload; 26 | switch (action.type) { 27 | case ActionTypes.DISK_USAGE_CHANGE: 28 | DiskUsageStore.setDiskUsage(action.data); 29 | break; 30 | default: 31 | break; 32 | } 33 | }); 34 | 35 | export default DiskUsageStore; 36 | -------------------------------------------------------------------------------- /client/src/javascript/util/filterTorrents.js: -------------------------------------------------------------------------------- 1 | import torrentStatusMap from '@shared/constants/torrentStatusMap'; 2 | 3 | export function filterTorrents(torrentList, opts) { 4 | const {type, filter} = opts; 5 | 6 | if (filter !== 'all') { 7 | if (type === 'status') { 8 | const statusFilter = torrentStatusMap[filter]; 9 | return torrentList.filter(torrent => torrent.status.includes(statusFilter)); 10 | } 11 | if (type === 'tracker') { 12 | return torrentList.filter(torrent => torrent.trackerURIs.includes(filter)); 13 | } 14 | if (type === 'tag') { 15 | return torrentList.filter(torrent => { 16 | if (filter === 'untagged') { 17 | return torrent.tags.length === 0; 18 | } 19 | 20 | return torrent.tags.includes(filter); 21 | }); 22 | } 23 | } 24 | 25 | return torrentList; 26 | } 27 | -------------------------------------------------------------------------------- /client/src/javascript/util/history.js: -------------------------------------------------------------------------------- 1 | import {createBrowserHistory} from 'history'; 2 | 3 | import stringUtil from '@shared/util/stringUtil'; 4 | import ConfigStore from '../stores/ConfigStore'; 5 | 6 | const history = createBrowserHistory({basename: stringUtil.withoutTrailingSlash(ConfigStore.getBaseURI())}); 7 | 8 | export default history; 9 | -------------------------------------------------------------------------------- /client/src/javascript/util/searchTorrents.js: -------------------------------------------------------------------------------- 1 | export function searchTorrents(torrents, searchString) { 2 | if (searchString !== '') { 3 | const queries = []; 4 | const searchTerms = searchString.replace(/,/g, ' ').split(' '); 5 | 6 | for (let i = 0, len = searchTerms.length; i < len; i++) { 7 | queries.push(new RegExp(searchTerms[i], 'gi')); 8 | } 9 | 10 | torrents = torrents.filter(torrent => { 11 | for (let i = 0, len = queries.length; i < len; i++) { 12 | if (!torrent.name.match(queries[i])) { 13 | return false; 14 | } 15 | } 16 | return true; 17 | }); 18 | } 19 | 20 | return torrents; 21 | } 22 | -------------------------------------------------------------------------------- /client/src/javascript/util/size.js: -------------------------------------------------------------------------------- 1 | export function compute(bytes, precision = 2) { 2 | const kilobyte = 1024; 3 | 4 | const megabyte = kilobyte * 1024; 5 | 6 | const gigabyte = megabyte * 1024; 7 | 8 | const terabyte = gigabyte * 1024; 9 | let value = 0; 10 | 11 | let unit = ''; 12 | 13 | if (bytes >= terabyte) { 14 | value = bytes / terabyte; 15 | unit = 'TB'; 16 | } else if (bytes >= gigabyte) { 17 | value = bytes / gigabyte; 18 | unit = 'GB'; 19 | } else if (bytes >= megabyte) { 20 | value = bytes / megabyte; 21 | unit = 'MB'; 22 | } else if (bytes >= kilobyte) { 23 | value = bytes / kilobyte; 24 | unit = 'kB'; 25 | } else { 26 | value = bytes; 27 | unit = 'B'; 28 | } 29 | 30 | value = Number(value); 31 | if (!!value && value >= 100) { 32 | value = Math.floor(value); 33 | } else if (!!value && value > 10) { 34 | value = Number(value.toFixed(precision - 1)); 35 | } else if (value) { 36 | value = Number(value.toFixed(precision)); 37 | } 38 | 39 | return { 40 | value, 41 | unit, 42 | }; 43 | } 44 | 45 | export function getTranslationString(unit) { 46 | const UNIT_TO_STRING_ID = { 47 | B: 'unit.size.byte', 48 | kB: 'unit.size.kilobyte', 49 | MB: 'unit.size.megabyte', 50 | GB: 'unit.size.gigabyte', 51 | TB: 'unit.size.terabyte', 52 | }; 53 | 54 | return UNIT_TO_STRING_ID[unit] || 'unit.size.byte'; 55 | } 56 | -------------------------------------------------------------------------------- /client/src/javascript/util/torrentStatusClasses.js: -------------------------------------------------------------------------------- 1 | import classnames from 'classnames'; 2 | import torrentStatusMap from '@shared/constants/torrentStatusMap'; 3 | 4 | export function torrentStatusClasses(torrent, ...classes) { 5 | return classnames(classes, { 6 | 'torrent--has-error': torrent.status.includes(torrentStatusMap.error), 7 | 'torrent--is-stopped': torrent.status.includes(torrentStatusMap.stopped), 8 | 'torrent--is-downloading': torrent.status.includes(torrentStatusMap.downloading), 9 | 'torrent--is-downloading--actively': torrent.status.includes(torrentStatusMap.activelyDownloading), 10 | 'torrent--is-uploading--actively': torrent.status.includes(torrentStatusMap.activelyUploading), 11 | 'torrent--is-seeding': torrent.status.includes(torrentStatusMap.seeding), 12 | 'torrent--is-completed': torrent.status.includes(torrentStatusMap.complete), 13 | 'torrent--is-checking': torrent.status.includes(torrentStatusMap.checking), 14 | 'torrent--is-inactive': torrent.status.includes(torrentStatusMap.inactive), 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /client/src/javascript/util/torrentStatusIcons.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import torrentStatusMap from '@shared/constants/torrentStatusMap'; 3 | 4 | import ErrorIcon from '../components/icons/ErrorIcon'; 5 | import SpinnerIcon from '../components/icons/SpinnerIcon'; 6 | import StartIcon from '../components/icons/StartIcon'; 7 | import StopIcon from '../components/icons/StopIcon'; 8 | 9 | const STATUS_ICON_MAP = { 10 | error: , 11 | hashChecking: , 12 | stopped: , 13 | running: , 14 | }; 15 | 16 | export function torrentStatusIcons(status) { 17 | let statusString; 18 | const statusConditions = { 19 | hashChecking: [status.includes(torrentStatusMap.checking)], 20 | error: [status.includes(torrentStatusMap.error)], 21 | stopped: [status.includes(torrentStatusMap.stopped)], 22 | running: [status.includes(torrentStatusMap.downloading), status.includes(torrentStatusMap.seeding)], 23 | }; 24 | 25 | Object.keys(statusConditions).some(conditionName => { 26 | const conditions = statusConditions[conditionName]; 27 | 28 | conditions.some(condition => { 29 | if (condition) { 30 | statusString = conditionName; 31 | } 32 | 33 | return condition; 34 | }); 35 | 36 | return statusString != null; 37 | }); 38 | 39 | return STATUS_ICON_MAP[statusString]; 40 | } 41 | -------------------------------------------------------------------------------- /client/src/javascript/util/validators.js: -------------------------------------------------------------------------------- 1 | import regEx from '@shared/util/regEx'; 2 | 3 | export const isNotEmpty = value => { 4 | return value != null && value !== ''; 5 | }; 6 | 7 | export const isRegExValid = regExToCheck => { 8 | try { 9 | // eslint-disable-next-line no-new 10 | new RegExp(regExToCheck); 11 | } catch (err) { 12 | return false; 13 | } 14 | 15 | return true; 16 | }; 17 | 18 | export const isURLValid = url => { 19 | return url != null && url !== '' && url.match(regEx.url) !== null; 20 | }; 21 | 22 | export const isPositiveInteger = value => { 23 | if (value === null || value === '') return false; 24 | 25 | const number = parseInt(value, 10); 26 | 27 | return !Number.isNaN(number) && number > 0; 28 | }; 29 | -------------------------------------------------------------------------------- /client/src/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /client/src/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /client/src/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/apple-touch-icon.png -------------------------------------------------------------------------------- /client/src/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #0e2337 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/src/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/favicon-16x16.png -------------------------------------------------------------------------------- /client/src/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/favicon-32x32.png -------------------------------------------------------------------------------- /client/src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/favicon.ico -------------------------------------------------------------------------------- /client/src/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Flood", 3 | "icons": [ 4 | { 5 | "src": "android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "start_url": "./index.html", 16 | "theme_color": "#ffffff", 17 | "background_color": "#1d2938", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /client/src/public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/mstile-144x144.png -------------------------------------------------------------------------------- /client/src/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/mstile-150x150.png -------------------------------------------------------------------------------- /client/src/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/mstile-310x150.png -------------------------------------------------------------------------------- /client/src/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/mstile-310x310.png -------------------------------------------------------------------------------- /client/src/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/client/src/public/mstile-70x70.png -------------------------------------------------------------------------------- /client/src/public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/src/sass/base/_animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes fade-in { 2 | 3 | 0% { 4 | opacity: 0; 5 | } 6 | 7 | 100% { 8 | opacity: 1; 9 | } 10 | 11 | } 12 | 13 | @keyframes fade-out { 14 | 15 | 0% { 16 | opacity: 1; 17 | } 18 | 19 | 100% { 20 | opacity: 0; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /client/src/sass/base/_layout.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | overflow: hidden; 5 | } 6 | 7 | .container { 8 | height: 100%; 9 | width: 100%; 10 | } 11 | 12 | #app, 13 | .application, 14 | .application__view { 15 | height: 100%; 16 | width: 100%; 17 | } 18 | 19 | .application { 20 | 21 | &, 22 | &__view { 23 | align-content: center; 24 | align-items: center; 25 | display: flex; 26 | flex: 1; 27 | justify-content: center; 28 | height: 100%; 29 | width: 100%; 30 | } 31 | 32 | &__content { 33 | align-items: center; 34 | display: flex; 35 | flex: 1 0 auto; 36 | flex-direction: column; 37 | height: 100%; 38 | justify-content: center; 39 | position: relative; 40 | } 41 | 42 | &__panel { 43 | display: flex; 44 | bottom: 0; 45 | left: 0; 46 | position: absolute; 47 | right: 0; 48 | top: 0; 49 | 50 | &--torrent-list { 51 | transition: transform 0.5s; 52 | z-index: 2; 53 | 54 | &.is-open { 55 | transform: translateX($torrent-details--width); 56 | } 57 | } 58 | 59 | &--torrent-details { 60 | right: 100% - $torrent-details--width; 61 | width: $torrent-details--width; 62 | z-index: 1; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /client/src/sass/base/_main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: $background; 3 | } 4 | 5 | ul { 6 | list-style: none; 7 | } 8 | -------------------------------------------------------------------------------- /client/src/sass/components/_app-wrapper.scss: -------------------------------------------------------------------------------- 1 | .application { 2 | &__loading-overlay { 3 | align-items: center; 4 | background: $light-blue; 5 | display: flex; 6 | flex-direction: column; 7 | font-size: 0.8em; 8 | height: 100%; 9 | justify-content: center; 10 | left: 0; 11 | opacity: 1; 12 | position: fixed; 13 | top: 0; 14 | width: 100%; 15 | z-index: 1000; 16 | 17 | &-exit { 18 | opacity: 1; 19 | transition: opacity 1s; 20 | 21 | &-active { 22 | opacity: 0; 23 | } 24 | } 25 | } 26 | 27 | &__entry-barrier { 28 | max-width: 500px; 29 | width: 100%; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/sass/components/_attached-panel.scss: -------------------------------------------------------------------------------- 1 | $attached-panel--background: #242b36; 2 | $attached-panel--foreground: #5e728c; 3 | 4 | $attached-panel--border: #1a2028; 5 | $attached-panel--border-radius: 4px; 6 | 7 | $attached-panel--padding--vertical: 10px; 8 | $attached-panel--padding--horizontal: 15px; 9 | 10 | $attached-panel--shadow: #05080a; 11 | 12 | .attached-panel { 13 | background: $attached-panel--background; 14 | border: 1px solid $attached-panel--border; 15 | border-radius: 0 0 $attached-panel--border-radius $attached-panel--border-radius; 16 | border-top-width: 0; 17 | color: $attached-panel--foreground; 18 | position: fixed; 19 | transition: opacity 0.25s; 20 | z-index: 100; 21 | 22 | &__content { 23 | padding: $attached-panel--padding--vertical $attached-panel--padding--horizontal; 24 | } 25 | 26 | &__wrapper { 27 | position: relative; 28 | } 29 | 30 | &-enter { 31 | opacity: 0; 32 | 33 | &-active { 34 | opacity: 1; 35 | } 36 | } 37 | 38 | &-exit { 39 | opacity: 1; 40 | 41 | &-active { 42 | opacity: 0; 43 | } 44 | } 45 | } 46 | 47 | .textbox { 48 | &--has-attached-panel { 49 | &--is-open { 50 | border-bottom-left-radius: 0; 51 | border-bottom-right-radius: 0; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /client/src/sass/components/_badge.scss: -------------------------------------------------------------------------------- 1 | $sidebar-filter--count--foreground: darken($grey, 5%); 2 | $sidebar-filter--count--background: lighten($grey, 20%); 3 | $sidebar-filter--count--background--active: $blue; 4 | 5 | .badge { 6 | background: $sidebar-filter--count--background; 7 | border-radius: 100px; 8 | color: $sidebar-filter--count--foreground; 9 | display: inline-block; 10 | font-size: 0.75rem; 11 | font-weight: 700; 12 | line-height: 1; 13 | margin-left: 10px; 14 | padding: 2px 5px; 15 | transition: background 0.25s; 16 | vertical-align: middle; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/sass/components/_connection-status.scss: -------------------------------------------------------------------------------- 1 | .connection-status { 2 | display: flex; 3 | align-items: center; 4 | 5 | &__icon { 6 | fill: $green--darker; 7 | height: 15px; 8 | margin-right: 5px; 9 | width: 15px; 10 | } 11 | 12 | &__copy { 13 | color: $light-grey--lighter; 14 | font-size: 14px; 15 | } 16 | } -------------------------------------------------------------------------------- /client/src/sass/components/_dependency-list.scss: -------------------------------------------------------------------------------- 1 | .dependency-list { 2 | margin-top: $spacing-unit; 3 | width: auto; 4 | 5 | &__dependency { 6 | color: $foreground; 7 | display: flex; 8 | opacity: 0.5; 9 | transition: opacity 0.25s; 10 | white-space: nowrap; 11 | 12 | &__icon { 13 | fill: $green; 14 | flex: 0 0 $spacing-unit * 2/5; 15 | height: $spacing-unit * 2/5; 16 | margin-right: $spacing-unit * 1/5; 17 | opacity: 0; 18 | transition: opacity 0.25s; 19 | width: $spacing-unit * 2/5; 20 | 21 | .icon { 22 | height: 10px; 23 | width: 10px; 24 | } 25 | } 26 | 27 | &--satisfied { 28 | opacity: 1; 29 | 30 | .dependency-list { 31 | 32 | &__dependency { 33 | 34 | &__icon { 35 | opacity: 1; 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /client/src/sass/components/_duration.scss: -------------------------------------------------------------------------------- 1 | .duration { 2 | 3 | &--segment { 4 | margin-right: 0.25em; 5 | 6 | &:last-child { 7 | margin-right: 0; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/sass/components/_mediainfo.scss: -------------------------------------------------------------------------------- 1 | .mediainfo { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | &__toolbar { 6 | display: flex; 7 | flex: 0 0 auto; 8 | justify-content: space-between; 9 | padding-bottom: $spacing-unit * 2/5; 10 | position: relative; 11 | 12 | .tooltip__wrapper { 13 | bottom: 0; 14 | position: absolute; 15 | right: 0; 16 | } 17 | } 18 | 19 | &__copy-button { 20 | 21 | &.tooltip { 22 | 23 | &__wrapper { 24 | position: absolute; 25 | right: 0; 26 | top: 0; 27 | } 28 | } 29 | 30 | .icon { 31 | fill: currentColor; 32 | height: 16px; 33 | width: 16px; 34 | } 35 | } 36 | 37 | &__output { 38 | flex: 1 1 auto; 39 | overflow: auto; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /client/src/sass/components/_peers-list.scss: -------------------------------------------------------------------------------- 1 | .peers-list { 2 | 3 | &__flag { 4 | display: inline-block; 5 | height: 10px; 6 | overflow: hidden; 7 | margin-right: $spacing-unit * 3/10; 8 | position: relative; 9 | width: 15px; 10 | vertical-align: baseline; 11 | 12 | &__image { 13 | height: 10px; 14 | left: 50%; 15 | position: absolute; 16 | top: 0; 17 | transform: translateX(-50%); 18 | width: auto; 19 | z-index: 2; 20 | } 21 | 22 | &__text { 23 | font-size: 0.8em; 24 | font-weight: bold; 25 | left: 50%; 26 | margin-top: 1px; 27 | position: absolute; 28 | top: 50%; 29 | transform: translate(-50%, -50%); 30 | z-index: 1; 31 | } 32 | } 33 | 34 | &__encryption { 35 | 36 | .icon { 37 | fill: $green; 38 | height: 12px; 39 | width: 12px; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/sass/components/_scrollbars.scss: -------------------------------------------------------------------------------- 1 | $scrollbar--thumb--background--inactive: rgba(#1a2f3d, 0.3); 2 | $scrollbar--thumb--background--hover: rgba(#1a2f3d, 0.6); 3 | $scrollbar--thumb--background--inverted--inactive: rgba(#e9eef2, 0.3); 4 | $scrollbar--thumb--background--inverted--hover: rgba(#e9eef2, 0.6); 5 | 6 | .scrollbars { 7 | 8 | &__thumb { 9 | background: $scrollbar--thumb--background--inactive; 10 | border-radius: 10px; 11 | cursor: pointer; 12 | opacity: 0; 13 | transition: background 0.25s, opacity 0.5s; 14 | z-index: 2; 15 | 16 | &:active { 17 | opacity: 1; 18 | } 19 | 20 | &:hover, 21 | &:active { 22 | background: $scrollbar--thumb--background--hover; 23 | } 24 | 25 | &--surrogate { 26 | display: block; 27 | height: 100%; 28 | width: 100%; 29 | } 30 | 31 | .is-inverted & { 32 | background: $scrollbar--thumb--background--inverted--inactive; 33 | 34 | &:hover, 35 | &:active { 36 | background: $scrollbar--thumb--background--inverted--hover; 37 | } 38 | } 39 | } 40 | 41 | &:hover { 42 | 43 | .scrollbars__thumb { 44 | opacity: 1; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/src/sass/components/_sort-dropdown.scss: -------------------------------------------------------------------------------- 1 | .sort-dropdown { 2 | 3 | &__item { 4 | align-items: center; 5 | display: flex; 6 | } 7 | 8 | &__indicator { 9 | border-left: 4px solid transparent; 10 | border-right: 4px solid transparent; 11 | border-top: 5px solid currentColor; 12 | display: inline-block; 13 | margin-left: auto; 14 | transition: transform 0.25s; 15 | vertical-align: middle; 16 | 17 | &--asc { 18 | transform: rotate(180deg); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/sass/components/_tags.scss: -------------------------------------------------------------------------------- 1 | $tag--background: rgba(#4a596d, 0.75); 2 | $tag--foreground: #1a2028; 3 | 4 | .tag { 5 | background: $tag--background; 6 | border-radius: 30px; 7 | color: $tag--foreground; 8 | display: inline-block; 9 | font-size: 0.9em; 10 | margin-right: $spacing-unit * 1/5; 11 | padding: 0 $spacing-unit * 2/5; 12 | white-space: nowrap; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/sass/components/_textbox-repeater.scss: -------------------------------------------------------------------------------- 1 | .textbox-repeater { 2 | 3 | .icon { 4 | height: 12px; 5 | width: 12px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/src/sass/components/_transfer-data.scss: -------------------------------------------------------------------------------- 1 | $transfer-data--download: $green; 2 | $transfer-data--upload: $blue; 3 | 4 | .transfer-data { 5 | 6 | &--download { 7 | color: $transfer-data--download; 8 | 9 | .icon { 10 | fill: $transfer-data--download; 11 | } 12 | } 13 | 14 | &--upload { 15 | color: $transfer-data--upload; 16 | 17 | .icon { 18 | fill: $transfer-data--upload; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/sass/tools/_colors.scss: -------------------------------------------------------------------------------- 1 | $dark-blue: #1d2938; 2 | $grey: #293341; 3 | $light-grey: #3A4553; 4 | $light-grey--lighter: lighten($light-grey, 40%); 5 | $blue: #258de5; 6 | $blue--lighter: saturate(lighten($blue, 10%), 100%); 7 | $green: #39ce83; 8 | $green--darker: darken(#39ce83, 4%); 9 | $light-blue: #e9eef2; 10 | $red: #e95779; 11 | $white: #fff; 12 | 13 | $background: $dark-blue; 14 | $foreground: #53718a; 15 | 16 | $sidebar--background: $grey; 17 | $main-content--background: $light-blue; 18 | -------------------------------------------------------------------------------- /client/src/sass/tools/_reset.scss: -------------------------------------------------------------------------------- 1 | th { 2 | font-weight: inherit; 3 | text-align: left; 4 | } 5 | -------------------------------------------------------------------------------- /client/src/sass/tools/_variables.scss: -------------------------------------------------------------------------------- 1 | @import 'colors'; 2 | 3 | $font-family: 'Roboto', sans-serif; 4 | 5 | $spacing-unit: 25px; 6 | 7 | $torrent-details--width: 85%; 8 | 9 | $textbox--background: #242b36; 10 | $textbox--foreground: #5e728c; 11 | $textbox--border: #1a2028; 12 | $textbox--fulfilled--background: $textbox--background; 13 | $form--element--border-radius: 4px; 14 | $textbox--padding--vertical: 10px; 15 | $textbox--padding--horizontal: 15px; 16 | $textbox--placeholder: #424d5e; 17 | $textbox--selection--foreground: #1a2028; 18 | $textbox--selection--background: $blue--lighter; 19 | $textbox--active--background: $textbox--background; 20 | $textbox--active--border: $blue; 21 | $textbox--active--foreground: $blue; 22 | $textbox--active--placeholder: $textbox--placeholder; 23 | -------------------------------------------------------------------------------- /client/src/sass/views/_feeds.scss: -------------------------------------------------------------------------------- 1 | .feed-list { 2 | 3 | &__feed-label { 4 | display: block; 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | } 8 | } -------------------------------------------------------------------------------- /config.docker.js: -------------------------------------------------------------------------------- 1 | const CONFIG = { 2 | baseURI: process.env.FLOOD_BASE_URI || '/', 3 | dbCleanInterval: 1000 * 60 * 60, 4 | dbPath: '/data/server/db/', 5 | floodServerPort: 3000, 6 | maxHistoryStates: 30, 7 | pollInterval: 1000 * 5, 8 | secret: process.env.FLOOD_SECRET || 'flood', 9 | scgi: { 10 | host: process.env.RTORRENT_SCGI_HOST || 'localhost', 11 | port: process.env.RTORRENT_SCGI_PORT || 5000, 12 | socket: process.env.RTORRENT_SOCK === 'true' || process.env.RTORRENT_SOCK === true, 13 | socketPath: process.env.RTORRENT_SOCK_PATH || '/data/rtorrent.sock', 14 | }, 15 | ssl: process.env.FLOOD_ENABLE_SSL === 'true' || process.env.FLOOD_ENABLE_SSL === true, 16 | sslKey: '/data/flood_ssl.key', 17 | sslCert: '/data/flood_ssl.cert', 18 | }; 19 | 20 | module.exports = CONFIG; 21 | -------------------------------------------------------------------------------- /custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | const content: any; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /flood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/flood.png -------------------------------------------------------------------------------- /server/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: 0, 4 | node: 1, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /server/bin/enforce-prerequisites.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const staticAssets = [path.join(__dirname, '../assets/index.html')]; 5 | 6 | const configFiles = [path.join(__dirname, '../../config.js')]; 7 | 8 | // Taken from react-scripts/check-required-files, but without console.logs. 9 | const doFilesExist = files => { 10 | try { 11 | files.forEach(filename => { 12 | fs.accessSync(filename, fs.F_OK); 13 | }); 14 | return true; 15 | } catch (err) { 16 | return false; 17 | } 18 | }; 19 | 20 | const enforcePrerequisites = () => 21 | new Promise((resolve, reject) => { 22 | if (!doFilesExist(configFiles)) { 23 | reject(new Error(`Configuration files missing. Please check the 'Configuring' section of README.md.`)); 24 | return; 25 | } 26 | 27 | if (!doFilesExist(staticAssets)) { 28 | reject( 29 | new Error( 30 | `Static assets (index.html) are missing. Please check the 'Compiling assets and starting the server' section of README.md.`, 31 | ), 32 | ); 33 | return; 34 | } 35 | 36 | return resolve(); 37 | }); 38 | 39 | module.exports = enforcePrerequisites; 40 | -------------------------------------------------------------------------------- /server/bin/migrations/fix-is-admin-flag.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const Users = require('../../models/Users'); 3 | 4 | const log = data => { 5 | if (process.env.DEBUG) { 6 | console.log(data); 7 | } 8 | }; 9 | 10 | const migrate = () => { 11 | log(chalk.green('Migrating data: resolving unset isAdmin flag')); 12 | 13 | return new Promise((migrateResolve, migrateReject) => { 14 | Users.listUsers((users, migrateError) => { 15 | if (migrateError) return migrateReject(migrateError); 16 | 17 | migrateResolve( 18 | Promise.all( 19 | users.map(user => { 20 | let userPatch = null; 21 | 22 | if (user.isAdmin == null) { 23 | userPatch = {isAdmin: true}; 24 | } 25 | 26 | if (userPatch != null) { 27 | log(chalk.yellow(`Migrating user ${user.username}`)); 28 | 29 | return new Promise((updateUserResolve, updateUserReject) => { 30 | Users.updateUser(user.username, userPatch, (response, updateUserError) => { 31 | if (updateUserError) { 32 | updateUserReject(updateUserError); 33 | return; 34 | } 35 | 36 | updateUserResolve(response); 37 | }); 38 | }); 39 | } 40 | 41 | return Promise.resolve(); 42 | }), 43 | ), 44 | ); 45 | }); 46 | }); 47 | }; 48 | 49 | module.exports = migrate; 50 | -------------------------------------------------------------------------------- /server/bin/migrations/run.js: -------------------------------------------------------------------------------- 1 | const perUserRtorrentInstances = require('./per-user-rtorrent-instances'); 2 | const fixIsAdminFlag = require('./fix-is-admin-flag'); 3 | 4 | const migrations = [perUserRtorrentInstances, fixIsAdminFlag]; 5 | 6 | const migrate = () => Promise.all(migrations.map(migration => migration())); 7 | 8 | module.exports = migrate; 9 | -------------------------------------------------------------------------------- /server/bin/start.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | const enforcePrerequisites = require('./enforce-prerequisites'); 4 | const migrateData = require('./migrations/run'); 5 | const {startWebServer} = require('./web-server'); 6 | 7 | enforcePrerequisites() 8 | .then(migrateData) 9 | .then(startWebServer) 10 | .catch(error => { 11 | console.log(chalk.red('Failed to start Flood:')); 12 | console.trace(error); 13 | process.exit(1); 14 | }); 15 | -------------------------------------------------------------------------------- /server/config/passport.js: -------------------------------------------------------------------------------- 1 | const JwtStrategy = require('passport-jwt').Strategy; 2 | 3 | const config = require('../../config'); 4 | const Users = require('../models/Users'); 5 | 6 | // Setup work and export for the JWT passport strategy. 7 | module.exports = passport => { 8 | const options = { 9 | jwtFromRequest: req => { 10 | let token = null; 11 | 12 | if (req && req.cookies) { 13 | token = req.cookies.jwt; 14 | } 15 | 16 | return token; 17 | }, 18 | secretOrKey: config.secret, 19 | }; 20 | 21 | passport.use( 22 | new JwtStrategy(options, (jwtPayload, callback) => { 23 | Users.lookupUser({username: jwtPayload.username}, (err, user) => { 24 | if (err) { 25 | return callback(err, false); 26 | } 27 | 28 | if (user) { 29 | return callback(null, user); 30 | } 31 | 32 | return callback(null, false); 33 | }); 34 | }), 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /server/constants/clientGatewayServiceEvents.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../../shared/util/objectUtil'); 2 | 3 | const clientGatewayServiceEvents = [ 4 | 'CLIENT_CONNECTION_STATE_CHANGE', 5 | 'PROCESS_TORRENT', 6 | 'PROCESS_TORRENT_LIST_END', 7 | 'PROCESS_TORRENT_LIST_START', 8 | 'PROCESS_TRANSFER_RATE_START', 9 | 'TORRENTS_REMOVED', 10 | ]; 11 | 12 | module.exports = objectUtil.createSymbolMapFromArray(clientGatewayServiceEvents); 13 | -------------------------------------------------------------------------------- /server/constants/diskUsageServiceEvents.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../../shared/util/objectUtil'); 2 | 3 | const diskUsageServiceEvents = ['DISK_USAGE_CHANGE']; 4 | 5 | module.exports = objectUtil.createSymbolMapFromArray(diskUsageServiceEvents); 6 | -------------------------------------------------------------------------------- /server/constants/fileListPropMap.js: -------------------------------------------------------------------------------- 1 | const fileListPropMap = new Map(); 2 | const defaultTransformer = value => value; 3 | 4 | fileListPropMap.set('path', { 5 | methodCall: 'f.path=', 6 | transformValue: defaultTransformer, 7 | }); 8 | 9 | fileListPropMap.set('pathComponents', { 10 | methodCall: 'f.path_components=', 11 | transformValue: defaultTransformer, 12 | }); 13 | 14 | fileListPropMap.set('priority', { 15 | methodCall: 'f.priority=', 16 | transformValue: defaultTransformer, 17 | }); 18 | 19 | fileListPropMap.set('sizeBytes', { 20 | methodCall: 'f.size_bytes=', 21 | transformValue: Number, 22 | }); 23 | 24 | fileListPropMap.set('sizeChunks', { 25 | methodCall: 'f.size_chunks=', 26 | transformValue: Number, 27 | }); 28 | 29 | fileListPropMap.set('completedChunks', { 30 | methodCall: 'f.completed_chunks=', 31 | transformValue: Number, 32 | }); 33 | 34 | module.exports = fileListPropMap; 35 | -------------------------------------------------------------------------------- /server/constants/historyServiceEvents.js: -------------------------------------------------------------------------------- 1 | const historySnapshotTypes = require('../../shared/constants/historySnapshotTypes'); 2 | const objectUtil = require('../../shared/util/objectUtil'); 3 | 4 | const torrentServiceEvents = [ 5 | 'FETCH_TRANSFER_SUMMARY_ERROR', 6 | 'FETCH_TRANSFER_SUMMARY_SUCCESS', 7 | 'TRANSFER_SUMMARY_DIFF_CHANGE', 8 | ].concat( 9 | // Create an array of event types based on the available snapshots. 10 | Object.keys(historySnapshotTypes).reduce((accumulator, snapshotType) => { 11 | accumulator.push(`${snapshotType}_SNAPSHOT_FULL_UPDATE`, `${snapshotType}_SNAPSHOT_DIFF_CHANGE`); 12 | 13 | return accumulator; 14 | }, []), 15 | ); 16 | 17 | module.exports = objectUtil.createSymbolMapFromArray(torrentServiceEvents); 18 | -------------------------------------------------------------------------------- /server/constants/notificationServiceEvents.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../../shared/util/objectUtil'); 2 | 3 | const notificationServiceEvents = ['NOTIFICATION_COUNT_CHANGE']; 4 | 5 | module.exports = objectUtil.createSymbolMapFromArray(notificationServiceEvents); 6 | -------------------------------------------------------------------------------- /server/constants/taxonomyServiceEvents.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../../shared/util/objectUtil'); 2 | 3 | const taxonomyServiceEvents = ['TAXONOMY_DIFF_CHANGE']; 4 | 5 | module.exports = objectUtil.createSymbolMapFromArray(taxonomyServiceEvents); 6 | -------------------------------------------------------------------------------- /server/constants/torrentServiceEvents.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../../shared/util/objectUtil'); 2 | 3 | const torrentServiceEvents = ['FETCH_TORRENT_LIST_ERROR', 'FETCH_TORRENT_LIST_SUCCESS', 'TORRENT_LIST_DIFF_CHANGE']; 4 | 5 | module.exports = objectUtil.createSymbolMapFromArray(torrentServiceEvents); 6 | -------------------------------------------------------------------------------- /server/constants/transferSummaryPropMap.js: -------------------------------------------------------------------------------- 1 | const transferSummaryPropMap = new Map(); 2 | 3 | transferSummaryPropMap.set('upRate', { 4 | methodCall: 'throttle.global_up.rate', 5 | transformValue: Number, 6 | }); 7 | 8 | transferSummaryPropMap.set('upTotal', { 9 | methodCall: 'throttle.global_up.total', 10 | transformValue: Number, 11 | }); 12 | 13 | transferSummaryPropMap.set('upThrottle', { 14 | methodCall: 'throttle.global_up.max_rate', 15 | transformValue: Number, 16 | }); 17 | 18 | transferSummaryPropMap.set('downRate', { 19 | methodCall: 'throttle.global_down.rate', 20 | transformValue: Number, 21 | }); 22 | 23 | transferSummaryPropMap.set('downTotal', { 24 | methodCall: 'throttle.global_down.total', 25 | transformValue: Number, 26 | }); 27 | 28 | transferSummaryPropMap.set('downThrottle', { 29 | methodCall: 'throttle.global_down.max_rate', 30 | transformValue: Number, 31 | }); 32 | 33 | module.exports = transferSummaryPropMap; 34 | -------------------------------------------------------------------------------- /server/middleware/appendUserServices.js: -------------------------------------------------------------------------------- 1 | const services = require('../services'); 2 | 3 | module.exports = (req, res, next) => { 4 | req.services = services.getAllServices(req.user); 5 | next(); 6 | }; 7 | -------------------------------------------------------------------------------- /server/middleware/booleanCoerce.js: -------------------------------------------------------------------------------- 1 | module.exports = key => (req, res, next) => { 2 | const value = req.body && req.body[key]; 3 | 4 | if (value && typeof value === 'string') { 5 | req.body[key] = value === 'true'; 6 | } 7 | 8 | next(); 9 | }; 10 | -------------------------------------------------------------------------------- /server/middleware/eventStream.js: -------------------------------------------------------------------------------- 1 | module.exports = (req, res, next) => { 2 | req.socket.setKeepAlive(true); 3 | req.socket.setTimeout(0); 4 | 5 | res.set({ 6 | 'Content-Type': 'text/event-stream', 7 | 'Cache-Control': 'no-cache', 8 | Connection: 'keep-alive', 9 | 'Access-Control-Allow-Origin': '*', 10 | 'X-Accel-Buffering': 'no', 11 | }); 12 | res.status(200); 13 | res.write('retry: 500\n\n'); 14 | 15 | // Keep the connection open by sending a message every so often. 16 | const keepAliveTimeout = setInterval(() => { 17 | res.write(':keep-alive\n\n'); 18 | res.flush(); 19 | }, 500); 20 | 21 | // cleanup on close 22 | res.on('close', () => { 23 | clearInterval(keepAliveTimeout); 24 | }); 25 | 26 | next(); 27 | }; 28 | -------------------------------------------------------------------------------- /server/middleware/requireAdmin.js: -------------------------------------------------------------------------------- 1 | module.exports = (req, res, next) => { 2 | if (req.user == null || !req.user.isAdmin) { 3 | return res 4 | .status(403) 5 | .json({message: 'User is not admin.'}) 6 | .send(); 7 | } 8 | next(); 9 | }; 10 | -------------------------------------------------------------------------------- /server/models/Filesystem.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const ospath = require('ospath'); 3 | const path = require('path'); 4 | 5 | const getDirectoryList = (options, callback) => { 6 | const sourcePath = (options.path || '/').replace(/^~/, ospath.home()); 7 | 8 | try { 9 | const directories = []; 10 | const files = []; 11 | 12 | fs.readdirSync(sourcePath).forEach(item => { 13 | const joinedPath = path.join(sourcePath, item); 14 | if (fs.existsSync(joinedPath)) { 15 | if (fs.statSync(joinedPath).isDirectory()) { 16 | directories.push(item); 17 | } else { 18 | files.push(item); 19 | } 20 | } 21 | }); 22 | 23 | const hasParent = /^.{0,}:?(\/|\\){1,1}\S{1,}/.test(sourcePath); 24 | 25 | callback({ 26 | directories, 27 | files, 28 | hasParent, 29 | path: sourcePath, 30 | separator: path.sep, 31 | }); 32 | } catch (error) { 33 | callback(null, error); 34 | } 35 | }; 36 | 37 | module.exports = {getDirectoryList}; 38 | -------------------------------------------------------------------------------- /server/models/ServerEvent.js: -------------------------------------------------------------------------------- 1 | class ServerEvent { 2 | constructor(res) { 3 | this.data = ''; 4 | this.res = res; 5 | 6 | // Add 2kb padding for IE. 7 | const padding = new Array(2049); 8 | res.write(`:${padding.join(' ')}\n`); 9 | } 10 | 11 | addData(data) { 12 | const lines = JSON.stringify(data).split(/\n/); 13 | 14 | this.data = lines.reduce((accumulator, datum) => `${this.data}data:${datum}\n`, this.data); 15 | } 16 | 17 | emit() { 18 | this.res.write(`${this.data}\n`); 19 | this.res.flush(); 20 | this.data = ''; 21 | } 22 | 23 | setID(id) { 24 | this.res.write(`id:${id}\n`); 25 | } 26 | 27 | setType(eventType) { 28 | this.res.write(`event:${eventType}\n`); 29 | } 30 | } 31 | 32 | module.exports = ServerEvent; 33 | -------------------------------------------------------------------------------- /server/models/TemporaryStorage.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const mkdirp = require('mkdirp'); 3 | const path = require('path'); 4 | 5 | class TemporaryStorage { 6 | constructor() { 7 | this.tempPath = path.join(path.dirname(require.main.filename), '..', 'temp'); 8 | 9 | mkdirp(this.tempPath); 10 | } 11 | 12 | deleteFile(filename) { 13 | fs.unlink(this.getTempPath(filename)); 14 | } 15 | 16 | getTempPath(filename) { 17 | return path.join(this.tempPath, filename); 18 | } 19 | } 20 | 21 | module.exports = new TemporaryStorage(); 22 | -------------------------------------------------------------------------------- /server/services/BaseService.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | 3 | class BaseService extends EventEmitter { 4 | constructor(user, services, ...eventEmitterConfig) { 5 | super(...eventEmitterConfig); 6 | if (!user || !user._id) throw new Error('Missing user ID'); 7 | this.user = user; 8 | this.services = services; 9 | } 10 | 11 | destroy() { 12 | delete this.services; 13 | delete this.user; 14 | } 15 | 16 | updateUser(user, services) { 17 | this.user = user; 18 | this.services = services; 19 | } 20 | } 21 | 22 | module.exports = BaseService; 23 | -------------------------------------------------------------------------------- /server/util/ajaxUtil.js: -------------------------------------------------------------------------------- 1 | const ajaxUtil = { 2 | getResponseFn: res => (data, error) => { 3 | if (error) { 4 | if (process.env.NODE_ENV === 'development') { 5 | console.trace(error); 6 | } 7 | 8 | if (typeof error === 'string') { 9 | error = { 10 | message: error, 11 | }; 12 | } 13 | 14 | res.status(500).json(error); 15 | } else { 16 | res.json(data); 17 | } 18 | }, 19 | }; 20 | 21 | module.exports = ajaxUtil; 22 | -------------------------------------------------------------------------------- /server/util/mediainfo.js: -------------------------------------------------------------------------------- 1 | const childProcess = require('child_process'); 2 | 3 | const services = require('../services'); 4 | 5 | module.exports = { 6 | getMediainfo(user, options, callback) { 7 | const torrentService = services.getTorrentService(user); 8 | const {hash} = options; 9 | 10 | if (hash == null) { 11 | callback(null, {error: 'Hash must be defined'}); 12 | return; 13 | } 14 | const selectedTorrent = torrentService.getTorrent(hash); 15 | try { 16 | childProcess.execFile( 17 | 'mediainfo', 18 | [selectedTorrent.basePath], 19 | {maxBuffer: 1024 * 2000}, 20 | (error, stdout, stderr) => { 21 | if (error) { 22 | callback(null, {error}); 23 | return; 24 | } 25 | 26 | if (stderr) { 27 | callback(null, {error: stderr}); 28 | return; 29 | } 30 | 31 | callback({output: stdout}); 32 | }, 33 | ); 34 | } catch (childProcessError) { 35 | callback(null, {error: childProcessError}); 36 | } 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /server/util/methodCallUtil.js: -------------------------------------------------------------------------------- 1 | const methodCallUtil = { 2 | getMethodCallConfigFromPropMap(map = new Map(), requestedKeys) { 3 | let desiredKeys = Array.from(map.keys()); 4 | 5 | if (requestedKeys != null) { 6 | desiredKeys = desiredKeys.filter(key => requestedKeys.includes(key)); 7 | } 8 | 9 | return desiredKeys.reduce( 10 | (accumulator, key) => { 11 | const {methodCall, transformValue} = map.get(key); 12 | 13 | accumulator.methodCalls.push(methodCall); 14 | accumulator.propLabels.push(key); 15 | accumulator.valueTransformations.push(transformValue); 16 | 17 | return accumulator; 18 | }, 19 | { 20 | methodCalls: [], 21 | propLabels: [], 22 | valueTransformations: [], 23 | }, 24 | ); 25 | }, 26 | }; 27 | 28 | module.exports = methodCallUtil; 29 | -------------------------------------------------------------------------------- /server/util/minifyUtil.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flood-UI/flood/769b5412b95f4f62d2b1085f935ed3cbc4bfe344/server/util/minifyUtil.js -------------------------------------------------------------------------------- /server/util/numberUtils.js: -------------------------------------------------------------------------------- 1 | const truncateTo = (num, precision = 0) => { 2 | const factor = 10 ** precision; 3 | return Math.floor(num * factor) / factor; 4 | }; 5 | 6 | module.exports = truncateTo; 7 | -------------------------------------------------------------------------------- /server/util/rTorrentPropMap.js: -------------------------------------------------------------------------------- 1 | const RTORRENT_PROPS_MAP = { 2 | transferData: { 3 | uploadRate: 'throttle.global_up.rate', 4 | uploadTotal: 'throttle.global_up.total', 5 | uploadThrottle: 'throttle.global_up.max_rate', 6 | downloadRate: 'throttle.global_down.rate', 7 | downloadTotal: 'throttle.global_down.total', 8 | downloadThrottle: 'throttle.global_down.max_rate', 9 | }, 10 | }; 11 | 12 | module.exports = RTORRENT_PROPS_MAP; 13 | -------------------------------------------------------------------------------- /server/views/error.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /server/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | body 6 | block content 7 | -------------------------------------------------------------------------------- /shared/constants/diffActionTypes.js: -------------------------------------------------------------------------------- 1 | const diffActionTypes = ['ITEM_ADDED', 'ITEM_CHANGED', 'ITEM_REMOVED']; 2 | 3 | module.exports = diffActionTypes.reduce((memo, key) => { 4 | memo[key] = key; 5 | 6 | return memo; 7 | }, {}); 8 | -------------------------------------------------------------------------------- /shared/constants/historySnapshotTypes.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../util/objectUtil'); 2 | 3 | const historySnapshotTypes = { 4 | FIVE_MINUTE: 'fiveMin', 5 | THIRTY_MINUTE: 'thirtyMin', 6 | HOUR: 'hour', 7 | WEEK: 'week', 8 | MONTH: 'month', 9 | YEAR: 'year', 10 | }; 11 | 12 | module.exports = objectUtil.reflect(historySnapshotTypes); 13 | -------------------------------------------------------------------------------- /shared/constants/serverEventTypes.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../util/objectUtil'); 2 | 3 | const serverEventTypes = [ 4 | 'CLIENT_CONNECTIVITY_STATUS_CHANGE', 5 | 'DISK_USAGE_CHANGE', 6 | 'NOTIFICATION_COUNT_CHANGE', 7 | 'TAXONOMY_FULL_UPDATE', 8 | 'TAXONOMY_DIFF_CHANGE', 9 | 'TORRENT_LIST_ACTION_TORRENT_ADDED', 10 | 'TORRENT_LIST_ACTION_TORRENT_DELETED', 11 | 'TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED', 12 | 'TORRENT_LIST_DIFF_CHANGE', 13 | 'TORRENT_LIST_FULL_UPDATE', 14 | 'TRANSFER_HISTORY_FULL_UPDATE', 15 | 'TRANSFER_SUMMARY_DIFF_CHANGE', 16 | 'TRANSFER_SUMMARY_FULL_UPDATE', 17 | ]; 18 | 19 | module.exports = objectUtil.createStringMapFromArray(serverEventTypes); 20 | -------------------------------------------------------------------------------- /shared/constants/torrentFilePropsMap.js: -------------------------------------------------------------------------------- 1 | const torrentFilePropsMap = { 2 | props: ['path', 'pathComponents', 'priority', 'sizeBytes', 'sizeChunks', 'completedChunks'], 3 | methods: ['f.path=', 'f.path_components=', 'f.priority=', 'f.size_bytes=', 'f.size_chunks=', 'f.completed_chunks='], 4 | }; 5 | 6 | module.exports = torrentFilePropsMap; 7 | -------------------------------------------------------------------------------- /shared/constants/torrentPeerPropsMap.js: -------------------------------------------------------------------------------- 1 | const torrentPeerPropsMap = { 2 | props: [ 3 | 'address', 4 | 'completedPercent', 5 | 'clientVersion', 6 | 'downloadRate', 7 | 'downloadTotal', 8 | 'uploadRate', 9 | 'uploadTotal', 10 | 'id', 11 | 'peerRate', 12 | 'peerTotal', 13 | 'isEncrypted', 14 | 'isIncoming', 15 | ], 16 | methods: [ 17 | 'p.address=', 18 | 'p.completed_percent=', 19 | 'p.client_version=', 20 | 'p.down_rate=', 21 | 'p.down_total=', 22 | 'p.up_rate=', 23 | 'p.up_total=', 24 | 'p.id=', 25 | 'p.peer_rate=', 26 | 'p.peer_total=', 27 | 'p.is_encrypted=', 28 | 'p.is_incoming=', 29 | ], 30 | }; 31 | 32 | module.exports = torrentPeerPropsMap; 33 | -------------------------------------------------------------------------------- /shared/constants/torrentStatusMap.js: -------------------------------------------------------------------------------- 1 | const objectUtil = require('../util/objectUtil'); 2 | 3 | const torrentStatusMap = objectUtil.reflect({ 4 | ch: 'checking', 5 | sd: 'seeding', 6 | p: 'paused', 7 | c: 'complete', 8 | d: 'downloading', 9 | ad: 'activelyDownloading', 10 | au: 'activelyUploading', 11 | s: 'stopped', 12 | e: 'error', 13 | i: 'inactive', 14 | a: 'active', 15 | }); 16 | 17 | torrentStatusMap.statusShorthand = ['ch', 'sd', 'p', 'c', 'd', 'ad', 'au', 's', 'e', 'i', 'a']; 18 | 19 | module.exports = torrentStatusMap; 20 | -------------------------------------------------------------------------------- /shared/constants/torrentTrackerPropsMap.js: -------------------------------------------------------------------------------- 1 | const torrentTrackerPropsMap = { 2 | props: ['group', 'url', 'id', 'minInterval', 'normalInterval', 'type'], 3 | methods: ['t.group=', 't.url=', 't.id=', 't.min_interval=', 't.normal_interval=', 't.type='], 4 | }; 5 | 6 | module.exports = torrentTrackerPropsMap; 7 | -------------------------------------------------------------------------------- /shared/util/regEx.js: -------------------------------------------------------------------------------- 1 | const regEx = { 2 | url: /^(?:https?|ftp):\/\/.{1,}\.{1}.{1,}/, 3 | domainName: /(?:https?|udp):\/\/(?:www\.)?([-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,18}\b)*(\/[/\d\w.-]*)*(?:[?])*(.+)*/i, 4 | cdata: //, 5 | }; 6 | 7 | module.exports = regEx; 8 | -------------------------------------------------------------------------------- /shared/util/stringUtil.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | capitalize: string => string.charAt(0).toUpperCase() + string.slice(1), 3 | 4 | pluralize: (string, count) => { 5 | if (count !== 1) { 6 | if (string.charAt(string.length - 1) === 'y') { 7 | return `${string.substring(0, string.length - 1)}ies`; 8 | } 9 | return `${string}s`; 10 | } 11 | 12 | return string; 13 | }, 14 | 15 | withoutTrailingSlash: input => input.replace(/\/{1,}$/, ''), 16 | }; 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "preserve", 4 | "sourceMap": true, 5 | "target": "esnext", 6 | "moduleResolution": "node", 7 | "allowJs": true, 8 | "noEmit": true, 9 | "strict": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "baseUrl": "./", 13 | "paths": { 14 | "@shared/*": ["shared/*"] 15 | } 16 | }, 17 | "include": ["./client/**/*.ts", "./client/**/*.tsx", "./server/**/*.ts", "./server/**/*.tsx", "./custom.d.ts"], 18 | "exclude": ["node_modules", "**/*.spec.ts"] 19 | } 20 | --------------------------------------------------------------------------------