Compare commits

..

33 Commits

Author SHA1 Message Date
Eugen Rochko
5322654945 Merge branch 'master' into skylight 2017-04-19 19:24:31 +02:00
Eugen Rochko
fcaa6ec4a6 Don't link robots.txt 2017-04-19 18:34:20 +02:00
Eugen Rochko
910ae09426 Merge branch 'master' into skylight 2017-04-19 17:48:44 +02:00
Eugen Rochko
9a97beb3a5 Merge branch 'master' into skylight 2017-04-16 23:33:35 +02:00
Eugen Rochko
1ebcdf84b5 Merge branch 'master' into skylight 2017-04-13 22:28:33 +02:00
Eugen Rochko
791c0edfe8 Merge branch 'master' into skylight 2017-04-13 22:22:26 +02:00
Eugen Rochko
17c2ba7366 Merge branch 'master' into skylight 2017-04-13 21:55:13 +02:00
Eugen Rochko
f4ab6b9f2c Merge branch 'master' into skylight 2017-04-12 21:12:07 +02:00
Eugen Rochko
f63ccd8787 Change deploy 2017-04-12 18:25:34 +02:00
Eugen Rochko
7a1903cdf7 Merge branch 'master' into skylight 2017-04-12 11:28:37 +02:00
Eugen Rochko
4142036f2b Merge branch 'master' into skylight 2017-04-09 21:02:59 +02:00
Eugen Rochko
24656a9f8f Merge branch 'master' into skylight 2017-04-09 19:14:08 +02:00
Eugen Rochko
47910f57da Merge branch 'master' into skylight 2017-04-09 15:01:17 +02:00
Eugen Rochko
2a4499bb23 Merge branch 'master' into skylight 2017-04-08 22:27:03 +02:00
Eugen Rochko
e258d05848 Merge branch 'master' into skylight 2017-04-08 13:27:04 +02:00
Eugen Rochko
7c81c0f7cb Merge branch 'master' into skylight 2017-04-08 13:24:25 +02:00
Eugen Rochko
1ec81ccec0 Merge branch 'master' into skylight 2017-04-07 13:09:25 +02:00
Eugen Rochko
3fa8505769 Merge branch 'master' into skylight 2017-04-07 12:25:10 +02:00
Eugen Rochko
011bcbf21f Merge branch 'feature-ox-fix' into skylight 2017-04-07 11:09:29 +02:00
Eugen Rochko
6164662e1f Force UTF8 encoding on generated XML 2017-04-07 11:05:36 +02:00
Eugen Rochko
ed4d2d8a69 Merge branch 'master' into skylight 2017-04-07 05:59:59 +02:00
Eugen Rochko
453c15ab76 Merge branch 'master' into skylight 2017-04-07 00:06:58 +02:00
Eugen Rochko
2dc2003b99 Disable prepared_statements in production to allow use of pgbouncer 2017-04-06 20:50:13 +02:00
Eugen Rochko
4d1217d8f9 Merge branch 'master' into skylight 2017-04-06 20:50:05 +02:00
Eugen Rochko
0468256ab0 Merge branch 'master' into skylight 2017-04-06 02:27:36 +02:00
Eugen Rochko
634110d8bd Move ProcessingWorker/SalmonWorker into pull queue 2017-04-06 00:21:00 +02:00
Eugen Rochko
40d390e85d Merge branch 'master' into skylight 2017-04-05 21:44:22 +02:00
Eugen Rochko
31e086de0e Merge branch 'master' into skylight 2017-04-05 20:00:21 +02:00
Eugen Rochko
196e44b30b Merge branch 'master' into skylight 2017-04-05 14:29:51 +02:00
Eugen Rochko
945e5bf9cc Merge branch 'master' into skylight 2017-04-05 13:34:46 +02:00
Eugen Rochko
a24d55eed8 Add sidekiq-skylight 2017-04-05 02:36:06 +02:00
Eugen Rochko
7f7a67a7c1 Merge branch 'master' into skylight 2017-04-05 02:34:54 +02:00
Eugen Rochko
300bc70123 Add Skylight 2017-04-04 22:32:22 +02:00
1544 changed files with 22133 additions and 70317 deletions

View File

@@ -1,65 +1,7 @@
{
"presets": [
"react",
[
"env",
{
"loose": true,
"modules": false,
"targets": {
"browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"]
}
}
]
],
"presets": ["es2015", "react"],
"plugins": [
"syntax-dynamic-import",
["transform-object-rest-spread", { "useBuiltIns": true }],
"transform-decorators-legacy",
"transform-class-properties",
[
"react-intl",
{
"messagesDir": "./build/messages"
}
],
"preval"
],
"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"
]
}
}
"transform-object-rest-spread"
]
}

View File

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

View File

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

View File

@@ -2,12 +2,10 @@
.env.*
public/system
public/assets
public/packs
node_modules
storybook
neo4j
vendor/bundle
.DS_Store
*.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
# You may set REDIS_URL instead for more advanced options
REDIS_HOST=redis
REDIS_PORT=6379
# You may set DATABASE_URL instead for more advanced options
# REDIS_DB=0
DB_HOST=db
DB_USER=postgres
DB_NAME=postgres
@@ -10,38 +9,19 @@ DB_PASS=
DB_PORT=5432
# 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
# 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
# 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 (`docker-compose run --rm web rake secret` if you use docker compose)
PAPERCLIP_SECRET=
SECRET_KEY_BASE=
OTP_SECRET=
# VAPID keys (used for push notifications
# You can generate the keys using the following command (first is the private key, second is the public one)
# You should only generate this once per instance. If you later decide to change it, all push subscription will
# be invalidated, requiring the users to access the website again to resubscribe.
#
# Generate with `rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
#
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
VAPID_PRIVATE_KEY=
VAPID_PUBLIC_KEY=
# Registrations
# Single user mode will disable registrations and redirect frontpage to the first profile
# SINGLE_USER_MODE=true
@@ -56,8 +36,7 @@ VAPID_PUBLIC_KEY=
# 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).
# then set SMTP_AUTH_METHOD to 'none' and leave SMTP_LOGIN and SMTP_PASSWORD blank
SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587
SMTP_LOGIN=
@@ -66,17 +45,16 @@ SMTP_FROM_ADDRESS=notifications@example.com
#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
#SMTP_TLS=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
# CDN_HOST=assets.example.com
# S3 (optional)
# S3_ENABLED=true
@@ -111,8 +89,3 @@ SMTP_FROM_ADDRESS=notifications@example.com
# 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

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: off
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: off
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
public/system
public/assets
public/packs
public/packs-test
.env
.env.production
node_modules/
build/
neo4j/
# Ignore Vagrant files
.vagrant/
@@ -34,7 +32,6 @@ config/deploy/*
# Ignore IDE files
.vscode/
.idea/
# Ignore postgres + redis volume optionally created by docker-compose
postgres
@@ -46,14 +43,3 @@ redis
# Ignore vim files
*~
*.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,19 +0,0 @@
.DS_Store
.git/
.gitignore
.bundle/
.cache/
config/deploy/*
coverage
docs/
.env
log/*.log
neo4j/
node_modules/
public/assets/
public/system/
spec/
tmp/
.vagrant/
vendor/bundle/

View File

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

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,45 +1,14 @@
AllCops:
TargetRubyVersion: 2.3
Exclude:
- 'spec/**/*'
- 'db/**/*'
- 'app/views/**/*'
- 'config/**/*'
- 'bin/*'
- 'Rakefile'
- 'node_modules/**/*'
- 'Vagrantfile'
- 'vendor/**/*'
Rails:
Enabled: true
Bundler/OrderedGems:
Style/PerlBackrefs:
AutoCorrect: false
Style/ClassAndModuleChildren:
Enabled: false
Layout/AccessModifierIndentation:
EnforcedStyle: indent
Layout/EmptyLineAfterMagicComment:
Enabled: false
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: space
Metrics/AbcSize:
Max: 100
Metrics/BlockLength:
Max: 35
Exclude:
- 'lib/tasks/**/*'
Metrics/BlockNesting:
Max: 3
Metrics/ClassLength:
CountComments: false
Max: 300
Metrics/CyclomaticComplexity:
Max: 25
Max: 2
Metrics/LineLength:
AllowURI: true
@@ -47,30 +16,37 @@ Metrics/LineLength:
Metrics/MethodLength:
CountComments: false
Max: 10
Metrics/AbcSize:
Max: 100
Metrics/BlockNesting:
Max: 3
Metrics/ClassLength:
CountComments: false
Max: 200
Metrics/CyclomaticComplexity:
Max: 15
Metrics/MethodLength:
Max: 55
Metrics/ModuleLength:
CountComments: false
Max: 200
Metrics/PerceivedComplexity:
Max: 10
Metrics/ParameterLists:
Max: 5
Max: 4
CountKeywordArgs: true
Metrics/PerceivedComplexity:
Max: 20
Rails:
Enabled: true
Rails/HasAndBelongsToMany:
Enabled: false
Rails/SkipsModelValidations:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/AccessModifierIndentation:
EnforcedStyle: indent
Style/CollectionMethods:
Enabled: true
@@ -86,25 +62,29 @@ Style/DoubleNegation:
Style/FrozenStringLiteralComment:
Enabled: true
Style/GuardClause:
Style/SpaceInsideHashLiteralBraces:
EnforcedStyle: space
Style/TrailingCommaInLiteral:
EnforcedStyleForMultiline: 'comma'
Style/RegexpLiteral:
Enabled: false
Style/Lambda:
Enabled: false
Style/PercentLiteralDelimiters:
PreferredDelimiters:
'%i': '()'
'%w': '()'
Style/PerlBackrefs:
AutoCorrect: false
Style/RegexpLiteral:
Rails/HasAndBelongsToMany:
Enabled: false
Style/SymbolArray:
Enabled: false
Style/TrailingCommaInLiteral:
EnforcedStyleForMultiline: 'comma'
AllCops:
TargetRubyVersion: 2.3
Exclude:
- 'spec/**/*'
- '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

@@ -2,3 +2,4 @@ node_modules/
.cache/
docs/
spec/
storybook/

View File

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

View File

@@ -1,9 +0,0 @@
ffmpeg
libicu-dev
libidn11
libidn11-dev
libpq-dev
libprotobuf-dev
libxdamage1
libxfixes3
protobuf-compiler

View File

@@ -1,36 +1,3 @@
# Contributing to Mastodon Glitch Edition #
Thank you for your interest in contributing to the `glitch-soc` project!
Here are some guidelines, and ways you can help.
> (This document is a bit of a work-in-progress, so please bear with us.
> If you don't see what you're looking for here, please don't hesitate to reach out!)
## Planning ##
Right now a lot of the planning for this project takes place in our development Discord, or through GitHub Issues and Projects.
We're working on ways to improve the planning structure and better solicit feedback, and if you feel like you can help in this respect, feel free to give us a holler.
## Documentation ##
The documentation for this repository is available at [`glitch-soc/docs`](https://github.com/glitch-soc/docs) (online at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/)).
Right now, we've mostly focused on the features that make this fork different from upstream in some manner.
Adding screenshots, improving descriptions, and so forth are all ways to help contribute to the project even if you don't know any code.
## Frontend Development ##
Check out [the documentation here](https://glitch-soc.github.io/docs/contributing/frontend/) for more information.
## Backend Development ##
See the guidelines below.
- - -
You should also try to follow the guidelines set out in the original `CONTRIBUTING.md` from `tootsuite/mastodon`, reproduced below.
<blockquote>
CONTRIBUTING
============
@@ -82,5 +49,3 @@ It is expected that you have a working development environment set up (see back-
* If you are introducing new strings, they must be using localization methods
If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
</blockquote>

View File

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

View File

@@ -3,69 +3,39 @@ FROM ruby:2.4.1-alpine
LABEL maintainer="https://github.com/tootsuite/mastodon" \
description="A GNU Social-compatible microblogging server"
ENV UID=991 GID=991 \
RAILS_SERVE_STATIC_FILES=true \
RAILS_ENV=production NODE_ENV=production
ARG LIBICONV_VERSION=1.15
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
ENV RAILS_ENV=production \
NODE_ENV=production
EXPOSE 3000 4000
WORKDIR /mastodon
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
&& echo "@edge https://nl.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
&& apk -U upgrade \
&& apk add -t build-dependencies \
build-base \
icu-dev \
libidn-dev \
libtool \
postgresql-dev \
protobuf-dev \
python \
&& apk add \
ca-certificates \
ffmpeg \
file \
git \
icu-libs \
imagemagick@edge \
libidn \
libpq \
nodejs-npm@edge \
nodejs@edge \
protobuf \
su-exec \
tini \
yarn@edge \
&& update-ca-certificates \
&& wget -O libiconv.tar.gz "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \
&& echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \
&& mkdir -p /tmp/src \
&& tar -xzf libiconv.tar.gz -C /tmp/src \
&& rm libiconv.tar.gz \
&& cd /tmp/src/libiconv-$LIBICONV_VERSION \
&& ./configure --prefix=/usr/local \
&& make -j$(getconf _NPROCESSORS_ONLN)\
&& make install \
&& libtool --finish /usr/local/lib \
&& cd /mastodon \
&& rm -rf /tmp/* /var/cache/apk/*
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
&& bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
&& yarn --ignore-optional --pure-lockfile
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
&& BUILD_DEPS=" \
postgresql-dev \
libxml2-dev \
libxslt-dev \
build-base" \
&& apk -U upgrade && apk add \
$BUILD_DEPS \
nodejs@edge \
nodejs-npm@edge \
libpq \
libxml2 \
libxslt \
ffmpeg \
file \
imagemagick@edge \
&& npm install -g npm@3 && npm install -g yarn \
&& 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/*
COPY . /mastodon
COPY docker_entrypoint.sh /usr/local/bin/run
RUN chmod +x /usr/local/bin/run
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
ENTRYPOINT ["/usr/local/bin/run"]
VOLUME /mastodon/public/system /mastodon/public/assets

171
Gemfile
View File

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

View File

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

View File

@@ -1,2 +1,2 @@
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,10 +1,66 @@
# Mastodon Glitch Edition #
Mastodon
========
> Now with automated deploys!
[![Build Status](http://img.shields.io/travis/tootsuite/mastodon.svg)][travis]
[![Code Climate](https://img.shields.io/codeclimate/github/tootsuite/mastodon.svg)][code_climate]
[![Build Status](https://travis-ci.org/glitch-soc/mastodon.svg?branch=master)](https://travis-ci.org/glitch-soc/mastodon)
[travis]: https://travis-ci.org/tootsuite/mastodon
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
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?
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.
- You can view documentation for this project at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/).
- And contributing guidelines are available [here](CONTRIBUTING.md) and [here](https://glitch-soc.github.io/docs/contributing/).
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)

54
Vagrantfile vendored
View File

@@ -1,8 +1,6 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
ENV["PORT"] ||= "3000"
$provision = <<SCRIPT
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'
# 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
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
# Add firewall rule to redirect 80 to 3000 and save
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_v6 boolean true | sudo debconf-set-selections
sudo apt-get install iptables-persistent -y
@@ -33,45 +31,47 @@ sudo apt-get install \
redis-tools \
postgresql \
postgresql-contrib \
protobuf-compiler \
yarn \
libicu-dev \
libidn11-dev \
libprotobuf-dev \
libreadline-dev \
-y
# Install rvm
read RUBY_VERSION < .ruby-version
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
source /home/vagrant/.rvm/scripts/rvm
# Install rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
# Install Ruby
rvm install ruby-$RUBY_VERSION
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
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
sudo -u postgres createuser -U postgres vagrant -s
sudo -u postgres createdb -U postgres mastodon_development
# Install gems and node modules
gem install bundler foreman
gem install bundler
bundle install
yarn install
# Build Mastodon
export $(cat ".env.vagrant" | xargs)
bundle exec rails db:setup
# Configure automatic loading of environment variable
echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile
bundle exec rails assets:precompile
SCRIPT
$start = <<SCRIPT
echo 'To start server'
echo ' $ vagrant ssh -c "cd /vagrant && foreman start"'
cd /vagrant
export $(cat ".env.vagrant" | xargs)
rails s -d -b 0.0.0.0
SCRIPT
@@ -83,7 +83,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider :virtualbox do |vb|
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.
# https://github.com/mitchellh/vagrant/issues/1172
@@ -113,10 +113,8 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.synced_folder ".", "/vagrant"
end
# Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080
config.vm.network :forwarded_port, guest: 3000, host: 3000
config.vm.network :forwarded_port, guest: 4000, host: 4000
config.vm.network :forwarded_port, guest: 8080, host: 8080
# Otherwise, you can access the site at http://localhost:3000
config.vm.network :forwarded_port, guest: 80, host: 3000
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
config.vm.provision :shell, inline: $provision, privileged: false

View File

@@ -2,7 +2,7 @@
"name": "Mastodon",
"description": "A GNU Social-compatible microblogging server",
"repository": "https://github.com/tootsuite/mastodon",
"logo": "https://github.com/tootsuite.png",
"logo": "https://github.com/tootsuite/mastodon/raw/master/app/assets/images/logo.png",
"env": {
"HEROKU": {
"description": "Leave this as true",
@@ -94,9 +94,6 @@
}
},
"buildpacks": [
{
"url": "https://github.com/heroku/heroku-buildpack-apt"
},
{
"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

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
app/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +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="#189efc"/></svg>

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

Binary file not shown.

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_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_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_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS';
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) {
return {
type: ACCOUNT_FETCH_REQUEST,
id,
id
};
};
export function fetchAccountSuccess(account) {
return {
type: ACCOUNT_FETCH_SUCCESS,
account,
account
};
};
@@ -101,7 +153,7 @@ export function fetchAccountFail(id, error) {
type: ACCOUNT_FETCH_FAIL,
id,
error,
skipAlert: true,
skipAlert: true
};
};
@@ -126,48 +178,100 @@ export function unfollowAccount(id) {
}).catch(error => {
dispatch(unfollowAccountFail(error));
});
};
}
};
export function followAccountRequest(id) {
return {
type: ACCOUNT_FOLLOW_REQUEST,
id,
id
};
};
export function followAccountSuccess(relationship) {
return {
type: ACCOUNT_FOLLOW_SUCCESS,
relationship,
relationship
};
};
export function followAccountFail(error) {
return {
type: ACCOUNT_FOLLOW_FAIL,
error,
error
};
};
export function unfollowAccountRequest(id) {
return {
type: ACCOUNT_UNFOLLOW_REQUEST,
id,
id
};
};
export function unfollowAccountSuccess(relationship) {
return {
type: ACCOUNT_UNFOLLOW_SUCCESS,
relationship,
relationship
};
};
export function unfollowAccountFail(error) {
return {
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,
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) {
return {
type: ACCOUNT_BLOCK_REQUEST,
id,
id
};
};
@@ -207,35 +311,35 @@ export function blockAccountSuccess(relationship, statuses) {
return {
type: ACCOUNT_BLOCK_SUCCESS,
relationship,
statuses,
statuses
};
};
export function blockAccountFail(error) {
return {
type: ACCOUNT_BLOCK_FAIL,
error,
error
};
};
export function unblockAccountRequest(id) {
return {
type: ACCOUNT_UNBLOCK_REQUEST,
id,
id
};
};
export function unblockAccountSuccess(relationship) {
return {
type: ACCOUNT_UNBLOCK_SUCCESS,
relationship,
relationship
};
};
export function unblockAccountFail(error) {
return {
type: ACCOUNT_UNBLOCK_FAIL,
error,
error
};
};
@@ -268,7 +372,7 @@ export function unmuteAccount(id) {
export function muteAccountRequest(id) {
return {
type: ACCOUNT_MUTE_REQUEST,
id,
id
};
};
@@ -276,35 +380,35 @@ export function muteAccountSuccess(relationship, statuses) {
return {
type: ACCOUNT_MUTE_SUCCESS,
relationship,
statuses,
statuses
};
};
export function muteAccountFail(error) {
return {
type: ACCOUNT_MUTE_FAIL,
error,
error
};
};
export function unmuteAccountRequest(id) {
return {
type: ACCOUNT_UNMUTE_REQUEST,
id,
id
};
};
export function unmuteAccountSuccess(relationship) {
return {
type: ACCOUNT_UNMUTE_SUCCESS,
relationship,
relationship
};
};
export function unmuteAccountFail(error) {
return {
type: ACCOUNT_UNMUTE_FAIL,
error,
error
};
};
@@ -327,7 +431,7 @@ export function fetchFollowers(id) {
export function fetchFollowersRequest(id) {
return {
type: FOLLOWERS_FETCH_REQUEST,
id,
id
};
};
@@ -336,7 +440,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
type: FOLLOWERS_FETCH_SUCCESS,
id,
accounts,
next,
next
};
};
@@ -344,7 +448,7 @@ export function fetchFollowersFail(id, error) {
return {
type: FOLLOWERS_FETCH_FAIL,
id,
error,
error
};
};
@@ -372,7 +476,7 @@ export function expandFollowers(id) {
export function expandFollowersRequest(id) {
return {
type: FOLLOWERS_EXPAND_REQUEST,
id,
id
};
};
@@ -381,7 +485,7 @@ export function expandFollowersSuccess(id, accounts, next) {
type: FOLLOWERS_EXPAND_SUCCESS,
id,
accounts,
next,
next
};
};
@@ -389,7 +493,7 @@ export function expandFollowersFail(id, error) {
return {
type: FOLLOWERS_EXPAND_FAIL,
id,
error,
error
};
};
@@ -411,7 +515,7 @@ export function fetchFollowing(id) {
export function fetchFollowingRequest(id) {
return {
type: FOLLOWING_FETCH_REQUEST,
id,
id
};
};
@@ -420,7 +524,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
type: FOLLOWING_FETCH_SUCCESS,
id,
accounts,
next,
next
};
};
@@ -428,7 +532,7 @@ export function fetchFollowingFail(id, error) {
return {
type: FOLLOWING_FETCH_FAIL,
id,
error,
error
};
};
@@ -456,7 +560,7 @@ export function expandFollowing(id) {
export function expandFollowingRequest(id) {
return {
type: FOLLOWING_EXPAND_REQUEST,
id,
id
};
};
@@ -465,7 +569,7 @@ export function expandFollowingSuccess(id, accounts, next) {
type: FOLLOWING_EXPAND_SUCCESS,
id,
accounts,
next,
next
};
};
@@ -473,7 +577,7 @@ export function expandFollowingFail(id, error) {
return {
type: FOLLOWING_EXPAND_FAIL,
id,
error,
error
};
};
@@ -500,7 +604,7 @@ export function fetchRelationshipsRequest(ids) {
return {
type: RELATIONSHIPS_FETCH_REQUEST,
ids,
skipLoading: true,
skipLoading: true
};
};
@@ -508,7 +612,7 @@ export function fetchRelationshipsSuccess(relationships) {
return {
type: RELATIONSHIPS_FETCH_SUCCESS,
relationships,
skipLoading: true,
skipLoading: true
};
};
@@ -516,7 +620,7 @@ export function fetchRelationshipsFail(error) {
return {
type: RELATIONSHIPS_FETCH_FAIL,
error,
skipLoading: true,
skipLoading: true
};
};
@@ -526,14 +630,14 @@ export function fetchFollowRequests() {
api(getState).get('/api/v1/follow_requests').then(response => {
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)));
};
};
export function fetchFollowRequestsRequest() {
return {
type: FOLLOW_REQUESTS_FETCH_REQUEST,
type: FOLLOW_REQUESTS_FETCH_REQUEST
};
};
@@ -541,14 +645,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
return {
type: FOLLOW_REQUESTS_FETCH_SUCCESS,
accounts,
next,
next
};
};
export function fetchFollowRequestsFail(error) {
return {
type: FOLLOW_REQUESTS_FETCH_FAIL,
error,
error
};
};
@@ -564,14 +668,14 @@ export function expandFollowRequests() {
api(getState).get(url).then(response => {
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)));
};
};
export function expandFollowRequestsRequest() {
return {
type: FOLLOW_REQUESTS_EXPAND_REQUEST,
type: FOLLOW_REQUESTS_EXPAND_REQUEST
};
};
@@ -579,14 +683,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
return {
type: FOLLOW_REQUESTS_EXPAND_SUCCESS,
accounts,
next,
next
};
};
export function expandFollowRequestsFail(error) {
return {
type: FOLLOW_REQUESTS_EXPAND_FAIL,
error,
error
};
};
@@ -596,7 +700,7 @@ export function authorizeFollowRequest(id) {
api(getState)
.post(`/api/v1/follow_requests/${id}/authorize`)
.then(() => dispatch(authorizeFollowRequestSuccess(id)))
.then(response => dispatch(authorizeFollowRequestSuccess(id)))
.catch(error => dispatch(authorizeFollowRequestFail(id, error)));
};
};
@@ -604,14 +708,14 @@ export function authorizeFollowRequest(id) {
export function authorizeFollowRequestRequest(id) {
return {
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
id,
id
};
};
export function authorizeFollowRequestSuccess(id) {
return {
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
id,
id
};
};
@@ -619,7 +723,7 @@ export function authorizeFollowRequestFail(id, error) {
return {
type: FOLLOW_REQUEST_AUTHORIZE_FAIL,
id,
error,
error
};
};
@@ -630,7 +734,7 @@ export function rejectFollowRequest(id) {
api(getState)
.post(`/api/v1/follow_requests/${id}/reject`)
.then(() => dispatch(rejectFollowRequestSuccess(id)))
.then(response => dispatch(rejectFollowRequestSuccess(id)))
.catch(error => dispatch(rejectFollowRequestFail(id, error)));
};
};
@@ -638,14 +742,14 @@ export function rejectFollowRequest(id) {
export function rejectFollowRequestRequest(id) {
return {
type: FOLLOW_REQUEST_REJECT_REQUEST,
id,
id
};
};
export function rejectFollowRequestSuccess(id) {
return {
type: FOLLOW_REQUEST_REJECT_SUCCESS,
id,
id
};
};
@@ -653,6 +757,6 @@ export function rejectFollowRequestFail(id, error) {
return {
type: FOLLOW_REQUEST_REJECT_FAIL,
id,
error,
error
};
};

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ export function fetchStatusCard(id) {
dispatch(fetchStatusCardRequest(id));
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;
}
@@ -28,7 +28,7 @@ export function fetchStatusCardRequest(id) {
return {
type: STATUS_CARD_FETCH_REQUEST,
id,
skipLoading: true,
skipLoading: true
};
};
@@ -37,7 +37,7 @@ export function fetchStatusCardSuccess(id, card) {
type: STATUS_CARD_FETCH_SUCCESS,
id,
card,
skipLoading: true,
skipLoading: true
};
};
@@ -47,6 +47,6 @@ export function fetchStatusCardFail(id, error) {
id,
error,
skipLoading: true,
skipAlert: true,
skipAlert: true
};
};

View File

@@ -2,6 +2,8 @@ import api from '../api';
import { updateTimeline } from './timelines';
import * as emojione from 'emojione';
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
@@ -22,20 +24,18 @@ export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
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_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
export function changeCompose(text) {
return {
type: COMPOSE_CHANGE,
text: text,
text: text
};
};
@@ -43,7 +43,7 @@ export function replyCompose(status, router) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_REPLY,
status: status,
status: status
});
if (!getState().getIn(['compose', 'mounted'])) {
@@ -54,7 +54,7 @@ export function replyCompose(status, router) {
export function cancelReplyCompose() {
return {
type: COMPOSE_REPLY_CANCEL,
type: COMPOSE_REPLY_CANCEL
};
};
@@ -62,7 +62,7 @@ export function mentionCompose(account, router) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_MENTION,
account: account,
account: account
});
if (!getState().getIn(['compose', 'mounted'])) {
@@ -73,27 +73,18 @@ export function mentionCompose(account, router) {
export function submitCompose() {
return function (dispatch, getState) {
let status = getState().getIn(['compose', 'text'], '');
const status = emojione.shortnameToUnicode(getState().getIn(['compose', 'text'], ''));
if (!status || !status.length) {
return;
}
dispatch(submitComposeRequest());
if (getState().getIn(['compose', 'advanced_options', 'do_not_federate'])) {
status = status + ' 👁️';
}
api(getState).post('/api/v1/statuses', {
status,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
sensitive: getState().getIn(['compose', 'sensitive']),
spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
visibility: getState().getIn(['compose', 'privacy']),
}, {
headers: {
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
},
visibility: getState().getIn(['compose', 'privacy'])
}).then(function (response) {
dispatch(submitComposeSuccess({ ...response.data }));
@@ -117,21 +108,21 @@ export function submitCompose() {
export function submitComposeRequest() {
return {
type: COMPOSE_SUBMIT_REQUEST,
type: COMPOSE_SUBMIT_REQUEST
};
};
export function submitComposeSuccess(status) {
return {
type: COMPOSE_SUBMIT_SUCCESS,
status: status,
status: status
};
};
export function submitComposeFail(error) {
return {
type: COMPOSE_SUBMIT_FAIL,
error: error,
error: error
};
};
@@ -149,7 +140,7 @@ export function uploadCompose(files) {
api(getState).post('/api/v1/media', data, {
onUploadProgress: function (e) {
dispatch(uploadComposeProgress(e.loaded, e.total));
},
}
}).then(function (response) {
dispatch(uploadComposeSuccess(response.data));
}).catch(function (error) {
@@ -161,7 +152,7 @@ export function uploadCompose(files) {
export function uploadComposeRequest() {
return {
type: COMPOSE_UPLOAD_REQUEST,
skipLoading: true,
skipLoading: true
};
};
@@ -169,7 +160,7 @@ export function uploadComposeProgress(loaded, total) {
return {
type: COMPOSE_UPLOAD_PROGRESS,
loaded: loaded,
total: total,
total: total
};
};
@@ -177,7 +168,7 @@ export function uploadComposeSuccess(media) {
return {
type: COMPOSE_UPLOAD_SUCCESS,
media: media,
skipLoading: true,
skipLoading: true
};
};
@@ -185,20 +176,20 @@ export function uploadComposeFail(error) {
return {
type: COMPOSE_UPLOAD_FAIL,
error: error,
skipLoading: true,
skipLoading: true
};
};
export function undoUploadCompose(media_id) {
return {
type: COMPOSE_UPLOAD_UNDO,
media_id: media_id,
media_id: media_id
};
};
export function clearComposeSuggestions() {
return {
type: COMPOSE_SUGGESTIONS_CLEAR,
type: COMPOSE_SUGGESTIONS_CLEAR
};
};
@@ -208,8 +199,8 @@ export function fetchComposeSuggestions(token) {
params: {
q: token,
resolve: false,
limit: 4,
},
limit: 4
}
}).then(response => {
dispatch(readyComposeSuggestions(token, response.data));
});
@@ -220,7 +211,7 @@ export function readyComposeSuggestions(token, accounts) {
return {
type: COMPOSE_SUGGESTIONS_READY,
token,
accounts,
accounts
};
};
@@ -232,30 +223,23 @@ export function selectComposeSuggestion(position, token, accountId) {
type: COMPOSE_SUGGESTION_SELECT,
position,
token,
completion,
completion
});
};
};
export function mountCompose() {
return {
type: COMPOSE_MOUNT,
type: COMPOSE_MOUNT
};
};
export function unmountCompose() {
return {
type: COMPOSE_UNMOUNT,
type: COMPOSE_UNMOUNT
};
};
export function toggleComposeAdvancedOption(option) {
return {
type: COMPOSE_ADVANCED_OPTIONS_CHANGE,
option: option,
};
}
export function changeComposeSensitivity() {
return {
type: COMPOSE_SENSITIVITY_CHANGE,
@@ -264,21 +248,21 @@ export function changeComposeSensitivity() {
export function changeComposeSpoilerness() {
return {
type: COMPOSE_SPOILERNESS_CHANGE,
type: COMPOSE_SPOILERNESS_CHANGE
};
};
export function changeComposeSpoilerText(text) {
return {
type: COMPOSE_SPOILER_TEXT_CHANGE,
text,
text
};
};
export function changeComposeVisibility(value) {
return {
type: COMPOSE_VISIBILITY_CHANGE,
value,
value
};
};
@@ -286,13 +270,6 @@ export function insertEmojiCompose(position, emoji) {
return {
type: COMPOSE_EMOJI_INSERT,
position,
emoji,
emoji
};
};
export function changeComposing(value) {
return {
type: COMPOSE_COMPOSING_CHANGE,
value,
};
}

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_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
@@ -23,7 +23,7 @@ export function fetchFavouritedStatuses() {
export function fetchFavouritedStatusesRequest() {
return {
type: FAVOURITED_STATUSES_FETCH_REQUEST,
type: FAVOURITED_STATUSES_FETCH_REQUEST
};
};
@@ -31,14 +31,14 @@ export function fetchFavouritedStatusesSuccess(statuses, next) {
return {
type: FAVOURITED_STATUSES_FETCH_SUCCESS,
statuses,
next,
next
};
};
export function fetchFavouritedStatusesFail(error) {
return {
type: FAVOURITED_STATUSES_FETCH_FAIL,
error,
error
};
};
@@ -63,7 +63,7 @@ export function expandFavouritedStatuses() {
export function expandFavouritedStatusesRequest() {
return {
type: FAVOURITED_STATUSES_EXPAND_REQUEST,
type: FAVOURITED_STATUSES_EXPAND_REQUEST
};
};
@@ -71,13 +71,13 @@ export function expandFavouritedStatusesSuccess(statuses, next) {
return {
type: FAVOURITED_STATUSES_EXPAND_SUCCESS,
statuses,
next,
next
};
};
export function expandFavouritedStatusesFail(error) {
return {
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_SUCCESS = 'REBLOG_SUCCESS';
@@ -53,7 +53,7 @@ export function unreblog(status) {
export function reblogRequest(status) {
return {
type: REBLOG_REQUEST,
status: status,
status: status
};
};
@@ -61,7 +61,7 @@ export function reblogSuccess(status, response) {
return {
type: REBLOG_SUCCESS,
status: status,
response: response,
response: response
};
};
@@ -69,14 +69,14 @@ export function reblogFail(status, error) {
return {
type: REBLOG_FAIL,
status: status,
error: error,
error: error
};
};
export function unreblogRequest(status) {
return {
type: UNREBLOG_REQUEST,
status: status,
status: status
};
};
@@ -84,7 +84,7 @@ export function unreblogSuccess(status, response) {
return {
type: UNREBLOG_SUCCESS,
status: status,
response: response,
response: response
};
};
@@ -92,7 +92,7 @@ export function unreblogFail(status, error) {
return {
type: UNREBLOG_FAIL,
status: status,
error: error,
error: error
};
};
@@ -123,7 +123,7 @@ export function unfavourite(status) {
export function favouriteRequest(status) {
return {
type: FAVOURITE_REQUEST,
status: status,
status: status
};
};
@@ -131,7 +131,7 @@ export function favouriteSuccess(status, response) {
return {
type: FAVOURITE_SUCCESS,
status: status,
response: response,
response: response
};
};
@@ -139,14 +139,14 @@ export function favouriteFail(status, error) {
return {
type: FAVOURITE_FAIL,
status: status,
error: error,
error: error
};
};
export function unfavouriteRequest(status) {
return {
type: UNFAVOURITE_REQUEST,
status: status,
status: status
};
};
@@ -154,7 +154,7 @@ export function unfavouriteSuccess(status, response) {
return {
type: UNFAVOURITE_SUCCESS,
status: status,
response: response,
response: response
};
};
@@ -162,7 +162,7 @@ export function unfavouriteFail(status, error) {
return {
type: UNFAVOURITE_FAIL,
status: status,
error: error,
error: error
};
};
@@ -181,7 +181,7 @@ export function fetchReblogs(id) {
export function fetchReblogsRequest(id) {
return {
type: REBLOGS_FETCH_REQUEST,
id,
id
};
};
@@ -189,14 +189,14 @@ export function fetchReblogsSuccess(id, accounts) {
return {
type: REBLOGS_FETCH_SUCCESS,
id,
accounts,
accounts
};
};
export function fetchReblogsFail(id, error) {
return {
type: REBLOGS_FETCH_FAIL,
error,
error
};
};
@@ -215,7 +215,7 @@ export function fetchFavourites(id) {
export function fetchFavouritesRequest(id) {
return {
type: FAVOURITES_FETCH_REQUEST,
id,
id
};
};
@@ -223,13 +223,13 @@ export function fetchFavouritesSuccess(id, accounts) {
return {
type: FAVOURITES_FETCH_SUCCESS,
id,
accounts,
accounts
};
};
export function fetchFavouritesFail(id, error) {
return {
type: FAVOURITES_FETCH_FAIL,
error,
error
};
};

View File

@@ -5,12 +5,12 @@ export function openModal(type, props) {
return {
type: MODAL_OPEN,
modalType: type,
modalProps: props,
modalProps: props
};
};
export function closeModal() {
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';
export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
@@ -23,7 +23,7 @@ export function fetchMutes() {
export function fetchMutesRequest() {
return {
type: MUTES_FETCH_REQUEST,
type: MUTES_FETCH_REQUEST
};
};
@@ -31,14 +31,14 @@ export function fetchMutesSuccess(accounts, next) {
return {
type: MUTES_FETCH_SUCCESS,
accounts,
next,
next
};
};
export function fetchMutesFail(error) {
return {
type: MUTES_FETCH_FAIL,
error,
error
};
};
@@ -62,7 +62,7 @@ export function expandMutes() {
export function expandMutesRequest() {
return {
type: MUTES_EXPAND_REQUEST,
type: MUTES_EXPAND_REQUEST
};
};
@@ -70,13 +70,13 @@ export function expandMutesSuccess(accounts, next) {
return {
type: MUTES_EXPAND_SUCCESS,
accounts,
next,
next
};
};
export function expandMutesFail(error) {
return {
type: MUTES_EXPAND_FAIL,
error,
error
};
};

View File

@@ -1,22 +1,11 @@
import api, { getLinks } from '../api';
import { List as ImmutableList } from 'immutable';
import api, { getLinks } from '../api'
import Immutable from 'immutable';
import IntlMessageFormat from 'intl-messageformat';
import { fetchRelationships } from './accounts';
import { defineMessages } from 'react-intl';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
// tracking the notif cleaning request
export const NOTIFICATIONS_DELETE_MARKED_REQUEST = 'NOTIFICATIONS_DELETE_MARKED_REQUEST';
export const NOTIFICATIONS_DELETE_MARKED_SUCCESS = 'NOTIFICATIONS_DELETE_MARKED_SUCCESS';
export const NOTIFICATIONS_DELETE_MARKED_FAIL = 'NOTIFICATIONS_DELETE_MARKED_FAIL';
export const NOTIFICATIONS_MARK_ALL_FOR_DELETE = 'NOTIFICATIONS_MARK_ALL_FOR_DELETE';
export const NOTIFICATIONS_ENTER_CLEARING_MODE = 'NOTIFICATIONS_ENTER_CLEARING_MODE'; // arg: yes
// Unmark notifications (when the cleaning mode is left)
export const NOTIFICATIONS_UNMARK_ALL_FOR_DELETE = 'NOTIFICATIONS_UNMARK_ALL_FOR_DELETE';
// Mark one for delete
export const NOTIFICATION_MARK_FOR_DELETE = 'NOTIFICATION_MARK_FOR_DELETE';
export const NOTIFICATIONS_REFRESH_REQUEST = 'NOTIFICATIONS_REFRESH_REQUEST';
export const NOTIFICATIONS_REFRESH_SUCCESS = 'NOTIFICATIONS_REFRESH_SUCCESS';
export const NOTIFICATIONS_REFRESH_FAIL = 'NOTIFICATIONS_REFRESH_FAIL';
@@ -28,10 +17,6 @@ export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
defineMessages({
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
});
const fetchRelatedRelationships = (dispatch, notifications) => {
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
@@ -40,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) {
return (dispatch, getState) => {
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
@@ -56,7 +35,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
notification,
account: notification.account,
status: notification.status,
meta: playSound ? { sound: 'boop' } : undefined,
meta: playSound ? { sound: 'boop' } : undefined
});
fetchRelatedRelationships(dispatch, [notification]);
@@ -64,13 +43,9 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
// Desktop notifications
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 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 });
notify.addEventListener('click', () => {
window.focus();
notify.close();
});
new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
}
};
};
@@ -79,78 +54,68 @@ const excludeTypesFromSettings = state => state.getIn(['settings', 'notification
export function refreshNotifications() {
return (dispatch, getState) => {
dispatch(refreshNotificationsRequest());
const params = {};
const ids = getState().getIn(['notifications', 'items']);
let skipLoading = false;
if (ids.size > 0) {
params.since_id = ids.first().get('id');
}
if (getState().getIn(['notifications', 'loaded'])) {
skipLoading = true;
}
params.exclude_types = excludeTypesFromSettings(getState());
dispatch(refreshNotificationsRequest(skipLoading));
api(getState).get('/api/v1/notifications', { params }).then(response => {
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);
}).catch(error => {
dispatch(refreshNotificationsFail(error, skipLoading));
dispatch(refreshNotificationsFail(error));
});
};
};
export function refreshNotificationsRequest(skipLoading) {
export function refreshNotificationsRequest() {
return {
type: NOTIFICATIONS_REFRESH_REQUEST,
skipLoading,
type: NOTIFICATIONS_REFRESH_REQUEST
};
};
export function refreshNotificationsSuccess(notifications, skipLoading, next) {
export function refreshNotificationsSuccess(notifications, next) {
return {
type: NOTIFICATIONS_REFRESH_SUCCESS,
notifications,
accounts: notifications.map(item => item.account),
statuses: notifications.map(item => item.status).filter(status => !!status),
skipLoading,
next,
next
};
};
export function refreshNotificationsFail(error, skipLoading) {
export function refreshNotificationsFail(error) {
return {
type: NOTIFICATIONS_REFRESH_FAIL,
error,
skipLoading,
error
};
};
export function expandNotifications() {
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;
}
const params = {
max_id: items.last().get('id'),
limit: 20,
exclude_types: excludeTypesFromSettings(getState()),
};
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');
dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null));
fetchRelatedRelationships(dispatch, response.data);
}).catch(error => {
@@ -161,7 +126,7 @@ export function expandNotifications() {
export function expandNotificationsRequest() {
return {
type: NOTIFICATIONS_EXPAND_REQUEST,
type: NOTIFICATIONS_EXPAND_REQUEST
};
};
@@ -171,21 +136,21 @@ export function expandNotificationsSuccess(notifications, next) {
notifications,
accounts: notifications.map(item => item.account),
statuses: notifications.map(item => item.status).filter(status => !!status),
next,
next
};
};
export function expandNotificationsFail(error) {
return {
type: NOTIFICATIONS_EXPAND_FAIL,
error,
error
};
};
export function clearNotifications() {
return (dispatch, getState) => {
dispatch({
type: NOTIFICATIONS_CLEAR,
type: NOTIFICATIONS_CLEAR
});
api(getState).post('/api/v1/notifications/clear');
@@ -195,70 +160,6 @@ export function clearNotifications() {
export function scrollTopNotifications(top) {
return {
type: NOTIFICATIONS_SCROLL_TOP,
top,
};
};
export function deleteMarkedNotifications() {
return (dispatch, getState) => {
dispatch(deleteMarkedNotificationsRequest());
let ids = [];
getState().getIn(['notifications', 'items']).forEach((n) => {
if (n.get('markedForDelete')) {
ids.push(n.get('id'));
}
});
if (ids.length === 0) {
return;
}
api(getState).delete(`/api/v1/notifications/destroy_multiple?ids[]=${ids.join('&ids[]=')}`).then(() => {
dispatch(deleteMarkedNotificationsSuccess());
}).catch(error => {
console.error(error);
dispatch(deleteMarkedNotificationsFail(error));
});
};
};
export function enterNotificationClearingMode(yes) {
return {
type: NOTIFICATIONS_ENTER_CLEARING_MODE,
yes: yes,
};
};
export function markAllNotifications(yes) {
return {
type: NOTIFICATIONS_MARK_ALL_FOR_DELETE,
yes: yes, // true, false or null. null = invert
};
};
export function deleteMarkedNotificationsRequest() {
return {
type: NOTIFICATIONS_DELETE_MARKED_REQUEST,
};
};
export function deleteMarkedNotificationsFail() {
return {
type: NOTIFICATIONS_DELETE_MARKED_FAIL,
};
};
export function markNotificationForDelete(id, yes) {
return {
type: NOTIFICATION_MARK_FOR_DELETE,
id: id,
yes: yes,
};
};
export function deleteMarkedNotificationsSuccess() {
return {
type: NOTIFICATIONS_DELETE_MARKED_SUCCESS,
top
};
};

View File

@@ -1,5 +1,4 @@
import api from '../api';
import { openModal, closeModal } from './modal';
export const REPORT_INIT = 'REPORT_INIT';
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 function initReport(account, status) {
return dispatch => {
dispatch({
type: REPORT_INIT,
account,
status,
});
dispatch(openModal('REPORT'));
return {
type: REPORT_INIT,
account,
status
};
};
export function cancelReport() {
return {
type: REPORT_CANCEL,
type: REPORT_CANCEL
};
};
@@ -44,37 +39,34 @@ export function submitReport() {
api(getState).post('/api/v1/reports', {
account_id: getState().getIn(['reports', 'new', 'account_id']),
status_ids: getState().getIn(['reports', 'new', 'status_ids']),
comment: getState().getIn(['reports', 'new', 'comment']),
}).then(response => {
dispatch(closeModal());
dispatch(submitReportSuccess(response.data));
}).catch(error => dispatch(submitReportFail(error)));
comment: getState().getIn(['reports', 'new', 'comment'])
}).then(response => dispatch(submitReportSuccess(response.data))).catch(error => dispatch(submitReportFail(error)));
};
};
export function submitReportRequest() {
return {
type: REPORT_SUBMIT_REQUEST,
type: REPORT_SUBMIT_REQUEST
};
};
export function submitReportSuccess(report) {
return {
type: REPORT_SUBMIT_SUCCESS,
report,
report
};
};
export function submitReportFail(error) {
return {
type: REPORT_SUBMIT_FAIL,
error,
error
};
};
export function changeReportComment(comment) {
return {
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_CLEAR = 'SEARCH_CLEAR';
@@ -11,13 +11,13 @@ export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL';
export function changeSearch(value) {
return {
type: SEARCH_CHANGE,
value,
value
};
};
export function clearSearch() {
return {
type: SEARCH_CLEAR,
type: SEARCH_CLEAR
};
};
@@ -34,8 +34,8 @@ export function submitSearch() {
api(getState).get('/api/v1/search', {
params: {
q: value,
resolve: true,
},
resolve: true
}
}).then(response => {
dispatch(fetchSearchSuccess(response.data));
}).catch(error => {
@@ -46,7 +46,7 @@ export function submitSearch() {
export function fetchSearchRequest() {
return {
type: SEARCH_FETCH_REQUEST,
type: SEARCH_FETCH_REQUEST
};
};
@@ -55,19 +55,19 @@ export function fetchSearchSuccess(results) {
type: SEARCH_FETCH_SUCCESS,
results,
accounts: results.accounts,
statuses: results.statuses,
statuses: results.statuses
};
};
export function fetchSearchFail(error) {
return {
type: SEARCH_FETCH_FAIL,
error,
error
};
};
export function showSearch() {
return {
type: SEARCH_SHOW,
type: SEARCH_SHOW
};
};

View File

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

View File

@@ -15,22 +15,11 @@ export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
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 const STATUS_SET_HEIGHT = 'STATUS_SET_HEIGHT';
export const STATUSES_CLEAR_HEIGHT = 'STATUSES_CLEAR_HEIGHT';
export function fetchStatusRequest(id, skipLoading) {
return {
type: STATUS_FETCH_REQUEST,
id,
skipLoading,
skipLoading
};
};
@@ -59,7 +48,7 @@ export function fetchStatusSuccess(status, skipLoading) {
return {
type: STATUS_FETCH_SUCCESS,
status,
skipLoading,
skipLoading
};
};
@@ -69,7 +58,7 @@ export function fetchStatusFail(id, error, skipLoading) {
id,
error,
skipLoading,
skipAlert: true,
skipAlert: true
};
};
@@ -77,7 +66,7 @@ export function deleteStatus(id) {
return (dispatch, getState) => {
dispatch(deleteStatusRequest(id));
api(getState).delete(`/api/v1/statuses/${id}`).then(() => {
api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
dispatch(deleteStatusSuccess(id));
dispatch(deleteFromTimelines(id));
}).catch(error => {
@@ -89,14 +78,14 @@ export function deleteStatus(id) {
export function deleteStatusRequest(id) {
return {
type: STATUS_DELETE_REQUEST,
id: id,
id: id
};
};
export function deleteStatusSuccess(id) {
return {
type: STATUS_DELETE_SUCCESS,
id: id,
id: id
};
};
@@ -104,7 +93,7 @@ export function deleteStatusFail(id, error) {
return {
type: STATUS_DELETE_FAIL,
id: id,
error: error,
error: error
};
};
@@ -116,7 +105,7 @@ export function fetchContext(id) {
dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants));
}).catch(error => {
if (error.response && error.response.status === 404) {
if (error.response.status === 404) {
dispatch(deleteFromTimelines(id));
}
@@ -128,7 +117,7 @@ export function fetchContext(id) {
export function fetchContextRequest(id) {
return {
type: CONTEXT_FETCH_REQUEST,
id,
id
};
};
@@ -138,7 +127,7 @@ export function fetchContextSuccess(id, ancestors, descendants) {
id,
ancestors,
descendants,
statuses: ancestors.concat(descendants),
statuses: ancestors.concat(descendants)
};
};
@@ -147,88 +136,6 @@ export function fetchContextFail(id, error) {
type: CONTEXT_FETCH_FAIL,
id,
error,
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,
};
};
export function setStatusHeight (id, height) {
return {
type: STATUS_SET_HEIGHT,
id,
height,
};
};
export function clearStatusesHeight () {
return {
type: STATUSES_CLEAR_HEIGHT,
skipAlert: true
};
};

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_LAZY = 'STORE_HYDRATE_LAZY';
const convertState = rawState =>
fromJS(rawState, (k, v) =>
Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x =>
Immutable.fromJS(rawState, (k, v) =>
Immutable.Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x =>
Number.isNaN(x * 1) ? x : x * 1));
export function hydrateStore(rawState) {
@@ -13,6 +12,6 @@ export function hydrateStore(rawState) {
return {
type: STORE_HYDRATE,
state,
state
};
};

View File

@@ -1,5 +1,5 @@
import api, { getLinks } from '../api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import api, { getLinks } from '../api'
import Immutable from 'immutable';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
@@ -23,7 +23,7 @@ export function refreshTimelineSuccess(timeline, statuses, skipLoading, next) {
timeline,
statuses,
skipLoading,
next,
next
};
};
@@ -35,7 +35,7 @@ export function updateTimeline(timeline, status) {
type: TIMELINE_UPDATE,
timeline,
status,
references,
references
});
};
};
@@ -51,98 +51,98 @@ export function deleteFromTimelines(id) {
id,
accountId,
references,
reblogOf,
reblogOf
});
};
};
export function refreshTimelineRequest(timeline, skipLoading) {
export function refreshTimelineRequest(timeline, id, skipLoading) {
return {
type: TIMELINE_REFRESH_REQUEST,
timeline,
skipLoading,
id,
skipLoading
};
};
export function refreshTimeline(timelineId, path, params = {}) {
export function refreshTimeline(timeline, id = null) {
return function (dispatch, getState) {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
if (timeline.get('isLoading') || timeline.get('online')) {
if (getState().getIn(['timelines', timeline, 'isLoading'])) {
return;
}
const ids = timeline.get('items', ImmutableList());
const ids = getState().getIn(['timelines', timeline, 'items'], Immutable.List());
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) {
params.since_id = newestId;
if (newestId !== null && getState().getIn(['timelines', timeline, 'loaded']) && (id === null || getState().getIn(['timelines', timeline, 'id']) === id)) {
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 => {
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 => {
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) {
return {
type: TIMELINE_REFRESH_FAIL,
timeline,
error,
skipLoading,
skipAlert: error.response && error.response.status === 404,
skipLoading
};
};
export function expandTimeline(timelineId, path, params = {}) {
export function expandTimeline(timeline) {
return (dispatch, getState) => {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
const ids = timeline.get('items', ImmutableList());
if (timeline.get('isLoading') || ids.size === 0) {
if (getState().getIn(['timelines', timeline, 'isLoading'])) {
return;
}
params.max_id = ids.last();
params.limit = 10;
if (getState().getIn(['timelines', timeline, 'items']).size === 0) {
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');
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null));
dispatch(expandTimelineSuccess(timeline, response.data, next ? next.uri : null));
}).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) {
return {
type: TIMELINE_EXPAND_REQUEST,
timeline,
timeline
};
};
@@ -151,7 +151,7 @@ export function expandTimelineSuccess(timeline, statuses, next) {
type: TIMELINE_EXPAND_SUCCESS,
timeline,
statuses,
next,
next
};
};
@@ -159,7 +159,7 @@ export function expandTimelineFail(timeline, error) {
return {
type: TIMELINE_EXPAND_FAIL,
timeline,
error,
error
};
};
@@ -167,20 +167,20 @@ export function scrollTopTimeline(timeline, top) {
return {
type: TIMELINE_SCROLL_TOP,
timeline,
top,
top
};
};
export function connectTimeline(timeline) {
return {
type: TIMELINE_CONNECT,
timeline,
timeline
};
};
export function disconnectTimeline(timeline) {
return {
type: TIMELINE_DISCONNECT,
timeline,
timeline
};
};

View File

@@ -13,7 +13,7 @@ export const getLinks = response => {
export default getState => axios.create({
headers: {
'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`,
'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`
},
transformResponse: [function (data) {
@@ -22,5 +22,5 @@ export default getState => axios.create({
} catch(Exception) {
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 PropTypes from 'prop-types';
import Avatar from './avatar';
import DisplayName from './display_name';
import Permalink from './permalink';
import IconButton from './icon_button';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute' }
});
@injectIntl
export default class Account extends ImmutablePureComponent {
const buttonsStyle = {
padding: '10px',
height: '18px'
};
static propTypes = {
const Account = React.createClass({
propTypes: {
account: ImmutablePropTypes.map.isRequired,
me: PropTypes.number.isRequired,
onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
me: React.PropTypes.number.isRequired,
onFollow: React.PropTypes.func.isRequired,
onBlock: React.PropTypes.func.isRequired,
onMute: React.PropTypes.func.isRequired,
intl: React.PropTypes.object.isRequired
},
handleFollow = () => {
mixins: [PureRenderMixin],
handleFollow () {
this.props.onFollow(this.props.account);
}
},
handleBlock = () => {
handleBlock () {
this.props.onBlock(this.props.account);
}
},
handleMute = () => {
handleMute () {
this.props.onMute(this.props.account);
}
},
render () {
const { account, me, intl } = this.props;
@@ -56,11 +60,11 @@ export default class Account extends ImmutablePureComponent {
const muting = account.getIn(['relationship', 'muting']);
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) {
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) {
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 {
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 (
<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')}`}>
<div className='account__avatar-wrapper'><Avatar account={account} 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} />
</Permalink>
<div className='account__relationship'>
<div style={buttonsStyle}>
{buttons}
</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 ImmutablePureComponent from 'react-immutable-pure-component';
import PureRenderMixin from 'react-addons-pure-render-mixin';
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 = {
media: ImmutablePropTypes.list.isRequired,
};
mixins: [PureRenderMixin],
render () {
const { media } = this.props;
@@ -29,5 +29,6 @@ export default class AttachmentList extends ImmutablePureComponent {
</div>
);
}
});
}
export default AttachmentList;

View File

@@ -1,10 +1,6 @@
import React from 'react';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { isRtl } from '../rtl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
const textAtCursorMatchesToken = (str, caretPosition) => {
let word;
@@ -31,35 +27,32 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
}
};
export default class AutosuggestTextarea extends ImmutablePureComponent {
const AutosuggestTextarea = React.createClass({
static propTypes = {
value: PropTypes.string,
propTypes: {
value: React.PropTypes.string,
suggestions: ImmutablePropTypes.list,
disabled: PropTypes.bool,
placeholder: PropTypes.string,
onSuggestionSelected: PropTypes.func.isRequired,
onSuggestionsClearRequested: PropTypes.func.isRequired,
onSuggestionsFetchRequested: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onKeyUp: PropTypes.func,
onKeyDown: PropTypes.func,
onPaste: PropTypes.func.isRequired,
autoFocus: PropTypes.bool,
};
disabled: React.PropTypes.bool,
placeholder: React.PropTypes.string,
onSuggestionSelected: React.PropTypes.func.isRequired,
onSuggestionsClearRequested: React.PropTypes.func.isRequired,
onSuggestionsFetchRequested: React.PropTypes.func.isRequired,
onChange: React.PropTypes.func.isRequired,
onKeyUp: React.PropTypes.func,
onKeyDown: React.PropTypes.func,
onPaste: React.PropTypes.func.isRequired,
},
static defaultProps = {
autoFocus: true,
};
getInitialState () {
return {
suggestionsHidden: false,
selectedSuggestion: 0,
lastToken: null,
tokenStart: 0
};
},
state = {
suggestionsHidden: false,
selectedSuggestion: 0,
lastToken: null,
tokenStart: 0,
};
onChange = (e) => {
onChange (e) {
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
if (token !== null && this.state.lastToken !== token) {
@@ -70,10 +63,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
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 { selectedSuggestion, suggestionsHidden } = this.state;
@@ -121,40 +118,46 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
}
this.props.onKeyDown(e);
}
},
onBlur = () => {
this.setState({ suggestionsHidden: true });
}
onBlur () {
// 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) => {
const suggestion = Number(e.currentTarget.getAttribute('data-index'));
onSuggestionClick (suggestion, e) {
e.preventDefault();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
this.textarea.focus();
}
},
componentWillReceiveProps (nextProps) {
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) {
this.setState({ suggestionsHidden: false });
}
}
},
setTextarea = (c) => {
setTextarea (c) {
this.textarea = c;
}
},
onPaste = (e) => {
onPaste (e) {
if (e.clipboardData && e.clipboardData.files.length === 1) {
this.props.onPaste(e.clipboardData.files);
this.props.onPaste(e.clipboardData.files)
e.preventDefault();
}
}
},
render () {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props;
const { value, suggestions, disabled, placeholder, onKeyUp } = this.props;
const { suggestionsHidden, selectedSuggestion } = this.state;
const style = { direction: 'ltr' };
const className = 'autosuggest-textarea__textarea';
const style = { direction: 'ltr' };
if (isRtl(value)) {
style.direction = 'rtl';
@@ -162,34 +165,29 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
return (
<div className='autosuggest-textarea'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
<Textarea
inputRef={this.setTextarea}
className='autosuggest-textarea__textarea'
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onBlur={this.onBlur}
onPaste={this.onPaste}
style={style}
/>
</label>
<textarea
ref={this.setTextarea}
className={className}
disabled={disabled}
placeholder={placeholder}
autoFocus={true}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onBlur={this.onBlur}
onPaste={this.onPaste}
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) => (
<div
role='button'
tabIndex='0'
key={suggestion}
data-index={suggestion}
className={`autosuggest-textarea__suggestions__item ${i === selectedSuggestion ? 'selected' : ''}`}
onMouseDown={this.onSuggestionClick}
>
onClick={this.onSuggestionClick.bind(this, suggestion)}>
<AutosuggestAccountContainer id={suggestion} />
</div>
))}
@@ -198,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,62 @@
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',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden'
};
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 from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import PropTypes from 'prop-types';
import { Motion, spring } from 'react-motion';
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) }}>
@@ -14,9 +11,9 @@ const Collapsable = ({ fullHeight, isVisible, children }) => (
);
Collapsable.propTypes = {
fullHeight: PropTypes.number.isRequired,
isVisible: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
fullHeight: React.PropTypes.number.isRequired,
isVisible: React.PropTypes.bool.isRequired,
children: React.PropTypes.node.isRequired
};
export default Collapsable;

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