Compare commits

..

2 Commits

1389 changed files with 19724 additions and 60297 deletions

View File

@@ -1,64 +1,7 @@
{ {
"presets": [ "presets": ["es2015", "react"],
"react",
[
"env",
{
"loose": true,
"modules": false,
"targets": {
"browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"]
}
}
]
],
"plugins": [ "plugins": [
"syntax-dynamic-import",
["transform-object-rest-spread", { "useBuiltIns": true }],
"transform-decorators-legacy", "transform-decorators-legacy",
"transform-class-properties", "transform-object-rest-spread"
[ ]
"react-intl",
{
"messagesDir": "./build/messages"
}
]
],
"env": {
"development": {
"plugins": [
"transform-react-jsx-source",
"transform-react-jsx-self"
]
},
"production": {
"plugins": [
"lodash",
[
"transform-react-remove-prop-types",
{
"mode": "remove",
"removeImport": true,
"additionalLibraries": [
"react-immutable-proptypes"
]
}
],
"transform-react-inline-elements",
[
"transform-runtime",
{
"helpers": true,
"polyfill": false,
"regenerator": false
}
]
]
},
"test": {
"plugins": [
"transform-es2015-modules-commonjs"
]
}
}
} }

View File

@@ -1,3 +1,2 @@
https://github.com/heroku/heroku-buildpack-apt
https://github.com/Scalingo/nodejs-buildpack https://github.com/Scalingo/nodejs-buildpack
https://github.com/Scalingo/ruby-buildpack https://github.com/Scalingo/ruby-buildpack

View File

@@ -1,21 +1,14 @@
engines: engines:
brakeman: duplication:
enabled: true enabled: false
bundler-audit: rubocop:
enabled: true enabled: true
duplication: eslint:
enabled: false enabled: true
eslint:
enabled: true
rubocop:
enabled: true
scss-lint:
enabled: true
ratings: ratings:
paths: paths:
- "**.rb" - "**.rb"
- "**.js" - "**.js"
- "**.scss"
exclude_paths: exclude_paths:
- spec/ - spec/
- vendor/asset - vendor/asset

View File

@@ -2,7 +2,6 @@
.env.* .env.*
public/system public/system
public/assets public/assets
public/packs
node_modules node_modules
storybook storybook
neo4j neo4j
@@ -10,5 +9,3 @@ vendor/bundle
.DS_Store .DS_Store
*.swp *.swp
*~ *~
postgres
redis

View File

@@ -1,111 +0,0 @@
# Service dependencies
# You may set REDIS_URL instead for more advanced options
REDIS_HOST=$DATA_REDIS_HOST
REDIS_PORT=6379
# REDIS_DB=0
# You may set DATABASE_URL instead for more advanced options
DB_HOST=$DATA_DB_HOST
DB_USER=$DATA_DB_USER
DB_NAME=gonano
DB_PASS=$DATA_DB_PASS
DB_PORT=5432
DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
# Federation
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
LOCAL_DOMAIN=${APP_NAME}.nanoapp.io
LOCAL_HTTPS=false
# Use this only if you need to run mastodon on a different domain than the one used for federation.
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
# WEB_DOMAIN=mastodon.example.com
# Use this if you want to have several aliases handler@example1.com
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
# be added. Comma separated values
# ALTERNATE_DOMAINS=example1.com,example2.com
# Application secrets
# Generate each with the `rake secret` task (`nanobox run bundle exec rake secret`)
PAPERCLIP_SECRET=$PAPERCLIP_SECRET
SECRET_KEY_BASE=$SECRET_KEY_BASE
OTP_SECRET=$OTP_SECRET
# Registrations
# Single user mode will disable registrations and redirect frontpage to the first profile
# SINGLE_USER_MODE=true
# Prevent registrations with following e-mail domains
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
# Only allow registrations with the following e-mail domains
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc
# Optionally change default language
# DEFAULT_LOCALE=de
# E-mail configuration
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
SMTP_SERVER=$SMTP_SERVER
SMTP_PORT=587
SMTP_LOGIN=$SMTP_LOGIN
SMTP_PASSWORD=$SMTP_PASSWORD
SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
#SMTP_AUTH_METHOD=plain
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
#SMTP_OPENSSL_VERIFY_MODE=peer
#SMTP_ENABLE_STARTTLS_AUTO=true
# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files.
# PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system
# PAPERCLIP_ROOT_URL=/system
# Optional asset host for multi-server setups
# CDN_HOST=https://assets.example.com
# S3 (optional)
# S3_ENABLED=true
# S3_BUCKET=
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# S3_REGION=
# S3_PROTOCOL=http
# S3_HOSTNAME=192.168.1.123:9000
# S3 (Minio Config (optional) Please check Minio instance for details)
# S3_ENABLED=true
# S3_BUCKET=
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# S3_REGION=
# S3_PROTOCOL=https
# S3_HOSTNAME=
# S3_ENDPOINT=
# S3_SIGNATURE_VERSION=
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
# S3_CLOUDFRONT_HOST=
# Streaming API integration
# STREAMING_API_BASE_URL=
# Advanced settings
# If you need to use pgBouncer, you need to disable prepared statements:
# PREPARED_STATEMENTS=false
# Cluster number setting for streaming API server.
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
STREAMING_CLUSTER_NUM=1
# Docker mastodon user
# If you use Docker, you may want to assign UID/GID manually.
# UID=1000
# GID=1000

View File

@@ -1,8 +1,7 @@
# Service dependencies # Service dependencies
# You may set REDIS_URL instead for more advanced options
REDIS_HOST=redis REDIS_HOST=redis
REDIS_PORT=6379 REDIS_PORT=6379
# You may set DATABASE_URL instead for more advanced options # REDIS_DB=0
DB_HOST=db DB_HOST=db
DB_USER=postgres DB_USER=postgres
DB_NAME=postgres DB_NAME=postgres
@@ -10,21 +9,13 @@ DB_PASS=
DB_PORT=5432 DB_PORT=5432
# Federation # Federation
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
LOCAL_DOMAIN=example.com LOCAL_DOMAIN=example.com
LOCAL_HTTPS=true LOCAL_HTTPS=true
# Use this only if you need to run mastodon on a different domain than the one used for federation. # Use this only if you need to run mastodon on a different domain than the one used for federation.
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md # Do not use this unless you know exactly what you are doing.
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
# WEB_DOMAIN=mastodon.example.com # WEB_DOMAIN=mastodon.example.com
# Use this if you want to have several aliases handler@example1.com
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
# be added. Comma separated values
# ALTERNATE_DOMAINS=example1.com,example2.com
# Application secrets # Application secrets
# Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose) # Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
PAPERCLIP_SECRET= PAPERCLIP_SECRET=
@@ -45,8 +36,7 @@ OTP_SECRET=
# E-mail configuration # E-mail configuration
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers # Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
# If you want to use an SMTP server without authentication (e.g local Postfix relay) # If you want to use an SMTP server without authentication (e.g local Postfix relay)
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and # then set SMTP_AUTH_METHOD to 'none' and leave SMTP_LOGIN and SMTP_PASSWORD blank
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
SMTP_SERVER=smtp.mailgun.org SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587 SMTP_PORT=587
SMTP_LOGIN= SMTP_LOGIN=
@@ -55,7 +45,6 @@ SMTP_FROM_ADDRESS=notifications@example.com
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN #SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail #SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
#SMTP_AUTH_METHOD=plain #SMTP_AUTH_METHOD=plain
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
#SMTP_OPENSSL_VERIFY_MODE=peer #SMTP_OPENSSL_VERIFY_MODE=peer
#SMTP_ENABLE_STARTTLS_AUTO=true #SMTP_ENABLE_STARTTLS_AUTO=true
@@ -65,7 +54,7 @@ SMTP_FROM_ADDRESS=notifications@example.com
# PAPERCLIP_ROOT_URL=/system # PAPERCLIP_ROOT_URL=/system
# Optional asset host for multi-server setups # Optional asset host for multi-server setups
# CDN_HOST=https://assets.example.com # CDN_HOST=assets.example.com
# S3 (optional) # S3 (optional)
# S3_ENABLED=true # S3_ENABLED=true
@@ -100,8 +89,3 @@ SMTP_FROM_ADDRESS=notifications@example.com
# Cluster number setting for streaming API server. # Cluster number setting for streaming API server.
# If you comment out following line, cluster number will be `numOfCpuCores - 1`. # If you comment out following line, cluster number will be `numOfCpuCores - 1`.
STREAMING_CLUSTER_NUM=1 STREAMING_CLUSTER_NUM=1
# Docker mastodon user
# If you use Docker, you may want to assign UID/GID manually.
# UID=1000
# GID=1000

79
.eslintrc Normal file
View File

@@ -0,0 +1,79 @@
{
"env": {
"browser": true,
"node": false,
"es6": true
},
"parser": "babel-eslint",
"plugins": [
"react",
"jsx-a11y"
],
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"arrowFunctions": true,
"jsx": true,
"destructuring": true,
"modules": true,
"spread": true
}
},
"rules": {
"no-cond-assign": 2,
"no-console": 1,
"no-irregular-whitespace": 2,
"no-unreachable": 2,
"valid-typeof": 2,
"consistent-return": 2,
"dot-notation": 2,
"eqeqeq": 2,
"no-fallthrough": 2,
"no-unused-expressions": 2,
"strict": 0,
"no-catch-shadow": 2,
"indent": [1, 2],
"brace-style": 1,
"comma-spacing": [1, {"before": false, "after": true}],
"comma-style": [1, "last"],
"no-mixed-spaces-and-tabs": 1,
"no-nested-ternary": 1,
"no-trailing-spaces": 1,
"react/jsx-wrap-multilines": 2,
"react/self-closing-comp": 2,
"react/prop-types": 2,
"react/no-multi-comp": 0,
"jsx-a11y/accessible-emoji": 1,
"jsx-a11y/anchor-has-content": 1,
"jsx-a11y/aria-activedescendant-has-tabindex": 1,
"jsx-a11y/aria-props": 1,
"jsx-a11y/aria-proptypes": 1,
"jsx-a11y/aria-role": 1,
"jsx-a11y/aria-unsupported-elements": 1,
"jsx-a11y/heading-has-content": 1,
"jsx-a11y/href-no-hash": 1,
"jsx-a11y/html-has-lang": 1,
"jsx-a11y/iframe-has-title": 1,
"jsx-a11y/img-has-alt": 1,
"jsx-a11y/img-redundant-alt": 1,
"jsx-a11y/label-has-for": 1,
"jsx-a11y/mouse-events-have-key-events": 1,
"jsx-a11y/no-access-key": 1,
"jsx-a11y/no-distracting-elements": 1,
"jsx-a11y/no-onchange": 1,
"jsx-a11y/no-redundant-roles": 1,
"jsx-a11y/onclick-has-focus": 1,
"jsx-a11y/onclick-has-role": 1,
"jsx-a11y/role-has-required-aria-props": 1,
"jsx-a11y/role-supports-aria-props": 1,
"jsx-a11y/scope": 1,
"jsx-a11y/tabindex-no-positive": 1
}
}

View File

@@ -1,126 +0,0 @@
---
root: true
env:
browser: true
node: true
es6: true
parser: babel-eslint
plugins:
- react
- jsx-a11y
parserOptions:
sourceType: module
ecmaFeatures:
arrowFunctions: true
jsx: true
destructuring: true
modules: true
spread: true
rules:
brace-style: warn
comma-dangle:
- error
- always-multiline
comma-spacing:
- warn
- before: false
after: true
comma-style:
- warn
- last
consistent-return: error
dot-notation: error
eqeqeq: error
indent:
- warn
- 2
jsx-quotes:
- error
- prefer-single
no-catch-shadow: error
no-cond-assign: error
no-console:
- warn
- allow:
- error
no-fallthrough: error
no-irregular-whitespace: error
no-mixed-spaces-and-tabs: warn
no-nested-ternary: warn
no-trailing-spaces: warn
no-undef: error
no-unreachable: error
no-unused-expressions: error
no-unused-vars:
- error
- vars: all
args: after-used
ignoreRestSiblings: true
object-curly-spacing:
- error
- always
padded-blocks:
- error
- classes: always
quotes:
- error
- single
semi: error
strict: off
valid-typeof: error
react/jsx-boolean-value: error
react/jsx-closing-bracket-location:
- error
- line-aligned
react/jsx-curly-spacing: error
react/jsx-equals-spacing: error
react/jsx-first-prop-new-line:
- error
- multiline-multiprop
react/jsx-indent:
- error
- 2
react/jsx-no-bind: error
react/jsx-no-duplicate-props: error
react/jsx-no-undef: error
react/jsx-tag-spacing: error
react/jsx-uses-react: error
react/jsx-uses-vars: error
react/jsx-wrap-multilines: error
react/no-multi-comp: off
react/no-string-refs: error
react/prop-types: error
react/self-closing-comp: error
jsx-a11y/accessible-emoji: warn
jsx-a11y/anchor-has-content: warn
jsx-a11y/aria-activedescendant-has-tabindex: warn
jsx-a11y/aria-props: warn
jsx-a11y/aria-proptypes: warn
jsx-a11y/aria-role: warn
jsx-a11y/aria-unsupported-elements: warn
jsx-a11y/heading-has-content: warn
jsx-a11y/href-no-hash: warn
jsx-a11y/html-has-lang: warn
jsx-a11y/iframe-has-title: warn
jsx-a11y/img-has-alt: warn
jsx-a11y/img-redundant-alt: warn
jsx-a11y/label-has-for: warn
jsx-a11y/mouse-events-have-key-events: warn
jsx-a11y/no-access-key: warn
jsx-a11y/no-distracting-elements: warn
jsx-a11y/no-onchange: warn
jsx-a11y/no-redundant-roles: warn
jsx-a11y/onclick-has-focus: warn
jsx-a11y/onclick-has-role: warn
jsx-a11y/role-has-required-aria-props: warn
jsx-a11y/role-supports-aria-props: warn
jsx-a11y/scope: warn
jsx-a11y/tabindex-no-positive: warn

View File

@@ -1 +0,0 @@
procfile: Procfile.dev

14
.gitattributes vendored
View File

@@ -1,14 +0,0 @@
* text=auto eol=lf
*.eot -text
*.gif -text
*.gz -text
*.ico -text
*.jpg -text
*.mp3 -text
*.ogg -text
*.png -text
*.ttf -text
*.webm -text
*.woff -text
*.woff2 -text
spec/fixtures/requests/** -text !eol

16
.gitignore vendored
View File

@@ -19,12 +19,10 @@
coverage coverage
public/system public/system
public/assets public/assets
public/packs
public/packs-test
.env .env
.env.production .env.production
node_modules/ node_modules/
build/ neo4j/
# Ignore Vagrant files # Ignore Vagrant files
.vagrant/ .vagrant/
@@ -34,7 +32,6 @@ config/deploy/*
# Ignore IDE files # Ignore IDE files
.vscode/ .vscode/
.idea/
# Ignore postgres + redis volume optionally created by docker-compose # Ignore postgres + redis volume optionally created by docker-compose
postgres postgres
@@ -46,14 +43,3 @@ redis
# Ignore vim files # Ignore vim files
*~ *~
*.swp *.swp
# Ignore npm debug log
npm-debug.log
# Ignore yarn log files
yarn-error.log
yarn-debug.log
# Ignore Docker option files
docker-compose.override.yml

View File

@@ -1,108 +0,0 @@
# Whether to ignore frontmatter at the beginning of HAML documents for
# frameworks such as Jekyll/Middleman
skip_frontmatter: false
exclude:
- 'vendor/**/*'
- 'spec/**/*'
- 'lib/templates/**/*'
- 'app/views/kaminari/**/*'
linters:
AltText:
enabled: false
ClassAttributeWithStaticValue:
enabled: true
ClassesBeforeIds:
enabled: true
ConsecutiveComments:
enabled: true
ConsecutiveSilentScripts:
enabled: true
max_consecutive: 2
EmptyObjectReference:
enabled: true
EmptyScript:
enabled: true
FinalNewline:
enabled: true
present: true
HtmlAttributes:
enabled: true
ImplicitDiv:
enabled: true
LeadingCommentSpace:
enabled: true
LineLength:
enabled: false
max: 80
MultilinePipe:
enabled: true
MultilineScript:
enabled: true
ObjectReferenceAttributes:
enabled: true
RuboCop:
enabled: true
# These cops are incredibly noisy when it comes to HAML templates, so we
# ignore them.
ignored_cops:
- Lint/BlockAlignment
- Lint/EndAlignment
- Lint/Void
- Metrics/BlockLength
- Metrics/LineLength
- Style/AlignParameters
- Style/BlockNesting
- Style/ElseAlignment
- Style/EndOfLine
- Style/FileName
- Style/FinalNewline
- Style/FrozenStringLiteralComment
- Style/IfUnlessModifier
- Style/IndentationWidth
- Style/Next
- Style/TrailingBlankLines
- Style/TrailingWhitespace
- Style/WhileUntilModifier
RubyComments:
enabled: true
SpaceBeforeScript:
enabled: true
SpaceInsideHashAttributes:
enabled: true
style: space
Indentation:
enabled: true
character: space # or tab
TagName:
enabled: true
TrailingWhitespace:
enabled: true
UnnecessaryInterpolation:
enabled: true
UnnecessaryStringOutput:
enabled: true

View File

@@ -1,20 +0,0 @@
.DS_Store
.git/
.gitignore
.bundle/
.cache/
config/deploy/*
coverage
docs/
.env
log/*.log
neo4j/
node_modules/
public/assets/
public/system/
spec/
storybook/
tmp/
.vagrant/
vendor/bundle/

View File

@@ -1,8 +0,0 @@
plugins:
postcss-smart-import: {}
precss: {}
autoprefixer:
browsers:
- last 2 versions
- IE >= 11
- iOS >= 9

View File

@@ -1 +0,0 @@
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/.apt/lib/x86_64-linux-gnu:/app/.apt/usr/lib/x86_64-linux-gnu/mesa:/app/.apt/usr/lib/x86_64-linux-gnu/pulseaudio

View File

@@ -1,35 +1,26 @@
AllCops: Rails:
TargetRubyVersion: 2.3 Enabled: true
Exclude:
- 'spec/**/*'
- 'db/**/*'
- 'app/views/**/*'
- 'config/**/*'
- 'bin/*'
- 'Rakefile'
- 'node_modules/**/*'
- 'Vagrantfile'
- 'vendor/**/*'
Bundler/OrderedGems: Style/PerlBackrefs:
AutoCorrect: false
Style/ClassAndModuleChildren:
Enabled: false Enabled: false
Layout/AccessModifierIndentation: Metrics/BlockNesting:
EnforcedStyle: indent Max: 2
Layout/EmptyLineAfterMagicComment: Metrics/LineLength:
AllowURI: true
Enabled: false Enabled: false
Layout/SpaceInsideHashLiteralBraces: Metrics/MethodLength:
EnforcedStyle: space CountComments: false
Max: 10
Metrics/AbcSize: Metrics/AbcSize:
Max: 100 Max: 100
Metrics/BlockLength:
Exclude:
- 'lib/tasks/**/*'
Metrics/BlockNesting: Metrics/BlockNesting:
Max: 3 Max: 3
@@ -40,36 +31,22 @@ Metrics/ClassLength:
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Max: 15 Max: 15
Metrics/LineLength:
AllowURI: true
Enabled: false
Metrics/MethodLength: Metrics/MethodLength:
CountComments: false
Max: 55 Max: 55
Metrics/ModuleLength: Metrics/ModuleLength:
CountComments: false CountComments: false
Max: 200 Max: 200
Metrics/PerceivedComplexity:
Max: 10
Metrics/ParameterLists: Metrics/ParameterLists:
Max: 4 Max: 4
CountKeywordArgs: true CountKeywordArgs: true
Metrics/PerceivedComplexity: Style/AccessModifierIndentation:
Max: 10 EnforcedStyle: indent
Rails:
Enabled: true
Rails/HasAndBelongsToMany:
Enabled: false
Rails/SkipsModelValidations:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/CollectionMethods: Style/CollectionMethods:
Enabled: true Enabled: true
@@ -85,25 +62,29 @@ Style/DoubleNegation:
Style/FrozenStringLiteralComment: Style/FrozenStringLiteralComment:
Enabled: true Enabled: true
Style/GuardClause: Style/SpaceInsideHashLiteralBraces:
EnforcedStyle: space
Style/TrailingCommaInLiteral:
EnforcedStyleForMultiline: 'comma'
Style/RegexpLiteral:
Enabled: false Enabled: false
Style/Lambda: Style/Lambda:
Enabled: false Enabled: false
Style/PercentLiteralDelimiters: Rails/HasAndBelongsToMany:
PreferredDelimiters:
'%i': '()'
'%w': '()'
Style/PerlBackrefs:
AutoCorrect: false
Style/RegexpLiteral:
Enabled: false Enabled: false
Style/SymbolArray: AllCops:
Enabled: false TargetRubyVersion: 2.3
Exclude:
Style/TrailingCommaInLiteral: - 'spec/**/*'
EnforcedStyleForMultiline: 'comma' - 'db/**/*'
- 'app/views/**/*'
- 'config/**/*'
- 'bin/*'
- 'Rakefile'
- 'node_modules/**/*'
- 'Vagrantfile'

View File

@@ -1,264 +0,0 @@
# Linter Documentation:
# https://github.com/brigade/scss-lint/blob/v0.42.2/lib/scss_lint/linter/README.md
scss_files: 'app/javascript/styles/**/*.scss'
exclude:
- app/javascript/styles/reset.scss
linters:
# Reports when you use improper spacing around ! (the "bang") in !default,
# !global, !important, and !optional flags.
BangFormat:
enabled: false
# Whether or not to prefer `border: 0` over `border: none`.
BorderZero:
enabled: false
# Reports when you define a rule set using a selector with chained classes
# (a.k.a. adjoining classes).
ChainedClasses:
enabled: false
# Prefer hexadecimal color codes over color keywords.
# (e.g. `color: green` is a color keyword)
ColorKeyword:
enabled: false
# Prefer color literals (keywords or hexadecimal codes) to be used only in
# variable declarations. They should be referred to via variables everywhere
# else.
ColorVariable:
enabled: true
# Which form of comments to prefer in CSS.
Comment:
enabled: false
# Reports @debug statements (which you probably left behind accidentally).
DebugStatement:
enabled: false
# Rule sets should be ordered as follows:
# - @extend declarations
# - @include declarations without inner @content
# - properties, @include declarations with inner @content
# - nested rule sets.
DeclarationOrder:
enabled: false
# `scss-lint:disable` control comments should be preceded by a comment
# explaining why these linters are being disabled for this file.
# See https://github.com/brigade/scss-lint#disabling-linters-via-source for
# more information.
DisableLinterReason:
enabled: true
# Reports when you define the same property twice in a single rule set.
DuplicateProperty:
enabled: false
# Separate rule, function, and mixin declarations with empty lines.
EmptyLineBetweenBlocks:
enabled: true
# Reports when you have an empty rule set.
EmptyRule:
enabled: true
# Reports when you have an @extend directive.
ExtendDirective:
enabled: false
# Files should always have a final newline. This results in better diffs
# when adding lines to the file, since SCM systems such as git won't
# think that you touched the last line.
FinalNewline:
enabled: false
# HEX colors should use three-character values where possible.
HexLength:
enabled: false
# HEX color values should use lower-case colors to differentiate between
# letters and numbers, e.g. `#E3E3E3` vs. `#e3e3e3`.
HexNotation:
enabled: true
# Avoid using ID selectors.
IdSelector:
enabled: false
# The basenames of @imported SCSS partials should not begin with an
# underscore and should not include the filename extension.
ImportPath:
enabled: false
# Avoid using !important in properties. It is usually indicative of a
# misunderstanding of CSS specificity and can lead to brittle code.
ImportantRule:
enabled: false
# Indentation should always be done in increments of 2 spaces.
Indentation:
enabled: true
width: 2
# Don't write leading zeros for numeric values with a decimal point.
LeadingZero:
enabled: false
# Reports when you define the same selector twice in a single sheet.
MergeableSelector:
enabled: false
# Functions, mixins, variables, and placeholders should be declared
# with all lowercase letters and hyphens instead of underscores.
NameFormat:
enabled: false
# Avoid nesting selectors too deeply.
NestingDepth:
enabled: false
# Always use placeholder selectors in @extend.
PlaceholderInExtend:
enabled: false
# Sort properties in a strict order.
PropertySortOrder:
enabled: false
# Reports when you use an unknown or disabled CSS property
# (ignoring vendor-prefixed properties).
PropertySpelling:
enabled: false
# Configure which units are allowed for property values.
PropertyUnits:
enabled: false
# Pseudo-elements, like ::before, and ::first-letter, should be declared
# with two colons. Pseudo-classes, like :hover and :first-child, should
# be declared with one colon.
PseudoElement:
enabled: true
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
QualifyingElement:
enabled: false
# Don't write selectors with a depth of applicability greater than 3.
SelectorDepth:
enabled: false
# Selectors should always use hyphenated-lowercase, rather than camelCase or
# snake_case.
SelectorFormat:
enabled: false
convention: hyphenated_lowercase
# Prefer the shortest shorthand form possible for properties that support it.
Shorthand:
enabled: true
# Each property should have its own line, except in the special case of
# single line rulesets.
SingleLinePerProperty:
enabled: true
allow_single_line_rule_sets: true
# Split selectors onto separate lines after each comma, and have each
# individual selector occupy a single line.
SingleLinePerSelector:
enabled: true
# Commas in lists should be followed by a space.
SpaceAfterComma:
enabled: false
# Properties should be formatted with a single space separating the colon
# from the property's value.
SpaceAfterPropertyColon:
enabled: true
# Properties should be formatted with no space between the name and the
# colon.
SpaceAfterPropertyName:
enabled: true
# Variables should be formatted with a single space separating the colon
# from the variable's value.
SpaceAfterVariableColon:
enabled: true
# Variables should be formatted with no space between the name and the
# colon.
SpaceAfterVariableName:
enabled: false
# Operators should be formatted with a single space on both sides of an
# infix operator.
SpaceAroundOperator:
enabled: true
# Opening braces should be preceded by a single space.
SpaceBeforeBrace:
enabled: true
# Parentheses should not be padded with spaces.
SpaceBetweenParens:
enabled: false
# Enforces that string literals should be written with a consistent form
# of quotes (single or double).
StringQuotes:
enabled: false
# Property values, @extend, @include, and @import directives, and variable
# declarations should always end with a semicolon.
TrailingSemicolon:
enabled: true
# Reports lines containing trailing whitespace.
TrailingWhitespace:
enabled: true
# Don't write trailing zeros for numeric values with a decimal point.
TrailingZero:
enabled: false
# Don't use the `all` keyword to specify transition properties.
TransitionAll:
enabled: false
# Numeric values should not contain unnecessary fractional portions.
UnnecessaryMantissa:
enabled: false
# Do not use parent selector references (&) when they would otherwise
# be unnecessary.
UnnecessaryParentReference:
enabled: false
# URLs should be valid and not contain protocols or domain names.
UrlFormat:
enabled: true
# URLs should always be enclosed within quotes.
UrlQuotes:
enabled: true
# Properties, like color and font, are easier to read and maintain
# when defined using variables rather than literals.
VariableForProperty:
enabled: false
# Avoid vendor prefixes. Or rather: don't write them yourself.
VendorPrefix:
enabled: false
# Omit length units on zero values, e.g. `0px` vs. `0`.
ZeroUnit:
enabled: true

View File

@@ -1,13 +1,5 @@
language: ruby language: ruby
cache: cache: bundler
bundler: true
yarn: true
directories:
- node_modules
- public/assets
- public/packs-test
dist: trusty
sudo: required
notifications: notifications:
email: false email: false
@@ -17,22 +9,9 @@ env:
- LOCAL_DOMAIN=cb6e6126.ngrok.io - LOCAL_DOMAIN=cb6e6126.ngrok.io
- LOCAL_HTTPS=true - LOCAL_HTTPS=true
- RAILS_ENV=test - RAILS_ENV=test
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true - CXX=g++-4.8
- PARALLEL_TEST_PROCESSORS=2
- "PATH=$HOME:$PATH"
addons: addons:
postgresql: 9.4 postgresql: 9.4
apt:
sources:
- ubuntu-toolchain-r-test
- trusty-media
packages:
- ffmpeg
- g++-6
- libprotobuf-dev
- protobuf-compiler
- libicu-dev
rvm: rvm:
- 2.3.4 - 2.3.4
@@ -41,18 +20,22 @@ rvm:
services: services:
- redis-server - redis-server
bundler_args: --without development production --retry=3 --jobs=3
before_install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get -qq update
- sudo apt-get -qq install g++-4.8
install: install:
- nvm install - nvm install
- npm install -g yarn - npm install -g yarn
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16 - bundle install
- yarn install - yarn install
before_script: before_script:
- bundle exec rake parallel:create parallel:load_schema parallel:prepare - bundle exec rails db:create db:migrate
- bundle exec rails assets:precompile
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
script: script:
- travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec - bundle exec rspec
- npm test - npm test
- bundle exec i18n-tasks unused - i18n-tasks unused

View File

@@ -1,6 +0,0 @@
protobuf-compiler
libprotobuf-dev
ffmpeg
libxdamage1
libxfixes3
libicu-dev

View File

@@ -1,4 +1,3 @@
# frozen_string_literal: true
require 'capistrano/setup' require 'capistrano/setup'
require 'capistrano/deploy' require 'capistrano/deploy'
require 'capistrano/scm/git' require 'capistrano/scm/git'
@@ -9,6 +8,7 @@ require 'capistrano/rbenv'
require 'capistrano/bundler' require 'capistrano/bundler'
require 'capistrano/yarn' require 'capistrano/yarn'
require 'capistrano/rails/assets' require 'capistrano/rails/assets'
require 'capistrano/faster_assets'
require 'capistrano/rails/migrations' require 'capistrano/rails/migrations'
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

View File

@@ -3,53 +3,39 @@ FROM ruby:2.4.1-alpine
LABEL maintainer="https://github.com/tootsuite/mastodon" \ LABEL maintainer="https://github.com/tootsuite/mastodon" \
description="A GNU Social-compatible microblogging server" description="A GNU Social-compatible microblogging server"
ENV UID=991 GID=991 \ ENV RAILS_ENV=production \
RAILS_SERVE_STATIC_FILES=true \ NODE_ENV=production
RAILS_ENV=production NODE_ENV=production
EXPOSE 3000 4000 EXPOSE 3000 4000
WORKDIR /mastodon WORKDIR /mastodon
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
&& apk -U upgrade \ && BUILD_DEPS=" \
&& apk add -t build-dependencies \ postgresql-dev \
build-base \
libxml2-dev \ libxml2-dev \
libxslt-dev \ libxslt-dev \
postgresql-dev \ build-base" \
protobuf-dev \ && apk -U upgrade && apk add \
python \ $BUILD_DEPS \
&& apk add \ nodejs@edge \
ca-certificates \ nodejs-npm@edge \
ffmpeg \
file \
git \
icu-dev \
imagemagick@edge \
libpq \ libpq \
libxml2 \ libxml2 \
libxslt \ libxslt \
nodejs-npm@edge \ ffmpeg \
nodejs@edge \ file \
protobuf \ imagemagick@edge \
su-exec \
tini \
&& npm install -g npm@3 && npm install -g yarn \ && npm install -g npm@3 && npm install -g yarn \
&& update-ca-certificates \ && bundle install --deployment --without test development \
&& yarn --ignore-optional \
&& yarn cache clean \
&& npm -g cache clean \
&& apk del $BUILD_DEPS \
&& rm -rf /tmp/* /var/cache/apk/* && rm -rf /tmp/* /var/cache/apk/*
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
RUN bundle install --deployment --without test development \
&& yarn --ignore-optional --pure-lockfile
COPY . /mastodon COPY . /mastodon
COPY docker_entrypoint.sh /usr/local/bin/run VOLUME /mastodon/public/system /mastodon/public/assets
RUN chmod +x /usr/local/bin/run
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
ENTRYPOINT ["/usr/local/bin/run"]

165
Gemfile
View File

@@ -3,108 +3,101 @@
source 'https://rubygems.org' source 'https://rubygems.org'
ruby '>= 2.3.0', '< 2.5.0' ruby '>= 2.3.0', '< 2.5.0'
gem 'pkg-config', '~> 1.2' gem 'pkg-config'
gem 'puma', '~> 3.8' gem 'rails', '~> 5.0.2'
gem 'rails', '~> 5.1.0' gem 'sass-rails', '~> 5.0'
gem 'uglifier', '~> 3.2' gem 'uglifier', '>= 1.3.0'
gem 'jquery-rails'
gem 'puma'
gem 'hamlit-rails', '~> 0.2' gem 'hamlit-rails'
gem 'pg', '~> 0.20' gem 'pg'
gem 'pghero', '~> 1.7' gem 'pghero'
gem 'dotenv-rails', '~> 2.2' gem 'dotenv-rails'
gem 'font-awesome-rails'
gem 'best_in_place', '~> 3.0.1'
gem 'aws-sdk', '~> 2.9'
gem 'paperclip', '~> 5.1' gem 'paperclip', '~> 5.1'
gem 'paperclip-av-transcoder', '~> 0.6' gem 'paperclip-av-transcoder'
gem 'aws-sdk', '>= 2.0'
gem 'active_model_serializers', '~> 0.10' gem 'addressable'
gem 'addressable', '~> 2.5' gem 'devise'
gem 'bootsnap' gem 'devise-two-factor'
gem 'browser' gem 'doorkeeper'
gem 'charlock_holmes', '~> 0.7.3' gem 'fast_blank'
gem 'cld3', '~> 3.1' gem 'goldfinger'
gem 'devise', '~> 4.2' gem 'hiredis'
gem 'devise-two-factor', '~> 3.0' gem 'htmlentities'
gem 'doorkeeper', '~> 4.2' gem 'http'
gem 'fast_blank', '~> 1.0' gem 'http_accept_language'
gem 'goldfinger', '~> 1.2' gem 'httplog'
gem 'hiredis', '~> 0.6' gem 'kaminari'
gem 'redis-namespace', '~> 1.5' gem 'link_header'
gem 'htmlentities', '~> 4.3' gem 'nokogiri'
gem 'http', '~> 2.2' gem 'oj'
gem 'http_accept_language', '~> 2.1' gem 'ostatus2', '~> 1.1'
gem 'httplog', '~> 0.99' gem 'ox'
gem 'kaminari', '~> 1.0' gem 'rabl'
gem 'link_header', '~> 0.0' gem 'rack-attack'
gem 'mime-types', '~> 3.1' gem 'rack-cors', require: 'rack/cors'
gem 'nokogiri', '~> 1.7' gem 'rack-timeout'
gem 'oj', '~> 3.0' gem 'rails-i18n'
gem 'ostatus2', '~> 2.0' gem 'rails-settings-cached'
gem 'ox', '~> 2.5' gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
gem 'pundit', '~> 1.1' gem 'rqrcode'
gem 'rabl', '~> 0.13' gem 'ruby-oembed', require: 'oembed'
gem 'rack-attack', '~> 5.0' gem 'sidekiq'
gem 'rack-cors', '~> 0.4', require: 'rack/cors' gem 'sidekiq-unique-jobs'
gem 'rack-timeout', '~> 0.4' gem 'simple-navigation'
gem 'rails-i18n', '~> 5.0' gem 'simple_form'
gem 'rails-settings-cached', '~> 0.6' gem 'sprockets-rails', :require => 'sprockets/railtie'
gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis'] gem 'statsd-instrument'
gem 'rqrcode', '~> 0.10' gem 'twitter-text'
gem 'ruby-oembed', '~> 0.12', require: 'oembed' gem 'tzinfo-data'
gem 'sanitize', '~> 4.4' gem 'whatlanguage'
gem 'sidekiq', '~> 5.0'
gem 'sidekiq-scheduler', '~> 2.1' gem 'react-rails'
gem 'sidekiq-unique-jobs', '~> 5.0' gem 'browserify-rails'
gem 'sidekiq-bulk', '~>0.1.1' gem 'autoprefixer-rails'
gem 'simple-navigation', '~> 4.0'
gem 'simple_form', '~> 3.4'
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
gem 'statsd-instrument', '~> 2.1'
gem 'twitter-text', '~> 1.14'
gem 'tzinfo-data', '~> 1.2017'
gem 'webpacker', '~> 2.0'
group :development, :test do group :development, :test do
gem 'fabrication', '~> 2.16' gem 'rspec-rails'
gem 'fuubar', '~> 2.2' gem 'pry-rails'
gem 'i18n-tasks', '~> 0.9', require: false gem 'fuubar'
gem 'pry-rails', '~> 0.3' gem 'fabrication'
gem 'rspec-rails', '~> 3.6' gem 'i18n-tasks', '~> 0.9.6'
end end
group :test do group :test do
gem 'capybara', '~> 2.14' gem 'capybara'
gem 'climate_control', '~> 0.2' gem 'faker'
gem 'faker', '~> 1.7' gem 'microformats2'
gem 'microformats2', '~> 3.0' gem 'rails-controller-testing'
gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq'
gem 'rspec-sidekiq', '~> 3.0' gem 'simplecov', require: false
gem 'simplecov', '~> 0.14', require: false gem 'webmock'
gem 'webmock', '~> 3.0'
gem 'parallel_tests', '~> 2.14'
end end
group :development do group :development do
gem 'active_record_query_trace', '~> 1.5'
gem 'annotate', '~> 2.7'
gem 'better_errors', '~> 2.1'
gem 'binding_of_caller', '~> 0.7'
gem 'bullet', '~> 5.5'
gem 'letter_opener', '~> 1.4'
gem 'letter_opener_web', '~> 1.3'
gem 'rubocop', require: false gem 'rubocop', require: false
gem 'brakeman', '~> 3.6', require: false gem 'better_errors'
gem 'bundler-audit', '~> 0.5', require: false gem 'binding_of_caller'
gem 'scss_lint', '~> 0.53', require: false gem 'letter_opener'
gem 'letter_opener_web'
gem 'bullet'
gem 'active_record_query_trace'
gem 'capistrano', '~> 3.8' gem 'capistrano', '3.8.0'
gem 'capistrano-rails', '~> 1.2' gem 'capistrano-rails'
gem 'capistrano-rbenv', '~> 2.1' gem 'capistrano-rbenv'
gem 'capistrano-yarn', '~> 2.0' gem 'capistrano-yarn'
gem 'capistrano-faster-assets', '~> 1.0'
end end
group :production do group :production do
gem 'lograge', '~> 0.5' gem 'rails_12factor'
gem 'redis-rails', '~> 5.0' gem 'redis-rails'
gem 'lograge'
end end

View File

@@ -1,89 +1,87 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (5.1.2) actioncable (5.0.2)
actionpack (= 5.1.2) actionpack (= 5.0.2)
nio4r (~> 2.0) nio4r (>= 1.2, < 3.0)
websocket-driver (~> 0.6.1) websocket-driver (~> 0.6.1)
actionmailer (5.1.2) actionmailer (5.0.2)
actionpack (= 5.1.2) actionpack (= 5.0.2)
actionview (= 5.1.2) actionview (= 5.0.2)
activejob (= 5.1.2) activejob (= 5.0.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (5.1.2) actionpack (5.0.2)
actionview (= 5.1.2) actionview (= 5.0.2)
activesupport (= 5.1.2) activesupport (= 5.0.2)
rack (~> 2.0) rack (~> 2.0)
rack-test (~> 0.6.3) rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.1.2) actionview (5.0.2)
activesupport (= 5.1.2) activesupport (= 5.0.2)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubis (~> 2.7.0)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.0, >= 1.0.3)
active_model_serializers (0.10.6)
actionpack (>= 4.1, < 6)
activemodel (>= 4.1, < 6)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.2)
active_record_query_trace (1.5.4) active_record_query_trace (1.5.4)
activejob (5.1.2) activejob (5.0.2)
activesupport (= 5.1.2) activesupport (= 5.0.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.1.2) activemodel (5.0.2)
activesupport (= 5.1.2) activesupport (= 5.0.2)
activerecord (5.1.2) activerecord (5.0.2)
activemodel (= 5.1.2) activemodel (= 5.0.2)
activesupport (= 5.1.2) activesupport (= 5.0.2)
arel (~> 8.0) arel (~> 7.0)
activesupport (5.1.2) activesupport (5.0.2)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7) i18n (~> 0.7)
minitest (~> 5.1) minitest (~> 5.1)
tzinfo (~> 1.1) tzinfo (~> 1.1)
addressable (2.5.1) addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2) public_suffix (~> 2.0, >= 2.0.2)
airbrussh (1.3.0) airbrussh (1.2.0)
sshkit (>= 1.6.1, != 1.7.0) sshkit (>= 1.6.1, != 1.7.0)
annotate (2.7.2) arel (7.1.4)
activerecord (>= 3.2, < 6.0)
rake (>= 10.4, < 13.0)
arel (8.0.0)
ast (2.3.0) ast (2.3.0)
attr_encrypted (3.0.3) attr_encrypted (3.0.3)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
autoprefixer-rails (6.7.7.1)
execjs
av (0.9.0) av (0.9.0)
cocaine (~> 0.5.3) cocaine (~> 0.5.3)
aws-sdk (2.10.6) aws-sdk (2.9.6)
aws-sdk-resources (= 2.10.6) aws-sdk-resources (= 2.9.6)
aws-sdk-core (2.10.6) aws-sdk-core (2.9.6)
aws-sigv4 (~> 1.0) aws-sigv4 (~> 1.0)
jmespath (~> 1.0) jmespath (~> 1.0)
aws-sdk-resources (2.10.6) aws-sdk-resources (2.9.6)
aws-sdk-core (= 2.10.6) aws-sdk-core (= 2.9.6)
aws-sigv4 (1.0.0) aws-sigv4 (1.0.0)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
bcrypt (3.1.11) bcrypt (3.1.11)
best_in_place (3.0.3)
actionpack (>= 3.2)
railties (>= 3.2)
better_errors (2.1.1) better_errors (2.1.1)
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubis (>= 2.6.6) erubis (>= 2.6.6)
rack (>= 0.9.0) rack (>= 0.9.0)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (1.1.1) browserify-rails (4.1.0)
msgpack (~> 1.0) addressable (>= 2.4.0)
brakeman (3.6.2) railties (>= 4.0.0, < 5.1)
browser (2.4.0) sprockets (>= 3.6.0)
builder (3.2.3) builder (3.2.3)
bullet (5.5.1) bullet (5.5.1)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0) uniform_notifier (~> 1.10.0)
bundler-audit (0.5.0) capistrano (3.8.0)
bundler (~> 1.2)
thor (~> 0.18)
capistrano (3.8.2)
airbrussh (>= 1.0.0) airbrussh (>= 1.0.0)
i18n i18n
rake (>= 10.0.0) rake (>= 10.0.0)
@@ -91,28 +89,25 @@ GEM
capistrano-bundler (1.2.0) capistrano-bundler (1.2.0)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.2) sshkit (~> 1.2)
capistrano-rails (1.3.0) capistrano-faster-assets (1.0.2)
capistrano (>= 3.1)
capistrano-rails (1.2.3)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-bundler (~> 1.1) capistrano-bundler (~> 1.1)
capistrano-rbenv (2.1.1) capistrano-rbenv (2.1.0)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.3) sshkit (~> 1.3)
capistrano-yarn (2.0.2) capistrano-yarn (2.0.2)
capistrano (~> 3.0) capistrano (~> 3.0)
capybara (2.14.4) capybara (2.13.0)
addressable addressable
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
rack (>= 1.0.0) rack (>= 1.0.0)
rack-test (>= 0.5.4) rack-test (>= 0.5.4)
xpath (~> 2.0) xpath (~> 2.0)
case_transform (0.2)
activesupport
charlock_holmes (0.7.3)
chunky_png (1.3.8) chunky_png (1.3.8)
cld3 (3.1.3) climate_control (0.1.0)
ffi (>= 1.1.0, < 1.10.0)
climate_control (0.2.0)
cocaine (0.5.8) cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
coderay (1.1.1) coderay (1.1.1)
@@ -121,12 +116,11 @@ GEM
connection_pool (2.2.1) connection_pool (2.2.1)
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
crass (1.0.2) debug_inspector (0.0.2)
debug_inspector (0.0.3) devise (4.2.1)
devise (4.3.0)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2) railties (>= 4.1.0, < 5.1)
responders responders
warden (~> 1.2.3) warden (~> 1.2.3)
devise-two-factor (3.0.0) devise-two-factor (3.0.0)
@@ -139,37 +133,35 @@ GEM
docile (1.1.5) docile (1.1.5)
domain_name (0.5.20170404) domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.2.6) doorkeeper (4.2.5)
railties (>= 4.2) railties (>= 4.2)
dotenv (2.2.1) dotenv (2.2.0)
dotenv-rails (2.2.1) dotenv-rails (2.2.0)
dotenv (= 2.2.1) dotenv (= 2.2.0)
railties (>= 3.2, < 5.2) railties (>= 3.2, < 5.1)
easy_translate (0.5.0) easy_translate (0.5.0)
json json
thread thread
thread_safe thread_safe
encryptor (3.0.0) encryptor (3.0.0)
erubi (1.6.1)
erubis (2.7.0) erubis (2.7.0)
et-orbi (1.0.5)
tzinfo
execjs (2.7.0) execjs (2.7.0)
fabrication (2.16.1) fabrication (2.16.1)
faker (1.7.3) faker (1.7.3)
i18n (~> 0.5) i18n (~> 0.5)
fast_blank (1.0.0) fast_blank (1.0.0)
ffi (1.9.18) font-awesome-rails (4.7.0.1)
railties (>= 3.2, < 5.1)
fuubar (2.2.0) fuubar (2.2.0)
rspec-core (~> 3.0) rspec-core (~> 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
globalid (0.4.0) globalid (0.3.7)
activesupport (>= 4.2.0) activesupport (>= 4.1.0)
goldfinger (1.2.0) goldfinger (1.1.2)
addressable (~> 2.4) addressable (~> 2.4)
http (~> 2.0) http (~> 2.0)
nokogiri (~> 1.6) nokogiri (~> 1.6)
hamlit (2.8.4) hamlit (2.8.1)
temple (>= 0.8.0) temple (>= 0.8.0)
thor thor
tilt tilt
@@ -178,25 +170,24 @@ GEM
activesupport (>= 4.0.1) activesupport (>= 4.0.1)
hamlit (>= 1.2.0) hamlit (>= 1.2.0)
railties (>= 4.0.1) railties (>= 4.0.1)
hashdiff (0.3.4) hashdiff (0.3.2)
highline (1.7.8) highline (1.7.8)
hiredis (0.6.1) hiredis (0.6.1)
htmlentities (4.3.4) htmlentities (4.3.4)
http (2.2.2) http (2.2.1)
addressable (~> 2.3) addressable (~> 2.3)
http-cookie (~> 1.0) http-cookie (~> 1.0)
http-form_data (~> 1.0.1) http-form_data (~> 1.0.1)
http_parser.rb (~> 0.6.0) http_parser.rb (~> 0.6.0)
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
http-form_data (1.0.3) http-form_data (1.0.1)
http_accept_language (2.1.1) http_accept_language (2.1.0)
http_parser.rb (0.6.0) http_parser.rb (0.6.0)
httplog (0.99.4) httplog (0.99.2)
colorize colorize
rack i18n (0.8.1)
i18n (0.8.4) i18n-tasks (0.9.13)
i18n-tasks (0.9.15)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
easy_translate (>= 0.5.0) easy_translate (>= 0.5.0)
@@ -207,8 +198,11 @@ GEM
rainbow (~> 2.2) rainbow (~> 2.2)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
jmespath (1.3.1) jmespath (1.3.1)
json (2.1.0) jquery-rails (4.3.1)
jsonapi-renderer (0.1.2) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.0.3)
kaminari (1.0.1) kaminari (1.0.1)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
kaminari-actionview (= 1.0.1) kaminari-actionview (= 1.0.1)
@@ -230,43 +224,40 @@ GEM
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
link_header (0.0.8) link_header (0.0.8)
lograge (0.5.1) lograge (0.4.1)
actionpack (>= 4, < 5.2) actionpack (>= 4, < 5.1)
activesupport (>= 4, < 5.2) activesupport (>= 4, < 5.1)
railties (>= 4, < 5.2) railties (>= 4, < 5.1)
loofah (2.0.3) loofah (2.0.3)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.6.6) mail (2.6.4)
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
method_source (0.8.2) method_source (0.8.2)
microformats2 (3.1.0) microformats2 (2.1.0)
activesupport
json json
nokogiri nokogiri
mime-types (3.1) mime-types (3.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521) mime-types-data (3.2016.0521)
mimemagic (0.3.2) mimemagic (0.3.2)
mini_portile2 (2.2.0) mini_portile2 (2.1.0)
minitest (5.10.2) minitest (5.10.1)
msgpack (1.1.0)
multi_json (1.12.1)
net-scp (1.2.1) net-scp (1.2.1)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (4.1.0) net-ssh (4.1.0)
nio4r (2.1.0) nio4r (2.0.0)
nokogiri (1.8.0) nokogiri (1.7.1)
mini_portile2 (~> 2.2.0) mini_portile2 (~> 2.1.0)
nokogumbo (1.4.13) oj (2.18.5)
nokogiri openssl (2.0.3)
oj (3.2.0)
openssl (2.0.4)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ostatus2 (2.0.1) ostatus2 (1.1.0)
addressable (~> 2.4) addressable (~> 2.4)
http (~> 2.0) http (~> 2.0)
nokogiri (~> 1.6) nokogiri (~> 1.6)
openssl (~> 2.0) openssl (~> 2.0)
ox (2.5.0) ox (2.4.11)
paperclip (5.1.0) paperclip (5.1.0)
activemodel (>= 4.2.0) activemodel (>= 4.2.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
@@ -276,15 +267,12 @@ GEM
paperclip-av-transcoder (0.6.4) paperclip-av-transcoder (0.6.4)
av (~> 0.9.0) av (~> 0.9.0)
paperclip (>= 2.5.2) paperclip (>= 2.5.2)
parallel (1.11.2)
parallel_tests (2.14.1)
parallel
parser (2.4.0.0) parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
pg (0.21.0) pg (0.20.0)
pghero (1.7.0) pghero (1.6.4)
activerecord activerecord
pkg-config (1.2.3) pkg-config (1.1.7)
powerpack (0.1.1) powerpack (0.1.1)
pry (0.10.4) pry (0.10.4)
coderay (~> 1.1.0) coderay (~> 1.1.0)
@@ -293,55 +281,63 @@ GEM
pry-rails (0.3.6) pry-rails (0.3.6)
pry (>= 0.10.4) pry (>= 0.10.4)
public_suffix (2.0.5) public_suffix (2.0.5)
puma (3.9.1) puma (3.8.2)
pundit (1.1.0)
activesupport (>= 3.0.0)
rabl (0.13.1) rabl (0.13.1)
activesupport (>= 2.3.14) activesupport (>= 2.3.14)
rack (2.0.3) rack (2.0.1)
rack-attack (5.0.1) rack-attack (5.0.1)
rack rack
rack-cors (0.4.1) rack-cors (0.4.1)
rack-protection (2.0.0) rack-protection (1.5.3)
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rack-timeout (0.4.2) rack-timeout (0.4.2)
rails (5.1.2) rails (5.0.2)
actioncable (= 5.1.2) actioncable (= 5.0.2)
actionmailer (= 5.1.2) actionmailer (= 5.0.2)
actionpack (= 5.1.2) actionpack (= 5.0.2)
actionview (= 5.1.2) actionview (= 5.0.2)
activejob (= 5.1.2) activejob (= 5.0.2)
activemodel (= 5.1.2) activemodel (= 5.0.2)
activerecord (= 5.1.2) activerecord (= 5.0.2)
activesupport (= 5.1.2) activesupport (= 5.0.2)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 5.1.2) railties (= 5.0.2)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.2) rails-controller-testing (1.0.1)
actionpack (~> 5.x, >= 5.0.1) actionpack (~> 5.x)
actionview (~> 5.x, >= 5.0.1) actionview (~> 5.x)
activesupport (~> 5.x) activesupport (~> 5.x)
rails-dom-testing (2.0.3) rails-dom-testing (2.0.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0, < 6.0)
nokogiri (>= 1.6) nokogiri (~> 1.6)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
rails-i18n (5.0.4) rails-i18n (5.0.3)
i18n (~> 0.7) i18n (~> 0.7)
railties (~> 5.0) railties (~> 5.0)
rails-settings-cached (0.6.5) rails-settings-cached (0.6.5)
rails (>= 4.2.0) rails (>= 4.2.0)
railties (5.1.2) rails_12factor (0.0.3)
actionpack (= 5.1.2) rails_serve_static_assets
activesupport (= 5.1.2) rails_stdout_logging
rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5)
railties (5.0.2)
actionpack (= 5.0.2)
activesupport (= 5.0.2)
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.2.2) rainbow (2.2.1)
rake
rake (12.0.0) rake (12.0.0)
react-rails (1.11.0)
babel-transpiler (>= 0.7.0)
connection_pool
execjs
railties (>= 3.2)
tilt
redis (3.3.3) redis (3.3.3)
redis-actionpack (5.0.1) redis-actionpack (5.0.1)
actionpack (>= 4.0, < 6) actionpack (>= 4.0, < 6)
@@ -350,10 +346,8 @@ GEM
redis-activesupport (5.0.2) redis-activesupport (5.0.2)
activesupport (>= 3, < 6) activesupport (>= 3, < 6)
redis-store (~> 1.3.0) redis-store (~> 1.3.0)
redis-namespace (1.5.3) redis-rack (2.0.1)
redis (~> 3.0, >= 3.0.4) rack (>= 2.0, < 3)
redis-rack (2.0.2)
rack (>= 1.5, < 3)
redis-store (>= 1.2, < 1.4) redis-store (>= 1.2, < 1.4)
redis-rails (5.0.2) redis-rails (5.0.2)
redis-actionpack (>= 5.0, < 6) redis-actionpack (>= 5.0, < 6)
@@ -361,34 +355,32 @@ GEM
redis-store (>= 1.2, < 2) redis-store (>= 1.2, < 2)
redis-store (1.3.0) redis-store (1.3.0)
redis (>= 2.2) redis (>= 2.2)
responders (2.4.0) responders (2.3.0)
actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.1)
railties (>= 4.2.0, < 5.3)
rotp (2.1.2) rotp (2.1.2)
rqrcode (0.10.1) rqrcode (0.10.1)
chunky_png (~> 1.0) chunky_png (~> 1.0)
rspec-core (3.6.0) rspec-core (3.5.4)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-expectations (3.6.0) rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-mocks (3.6.0) rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-rails (3.6.0) rspec-rails (3.5.2)
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
railties (>= 3.0) railties (>= 3.0)
rspec-core (~> 3.6.0) rspec-core (~> 3.5.0)
rspec-expectations (~> 3.6.0) rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.6.0) rspec-mocks (~> 3.5.0)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-sidekiq (3.0.3) rspec-sidekiq (3.0.0)
rspec-core (~> 3.0, >= 3.0.0) rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0) sidekiq (>= 2.4.0)
rspec-support (3.6.0) rspec-support (3.5.0)
rubocop (0.49.1) rubocop (0.48.1)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0) parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
@@ -396,43 +388,32 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-oembed (0.12.0) ruby-oembed (0.12.0)
ruby-progressbar (1.8.1) ruby-progressbar (1.8.1)
rufus-scheduler (3.4.2)
et-orbi (~> 1.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (4.5.0) sass (3.4.23)
crass (~> 1.0.2) sass-rails (5.0.6)
nokogiri (>= 1.4.4) railties (>= 4.0.0, < 6)
nokogumbo (~> 1.4.1) sass (~> 3.1)
sass (3.4.24) sprockets (>= 2.8, < 4.0)
scss_lint (0.54.0) sprockets-rails (>= 2.0, < 4.0)
rake (>= 0.9, < 13) tilt (>= 1.1, < 3)
sass (~> 3.4.20) sidekiq (4.2.10)
sidekiq (5.0.3)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
redis (~> 3.3, >= 3.3.3) redis (~> 3.2, >= 3.2.1)
sidekiq-bulk (0.1.1) sidekiq-unique-jobs (5.0.0)
activesupport sidekiq (>= 4.0)
sidekiq thor
sidekiq-scheduler (2.1.7)
redis (~> 3)
rufus-scheduler (~> 3.2)
sidekiq (>= 3)
tilt (>= 1.4.0)
sidekiq-unique-jobs (5.0.8)
sidekiq (>= 4.0, <= 6.0)
thor (~> 0)
simple-navigation (4.0.5) simple-navigation (4.0.5)
activesupport (>= 2.3.2) activesupport (>= 2.3.2)
simple_form (3.5.0) simple_form (3.4.0)
actionpack (> 4, < 5.2) actionpack (> 4, < 5.1)
activemodel (> 4, < 5.2) activemodel (> 4, < 5.1)
simplecov (0.14.1) simplecov (0.14.1)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
simplecov-html (0.10.1) simplecov-html (0.10.0)
slop (3.6.0) slop (3.6.0)
sprockets (3.7.1) sprockets (3.7.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
@@ -446,13 +427,13 @@ GEM
net-ssh (>= 2.8.0) net-ssh (>= 2.8.0)
statsd-instrument (2.1.2) statsd-instrument (2.1.2)
temple (0.8.0) temple (0.8.0)
terminal-table (1.8.0) terminal-table (1.7.3)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1.1)
thor (0.19.4) thor (0.19.4)
thread (0.2.2) thread (0.2.2)
thread_safe (0.3.6) thread_safe (0.3.6)
tilt (2.0.7) tilt (2.0.7)
twitter-text (1.14.6) twitter-text (1.14.5)
unf (~> 0.1.0) unf (~> 0.1.0)
tzinfo (1.2.3) tzinfo (1.2.3)
thread_safe (~> 0.1) thread_safe (~> 0.1)
@@ -462,120 +443,109 @@ GEM
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.4) unf_ext (0.0.7.3)
unicode-display_width (1.3.0) unicode-display_width (1.1.3)
uniform_notifier (1.10.0) uniform_notifier (1.10.0)
warden (1.2.7) warden (1.2.7)
rack (>= 1.0) rack (>= 1.0)
webmock (3.0.1) webmock (2.3.2)
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff hashdiff
webpacker (2.0)
activesupport (>= 4.2)
multi_json (~> 1.2)
railties (>= 4.2)
websocket-driver (0.6.5) websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2) websocket-extensions (0.1.2)
xpath (2.1.0) whatlanguage (1.0.6)
xpath (2.0.0)
nokogiri (~> 1.3) nokogiri (~> 1.3)
PLATFORMS PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
active_model_serializers (~> 0.10) active_record_query_trace
active_record_query_trace (~> 1.5) addressable
addressable (~> 2.5) autoprefixer-rails
annotate (~> 2.7) aws-sdk (>= 2.0)
aws-sdk (~> 2.9) best_in_place (~> 3.0.1)
better_errors (~> 2.1) better_errors
binding_of_caller (~> 0.7) binding_of_caller
bootsnap browserify-rails
brakeman (~> 3.6) bullet
browser capistrano (= 3.8.0)
bullet (~> 5.5) capistrano-faster-assets (~> 1.0)
bundler-audit (~> 0.5) capistrano-rails
capistrano (~> 3.8) capistrano-rbenv
capistrano-rails (~> 1.2) capistrano-yarn
capistrano-rbenv (~> 2.1) capybara
capistrano-yarn (~> 2.0) devise
capybara (~> 2.14) devise-two-factor
charlock_holmes (~> 0.7.3) doorkeeper
cld3 (~> 3.1) dotenv-rails
climate_control (~> 0.2) fabrication
devise (~> 4.2) faker
devise-two-factor (~> 3.0) fast_blank
doorkeeper (~> 4.2) font-awesome-rails
dotenv-rails (~> 2.2) fuubar
fabrication (~> 2.16) goldfinger
faker (~> 1.7) hamlit-rails
fast_blank (~> 1.0) hiredis
fuubar (~> 2.2) htmlentities
goldfinger (~> 1.2) http
hamlit-rails (~> 0.2) http_accept_language
hiredis (~> 0.6) httplog
htmlentities (~> 4.3) i18n-tasks (~> 0.9.6)
http (~> 2.2) jquery-rails
http_accept_language (~> 2.1) kaminari
httplog (~> 0.99) letter_opener
i18n-tasks (~> 0.9) letter_opener_web
kaminari (~> 1.0) link_header
letter_opener (~> 1.4) lograge
letter_opener_web (~> 1.3) microformats2
link_header (~> 0.0) nokogiri
lograge (~> 0.5) oj
microformats2 (~> 3.0) ostatus2 (~> 1.1)
mime-types (~> 3.1) ox
nokogiri (~> 1.7)
oj (~> 3.0)
ostatus2 (~> 2.0)
ox (~> 2.5)
paperclip (~> 5.1) paperclip (~> 5.1)
paperclip-av-transcoder (~> 0.6) paperclip-av-transcoder
parallel_tests (~> 2.14) pg
pg (~> 0.20) pghero
pghero (~> 1.7) pkg-config
pkg-config (~> 1.2) pry-rails
pry-rails (~> 0.3) puma
puma (~> 3.8) rabl
pundit (~> 1.1) rack-attack
rabl (~> 0.13) rack-cors
rack-attack (~> 5.0) rack-timeout
rack-cors (~> 0.4) rails (~> 5.0.2)
rack-timeout (~> 0.4) rails-controller-testing
rails (~> 5.1.0) rails-i18n
rails-controller-testing (~> 1.0) rails-settings-cached
rails-i18n (~> 5.0) rails_12factor
rails-settings-cached (~> 0.6) react-rails
redis (~> 3.3) redis (~> 3.2)
redis-namespace (~> 1.5) redis-rails
redis-rails (~> 5.0) rqrcode
rqrcode (~> 0.10) rspec-rails
rspec-rails (~> 3.6) rspec-sidekiq
rspec-sidekiq (~> 3.0)
rubocop rubocop
ruby-oembed (~> 0.12) ruby-oembed
sanitize (~> 4.4) sass-rails (~> 5.0)
scss_lint (~> 0.53) sidekiq
sidekiq (~> 5.0) sidekiq-unique-jobs
sidekiq-bulk (~> 0.1.1) simple-navigation
sidekiq-scheduler (~> 2.1) simple_form
sidekiq-unique-jobs (~> 5.0) simplecov
simple-navigation (~> 4.0) sprockets-rails
simple_form (~> 3.4) statsd-instrument
simplecov (~> 0.14) twitter-text
sprockets-rails (~> 3.2) tzinfo-data
statsd-instrument (~> 2.1) uglifier (>= 1.3.0)
twitter-text (~> 1.14) webmock
tzinfo-data (~> 1.2017) whatlanguage
uglifier (~> 3.2)
webmock (~> 3.0)
webpacker (~> 2.0)
RUBY VERSION RUBY VERSION
ruby 2.4.1p111 ruby 2.4.1p111
BUNDLED WITH BUNDLED WITH
1.15.1 1.14.6

View File

@@ -1,2 +1,2 @@
web: bundle exec puma -C config/puma.rb web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq worker: bundle exec sidekiq -q default -q push -q pull -q mailers

View File

@@ -1,4 +0,0 @@
web: PORT=3000 bundle exec puma -C config/puma.rb
sidekiq: PORT=3000 bundle exec sidekiq
stream: PORT=4000 yarn run start
webpack: ./bin/webpack-dev-server --host 0.0.0.0

View File

@@ -1,7 +1,66 @@
Mastodon Glitch Edition Mastodon
======== ========
Now with automated deploys!
[![Build Status](https://travis-ci.org/glitch-soc/mastodon.svg?branch=master)](https://travis-ci.org/glitch-soc/mastodon) [![Build Status](http://img.shields.io/travis/tootsuite/mastodon.svg)][travis]
[![Code Climate](https://img.shields.io/codeclimate/github/tootsuite/mastodon.svg)][code_climate]
So here's the deal: we all work on this code, and then it runs on dev.glitch.social and anyone who uses that does so absolutely at their own risk. can you dig it? [travis]: https://travis-ci.org/tootsuite/mastodon
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
Mastodon is a free, open-source social network server. A decentralized solution to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly.
An alternative implementation of the GNU social project. Based on [ActivityStreams](https://en.wikipedia.org/wiki/Activity_Streams_(format)), [Webfinger](https://en.wikipedia.org/wiki/WebFinger), [PubsubHubbub](https://en.wikipedia.org/wiki/PubSubHubbub) and [Salmon](https://en.wikipedia.org/wiki/Salmon_(protocol)).
Click on the screenshot to watch a demo of the UI:
[![Screenshot](https://i.imgur.com/T2q5V65.png)][youtube_demo]
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
The project focus is a clean REST API and a good user interface. Ruby on Rails is used for the back-end, while React.js and Redux are used for the dynamic front-end. A static front-end for public resources (profiles and statuses) is also provided.
If you would like, you can [support the development of this project on Patreon][patreon]. Alternatively, you can donate to this BTC address: `17j2g7vpgHhLuXhN4bueZFCvdxxieyRVWd`
[patreon]: https://www.patreon.com/user?u=619786
## Resources
- [List of Mastodon instances](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/List-of-Mastodon-instances.md)
- [Use this tool to find Twitter friends on Mastodon](https://mastodon-bridge.herokuapp.com)
- [API overview](https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md)
- [Frequently Asked Questions](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md)
- [List of apps](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md)
## Features
- **Fully interoperable with GNU social and any OStatus platform**
Whatever implements Atom feeds, ActivityStreams, Salmon, PubSubHubbub and Webfinger is part of the network
- **Real-time timeline updates**
See the updates of people you're following appear in real-time in the UI via WebSockets
- **Federated thread resolving**
If someone you follow replies to a user unknown to the server, the server fetches the full thread so you can view it without leaving the UI
- **Media attachments like images and WebM**
Upload and view images and WebM videos attached to the updates
- **OAuth2 and a straightforward REST API**
Mastodon acts as an OAuth2 provider so 3rd party apps can use the API, which is RESTful and simple
- **Background processing for long-running tasks**
Mastodon tries to be as fast and responsive as possible, so all long-running tasks that can be delegated to background processing, are
- **Deployable via Docker**
You don't need to mess with dependencies and configuration if you want to try Mastodon, if you have Docker and Docker Compose the deployment is extremely easy
## Deployment
There are guides in the documentation repository for [deploying on various platforms](https://github.com/tootsuite/documentation#running-mastodon).
## Contributing
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository. [Here are the guidelines for code contributions](CONTRIBUTING.md)
**IRC channel**: #mastodon on irc.freenode.net
## Extra credits
- The [Emoji One](https://github.com/Ranks/emojione) pack has been used for the emojis
- The error page image courtesy of [Dopatwo](https://www.youtube.com/user/dopatwo)
![Mastodon error image](https://mastodon.social/oops.png)

53
Vagrantfile vendored
View File

@@ -1,8 +1,6 @@
# -*- mode: ruby -*- # -*- mode: ruby -*-
# vi: set ft=ruby : # vi: set ft=ruby :
ENV["PORT"] ||= "3000"
$provision = <<SCRIPT $provision = <<SCRIPT
cd /vagrant # This is where the host folder/repo is mounted cd /vagrant # This is where the host folder/repo is mounted
@@ -12,10 +10,10 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
# Add repo for NodeJS # Add repo for NodeJS
curl -sL https://deb.nodesource.com/setup_6.x | sudo bash - curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
# Add firewall rule to redirect 80 to PORT and save # Add firewall rule to redirect 80 to 3000 and save
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections
echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections
sudo apt-get install iptables-persistent -y sudo apt-get install iptables-persistent -y
@@ -33,44 +31,47 @@ sudo apt-get install \
redis-tools \ redis-tools \
postgresql \ postgresql \
postgresql-contrib \ postgresql-contrib \
protobuf-compiler \
yarn \ yarn \
libprotobuf-dev \
libreadline-dev \ libreadline-dev \
libicu-dev \
-y -y
# Install rvm # Install rbenv
read RUBY_VERSION < .ruby-version git clone https://github.com/rbenv/rbenv.git ~/.rbenv
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 cd ~/.rbenv && src/configure && make -C src
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
source /home/vagrant/.rvm/scripts/rvm echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
# Install Ruby git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
rvm install ruby-$RUBY_VERSION
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
cd /vagrant
echo "Compiling Ruby $(cat .ruby-version): warning, this takes a while!!!"
rbenv install $(cat .ruby-version)
rbenv global $(cat .ruby-version)
# Configure database # Configure database
sudo -u postgres createuser -U postgres vagrant -s sudo -u postgres createuser -U postgres vagrant -s
sudo -u postgres createdb -U postgres mastodon_development sudo -u postgres createdb -U postgres mastodon_development
# Install gems and node modules # Install gems and node modules
gem install bundler foreman gem install bundler
bundle install bundle install
yarn install yarn install
# Build Mastodon # Build Mastodon
export $(cat ".env.vagrant" | xargs)
bundle exec rails db:setup bundle exec rails db:setup
bundle exec rails assets:precompile
# Configure automatic loading of environment variable
echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile
SCRIPT SCRIPT
$start = <<SCRIPT $start = <<SCRIPT
echo 'To start server' cd /vagrant
echo ' $ vagrant ssh -c "cd /vagrant && foreman start"' export $(cat ".env.vagrant" | xargs)
rails s -d -b 0.0.0.0
SCRIPT SCRIPT
@@ -82,7 +83,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider :virtualbox do |vb| config.vm.provider :virtualbox do |vb|
vb.name = "mastodon" vb.name = "mastodon"
vb.customize ["modifyvm", :id, "--memory", "2048"] vb.customize ["modifyvm", :id, "--memory", "1024"]
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions. # Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
# https://github.com/mitchellh/vagrant/issues/1172 # https://github.com/mitchellh/vagrant/issues/1172
@@ -112,10 +113,8 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.synced_folder ".", "/vagrant" config.vm.synced_folder ".", "/vagrant"
end end
# Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080 # Otherwise, you can access the site at http://localhost:3000
config.vm.network :forwarded_port, guest: 3000, host: 3000 config.vm.network :forwarded_port, guest: 80, host: 3000
config.vm.network :forwarded_port, guest: 4000, host: 4000
config.vm.network :forwarded_port, guest: 8080, host: 8080
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision' # Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
config.vm.provision :shell, inline: $provision, privileged: false config.vm.provision :shell, inline: $provision, privileged: false

View File

@@ -94,9 +94,6 @@
} }
}, },
"buildpacks": [ "buildpacks": [
{
"url": "https://github.com/heroku/heroku-buildpack-apt"
},
{ {
"url": "heroku/nodejs" "url": "heroku/nodejs"
}, },

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 339 KiB

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 258 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" height="1000" width="1000"><path d="M500 0a500 500 0 0 0-353.553 146.447 500 500 0 1 0 707.106 707.106A500 500 0 0 0 500 0zm-.059 280.05h107.12c-19.071 13.424-26.187 51.016-27.12 73.843V562.05c0 44.32-35.68 80-80 80s-80-35.68-80-80v-202c0-44.32 35.68-80 80-80zm-.441 52c-15.464 0-28 12.537-28 28 0 15.465 12.536 28 28 28s28-12.535 28-28c0-15.463-12.536-28-28-28zm-279.059 7.9c44.32 0 80 35.68 80 80v206.157c.933 22.827 8.049 60.42 27.12 73.842H220.44c-44.32 0-80-35.68-80-80v-200c0-44.32 35.68-80 80-80zm559.12 0c44.32 0 80 35.68 80 80v200c0 44.32-35.68 80-80 80H672.44c19.071-13.424 26.187-51.016 27.12-73.843V419.95c0-44.32 35.68-80 80-80zM220 392c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zm560 0c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zm-280.5 40.05c-15.464 0-28 12.537-28 28 0 15.465 12.536 28 28 28s28-12.535 28-28c0-15.463-12.536-28-28-28zM220 491.95c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28zm560 0c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28zM499.5 532c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zM220 591.95c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28zm560 0c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28z" fill="#fff"/></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" height="1000" width="1000"><path d="M500 0a500 500 0 0 0-353.553 146.447 500 500 0 1 0 707.106 707.106A500 500 0 0 0 500 0zm-.059 280.05h107.12c-19.071 13.424-26.187 51.016-27.12 73.843V562.05c0 44.32-35.68 80-80 80s-80-35.68-80-80v-202c0-44.32 35.68-80 80-80zm-.441 52c-15.464 0-28 12.537-28 28 0 15.465 12.536 28 28 28s28-12.535 28-28c0-15.463-12.536-28-28-28zm-279.059 7.9c44.32 0 80 35.68 80 80v206.157c.933 22.827 8.049 60.42 27.12 73.842H220.44c-44.32 0-80-35.68-80-80v-200c0-44.32 35.68-80 80-80zm559.12 0c44.32 0 80 35.68 80 80v200c0 44.32-35.68 80-80 80H672.44c19.071-13.424 26.187-51.016 27.12-73.843V419.95c0-44.32 35.68-80 80-80zM220 392c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zm560 0c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zm-280.5 40.05c-15.464 0-28 12.537-28 28 0 15.465 12.536 28 28 28s28-12.535 28-28c0-15.463-12.536-28-28-28zM220 491.95c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28zm560 0c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28zM499.5 532c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zM220 591.95c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28zm560 0c-15.464 0-28 12.535-28 28 0 15.463 12.536 28 28 28s28-12.537 28-28c0-15.465-12.536-28-28-28z" fill="#189efc"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

@@ -0,0 +1,15 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require components

View File

@@ -0,0 +1,8 @@
//= require jquery
//= require jquery_ujs
//= require extras
//= require best_in_place
$(function () {
$(".best_in_place").best_in_place();
});

View File

@@ -0,0 +1,15 @@
//= require_self
//= require react_ujs
window.React = require('react');
window.ReactDOM = require('react-dom');
window.Perf = require('react-addons-perf');
if (!window.Intl) {
require('intl');
require('intl/locale-data/jsonp/en.js');
}
//= require_tree ./components
window.Mastodon = require('./components/containers/mastodon');

View File

@@ -1,4 +1,5 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
import Immutable from 'immutable';
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS'; export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
@@ -28,6 +29,14 @@ export const ACCOUNT_UNMUTE_REQUEST = 'ACCOUNT_UNMUTE_REQUEST';
export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS'; export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS';
export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL'; export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL';
export const ACCOUNT_TIMELINE_FETCH_REQUEST = 'ACCOUNT_TIMELINE_FETCH_REQUEST';
export const ACCOUNT_TIMELINE_FETCH_SUCCESS = 'ACCOUNT_TIMELINE_FETCH_SUCCESS';
export const ACCOUNT_TIMELINE_FETCH_FAIL = 'ACCOUNT_TIMELINE_FETCH_FAIL';
export const ACCOUNT_TIMELINE_EXPAND_REQUEST = 'ACCOUNT_TIMELINE_EXPAND_REQUEST';
export const ACCOUNT_TIMELINE_EXPAND_SUCCESS = 'ACCOUNT_TIMELINE_EXPAND_SUCCESS';
export const ACCOUNT_TIMELINE_EXPAND_FAIL = 'ACCOUNT_TIMELINE_EXPAND_FAIL';
export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST'; export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST';
export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS'; export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS';
export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL'; export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL';
@@ -82,17 +91,60 @@ export function fetchAccount(id) {
}; };
}; };
export function fetchAccountTimeline(id, replace = false) {
return (dispatch, getState) => {
const ids = getState().getIn(['timelines', 'accounts_timelines', id, 'items'], Immutable.List());
const newestId = ids.size > 0 ? ids.first() : null;
let params = '';
let skipLoading = false;
if (newestId !== null && !replace) {
params = `?since_id=${newestId}`;
skipLoading = true;
}
dispatch(fetchAccountTimelineRequest(id, skipLoading));
api(getState).get(`/api/v1/accounts/${id}/statuses${params}`).then(response => {
dispatch(fetchAccountTimelineSuccess(id, response.data, replace, skipLoading));
}).catch(error => {
dispatch(fetchAccountTimelineFail(id, error, skipLoading));
});
};
};
export function expandAccountTimeline(id) {
return (dispatch, getState) => {
const lastId = getState().getIn(['timelines', 'accounts_timelines', id, 'items'], Immutable.List()).last();
dispatch(expandAccountTimelineRequest(id));
api(getState).get(`/api/v1/accounts/${id}/statuses`, {
params: {
limit: 10,
max_id: lastId
}
}).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandAccountTimelineSuccess(id, response.data, next));
}).catch(error => {
dispatch(expandAccountTimelineFail(id, error));
});
};
};
export function fetchAccountRequest(id) { export function fetchAccountRequest(id) {
return { return {
type: ACCOUNT_FETCH_REQUEST, type: ACCOUNT_FETCH_REQUEST,
id, id
}; };
}; };
export function fetchAccountSuccess(account) { export function fetchAccountSuccess(account) {
return { return {
type: ACCOUNT_FETCH_SUCCESS, type: ACCOUNT_FETCH_SUCCESS,
account, account
}; };
}; };
@@ -101,7 +153,7 @@ export function fetchAccountFail(id, error) {
type: ACCOUNT_FETCH_FAIL, type: ACCOUNT_FETCH_FAIL,
id, id,
error, error,
skipAlert: true, skipAlert: true
}; };
}; };
@@ -126,48 +178,100 @@ export function unfollowAccount(id) {
}).catch(error => { }).catch(error => {
dispatch(unfollowAccountFail(error)); dispatch(unfollowAccountFail(error));
}); });
}; }
}; };
export function followAccountRequest(id) { export function followAccountRequest(id) {
return { return {
type: ACCOUNT_FOLLOW_REQUEST, type: ACCOUNT_FOLLOW_REQUEST,
id, id
}; };
}; };
export function followAccountSuccess(relationship) { export function followAccountSuccess(relationship) {
return { return {
type: ACCOUNT_FOLLOW_SUCCESS, type: ACCOUNT_FOLLOW_SUCCESS,
relationship, relationship
}; };
}; };
export function followAccountFail(error) { export function followAccountFail(error) {
return { return {
type: ACCOUNT_FOLLOW_FAIL, type: ACCOUNT_FOLLOW_FAIL,
error, error
}; };
}; };
export function unfollowAccountRequest(id) { export function unfollowAccountRequest(id) {
return { return {
type: ACCOUNT_UNFOLLOW_REQUEST, type: ACCOUNT_UNFOLLOW_REQUEST,
id, id
}; };
}; };
export function unfollowAccountSuccess(relationship) { export function unfollowAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNFOLLOW_SUCCESS, type: ACCOUNT_UNFOLLOW_SUCCESS,
relationship, relationship
}; };
}; };
export function unfollowAccountFail(error) { export function unfollowAccountFail(error) {
return { return {
type: ACCOUNT_UNFOLLOW_FAIL, type: ACCOUNT_UNFOLLOW_FAIL,
error
};
};
export function fetchAccountTimelineRequest(id, skipLoading) {
return {
type: ACCOUNT_TIMELINE_FETCH_REQUEST,
id,
skipLoading
};
};
export function fetchAccountTimelineSuccess(id, statuses, replace, skipLoading) {
return {
type: ACCOUNT_TIMELINE_FETCH_SUCCESS,
id,
statuses,
replace,
skipLoading
};
};
export function fetchAccountTimelineFail(id, error, skipLoading) {
return {
type: ACCOUNT_TIMELINE_FETCH_FAIL,
id,
error, error,
skipLoading,
skipAlert: error.response.status === 404
};
};
export function expandAccountTimelineRequest(id) {
return {
type: ACCOUNT_TIMELINE_EXPAND_REQUEST,
id
};
};
export function expandAccountTimelineSuccess(id, statuses, next) {
return {
type: ACCOUNT_TIMELINE_EXPAND_SUCCESS,
id,
statuses,
next
};
};
export function expandAccountTimelineFail(id, error) {
return {
type: ACCOUNT_TIMELINE_EXPAND_FAIL,
id,
error
}; };
}; };
@@ -199,7 +303,7 @@ export function unblockAccount(id) {
export function blockAccountRequest(id) { export function blockAccountRequest(id) {
return { return {
type: ACCOUNT_BLOCK_REQUEST, type: ACCOUNT_BLOCK_REQUEST,
id, id
}; };
}; };
@@ -207,35 +311,35 @@ export function blockAccountSuccess(relationship, statuses) {
return { return {
type: ACCOUNT_BLOCK_SUCCESS, type: ACCOUNT_BLOCK_SUCCESS,
relationship, relationship,
statuses, statuses
}; };
}; };
export function blockAccountFail(error) { export function blockAccountFail(error) {
return { return {
type: ACCOUNT_BLOCK_FAIL, type: ACCOUNT_BLOCK_FAIL,
error, error
}; };
}; };
export function unblockAccountRequest(id) { export function unblockAccountRequest(id) {
return { return {
type: ACCOUNT_UNBLOCK_REQUEST, type: ACCOUNT_UNBLOCK_REQUEST,
id, id
}; };
}; };
export function unblockAccountSuccess(relationship) { export function unblockAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNBLOCK_SUCCESS, type: ACCOUNT_UNBLOCK_SUCCESS,
relationship, relationship
}; };
}; };
export function unblockAccountFail(error) { export function unblockAccountFail(error) {
return { return {
type: ACCOUNT_UNBLOCK_FAIL, type: ACCOUNT_UNBLOCK_FAIL,
error, error
}; };
}; };
@@ -268,7 +372,7 @@ export function unmuteAccount(id) {
export function muteAccountRequest(id) { export function muteAccountRequest(id) {
return { return {
type: ACCOUNT_MUTE_REQUEST, type: ACCOUNT_MUTE_REQUEST,
id, id
}; };
}; };
@@ -276,35 +380,35 @@ export function muteAccountSuccess(relationship, statuses) {
return { return {
type: ACCOUNT_MUTE_SUCCESS, type: ACCOUNT_MUTE_SUCCESS,
relationship, relationship,
statuses, statuses
}; };
}; };
export function muteAccountFail(error) { export function muteAccountFail(error) {
return { return {
type: ACCOUNT_MUTE_FAIL, type: ACCOUNT_MUTE_FAIL,
error, error
}; };
}; };
export function unmuteAccountRequest(id) { export function unmuteAccountRequest(id) {
return { return {
type: ACCOUNT_UNMUTE_REQUEST, type: ACCOUNT_UNMUTE_REQUEST,
id, id
}; };
}; };
export function unmuteAccountSuccess(relationship) { export function unmuteAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNMUTE_SUCCESS, type: ACCOUNT_UNMUTE_SUCCESS,
relationship, relationship
}; };
}; };
export function unmuteAccountFail(error) { export function unmuteAccountFail(error) {
return { return {
type: ACCOUNT_UNMUTE_FAIL, type: ACCOUNT_UNMUTE_FAIL,
error, error
}; };
}; };
@@ -327,7 +431,7 @@ export function fetchFollowers(id) {
export function fetchFollowersRequest(id) { export function fetchFollowersRequest(id) {
return { return {
type: FOLLOWERS_FETCH_REQUEST, type: FOLLOWERS_FETCH_REQUEST,
id, id
}; };
}; };
@@ -336,7 +440,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
type: FOLLOWERS_FETCH_SUCCESS, type: FOLLOWERS_FETCH_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -344,7 +448,7 @@ export function fetchFollowersFail(id, error) {
return { return {
type: FOLLOWERS_FETCH_FAIL, type: FOLLOWERS_FETCH_FAIL,
id, id,
error, error
}; };
}; };
@@ -372,7 +476,7 @@ export function expandFollowers(id) {
export function expandFollowersRequest(id) { export function expandFollowersRequest(id) {
return { return {
type: FOLLOWERS_EXPAND_REQUEST, type: FOLLOWERS_EXPAND_REQUEST,
id, id
}; };
}; };
@@ -381,7 +485,7 @@ export function expandFollowersSuccess(id, accounts, next) {
type: FOLLOWERS_EXPAND_SUCCESS, type: FOLLOWERS_EXPAND_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -389,7 +493,7 @@ export function expandFollowersFail(id, error) {
return { return {
type: FOLLOWERS_EXPAND_FAIL, type: FOLLOWERS_EXPAND_FAIL,
id, id,
error, error
}; };
}; };
@@ -411,7 +515,7 @@ export function fetchFollowing(id) {
export function fetchFollowingRequest(id) { export function fetchFollowingRequest(id) {
return { return {
type: FOLLOWING_FETCH_REQUEST, type: FOLLOWING_FETCH_REQUEST,
id, id
}; };
}; };
@@ -420,7 +524,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
type: FOLLOWING_FETCH_SUCCESS, type: FOLLOWING_FETCH_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -428,7 +532,7 @@ export function fetchFollowingFail(id, error) {
return { return {
type: FOLLOWING_FETCH_FAIL, type: FOLLOWING_FETCH_FAIL,
id, id,
error, error
}; };
}; };
@@ -456,7 +560,7 @@ export function expandFollowing(id) {
export function expandFollowingRequest(id) { export function expandFollowingRequest(id) {
return { return {
type: FOLLOWING_EXPAND_REQUEST, type: FOLLOWING_EXPAND_REQUEST,
id, id
}; };
}; };
@@ -465,7 +569,7 @@ export function expandFollowingSuccess(id, accounts, next) {
type: FOLLOWING_EXPAND_SUCCESS, type: FOLLOWING_EXPAND_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -473,7 +577,7 @@ export function expandFollowingFail(id, error) {
return { return {
type: FOLLOWING_EXPAND_FAIL, type: FOLLOWING_EXPAND_FAIL,
id, id,
error, error
}; };
}; };
@@ -500,7 +604,7 @@ export function fetchRelationshipsRequest(ids) {
return { return {
type: RELATIONSHIPS_FETCH_REQUEST, type: RELATIONSHIPS_FETCH_REQUEST,
ids, ids,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -508,7 +612,7 @@ export function fetchRelationshipsSuccess(relationships) {
return { return {
type: RELATIONSHIPS_FETCH_SUCCESS, type: RELATIONSHIPS_FETCH_SUCCESS,
relationships, relationships,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -516,7 +620,7 @@ export function fetchRelationshipsFail(error) {
return { return {
type: RELATIONSHIPS_FETCH_FAIL, type: RELATIONSHIPS_FETCH_FAIL,
error, error,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -526,14 +630,14 @@ export function fetchFollowRequests() {
api(getState).get('/api/v1/follow_requests').then(response => { api(getState).get('/api/v1/follow_requests').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null))
}).catch(error => dispatch(fetchFollowRequestsFail(error))); }).catch(error => dispatch(fetchFollowRequestsFail(error)));
}; };
}; };
export function fetchFollowRequestsRequest() { export function fetchFollowRequestsRequest() {
return { return {
type: FOLLOW_REQUESTS_FETCH_REQUEST, type: FOLLOW_REQUESTS_FETCH_REQUEST
}; };
}; };
@@ -541,14 +645,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
return { return {
type: FOLLOW_REQUESTS_FETCH_SUCCESS, type: FOLLOW_REQUESTS_FETCH_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function fetchFollowRequestsFail(error) { export function fetchFollowRequestsFail(error) {
return { return {
type: FOLLOW_REQUESTS_FETCH_FAIL, type: FOLLOW_REQUESTS_FETCH_FAIL,
error, error
}; };
}; };
@@ -564,14 +668,14 @@ export function expandFollowRequests() {
api(getState).get(url).then(response => { api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null))
}).catch(error => dispatch(expandFollowRequestsFail(error))); }).catch(error => dispatch(expandFollowRequestsFail(error)));
}; };
}; };
export function expandFollowRequestsRequest() { export function expandFollowRequestsRequest() {
return { return {
type: FOLLOW_REQUESTS_EXPAND_REQUEST, type: FOLLOW_REQUESTS_EXPAND_REQUEST
}; };
}; };
@@ -579,14 +683,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
return { return {
type: FOLLOW_REQUESTS_EXPAND_SUCCESS, type: FOLLOW_REQUESTS_EXPAND_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function expandFollowRequestsFail(error) { export function expandFollowRequestsFail(error) {
return { return {
type: FOLLOW_REQUESTS_EXPAND_FAIL, type: FOLLOW_REQUESTS_EXPAND_FAIL,
error, error
}; };
}; };
@@ -596,7 +700,7 @@ export function authorizeFollowRequest(id) {
api(getState) api(getState)
.post(`/api/v1/follow_requests/${id}/authorize`) .post(`/api/v1/follow_requests/${id}/authorize`)
.then(() => dispatch(authorizeFollowRequestSuccess(id))) .then(response => dispatch(authorizeFollowRequestSuccess(id)))
.catch(error => dispatch(authorizeFollowRequestFail(id, error))); .catch(error => dispatch(authorizeFollowRequestFail(id, error)));
}; };
}; };
@@ -604,14 +708,14 @@ export function authorizeFollowRequest(id) {
export function authorizeFollowRequestRequest(id) { export function authorizeFollowRequestRequest(id) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST, type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
id, id
}; };
}; };
export function authorizeFollowRequestSuccess(id) { export function authorizeFollowRequestSuccess(id) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS, type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
id, id
}; };
}; };
@@ -619,7 +723,7 @@ export function authorizeFollowRequestFail(id, error) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_FAIL, type: FOLLOW_REQUEST_AUTHORIZE_FAIL,
id, id,
error, error
}; };
}; };
@@ -630,7 +734,7 @@ export function rejectFollowRequest(id) {
api(getState) api(getState)
.post(`/api/v1/follow_requests/${id}/reject`) .post(`/api/v1/follow_requests/${id}/reject`)
.then(() => dispatch(rejectFollowRequestSuccess(id))) .then(response => dispatch(rejectFollowRequestSuccess(id)))
.catch(error => dispatch(rejectFollowRequestFail(id, error))); .catch(error => dispatch(rejectFollowRequestFail(id, error)));
}; };
}; };
@@ -638,14 +742,14 @@ export function rejectFollowRequest(id) {
export function rejectFollowRequestRequest(id) { export function rejectFollowRequestRequest(id) {
return { return {
type: FOLLOW_REQUEST_REJECT_REQUEST, type: FOLLOW_REQUEST_REJECT_REQUEST,
id, id
}; };
}; };
export function rejectFollowRequestSuccess(id) { export function rejectFollowRequestSuccess(id) {
return { return {
type: FOLLOW_REQUEST_REJECT_SUCCESS, type: FOLLOW_REQUEST_REJECT_SUCCESS,
id, id
}; };
}; };
@@ -653,6 +757,6 @@ export function rejectFollowRequestFail(id, error) {
return { return {
type: FOLLOW_REQUEST_REJECT_FAIL, type: FOLLOW_REQUEST_REJECT_FAIL,
id, id,
error, error
}; };
}; };

View File

@@ -5,13 +5,13 @@ export const ALERT_CLEAR = 'ALERT_CLEAR';
export function dismissAlert(alert) { export function dismissAlert(alert) {
return { return {
type: ALERT_DISMISS, type: ALERT_DISMISS,
alert, alert
}; };
}; };
export function clearAlert() { export function clearAlert() {
return { return {
type: ALERT_CLEAR, type: ALERT_CLEAR
}; };
}; };
@@ -19,6 +19,6 @@ export function showAlert(title, message) {
return { return {
type: ALERT_SHOW, type: ALERT_SHOW,
title, title,
message, message
}; };
}; };

View File

@@ -1,4 +1,4 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST'; export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST';
@@ -23,7 +23,7 @@ export function fetchBlocks() {
export function fetchBlocksRequest() { export function fetchBlocksRequest() {
return { return {
type: BLOCKS_FETCH_REQUEST, type: BLOCKS_FETCH_REQUEST
}; };
}; };
@@ -31,14 +31,14 @@ export function fetchBlocksSuccess(accounts, next) {
return { return {
type: BLOCKS_FETCH_SUCCESS, type: BLOCKS_FETCH_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function fetchBlocksFail(error) { export function fetchBlocksFail(error) {
return { return {
type: BLOCKS_FETCH_FAIL, type: BLOCKS_FETCH_FAIL,
error, error
}; };
}; };
@@ -62,7 +62,7 @@ export function expandBlocks() {
export function expandBlocksRequest() { export function expandBlocksRequest() {
return { return {
type: BLOCKS_EXPAND_REQUEST, type: BLOCKS_EXPAND_REQUEST
}; };
}; };
@@ -70,13 +70,13 @@ export function expandBlocksSuccess(accounts, next) {
return { return {
type: BLOCKS_EXPAND_SUCCESS, type: BLOCKS_EXPAND_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function expandBlocksFail(error) { export function expandBlocksFail(error) {
return { return {
type: BLOCKS_EXPAND_FAIL, type: BLOCKS_EXPAND_FAIL,
error, error
}; };
}; };

View File

@@ -13,7 +13,7 @@ export function fetchStatusCard(id) {
dispatch(fetchStatusCardRequest(id)); dispatch(fetchStatusCardRequest(id));
api(getState).get(`/api/v1/statuses/${id}/card`).then(response => { api(getState).get(`/api/v1/statuses/${id}/card`).then(response => {
if (!response.data.url) { if (!response.data.url || !response.data.title || !response.data.description) {
return; return;
} }
@@ -28,7 +28,7 @@ export function fetchStatusCardRequest(id) {
return { return {
type: STATUS_CARD_FETCH_REQUEST, type: STATUS_CARD_FETCH_REQUEST,
id, id,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -37,7 +37,7 @@ export function fetchStatusCardSuccess(id, card) {
type: STATUS_CARD_FETCH_SUCCESS, type: STATUS_CARD_FETCH_SUCCESS,
id, id,
card, card,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -47,6 +47,6 @@ export function fetchStatusCardFail(id, error) {
id, id,
error, error,
skipLoading: true, skipLoading: true,
skipAlert: true, skipAlert: true
}; };
}; };

View File

@@ -24,7 +24,6 @@ export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
export const COMPOSE_MOUNT = 'COMPOSE_MOUNT'; export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT'; export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
export const COMPOSE_ADVANCED_OPTIONS_CHANGE = 'COMPOSE_ADVANCED_OPTIONS_CHANGE';
export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE'; export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE'; export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE'; export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
@@ -36,7 +35,7 @@ export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
export function changeCompose(text) { export function changeCompose(text) {
return { return {
type: COMPOSE_CHANGE, type: COMPOSE_CHANGE,
text: text, text: text
}; };
}; };
@@ -44,7 +43,7 @@ export function replyCompose(status, router) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: COMPOSE_REPLY, type: COMPOSE_REPLY,
status: status, status: status
}); });
if (!getState().getIn(['compose', 'mounted'])) { if (!getState().getIn(['compose', 'mounted'])) {
@@ -55,7 +54,7 @@ export function replyCompose(status, router) {
export function cancelReplyCompose() { export function cancelReplyCompose() {
return { return {
type: COMPOSE_REPLY_CANCEL, type: COMPOSE_REPLY_CANCEL
}; };
}; };
@@ -63,7 +62,7 @@ export function mentionCompose(account, router) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: COMPOSE_MENTION, type: COMPOSE_MENTION,
account: account, account: account
}); });
if (!getState().getIn(['compose', 'mounted'])) { if (!getState().getIn(['compose', 'mounted'])) {
@@ -74,25 +73,18 @@ export function mentionCompose(account, router) {
export function submitCompose() { export function submitCompose() {
return function (dispatch, getState) { return function (dispatch, getState) {
let status = emojione.shortnameToUnicode(getState().getIn(['compose', 'text'], '')); const status = emojione.shortnameToUnicode(getState().getIn(['compose', 'text'], ''));
if (!status || !status.length) { if (!status || !status.length) {
return; return;
} }
dispatch(submitComposeRequest()); dispatch(submitComposeRequest());
if (getState().getIn(['compose', 'advanced_options', 'do_not_federate'])) {
status = status + ' 👁️';
}
api(getState).post('/api/v1/statuses', { api(getState).post('/api/v1/statuses', {
status, status,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')), media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
sensitive: getState().getIn(['compose', 'sensitive']), sensitive: getState().getIn(['compose', 'sensitive']),
spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''), spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
visibility: getState().getIn(['compose', 'privacy']), visibility: getState().getIn(['compose', 'privacy'])
}, {
headers: {
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
},
}).then(function (response) { }).then(function (response) {
dispatch(submitComposeSuccess({ ...response.data })); dispatch(submitComposeSuccess({ ...response.data }));
@@ -116,21 +108,21 @@ export function submitCompose() {
export function submitComposeRequest() { export function submitComposeRequest() {
return { return {
type: COMPOSE_SUBMIT_REQUEST, type: COMPOSE_SUBMIT_REQUEST
}; };
}; };
export function submitComposeSuccess(status) { export function submitComposeSuccess(status) {
return { return {
type: COMPOSE_SUBMIT_SUCCESS, type: COMPOSE_SUBMIT_SUCCESS,
status: status, status: status
}; };
}; };
export function submitComposeFail(error) { export function submitComposeFail(error) {
return { return {
type: COMPOSE_SUBMIT_FAIL, type: COMPOSE_SUBMIT_FAIL,
error: error, error: error
}; };
}; };
@@ -148,7 +140,7 @@ export function uploadCompose(files) {
api(getState).post('/api/v1/media', data, { api(getState).post('/api/v1/media', data, {
onUploadProgress: function (e) { onUploadProgress: function (e) {
dispatch(uploadComposeProgress(e.loaded, e.total)); dispatch(uploadComposeProgress(e.loaded, e.total));
}, }
}).then(function (response) { }).then(function (response) {
dispatch(uploadComposeSuccess(response.data)); dispatch(uploadComposeSuccess(response.data));
}).catch(function (error) { }).catch(function (error) {
@@ -160,7 +152,7 @@ export function uploadCompose(files) {
export function uploadComposeRequest() { export function uploadComposeRequest() {
return { return {
type: COMPOSE_UPLOAD_REQUEST, type: COMPOSE_UPLOAD_REQUEST,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -168,7 +160,7 @@ export function uploadComposeProgress(loaded, total) {
return { return {
type: COMPOSE_UPLOAD_PROGRESS, type: COMPOSE_UPLOAD_PROGRESS,
loaded: loaded, loaded: loaded,
total: total, total: total
}; };
}; };
@@ -176,7 +168,7 @@ export function uploadComposeSuccess(media) {
return { return {
type: COMPOSE_UPLOAD_SUCCESS, type: COMPOSE_UPLOAD_SUCCESS,
media: media, media: media,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -184,20 +176,20 @@ export function uploadComposeFail(error) {
return { return {
type: COMPOSE_UPLOAD_FAIL, type: COMPOSE_UPLOAD_FAIL,
error: error, error: error,
skipLoading: true, skipLoading: true
}; };
}; };
export function undoUploadCompose(media_id) { export function undoUploadCompose(media_id) {
return { return {
type: COMPOSE_UPLOAD_UNDO, type: COMPOSE_UPLOAD_UNDO,
media_id: media_id, media_id: media_id
}; };
}; };
export function clearComposeSuggestions() { export function clearComposeSuggestions() {
return { return {
type: COMPOSE_SUGGESTIONS_CLEAR, type: COMPOSE_SUGGESTIONS_CLEAR
}; };
}; };
@@ -207,8 +199,8 @@ export function fetchComposeSuggestions(token) {
params: { params: {
q: token, q: token,
resolve: false, resolve: false,
limit: 4, limit: 4
}, }
}).then(response => { }).then(response => {
dispatch(readyComposeSuggestions(token, response.data)); dispatch(readyComposeSuggestions(token, response.data));
}); });
@@ -219,7 +211,7 @@ export function readyComposeSuggestions(token, accounts) {
return { return {
type: COMPOSE_SUGGESTIONS_READY, type: COMPOSE_SUGGESTIONS_READY,
token, token,
accounts, accounts
}; };
}; };
@@ -231,30 +223,23 @@ export function selectComposeSuggestion(position, token, accountId) {
type: COMPOSE_SUGGESTION_SELECT, type: COMPOSE_SUGGESTION_SELECT,
position, position,
token, token,
completion, completion
}); });
}; };
}; };
export function mountCompose() { export function mountCompose() {
return { return {
type: COMPOSE_MOUNT, type: COMPOSE_MOUNT
}; };
}; };
export function unmountCompose() { export function unmountCompose() {
return { return {
type: COMPOSE_UNMOUNT, type: COMPOSE_UNMOUNT
}; };
}; };
export function changeComposeAdvancedOption(option) {
return {
type: COMPOSE_ADVANCED_OPTIONS_CHANGE,
option: option,
};
}
export function changeComposeSensitivity() { export function changeComposeSensitivity() {
return { return {
type: COMPOSE_SENSITIVITY_CHANGE, type: COMPOSE_SENSITIVITY_CHANGE,
@@ -263,21 +248,21 @@ export function changeComposeSensitivity() {
export function changeComposeSpoilerness() { export function changeComposeSpoilerness() {
return { return {
type: COMPOSE_SPOILERNESS_CHANGE, type: COMPOSE_SPOILERNESS_CHANGE
}; };
}; };
export function changeComposeSpoilerText(text) { export function changeComposeSpoilerText(text) {
return { return {
type: COMPOSE_SPOILER_TEXT_CHANGE, type: COMPOSE_SPOILER_TEXT_CHANGE,
text, text
}; };
}; };
export function changeComposeVisibility(value) { export function changeComposeVisibility(value) {
return { return {
type: COMPOSE_VISIBILITY_CHANGE, type: COMPOSE_VISIBILITY_CHANGE,
value, value
}; };
}; };
@@ -285,6 +270,6 @@ export function insertEmojiCompose(position, emoji) {
return { return {
type: COMPOSE_EMOJI_INSERT, type: COMPOSE_EMOJI_INSERT,
position, position,
emoji, emoji
}; };
}; };

View File

@@ -1,4 +1,4 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST'; export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS'; export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
@@ -23,7 +23,7 @@ export function fetchFavouritedStatuses() {
export function fetchFavouritedStatusesRequest() { export function fetchFavouritedStatusesRequest() {
return { return {
type: FAVOURITED_STATUSES_FETCH_REQUEST, type: FAVOURITED_STATUSES_FETCH_REQUEST
}; };
}; };
@@ -31,14 +31,14 @@ export function fetchFavouritedStatusesSuccess(statuses, next) {
return { return {
type: FAVOURITED_STATUSES_FETCH_SUCCESS, type: FAVOURITED_STATUSES_FETCH_SUCCESS,
statuses, statuses,
next, next
}; };
}; };
export function fetchFavouritedStatusesFail(error) { export function fetchFavouritedStatusesFail(error) {
return { return {
type: FAVOURITED_STATUSES_FETCH_FAIL, type: FAVOURITED_STATUSES_FETCH_FAIL,
error, error
}; };
}; };
@@ -63,7 +63,7 @@ export function expandFavouritedStatuses() {
export function expandFavouritedStatusesRequest() { export function expandFavouritedStatusesRequest() {
return { return {
type: FAVOURITED_STATUSES_EXPAND_REQUEST, type: FAVOURITED_STATUSES_EXPAND_REQUEST
}; };
}; };
@@ -71,13 +71,13 @@ export function expandFavouritedStatusesSuccess(statuses, next) {
return { return {
type: FAVOURITED_STATUSES_EXPAND_SUCCESS, type: FAVOURITED_STATUSES_EXPAND_SUCCESS,
statuses, statuses,
next, next
}; };
}; };
export function expandFavouritedStatusesFail(error) { export function expandFavouritedStatusesFail(error) {
return { return {
type: FAVOURITED_STATUSES_EXPAND_FAIL, type: FAVOURITED_STATUSES_EXPAND_FAIL,
error, error
}; };
}; };

View File

@@ -1,4 +1,4 @@
import api from '../api'; import api from '../api'
export const REBLOG_REQUEST = 'REBLOG_REQUEST'; export const REBLOG_REQUEST = 'REBLOG_REQUEST';
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS'; export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
@@ -53,7 +53,7 @@ export function unreblog(status) {
export function reblogRequest(status) { export function reblogRequest(status) {
return { return {
type: REBLOG_REQUEST, type: REBLOG_REQUEST,
status: status, status: status
}; };
}; };
@@ -61,7 +61,7 @@ export function reblogSuccess(status, response) {
return { return {
type: REBLOG_SUCCESS, type: REBLOG_SUCCESS,
status: status, status: status,
response: response, response: response
}; };
}; };
@@ -69,14 +69,14 @@ export function reblogFail(status, error) {
return { return {
type: REBLOG_FAIL, type: REBLOG_FAIL,
status: status, status: status,
error: error, error: error
}; };
}; };
export function unreblogRequest(status) { export function unreblogRequest(status) {
return { return {
type: UNREBLOG_REQUEST, type: UNREBLOG_REQUEST,
status: status, status: status
}; };
}; };
@@ -84,7 +84,7 @@ export function unreblogSuccess(status, response) {
return { return {
type: UNREBLOG_SUCCESS, type: UNREBLOG_SUCCESS,
status: status, status: status,
response: response, response: response
}; };
}; };
@@ -92,7 +92,7 @@ export function unreblogFail(status, error) {
return { return {
type: UNREBLOG_FAIL, type: UNREBLOG_FAIL,
status: status, status: status,
error: error, error: error
}; };
}; };
@@ -123,7 +123,7 @@ export function unfavourite(status) {
export function favouriteRequest(status) { export function favouriteRequest(status) {
return { return {
type: FAVOURITE_REQUEST, type: FAVOURITE_REQUEST,
status: status, status: status
}; };
}; };
@@ -131,7 +131,7 @@ export function favouriteSuccess(status, response) {
return { return {
type: FAVOURITE_SUCCESS, type: FAVOURITE_SUCCESS,
status: status, status: status,
response: response, response: response
}; };
}; };
@@ -139,14 +139,14 @@ export function favouriteFail(status, error) {
return { return {
type: FAVOURITE_FAIL, type: FAVOURITE_FAIL,
status: status, status: status,
error: error, error: error
}; };
}; };
export function unfavouriteRequest(status) { export function unfavouriteRequest(status) {
return { return {
type: UNFAVOURITE_REQUEST, type: UNFAVOURITE_REQUEST,
status: status, status: status
}; };
}; };
@@ -154,7 +154,7 @@ export function unfavouriteSuccess(status, response) {
return { return {
type: UNFAVOURITE_SUCCESS, type: UNFAVOURITE_SUCCESS,
status: status, status: status,
response: response, response: response
}; };
}; };
@@ -162,7 +162,7 @@ export function unfavouriteFail(status, error) {
return { return {
type: UNFAVOURITE_FAIL, type: UNFAVOURITE_FAIL,
status: status, status: status,
error: error, error: error
}; };
}; };
@@ -181,7 +181,7 @@ export function fetchReblogs(id) {
export function fetchReblogsRequest(id) { export function fetchReblogsRequest(id) {
return { return {
type: REBLOGS_FETCH_REQUEST, type: REBLOGS_FETCH_REQUEST,
id, id
}; };
}; };
@@ -189,14 +189,14 @@ export function fetchReblogsSuccess(id, accounts) {
return { return {
type: REBLOGS_FETCH_SUCCESS, type: REBLOGS_FETCH_SUCCESS,
id, id,
accounts, accounts
}; };
}; };
export function fetchReblogsFail(id, error) { export function fetchReblogsFail(id, error) {
return { return {
type: REBLOGS_FETCH_FAIL, type: REBLOGS_FETCH_FAIL,
error, error
}; };
}; };
@@ -215,7 +215,7 @@ export function fetchFavourites(id) {
export function fetchFavouritesRequest(id) { export function fetchFavouritesRequest(id) {
return { return {
type: FAVOURITES_FETCH_REQUEST, type: FAVOURITES_FETCH_REQUEST,
id, id
}; };
}; };
@@ -223,13 +223,13 @@ export function fetchFavouritesSuccess(id, accounts) {
return { return {
type: FAVOURITES_FETCH_SUCCESS, type: FAVOURITES_FETCH_SUCCESS,
id, id,
accounts, accounts
}; };
}; };
export function fetchFavouritesFail(id, error) { export function fetchFavouritesFail(id, error) {
return { return {
type: FAVOURITES_FETCH_FAIL, type: FAVOURITES_FETCH_FAIL,
error, error
}; };
}; };

View File

@@ -5,12 +5,12 @@ export function openModal(type, props) {
return { return {
type: MODAL_OPEN, type: MODAL_OPEN,
modalType: type, modalType: type,
modalProps: props, modalProps: props
}; };
}; };
export function closeModal() { export function closeModal() {
return { return {
type: MODAL_CLOSE, type: MODAL_CLOSE
}; };
}; };

View File

@@ -1,4 +1,4 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST'; export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
@@ -23,7 +23,7 @@ export function fetchMutes() {
export function fetchMutesRequest() { export function fetchMutesRequest() {
return { return {
type: MUTES_FETCH_REQUEST, type: MUTES_FETCH_REQUEST
}; };
}; };
@@ -31,14 +31,14 @@ export function fetchMutesSuccess(accounts, next) {
return { return {
type: MUTES_FETCH_SUCCESS, type: MUTES_FETCH_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function fetchMutesFail(error) { export function fetchMutesFail(error) {
return { return {
type: MUTES_FETCH_FAIL, type: MUTES_FETCH_FAIL,
error, error
}; };
}; };
@@ -62,7 +62,7 @@ export function expandMutes() {
export function expandMutesRequest() { export function expandMutesRequest() {
return { return {
type: MUTES_EXPAND_REQUEST, type: MUTES_EXPAND_REQUEST
}; };
}; };
@@ -70,13 +70,13 @@ export function expandMutesSuccess(accounts, next) {
return { return {
type: MUTES_EXPAND_SUCCESS, type: MUTES_EXPAND_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function expandMutesFail(error) { export function expandMutesFail(error) {
return { return {
type: MUTES_EXPAND_FAIL, type: MUTES_EXPAND_FAIL,
error, error
}; };
}; };

View File

@@ -1,8 +1,8 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
import { List as ImmutableList } from 'immutable'; import Immutable from 'immutable';
import IntlMessageFormat from 'intl-messageformat'; import IntlMessageFormat from 'intl-messageformat';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { defineMessages } from 'react-intl';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
@@ -17,10 +17,6 @@ export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
defineMessages({
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
});
const fetchRelatedRelationships = (dispatch, notifications) => { const fetchRelatedRelationships = (dispatch, notifications) => {
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id); const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
@@ -29,12 +25,6 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
} }
}; };
const unescapeHTML = (html) => {
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
return wrapper.textContent;
};
export function updateNotifications(notification, intlMessages, intlLocale) { export function updateNotifications(notification, intlMessages, intlLocale) {
return (dispatch, getState) => { return (dispatch, getState) => {
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
@@ -45,7 +35,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
notification, notification,
account: notification.account, account: notification.account,
status: notification.status, status: notification.status,
meta: playSound ? { sound: 'boop' } : undefined, meta: playSound ? { sound: 'boop' } : undefined
}); });
fetchRelatedRelationships(dispatch, [notification]); fetchRelatedRelationships(dispatch, [notification]);
@@ -53,13 +43,9 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
// Desktop notifications // Desktop notifications
if (typeof window.Notification !== 'undefined' && showAlert) { if (typeof window.Notification !== 'undefined' && showAlert) {
const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username }); const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : ''); const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : $('<p>').html(notification.status ? notification.status.content : '').text();
const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id }); new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
notify.addEventListener('click', () => {
window.focus();
notify.close();
});
} }
}; };
}; };
@@ -68,78 +54,68 @@ const excludeTypesFromSettings = state => state.getIn(['settings', 'notification
export function refreshNotifications() { export function refreshNotifications() {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(refreshNotificationsRequest());
const params = {}; const params = {};
const ids = getState().getIn(['notifications', 'items']); const ids = getState().getIn(['notifications', 'items']);
let skipLoading = false;
if (ids.size > 0) { if (ids.size > 0) {
params.since_id = ids.first().get('id'); params.since_id = ids.first().get('id');
} }
if (getState().getIn(['notifications', 'loaded'])) {
skipLoading = true;
}
params.exclude_types = excludeTypesFromSettings(getState()); params.exclude_types = excludeTypesFromSettings(getState());
dispatch(refreshNotificationsRequest(skipLoading));
api(getState).get('/api/v1/notifications', { params }).then(response => { api(getState).get('/api/v1/notifications', { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(refreshNotificationsSuccess(response.data, skipLoading, next ? next.uri : null)); dispatch(refreshNotificationsSuccess(response.data, next ? next.uri : null));
fetchRelatedRelationships(dispatch, response.data); fetchRelatedRelationships(dispatch, response.data);
}).catch(error => { }).catch(error => {
dispatch(refreshNotificationsFail(error, skipLoading)); dispatch(refreshNotificationsFail(error));
}); });
}; };
}; };
export function refreshNotificationsRequest(skipLoading) { export function refreshNotificationsRequest() {
return { return {
type: NOTIFICATIONS_REFRESH_REQUEST, type: NOTIFICATIONS_REFRESH_REQUEST
skipLoading,
}; };
}; };
export function refreshNotificationsSuccess(notifications, skipLoading, next) { export function refreshNotificationsSuccess(notifications, next) {
return { return {
type: NOTIFICATIONS_REFRESH_SUCCESS, type: NOTIFICATIONS_REFRESH_SUCCESS,
notifications, notifications,
accounts: notifications.map(item => item.account), accounts: notifications.map(item => item.account),
statuses: notifications.map(item => item.status).filter(status => !!status), statuses: notifications.map(item => item.status).filter(status => !!status),
skipLoading, next
next,
}; };
}; };
export function refreshNotificationsFail(error, skipLoading) { export function refreshNotificationsFail(error) {
return { return {
type: NOTIFICATIONS_REFRESH_FAIL, type: NOTIFICATIONS_REFRESH_FAIL,
error, error
skipLoading,
}; };
}; };
export function expandNotifications() { export function expandNotifications() {
return (dispatch, getState) => { return (dispatch, getState) => {
const items = getState().getIn(['notifications', 'items'], ImmutableList()); const url = getState().getIn(['notifications', 'next'], null);
if (getState().getIn(['notifications', 'isLoading']) || items.size === 0) { if (url === null || getState().getIn(['notifications', 'isLoading'])) {
return; return;
} }
const params = {
max_id: items.last().get('id'),
limit: 20,
exclude_types: excludeTypesFromSettings(getState()),
};
dispatch(expandNotificationsRequest()); dispatch(expandNotificationsRequest());
api(getState).get('/api/v1/notifications', { params }).then(response => { const params = {};
params.exclude_types = excludeTypesFromSettings(getState());
api(getState).get(url, params).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null)); dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null));
fetchRelatedRelationships(dispatch, response.data); fetchRelatedRelationships(dispatch, response.data);
}).catch(error => { }).catch(error => {
@@ -150,7 +126,7 @@ export function expandNotifications() {
export function expandNotificationsRequest() { export function expandNotificationsRequest() {
return { return {
type: NOTIFICATIONS_EXPAND_REQUEST, type: NOTIFICATIONS_EXPAND_REQUEST
}; };
}; };
@@ -160,21 +136,21 @@ export function expandNotificationsSuccess(notifications, next) {
notifications, notifications,
accounts: notifications.map(item => item.account), accounts: notifications.map(item => item.account),
statuses: notifications.map(item => item.status).filter(status => !!status), statuses: notifications.map(item => item.status).filter(status => !!status),
next, next
}; };
}; };
export function expandNotificationsFail(error) { export function expandNotificationsFail(error) {
return { return {
type: NOTIFICATIONS_EXPAND_FAIL, type: NOTIFICATIONS_EXPAND_FAIL,
error, error
}; };
}; };
export function clearNotifications() { export function clearNotifications() {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: NOTIFICATIONS_CLEAR, type: NOTIFICATIONS_CLEAR
}); });
api(getState).post('/api/v1/notifications/clear'); api(getState).post('/api/v1/notifications/clear');
@@ -184,6 +160,6 @@ export function clearNotifications() {
export function scrollTopNotifications(top) { export function scrollTopNotifications(top) {
return { return {
type: NOTIFICATIONS_SCROLL_TOP, type: NOTIFICATIONS_SCROLL_TOP,
top, top
}; };
}; };

View File

@@ -1,5 +1,4 @@
import api from '../api'; import api from '../api';
import { openModal, closeModal } from './modal';
export const REPORT_INIT = 'REPORT_INIT'; export const REPORT_INIT = 'REPORT_INIT';
export const REPORT_CANCEL = 'REPORT_CANCEL'; export const REPORT_CANCEL = 'REPORT_CANCEL';
@@ -12,20 +11,16 @@ export const REPORT_STATUS_TOGGLE = 'REPORT_STATUS_TOGGLE';
export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE'; export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE';
export function initReport(account, status) { export function initReport(account, status) {
return dispatch => { return {
dispatch({ type: REPORT_INIT,
type: REPORT_INIT, account,
account, status
status,
});
dispatch(openModal('REPORT'));
}; };
}; };
export function cancelReport() { export function cancelReport() {
return { return {
type: REPORT_CANCEL, type: REPORT_CANCEL
}; };
}; };
@@ -44,37 +39,34 @@ export function submitReport() {
api(getState).post('/api/v1/reports', { api(getState).post('/api/v1/reports', {
account_id: getState().getIn(['reports', 'new', 'account_id']), account_id: getState().getIn(['reports', 'new', 'account_id']),
status_ids: getState().getIn(['reports', 'new', 'status_ids']), status_ids: getState().getIn(['reports', 'new', 'status_ids']),
comment: getState().getIn(['reports', 'new', 'comment']), comment: getState().getIn(['reports', 'new', 'comment'])
}).then(response => { }).then(response => dispatch(submitReportSuccess(response.data))).catch(error => dispatch(submitReportFail(error)));
dispatch(closeModal());
dispatch(submitReportSuccess(response.data));
}).catch(error => dispatch(submitReportFail(error)));
}; };
}; };
export function submitReportRequest() { export function submitReportRequest() {
return { return {
type: REPORT_SUBMIT_REQUEST, type: REPORT_SUBMIT_REQUEST
}; };
}; };
export function submitReportSuccess(report) { export function submitReportSuccess(report) {
return { return {
type: REPORT_SUBMIT_SUCCESS, type: REPORT_SUBMIT_SUCCESS,
report, report
}; };
}; };
export function submitReportFail(error) { export function submitReportFail(error) {
return { return {
type: REPORT_SUBMIT_FAIL, type: REPORT_SUBMIT_FAIL,
error, error
}; };
}; };
export function changeReportComment(comment) { export function changeReportComment(comment) {
return { return {
type: REPORT_COMMENT_CHANGE, type: REPORT_COMMENT_CHANGE,
comment, comment
}; };
}; };

View File

@@ -1,4 +1,4 @@
import api from '../api'; import api from '../api'
export const SEARCH_CHANGE = 'SEARCH_CHANGE'; export const SEARCH_CHANGE = 'SEARCH_CHANGE';
export const SEARCH_CLEAR = 'SEARCH_CLEAR'; export const SEARCH_CLEAR = 'SEARCH_CLEAR';
@@ -11,13 +11,13 @@ export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL';
export function changeSearch(value) { export function changeSearch(value) {
return { return {
type: SEARCH_CHANGE, type: SEARCH_CHANGE,
value, value
}; };
}; };
export function clearSearch() { export function clearSearch() {
return { return {
type: SEARCH_CLEAR, type: SEARCH_CLEAR
}; };
}; };
@@ -34,8 +34,8 @@ export function submitSearch() {
api(getState).get('/api/v1/search', { api(getState).get('/api/v1/search', {
params: { params: {
q: value, q: value,
resolve: true, resolve: true
}, }
}).then(response => { }).then(response => {
dispatch(fetchSearchSuccess(response.data)); dispatch(fetchSearchSuccess(response.data));
}).catch(error => { }).catch(error => {
@@ -46,7 +46,7 @@ export function submitSearch() {
export function fetchSearchRequest() { export function fetchSearchRequest() {
return { return {
type: SEARCH_FETCH_REQUEST, type: SEARCH_FETCH_REQUEST
}; };
}; };
@@ -55,19 +55,19 @@ export function fetchSearchSuccess(results) {
type: SEARCH_FETCH_SUCCESS, type: SEARCH_FETCH_SUCCESS,
results, results,
accounts: results.accounts, accounts: results.accounts,
statuses: results.statuses, statuses: results.statuses
}; };
}; };
export function fetchSearchFail(error) { export function fetchSearchFail(error) {
return { return {
type: SEARCH_FETCH_FAIL, type: SEARCH_FETCH_FAIL,
error, error
}; };
}; };
export function showSearch() { export function showSearch() {
return { return {
type: SEARCH_SHOW, type: SEARCH_SHOW
}; };
}; };

View File

@@ -3,21 +3,17 @@ import axios from 'axios';
export const SETTING_CHANGE = 'SETTING_CHANGE'; export const SETTING_CHANGE = 'SETTING_CHANGE';
export function changeSetting(key, value) { export function changeSetting(key, value) {
return dispatch => { return {
dispatch({ type: SETTING_CHANGE,
type: SETTING_CHANGE, key,
key, value
value,
});
dispatch(saveSettings());
}; };
}; };
export function saveSettings() { export function saveSettings() {
return (_, getState) => { return (_, getState) => {
axios.put('/api/web/settings', { axios.put('/api/web/settings', {
data: getState().get('settings').toJS(), data: getState().get('settings').toJS()
}); });
}; };
}; };

View File

@@ -15,19 +15,11 @@ export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS'; export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL'; export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL';
export const STATUS_MUTE_REQUEST = 'STATUS_MUTE_REQUEST';
export const STATUS_MUTE_SUCCESS = 'STATUS_MUTE_SUCCESS';
export const STATUS_MUTE_FAIL = 'STATUS_MUTE_FAIL';
export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL';
export function fetchStatusRequest(id, skipLoading) { export function fetchStatusRequest(id, skipLoading) {
return { return {
type: STATUS_FETCH_REQUEST, type: STATUS_FETCH_REQUEST,
id, id,
skipLoading, skipLoading
}; };
}; };
@@ -56,7 +48,7 @@ export function fetchStatusSuccess(status, skipLoading) {
return { return {
type: STATUS_FETCH_SUCCESS, type: STATUS_FETCH_SUCCESS,
status, status,
skipLoading, skipLoading
}; };
}; };
@@ -66,7 +58,7 @@ export function fetchStatusFail(id, error, skipLoading) {
id, id,
error, error,
skipLoading, skipLoading,
skipAlert: true, skipAlert: true
}; };
}; };
@@ -74,7 +66,7 @@ export function deleteStatus(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(deleteStatusRequest(id)); dispatch(deleteStatusRequest(id));
api(getState).delete(`/api/v1/statuses/${id}`).then(() => { api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
dispatch(deleteStatusSuccess(id)); dispatch(deleteStatusSuccess(id));
dispatch(deleteFromTimelines(id)); dispatch(deleteFromTimelines(id));
}).catch(error => { }).catch(error => {
@@ -86,14 +78,14 @@ export function deleteStatus(id) {
export function deleteStatusRequest(id) { export function deleteStatusRequest(id) {
return { return {
type: STATUS_DELETE_REQUEST, type: STATUS_DELETE_REQUEST,
id: id, id: id
}; };
}; };
export function deleteStatusSuccess(id) { export function deleteStatusSuccess(id) {
return { return {
type: STATUS_DELETE_SUCCESS, type: STATUS_DELETE_SUCCESS,
id: id, id: id
}; };
}; };
@@ -101,7 +93,7 @@ export function deleteStatusFail(id, error) {
return { return {
type: STATUS_DELETE_FAIL, type: STATUS_DELETE_FAIL,
id: id, id: id,
error: error, error: error
}; };
}; };
@@ -125,7 +117,7 @@ export function fetchContext(id) {
export function fetchContextRequest(id) { export function fetchContextRequest(id) {
return { return {
type: CONTEXT_FETCH_REQUEST, type: CONTEXT_FETCH_REQUEST,
id, id
}; };
}; };
@@ -135,7 +127,7 @@ export function fetchContextSuccess(id, ancestors, descendants) {
id, id,
ancestors, ancestors,
descendants, descendants,
statuses: ancestors.concat(descendants), statuses: ancestors.concat(descendants)
}; };
}; };
@@ -144,74 +136,6 @@ export function fetchContextFail(id, error) {
type: CONTEXT_FETCH_FAIL, type: CONTEXT_FETCH_FAIL,
id, id,
error, error,
skipAlert: true, skipAlert: true
};
};
export function muteStatus(id) {
return (dispatch, getState) => {
dispatch(muteStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/mute`).then(() => {
dispatch(muteStatusSuccess(id));
}).catch(error => {
dispatch(muteStatusFail(id, error));
});
};
};
export function muteStatusRequest(id) {
return {
type: STATUS_MUTE_REQUEST,
id,
};
};
export function muteStatusSuccess(id) {
return {
type: STATUS_MUTE_SUCCESS,
id,
};
};
export function muteStatusFail(id, error) {
return {
type: STATUS_MUTE_FAIL,
id,
error,
};
};
export function unmuteStatus(id) {
return (dispatch, getState) => {
dispatch(unmuteStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => {
dispatch(unmuteStatusSuccess(id));
}).catch(error => {
dispatch(unmuteStatusFail(id, error));
});
};
};
export function unmuteStatusRequest(id) {
return {
type: STATUS_UNMUTE_REQUEST,
id,
};
};
export function unmuteStatusSuccess(id) {
return {
type: STATUS_UNMUTE_SUCCESS,
id,
};
};
export function unmuteStatusFail(id, error) {
return {
type: STATUS_UNMUTE_FAIL,
id,
error,
}; };
}; };

View File

@@ -1,11 +1,10 @@
import { Iterable, fromJS } from 'immutable'; import Immutable from 'immutable';
export const STORE_HYDRATE = 'STORE_HYDRATE'; export const STORE_HYDRATE = 'STORE_HYDRATE';
export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY';
const convertState = rawState => const convertState = rawState =>
fromJS(rawState, (k, v) => Immutable.fromJS(rawState, (k, v) =>
Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x => Immutable.Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x =>
Number.isNaN(x * 1) ? x : x * 1)); Number.isNaN(x * 1) ? x : x * 1));
export function hydrateStore(rawState) { export function hydrateStore(rawState) {
@@ -13,6 +12,6 @@ export function hydrateStore(rawState) {
return { return {
type: STORE_HYDRATE, type: STORE_HYDRATE,
state, state
}; };
}; };

View File

@@ -1,5 +1,5 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import Immutable from 'immutable';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE'; export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE'; export const TIMELINE_DELETE = 'TIMELINE_DELETE';
@@ -23,7 +23,7 @@ export function refreshTimelineSuccess(timeline, statuses, skipLoading, next) {
timeline, timeline,
statuses, statuses,
skipLoading, skipLoading,
next, next
}; };
}; };
@@ -35,7 +35,7 @@ export function updateTimeline(timeline, status) {
type: TIMELINE_UPDATE, type: TIMELINE_UPDATE,
timeline, timeline,
status, status,
references, references
}); });
}; };
}; };
@@ -51,98 +51,98 @@ export function deleteFromTimelines(id) {
id, id,
accountId, accountId,
references, references,
reblogOf, reblogOf
}); });
}; };
}; };
export function refreshTimelineRequest(timeline, skipLoading) { export function refreshTimelineRequest(timeline, id, skipLoading) {
return { return {
type: TIMELINE_REFRESH_REQUEST, type: TIMELINE_REFRESH_REQUEST,
timeline, timeline,
skipLoading, id,
skipLoading
}; };
}; };
export function refreshTimeline(timelineId, path, params = {}) { export function refreshTimeline(timeline, id = null) {
return function (dispatch, getState) { return function (dispatch, getState) {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); if (getState().getIn(['timelines', timeline, 'isLoading'])) {
if (timeline.get('isLoading') || timeline.get('online')) {
return; return;
} }
const ids = timeline.get('items', ImmutableList()); const ids = getState().getIn(['timelines', timeline, 'items'], Immutable.List());
const newestId = ids.size > 0 ? ids.first() : null; const newestId = ids.size > 0 ? ids.first() : null;
let params = getState().getIn(['timelines', timeline, 'params'], {});
const path = getState().getIn(['timelines', timeline, 'path'])(id);
let skipLoading = timeline.get('loaded'); let skipLoading = false;
if (newestId !== null) { if (newestId !== null && getState().getIn(['timelines', timeline, 'loaded']) && (id === null || getState().getIn(['timelines', timeline, 'id']) === id)) {
params.since_id = newestId; if (id === null && getState().getIn(['timelines', timeline, 'online'])) {
// Skip refreshing when timeline is live anyway
return;
}
params = { ...params, since_id: newestId };
skipLoading = true;
} }
dispatch(refreshTimelineRequest(timelineId, skipLoading)); dispatch(refreshTimelineRequest(timeline, id, skipLoading));
api(getState).get(path, { params }).then(response => { api(getState).get(path, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null)); dispatch(refreshTimelineSuccess(timeline, response.data, skipLoading, next ? next.uri : null));
}).catch(error => { }).catch(error => {
dispatch(refreshTimelineFail(timelineId, error, skipLoading)); dispatch(refreshTimelineFail(timeline, error, skipLoading));
}); });
}; };
}; };
export const refreshHomeTimeline = () => refreshTimeline('home', '/api/v1/timelines/home');
export const refreshPublicTimeline = () => refreshTimeline('public', '/api/v1/timelines/public');
export const refreshCommunityTimeline = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true });
export const refreshAccountTimeline = accountId => refreshTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
export const refreshHashtagTimeline = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
export function refreshTimelineFail(timeline, error, skipLoading) { export function refreshTimelineFail(timeline, error, skipLoading) {
return { return {
type: TIMELINE_REFRESH_FAIL, type: TIMELINE_REFRESH_FAIL,
timeline, timeline,
error, error,
skipLoading, skipLoading
skipAlert: error.response.status === 404,
}; };
}; };
export function expandTimeline(timelineId, path, params = {}) { export function expandTimeline(timeline) {
return (dispatch, getState) => { return (dispatch, getState) => {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); if (getState().getIn(['timelines', timeline, 'isLoading'])) {
const ids = timeline.get('items', ImmutableList());
if (timeline.get('isLoading') || ids.size === 0) {
return; return;
} }
params.max_id = ids.last(); if (getState().getIn(['timelines', timeline, 'items']).size === 0) {
params.limit = 10; return;
}
dispatch(expandTimelineRequest(timelineId)); const path = getState().getIn(['timelines', timeline, 'path'])(getState().getIn(['timelines', timeline, 'id']));
const params = getState().getIn(['timelines', timeline, 'params'], {});
const lastId = getState().getIn(['timelines', timeline, 'items']).last();
api(getState).get(path, { params }).then(response => { dispatch(expandTimelineRequest(timeline));
api(getState).get(path, {
params: {
...params,
max_id: lastId,
limit: 10
}
}).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null)); dispatch(expandTimelineSuccess(timeline, response.data, next ? next.uri : null));
}).catch(error => { }).catch(error => {
dispatch(expandTimelineFail(timelineId, error)); dispatch(expandTimelineFail(timeline, error));
}); });
}; };
}; };
export const expandHomeTimeline = () => expandTimeline('home', '/api/v1/timelines/home');
export const expandPublicTimeline = () => expandTimeline('public', '/api/v1/timelines/public');
export const expandCommunityTimeline = () => expandTimeline('community', '/api/v1/timelines/public', { local: true });
export const expandAccountTimeline = accountId => expandTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
export const expandAccountMediaTimeline = accountId => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
export const expandHashtagTimeline = hashtag => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
export function expandTimelineRequest(timeline) { export function expandTimelineRequest(timeline) {
return { return {
type: TIMELINE_EXPAND_REQUEST, type: TIMELINE_EXPAND_REQUEST,
timeline, timeline
}; };
}; };
@@ -151,7 +151,7 @@ export function expandTimelineSuccess(timeline, statuses, next) {
type: TIMELINE_EXPAND_SUCCESS, type: TIMELINE_EXPAND_SUCCESS,
timeline, timeline,
statuses, statuses,
next, next
}; };
}; };
@@ -159,7 +159,7 @@ export function expandTimelineFail(timeline, error) {
return { return {
type: TIMELINE_EXPAND_FAIL, type: TIMELINE_EXPAND_FAIL,
timeline, timeline,
error, error
}; };
}; };
@@ -167,20 +167,20 @@ export function scrollTopTimeline(timeline, top) {
return { return {
type: TIMELINE_SCROLL_TOP, type: TIMELINE_SCROLL_TOP,
timeline, timeline,
top, top
}; };
}; };
export function connectTimeline(timeline) { export function connectTimeline(timeline) {
return { return {
type: TIMELINE_CONNECT, type: TIMELINE_CONNECT,
timeline, timeline
}; };
}; };
export function disconnectTimeline(timeline) { export function disconnectTimeline(timeline) {
return { return {
type: TIMELINE_DISCONNECT, type: TIMELINE_DISCONNECT,
timeline, timeline
}; };
}; };

View File

@@ -13,7 +13,7 @@ export const getLinks = response => {
export default getState => axios.create({ export default getState => axios.create({
headers: { headers: {
'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`, 'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`
}, },
transformResponse: [function (data) { transformResponse: [function (data) {
@@ -22,5 +22,5 @@ export default getState => axios.create({
} catch(Exception) { } catch(Exception) {
return data; return data;
} }
}], }]
}); });

View File

@@ -1,44 +1,48 @@
import React from 'react'; import PureRenderMixin from 'react-addons-pure-render-mixin';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import Avatar from './avatar'; import Avatar from './avatar';
import DisplayName from './display_name'; import DisplayName from './display_name';
import Permalink from './permalink'; import Permalink from './permalink';
import IconButton from './icon_button'; import IconButton from './icon_button';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
const messages = defineMessages({ const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' }, follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' }, requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, unmute: { id: 'account.unmute', defaultMessage: 'Unmute' }
}); });
@injectIntl const buttonsStyle = {
export default class Account extends ImmutablePureComponent { padding: '10px',
height: '18px'
};
static propTypes = { const Account = React.createClass({
propTypes: {
account: ImmutablePropTypes.map.isRequired, account: ImmutablePropTypes.map.isRequired,
me: PropTypes.number.isRequired, me: React.PropTypes.number.isRequired,
onFollow: PropTypes.func.isRequired, onFollow: React.PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired, onBlock: React.PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired, onMute: React.PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: React.PropTypes.object.isRequired
}; },
handleFollow = () => { mixins: [PureRenderMixin],
handleFollow () {
this.props.onFollow(this.props.account); this.props.onFollow(this.props.account);
} },
handleBlock = () => { handleBlock () {
this.props.onBlock(this.props.account); this.props.onBlock(this.props.account);
} },
handleMute = () => { handleMute () {
this.props.onMute(this.props.account); this.props.onMute(this.props.account);
} },
render () { render () {
const { account, me, intl } = this.props; const { account, me, intl } = this.props;
@@ -56,11 +60,11 @@ export default class Account extends ImmutablePureComponent {
const muting = account.getIn(['relationship', 'muting']); const muting = account.getIn(['relationship', 'muting']);
if (requested) { if (requested) {
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />; buttons = <IconButton disabled={true} icon='hourglass' title={intl.formatMessage(messages.requested)} />
} else if (blocking) { } else if (blocking) {
buttons = <IconButton active icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />; buttons = <IconButton active={true} icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
} else if (muting) { } else if (muting) {
buttons = <IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />; buttons = <IconButton active={true} icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />;
} else { } else {
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />; buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
} }
@@ -68,13 +72,13 @@ export default class Account extends ImmutablePureComponent {
return ( return (
<div className='account'> <div className='account'>
<div className='account__wrapper'> <div style={{ display: 'flex' }}>
<Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}> <Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
<div className='account__avatar-wrapper'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={36} /></div> <div style={{ float: 'left', marginLeft: '12px', marginRight: '10px' }}><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={36} /></div>
<DisplayName account={account} /> <DisplayName account={account} />
</Permalink> </Permalink>
<div className='account__relationship'> <div style={buttonsStyle}>
{buttons} {buttons}
</div> </div>
</div> </div>
@@ -82,4 +86,6 @@ export default class Account extends ImmutablePureComponent {
); );
} }
} });
export default injectIntl(Account);

View File

@@ -1,14 +1,14 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import PureRenderMixin from 'react-addons-pure-render-mixin';
const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
export default class AttachmentList extends ImmutablePureComponent { const AttachmentList = React.createClass({
propTypes: {
media: ImmutablePropTypes.list.isRequired
},
static propTypes = { mixins: [PureRenderMixin],
media: ImmutablePropTypes.list.isRequired,
};
render () { render () {
const { media } = this.props; const { media } = this.props;
@@ -29,5 +29,6 @@ export default class AttachmentList extends ImmutablePureComponent {
</div> </div>
); );
} }
});
} export default AttachmentList;

View File

@@ -1,10 +1,6 @@
import React from 'react';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { isRtl } from '../rtl'; import { isRtl } from '../rtl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
const textAtCursorMatchesToken = (str, caretPosition) => { const textAtCursorMatchesToken = (str, caretPosition) => {
let word; let word;
@@ -31,35 +27,32 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
} }
}; };
export default class AutosuggestTextarea extends ImmutablePureComponent { const AutosuggestTextarea = React.createClass({
static propTypes = { propTypes: {
value: PropTypes.string, value: React.PropTypes.string,
suggestions: ImmutablePropTypes.list, suggestions: ImmutablePropTypes.list,
disabled: PropTypes.bool, disabled: React.PropTypes.bool,
placeholder: PropTypes.string, placeholder: React.PropTypes.string,
onSuggestionSelected: PropTypes.func.isRequired, onSuggestionSelected: React.PropTypes.func.isRequired,
onSuggestionsClearRequested: PropTypes.func.isRequired, onSuggestionsClearRequested: React.PropTypes.func.isRequired,
onSuggestionsFetchRequested: PropTypes.func.isRequired, onSuggestionsFetchRequested: React.PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired, onChange: React.PropTypes.func.isRequired,
onKeyUp: PropTypes.func, onKeyUp: React.PropTypes.func,
onKeyDown: PropTypes.func, onKeyDown: React.PropTypes.func,
onPaste: PropTypes.func.isRequired, onPaste: React.PropTypes.func.isRequired,
autoFocus: PropTypes.bool, },
};
static defaultProps = { getInitialState () {
autoFocus: true, return {
}; suggestionsHidden: false,
selectedSuggestion: 0,
lastToken: null,
tokenStart: 0
};
},
state = { onChange (e) {
suggestionsHidden: false,
selectedSuggestion: 0,
lastToken: null,
tokenStart: 0,
};
onChange = (e) => {
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart); const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
if (token !== null && this.state.lastToken !== token) { if (token !== null && this.state.lastToken !== token) {
@@ -70,10 +63,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
this.props.onSuggestionsClearRequested(); this.props.onSuggestionsClearRequested();
} }
this.props.onChange(e); // auto-resize textarea
} e.target.style.height = 'auto';
e.target.style.height = `${e.target.scrollHeight}px`;
onKeyDown = (e) => { this.props.onChange(e);
},
onKeyDown (e) {
const { suggestions, disabled } = this.props; const { suggestions, disabled } = this.props;
const { selectedSuggestion, suggestionsHidden } = this.state; const { selectedSuggestion, suggestionsHidden } = this.state;
@@ -121,40 +118,46 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
} }
this.props.onKeyDown(e); this.props.onKeyDown(e);
} },
onBlur = () => { onBlur () {
this.setState({ suggestionsHidden: true }); // If we hide the suggestions immediately, then this will prevent the
} // onClick for the suggestions themselves from firing.
// Setting a short window for that to take place before hiding the
// suggestions ensures that can't happen.
setTimeout(() => {
this.setState({ suggestionsHidden: true });
}, 100);
},
onSuggestionClick = (e) => { onSuggestionClick (suggestion, e) {
const suggestion = Number(e.currentTarget.getAttribute('data-index'));
e.preventDefault(); e.preventDefault();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
this.textarea.focus(); this.textarea.focus();
} },
componentWillReceiveProps (nextProps) { componentWillReceiveProps (nextProps) {
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) { if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) {
this.setState({ suggestionsHidden: false }); this.setState({ suggestionsHidden: false });
} }
} },
setTextarea = (c) => { setTextarea (c) {
this.textarea = c; this.textarea = c;
} },
onPaste = (e) => { onPaste (e) {
if (e.clipboardData && e.clipboardData.files.length === 1) { if (e.clipboardData && e.clipboardData.files.length === 1) {
this.props.onPaste(e.clipboardData.files); this.props.onPaste(e.clipboardData.files)
e.preventDefault(); e.preventDefault();
} }
} },
render () { render () {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props; const { value, suggestions, disabled, placeholder, onKeyUp } = this.props;
const { suggestionsHidden, selectedSuggestion } = this.state; const { suggestionsHidden, selectedSuggestion } = this.state;
const style = { direction: 'ltr' }; const className = 'autosuggest-textarea__textarea';
const style = { direction: 'ltr' };
if (isRtl(value)) { if (isRtl(value)) {
style.direction = 'rtl'; style.direction = 'rtl';
@@ -162,12 +165,12 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
return ( return (
<div className='autosuggest-textarea'> <div className='autosuggest-textarea'>
<Textarea <textarea
inputRef={this.setTextarea} ref={this.setTextarea}
className='autosuggest-textarea__textarea' className={className}
disabled={disabled} disabled={disabled}
placeholder={placeholder} placeholder={placeholder}
autoFocus={autoFocus} autoFocus={true}
value={value} value={value}
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
@@ -177,16 +180,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
style={style} style={style}
/> />
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}> <div style={{ display: (suggestions.size > 0 && !suggestionsHidden) ? 'block' : 'none' }} className='autosuggest-textarea__suggestions'>
{suggestions.map((suggestion, i) => ( {suggestions.map((suggestion, i) => (
<div <div
role='button' role='button'
tabIndex='0' tabIndex='0'
key={suggestion} key={suggestion}
data-index={suggestion}
className={`autosuggest-textarea__suggestions__item ${i === selectedSuggestion ? 'selected' : ''}`} className={`autosuggest-textarea__suggestions__item ${i === selectedSuggestion ? 'selected' : ''}`}
onMouseDown={this.onSuggestionClick} onClick={this.onSuggestionClick.bind(this, suggestion)}>
>
<AutosuggestAccountContainer id={suggestion} /> <AutosuggestAccountContainer id={suggestion} />
</div> </div>
))} ))}
@@ -195,4 +196,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
); );
} }
} });
export default AutosuggestTextarea;

View File

@@ -0,0 +1,64 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
const Avatar = React.createClass({
propTypes: {
src: React.PropTypes.string.isRequired,
staticSrc: React.PropTypes.string,
size: React.PropTypes.number.isRequired,
style: React.PropTypes.object,
animate: React.PropTypes.bool
},
getDefaultProps () {
return {
animate: false
};
},
getInitialState () {
return {
hovering: false
};
},
mixins: [PureRenderMixin],
handleMouseEnter () {
this.setState({ hovering: true });
},
handleMouseLeave () {
this.setState({ hovering: false });
},
render () {
const { src, size, staticSrc, animate } = this.props;
const { hovering } = this.state;
const style = {
...this.props.style,
width: `${size}px`,
height: `${size}px`,
backgroundSize: `${size}px ${size}px`
};
if (hovering || animate) {
style.backgroundImage = `url(${src})`;
} else {
style.backgroundImage = `url(${staticSrc})`;
}
return (
<div
className='avatar'
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
style={style}
/>
);
}
});
export default Avatar;

View File

@@ -0,0 +1,59 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
const Button = React.createClass({
propTypes: {
text: React.PropTypes.node,
onClick: React.PropTypes.func,
disabled: React.PropTypes.bool,
block: React.PropTypes.bool,
secondary: React.PropTypes.bool,
size: React.PropTypes.number,
style: React.PropTypes.object,
children: React.PropTypes.node
},
getDefaultProps () {
return {
size: 36
};
},
mixins: [PureRenderMixin],
handleClick (e) {
if (!this.props.disabled) {
this.props.onClick();
}
},
render () {
const style = {
fontFamily: 'inherit',
display: this.props.block ? 'block' : 'inline-block',
width: this.props.block ? '100%' : 'auto',
position: 'relative',
boxSizing: 'border-box',
textAlign: 'center',
border: '10px none',
fontSize: '14px',
fontWeight: '500',
letterSpacing: '0',
padding: `0 ${this.props.size / 2.25}px`,
height: `${this.props.size}px`,
cursor: 'pointer',
lineHeight: `${this.props.size}px`,
borderRadius: '4px',
textDecoration: 'none'
};
return (
<button className={`button ${this.props.secondary ? 'button-secondary' : ''}`} disabled={this.props.disabled} onClick={this.handleClick} style={{ ...style, ...this.props.style }}>
{this.props.text || this.props.children}
</button>
);
}
});
export default Button;

View File

@@ -1,7 +1,4 @@
import React from 'react'; import { Motion, spring } from 'react-motion';
import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import PropTypes from 'prop-types';
const Collapsable = ({ fullHeight, isVisible, children }) => ( const Collapsable = ({ fullHeight, isVisible, children }) => (
<Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}> <Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}>
@@ -14,9 +11,9 @@ const Collapsable = ({ fullHeight, isVisible, children }) => (
); );
Collapsable.propTypes = { Collapsable.propTypes = {
fullHeight: PropTypes.number.isRequired, fullHeight: React.PropTypes.number.isRequired,
isVisible: PropTypes.bool.isRequired, isVisible: React.PropTypes.bool.isRequired,
children: PropTypes.node.isRequired, children: React.PropTypes.node.isRequired
}; };
export default Collapsable; export default Collapsable;

View File

@@ -0,0 +1,33 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
import { FormattedMessage } from 'react-intl';
const iconStyle = {
display: 'inline-block',
marginRight: '5px'
};
const ColumnBackButton = React.createClass({
contextTypes: {
router: React.PropTypes.object
},
mixins: [PureRenderMixin],
handleClick () {
if (window.history && window.history.length === 1) this.context.router.push("/");
else this.context.router.goBack();
},
render () {
return (
<div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button'>
<i className='fa fa-fw fa-chevron-left' style={iconStyle} />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</div>
);
}
});
export default ColumnBackButton;

View File

@@ -0,0 +1,44 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
import { FormattedMessage } from 'react-intl';
const outerStyle = {
position: 'absolute',
right: '0',
top: '-48px',
padding: '15px',
fontSize: '16px',
flex: '0 0 auto',
cursor: 'pointer'
};
const iconStyle = {
display: 'inline-block',
marginRight: '5px'
};
const ColumnBackButtonSlim = React.createClass({
contextTypes: {
router: React.PropTypes.object
},
mixins: [PureRenderMixin],
handleClick () {
this.context.router.push('/');
},
render () {
return (
<div style={{ position: 'relative' }}>
<div role='button' tabIndex='0' style={outerStyle} onClick={this.handleClick} className='column-back-button'>
<i className='fa fa-fw fa-chevron-left' style={iconStyle} />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</div>
</div>
);
}
});
export default ColumnBackButtonSlim;

Some files were not shown because too many files have changed in this diff Show More