Compare commits

..

5 Commits

Author SHA1 Message Date
Eugen Rochko
a0f7453c6e Bump version to 1.3.3 2017-05-06 13:53:43 +02:00
Eugen Rochko
46a1e16f21 Fix Scheduler::SubscriptionsScheduler (#2834)
* Fix Scheduler::SubscriptionsScheduler, add worker test for it

* Change production log level of Sidekiq to "warn" instead of "info"
2017-05-06 13:52:42 +02:00
Eugen Rochko
f3f7a3840a Fix #2706 - Always respond with 200 to PuSH payloads (#2733)
Fix #2196 - Respond with 201 when Salmon accepted, 400 when unverified
Fix #2629 - Correctly handle confirm_domain? for local accounts
Unify rules for extracting author acct from XML, prefer <email>, fall back
to <name> + <uri> (see also #2017, #2172)
2017-05-05 22:01:04 +02:00
Eugen Rochko
7539254e96 Likely fix #2458, fix #2031 - handle out-of-order deletes for statuses (#2734)
* Likely fix #2458, fix #2031 - handle out-of-order deletes for statuses

If a delete arrives before the original status, cache that information
for 6h, and if the original status arrives in that window, ignore it

* Add test case
2017-05-05 21:58:29 +02:00
Eugen Rochko
456478c4e1 More robust PuSH subscription refreshes (#2799)
* Fix #2473 - Use sidekiq scheduler to refresh PuSH subscriptions instead of cron

Fix an issue where / in domain would raise exception in TagManager#normalize_domain

PuSH subscriptions refresh done in a round-robin way to avoid hammering a single
server's hub in sequence. Correct handling of failures/retries through Sidekiq (see
also #2613). Optimize Account#with_followers scope. Also, since subscriptions
are now delegated to Sidekiq jobs, an uncaught exception will not stop the entire
refreshing operation halfway through

Fix #2702 - Correct user agent header on outgoing http requests

* Add test for SubscribeService

* Extract #expiring_accounts into method

* Make mastodon:push:refresh no-op

* Queues are now defined in sidekiq.yml

* Queues are now in sidekiq.yml
2017-05-05 21:53:01 +02:00
886 changed files with 8625 additions and 25149 deletions

View File

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

View File

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

View File

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

View File

@@ -9,5 +9,3 @@ vendor/bundle
.DS_Store .DS_Store
*.swp *.swp
*~ *~
postgres
redis

View File

@@ -1,109 +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
# 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=assets.example.com
# S3 (optional)
# S3_ENABLED=true
# S3_BUCKET=
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# S3_REGION=
# S3_PROTOCOL=http
# S3_HOSTNAME=192.168.1.123:9000
# S3 (Minio Config (optional) Please check Minio instance for details)
# S3_ENABLED=true
# S3_BUCKET=
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# S3_REGION=
# S3_PROTOCOL=https
# S3_HOSTNAME=
# S3_ENDPOINT=
# S3_SIGNATURE_VERSION=
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
# S3_CLOUDFRONT_HOST=
# Streaming API integration
# STREAMING_API_BASE_URL=
# Advanced settings
# If you need to use pgBouncer, you need to disable prepared statements:
# PREPARED_STATEMENTS=false
# Cluster number setting for streaming API server.
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
STREAMING_CLUSTER_NUM=1
# Docker mastodon user
# If you use Docker, you may want to assign UID/GID manually.
# UID=1000
# GID=1000

View File

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

79
.eslintrc.json 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,88 +0,0 @@
---
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: error
no-console: warn
no-irregular-whitespace: error
no-unreachable: error
valid-typeof: error
consistent-return: error
dot-notation: error
eqeqeq: error
no-fallthrough: error
no-unused-expressions: error
strict: off
no-catch-shadow: error
indent:
- warn
- 2
brace-style: warn
comma-spacing:
- warn
- before: false
after: true
comma-style:
- warn
- last
no-mixed-spaces-and-tabs: warn
no-nested-ternary: warn
no-trailing-spaces: warn
semi: error
padded-blocks:
- error
- classes: always
comma-dangle:
- error
- always-multiline
react/jsx-wrap-multilines: error
react/jsx-no-bind: error
react/self-closing-comp: error
react/prop-types: error
react/no-multi-comp: off
jsx-a11y/accessible-emoji: warn
jsx-a11y/anchor-has-content: warn
jsx-a11y/aria-activedescendant-has-tabindex: warn
jsx-a11y/aria-props: warn
jsx-a11y/aria-proptypes: warn
jsx-a11y/aria-role: warn
jsx-a11y/aria-unsupported-elements: warn
jsx-a11y/heading-has-content: warn
jsx-a11y/href-no-hash: warn
jsx-a11y/html-has-lang: warn
jsx-a11y/iframe-has-title: warn
jsx-a11y/img-has-alt: warn
jsx-a11y/img-redundant-alt: warn
jsx-a11y/label-has-for: warn
jsx-a11y/mouse-events-have-key-events: warn
jsx-a11y/no-access-key: warn
jsx-a11y/no-distracting-elements: warn
jsx-a11y/no-onchange: warn
jsx-a11y/no-redundant-roles: warn
jsx-a11y/onclick-has-focus: warn
jsx-a11y/onclick-has-role: warn
jsx-a11y/role-has-required-aria-props: warn
jsx-a11y/role-supports-aria-props: warn
jsx-a11y/scope: warn
jsx-a11y/tabindex-no-positive: warn

View File

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

14
.gitignore vendored
View File

@@ -19,11 +19,10 @@
coverage coverage
public/system public/system
public/assets public/assets
public/packs
.env .env
.env.production .env.production
node_modules/ node_modules/
build/ neo4j/
# Ignore Vagrant files # Ignore Vagrant files
.vagrant/ .vagrant/
@@ -44,14 +43,3 @@ redis
# Ignore vim files # Ignore vim files
*~ *~
*.swp *.swp
# Ignore npm debug log
npm-debug.log
# Ignore yarn log files
yarn-error.log
yarn-debug.log
# Ignore Docker option files
docker-compose.override.yml

View File

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

View File

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

View File

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

View File

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

View File

@@ -74,15 +74,9 @@ Style/RegexpLiteral:
Style/Lambda: Style/Lambda:
Enabled: false Enabled: false
Style/GuardClause:
Enabled: false
Rails/HasAndBelongsToMany: Rails/HasAndBelongsToMany:
Enabled: false Enabled: false
Bundler/OrderedGems:
Enabled: false
AllCops: AllCops:
TargetRubyVersion: 2.3 TargetRubyVersion: 2.3
Exclude: Exclude:
@@ -94,4 +88,3 @@ AllCops:
- 'Rakefile' - 'Rakefile'
- 'node_modules/**/*' - 'node_modules/**/*'
- 'Vagrantfile' - 'Vagrantfile'
- 'vendor/**/*'

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

@@ -15,10 +15,7 @@ env:
- LOCAL_DOMAIN=cb6e6126.ngrok.io - LOCAL_DOMAIN=cb6e6126.ngrok.io
- LOCAL_HTTPS=true - LOCAL_HTTPS=true
- RAILS_ENV=test - RAILS_ENV=test
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true - CXX=g++-4.8
- PARALLEL_TEST_PROCESSORS=2
- "PATH=$HOME:$PATH"
addons: addons:
postgresql: 9.4 postgresql: 9.4
apt: apt:
@@ -26,10 +23,8 @@ addons:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- trusty-media - trusty-media
packages: packages:
- g++-4.8
- ffmpeg - ffmpeg
- g++-6
- libprotobuf-dev
- protobuf-compiler
rvm: rvm:
- 2.3.4 - 2.3.4
@@ -38,18 +33,18 @@ rvm:
services: services:
- redis-server - redis-server
bundler_args: --without development production --retry=3 --jobs=3
install: install:
- nvm install - nvm install
- npm install -g yarn - npm install -g yarn
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16 - bundle install
- yarn install - yarn install
before_script: before_script:
- bundle exec rake parallel:create parallel:load_schema parallel:prepare - bundle exec rails db:create db:migrate
- bundle exec rails assets:precompile
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
script: script:
- bundle exec parallel_test spec/ --group-by filesize --type rspec - bundle exec rspec
- npm test - npm test
- bundle exec i18n-tasks unused - i18n-tasks unused

View File

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

View File

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

View File

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

161
Gemfile
View File

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

View File

@@ -1,40 +1,40 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (5.0.3) actioncable (5.0.2)
actionpack (= 5.0.3) actionpack (= 5.0.2)
nio4r (>= 1.2, < 3.0) nio4r (>= 1.2, < 3.0)
websocket-driver (~> 0.6.1) websocket-driver (~> 0.6.1)
actionmailer (5.0.3) actionmailer (5.0.2)
actionpack (= 5.0.3) actionpack (= 5.0.2)
actionview (= 5.0.3) actionview (= 5.0.2)
activejob (= 5.0.3) activejob (= 5.0.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (5.0.3) actionpack (5.0.2)
actionview (= 5.0.3) actionview (= 5.0.2)
activesupport (= 5.0.3) activesupport (= 5.0.2)
rack (~> 2.0) rack (~> 2.0)
rack-test (~> 0.6.3) rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.0.3) actionview (5.0.2)
activesupport (= 5.0.3) activesupport (= 5.0.2)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.0, >= 1.0.3)
active_record_query_trace (1.5.4) active_record_query_trace (1.5.4)
activejob (5.0.3) activejob (5.0.2)
activesupport (= 5.0.3) activesupport (= 5.0.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.0.3) activemodel (5.0.2)
activesupport (= 5.0.3) activesupport (= 5.0.2)
activerecord (5.0.3) activerecord (5.0.2)
activemodel (= 5.0.3) activemodel (= 5.0.2)
activesupport (= 5.0.3) activesupport (= 5.0.2)
arel (~> 7.0) arel (~> 7.0)
activesupport (5.0.3) activesupport (5.0.2)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7) i18n (~> 0.7)
minitest (~> 5.1) minitest (~> 5.1)
@@ -43,41 +43,45 @@ GEM
public_suffix (~> 2.0, >= 2.0.2) public_suffix (~> 2.0, >= 2.0.2)
airbrussh (1.2.0) airbrussh (1.2.0)
sshkit (>= 1.6.1, != 1.7.0) sshkit (>= 1.6.1, != 1.7.0)
annotate (2.7.1)
activerecord (>= 3.2, < 6.0)
rake (>= 10.4, < 12.0)
arel (7.1.4) arel (7.1.4)
ast (2.3.0) ast (2.3.0)
attr_encrypted (3.0.3) attr_encrypted (3.0.3)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
autoprefixer-rails (6.7.7.2)
execjs
av (0.9.0) av (0.9.0)
cocaine (~> 0.5.3) cocaine (~> 0.5.3)
aws-sdk (2.9.21) aws-sdk (2.9.12)
aws-sdk-resources (= 2.9.21) aws-sdk-resources (= 2.9.12)
aws-sdk-core (2.9.21) aws-sdk-core (2.9.12)
aws-sigv4 (~> 1.0) aws-sigv4 (~> 1.0)
jmespath (~> 1.0) jmespath (~> 1.0)
aws-sdk-resources (2.9.21) aws-sdk-resources (2.9.12)
aws-sdk-core (= 2.9.21) aws-sdk-core (= 2.9.12)
aws-sigv4 (1.0.0) aws-sigv4 (1.0.0)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
bcrypt (3.1.11) bcrypt (3.1.11)
best_in_place (3.0.3)
actionpack (>= 3.2)
railties (>= 3.2)
better_errors (2.1.1) better_errors (2.1.1)
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubis (>= 2.6.6) erubis (>= 2.6.6)
rack (>= 0.9.0) rack (>= 0.9.0)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (0.3.0) browserify-rails (4.1.0)
msgpack (~> 1.0) addressable (>= 2.4.0)
brakeman (3.6.1) railties (>= 4.0.0, < 5.1)
sprockets (>= 3.6.0)
builder (3.2.3) builder (3.2.3)
bullet (5.5.1) bullet (5.5.1)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0) uniform_notifier (~> 1.10.0)
bundler-audit (0.5.0) capistrano (3.8.0)
bundler (~> 1.2)
thor (~> 0.18)
capistrano (3.8.1)
airbrussh (>= 1.0.0) airbrussh (>= 1.0.0)
i18n i18n
rake (>= 10.0.0) rake (>= 10.0.0)
@@ -85,6 +89,8 @@ GEM
capistrano-bundler (1.2.0) capistrano-bundler (1.2.0)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.2) sshkit (~> 1.2)
capistrano-faster-assets (1.0.2)
capistrano (>= 3.1)
capistrano-rails (1.2.3) capistrano-rails (1.2.3)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-bundler (~> 1.1) capistrano-bundler (~> 1.1)
@@ -93,7 +99,7 @@ GEM
sshkit (~> 1.3) sshkit (~> 1.3)
capistrano-yarn (2.0.2) capistrano-yarn (2.0.2)
capistrano (~> 3.0) capistrano (~> 3.0)
capybara (2.14.0) capybara (2.13.0)
addressable addressable
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
@@ -101,23 +107,28 @@ GEM
rack-test (>= 0.5.4) rack-test (>= 0.5.4)
xpath (~> 2.0) xpath (~> 2.0)
chunky_png (1.3.8) chunky_png (1.3.8)
cld3 (3.1.2) climate_control (0.1.0)
ffi (>= 1.1.0, < 1.10.0)
climate_control (0.2.0)
cocaine (0.5.8) cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
coderay (1.1.1) coderay (1.1.1)
coffee-rails (4.2.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.2.x)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
colorize (0.8.1) colorize (0.8.1)
concurrent-ruby (1.0.5) concurrent-ruby (1.0.5)
connection_pool (2.2.1) connection_pool (2.2.1)
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
crass (1.0.2) crass (1.0.2)
debug_inspector (0.0.3) debug_inspector (0.0.2)
devise (4.3.0) devise (4.2.1)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2) railties (>= 4.1.0, < 5.1)
responders responders
warden (~> 1.2.3) warden (~> 1.2.3)
devise-two-factor (3.0.0) devise-two-factor (3.0.0)
@@ -132,24 +143,25 @@ GEM
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.2.5) doorkeeper (4.2.5)
railties (>= 4.2) railties (>= 4.2)
dotenv (2.2.1) dotenv (2.2.0)
dotenv-rails (2.2.1) dotenv-rails (2.2.0)
dotenv (= 2.2.1) dotenv (= 2.2.0)
railties (>= 3.2, < 5.2) railties (>= 3.2, < 5.1)
easy_translate (0.5.0) easy_translate (0.5.0)
json json
thread thread
thread_safe thread_safe
encryptor (3.0.0) encryptor (3.0.0)
erubis (2.7.0) erubis (2.7.0)
et-orbi (1.0.4) et-orbi (1.0.3)
tzinfo tzinfo
execjs (2.7.0) execjs (2.7.0)
fabrication (2.16.1) fabrication (2.16.1)
faker (1.7.3) faker (1.7.3)
i18n (~> 0.5) i18n (~> 0.5)
fast_blank (1.0.0) fast_blank (1.0.0)
ffi (1.9.18) font-awesome-rails (4.7.0.1)
railties (>= 3.2, < 5.1)
fuubar (2.2.0) fuubar (2.2.0)
rspec-core (~> 3.0) rspec-core (~> 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
@@ -168,7 +180,7 @@ GEM
activesupport (>= 4.0.1) activesupport (>= 4.0.1)
hamlit (>= 1.2.0) hamlit (>= 1.2.0)
railties (>= 4.0.1) railties (>= 4.0.1)
hashdiff (0.3.4) hashdiff (0.3.2)
highline (1.7.8) highline (1.7.8)
hiredis (0.6.1) hiredis (0.6.1)
htmlentities (4.3.4) htmlentities (4.3.4)
@@ -179,14 +191,14 @@ GEM
http_parser.rb (~> 0.6.0) http_parser.rb (~> 0.6.0)
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
http-form_data (1.0.3) http-form_data (1.0.1)
http_accept_language (2.1.0) http_accept_language (2.1.0)
http_parser.rb (0.6.0) http_parser.rb (0.6.0)
httplog (0.99.3) httplog (0.99.3)
colorize colorize
rack rack
i18n (0.8.1) i18n (0.8.1)
i18n-tasks (0.9.15) i18n-tasks (0.9.13)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
easy_translate (>= 0.5.0) easy_translate (>= 0.5.0)
@@ -197,6 +209,10 @@ GEM
rainbow (~> 2.2) rainbow (~> 2.2)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
jmespath (1.3.1) jmespath (1.3.1)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.1.0) json (2.1.0)
kaminari (1.0.1) kaminari (1.0.1)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
@@ -219,16 +235,19 @@ GEM
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
link_header (0.0.8) link_header (0.0.8)
lograge (0.5.1) local_time (1.0.3)
actionpack (>= 4, < 5.2) coffee-rails
activesupport (>= 4, < 5.2) lograge (0.4.1)
railties (>= 4, < 5.2) actionpack (>= 4, < 5.1)
activesupport (>= 4, < 5.1)
railties (>= 4, < 5.1)
loofah (2.0.3) loofah (2.0.3)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.6.5) mail (2.6.5)
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
method_source (0.8.2) method_source (0.8.2)
microformats2 (3.1.0) microformats2 (2.1.0)
activesupport
json json
nokogiri nokogiri
mime-types (3.1) mime-types (3.1)
@@ -236,18 +255,16 @@ GEM
mime-types-data (3.2016.0521) mime-types-data (3.2016.0521)
mimemagic (0.3.2) mimemagic (0.3.2)
mini_portile2 (2.1.0) mini_portile2 (2.1.0)
minitest (5.10.2) minitest (5.10.1)
msgpack (1.1.0)
multi_json (1.12.1)
net-scp (1.2.1) net-scp (1.2.1)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (4.1.0) net-ssh (4.1.0)
nio4r (2.0.0) nio4r (2.0.0)
nokogiri (1.7.2) nokogiri (1.7.1)
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.1.0)
nokogumbo (1.4.11) nokogumbo (1.4.10)
nokogiri nokogiri
oj (3.0.9) oj (3.0.2)
openssl (2.0.3) openssl (2.0.3)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ostatus2 (2.0.0) ostatus2 (2.0.0)
@@ -255,7 +272,7 @@ GEM
http (~> 2.0) http (~> 2.0)
nokogiri (~> 1.6) nokogiri (~> 1.6)
openssl (~> 2.0) openssl (~> 2.0)
ox (2.5.0) ox (2.4.13)
paperclip (5.1.0) paperclip (5.1.0)
activemodel (>= 4.2.0) activemodel (>= 4.2.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
@@ -265,13 +282,10 @@ GEM
paperclip-av-transcoder (0.6.4) paperclip-av-transcoder (0.6.4)
av (~> 0.9.0) av (~> 0.9.0)
paperclip (>= 2.5.2) paperclip (>= 2.5.2)
parallel (1.11.2)
parallel_tests (2.14.1)
parallel
parser (2.4.0.0) parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
pg (0.20.0) pg (0.20.0)
pghero (1.7.0) pghero (1.6.5)
activerecord activerecord
pkg-config (1.2.0) pkg-config (1.2.0)
powerpack (0.1.1) powerpack (0.1.1)
@@ -285,50 +299,61 @@ GEM
puma (3.8.2) puma (3.8.2)
rabl (0.13.1) rabl (0.13.1)
activesupport (>= 2.3.14) activesupport (>= 2.3.14)
rack (2.0.3) rack (2.0.1)
rack-attack (5.0.1) rack-attack (5.0.1)
rack rack
rack-cors (0.4.1) rack-cors (0.4.1)
rack-protection (2.0.0) rack-protection (1.5.3)
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rack-timeout (0.4.2) rack-timeout (0.4.2)
rails (5.0.3) rails (5.0.2)
actioncable (= 5.0.3) actioncable (= 5.0.2)
actionmailer (= 5.0.3) actionmailer (= 5.0.2)
actionpack (= 5.0.3) actionpack (= 5.0.2)
actionview (= 5.0.3) actionview (= 5.0.2)
activejob (= 5.0.3) activejob (= 5.0.2)
activemodel (= 5.0.3) activemodel (= 5.0.2)
activerecord (= 5.0.3) activerecord (= 5.0.2)
activesupport (= 5.0.3) activesupport (= 5.0.2)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 5.0.3) railties (= 5.0.2)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.2) rails-controller-testing (1.0.1)
actionpack (~> 5.x, >= 5.0.1) actionpack (~> 5.x)
actionview (~> 5.x, >= 5.0.1) actionview (~> 5.x)
activesupport (~> 5.x) activesupport (~> 5.x)
rails-dom-testing (2.0.3) rails-dom-testing (2.0.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0, < 6.0)
nokogiri (>= 1.6) nokogiri (~> 1.6)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
rails-i18n (5.0.4) rails-i18n (5.0.3)
i18n (~> 0.7) i18n (~> 0.7)
railties (~> 5.0) railties (~> 5.0)
rails-settings-cached (0.6.5) rails-settings-cached (0.6.5)
rails (>= 4.2.0) rails (>= 4.2.0)
railties (5.0.3) rails_12factor (0.0.3)
actionpack (= 5.0.3) rails_serve_static_assets
activesupport (= 5.0.3) rails_stdout_logging
rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5)
railties (5.0.2)
actionpack (= 5.0.2)
activesupport (= 5.0.2)
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.2.2) rainbow (2.2.2)
rake rake
rake (11.3.0) rake (12.0.0)
react-rails (1.11.0)
babel-transpiler (>= 0.7.0)
connection_pool
execjs
railties (>= 3.2)
tilt
redis (3.3.3) redis (3.3.3)
redis-actionpack (5.0.1) redis-actionpack (5.0.1)
actionpack (>= 4.0, < 6) actionpack (>= 4.0, < 6)
@@ -337,8 +362,6 @@ GEM
redis-activesupport (5.0.2) redis-activesupport (5.0.2)
activesupport (>= 3, < 6) activesupport (>= 3, < 6)
redis-store (~> 1.3.0) redis-store (~> 1.3.0)
redis-namespace (1.5.3)
redis (~> 3.0, >= 3.0.4)
redis-rack (2.0.2) redis-rack (2.0.2)
rack (>= 1.5, < 3) rack (>= 1.5, < 3)
redis-store (>= 1.2, < 1.4) redis-store (>= 1.2, < 1.4)
@@ -348,32 +371,31 @@ GEM
redis-store (>= 1.2, < 2) redis-store (>= 1.2, < 2)
redis-store (1.3.0) redis-store (1.3.0)
redis (>= 2.2) redis (>= 2.2)
responders (2.4.0) responders (2.3.0)
actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.1)
railties (>= 4.2.0, < 5.3)
rotp (2.1.2) rotp (2.1.2)
rqrcode (0.10.1) rqrcode (0.10.1)
chunky_png (~> 1.0) chunky_png (~> 1.0)
rspec-core (3.6.0) rspec-core (3.5.4)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-expectations (3.6.0) rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-mocks (3.6.0) rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-rails (3.6.0) rspec-rails (3.5.2)
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
railties (>= 3.0) railties (>= 3.0)
rspec-core (~> 3.6.0) rspec-core (~> 3.5.0)
rspec-expectations (~> 3.6.0) rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.6.0) rspec-mocks (~> 3.5.0)
rspec-support (~> 3.6.0) rspec-support (~> 3.5.0)
rspec-sidekiq (3.0.1) rspec-sidekiq (3.0.0)
rspec-core (~> 3.0, >= 3.0.0) rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0) sidekiq (>= 2.4.0)
rspec-support (3.6.0) rspec-support (3.5.0)
rubocop (0.48.1) rubocop (0.48.1)
parser (>= 2.3.3.1, < 3.0) parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
@@ -389,33 +411,36 @@ GEM
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
nokogumbo (~> 1.4.1) nokogumbo (~> 1.4.1)
sass (3.4.24) sass (3.4.23)
scss_lint (0.53.0) sass-rails (5.0.6)
rake (>= 0.9, < 13) railties (>= 4.0.0, < 6)
sass (~> 3.4.20) sass (~> 3.1)
sidekiq (5.0.0) sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sidekiq (4.2.10)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
redis (~> 3.3, >= 3.3.3) redis (~> 3.2, >= 3.2.1)
sidekiq-scheduler (2.1.4) sidekiq-scheduler (2.1.4)
redis (~> 3) redis (~> 3)
rufus-scheduler (~> 3.2) rufus-scheduler (~> 3.2)
sidekiq (>= 3) sidekiq (>= 3)
tilt (>= 1.4.0) tilt (>= 1.4.0)
sidekiq-unique-jobs (5.0.8) sidekiq-unique-jobs (5.0.0)
sidekiq (>= 4.0, <= 6.0) sidekiq (>= 4.0)
thor (~> 0) thor
simple-navigation (4.0.5) simple-navigation (4.0.5)
activesupport (>= 2.3.2) activesupport (>= 2.3.2)
simple_form (3.5.0) simple_form (3.4.0)
actionpack (> 4, < 5.2) actionpack (> 4, < 5.1)
activemodel (> 4, < 5.2) activemodel (> 4, < 5.1)
simplecov (0.14.1) simplecov (0.14.1)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
simplecov-html (0.10.1) simplecov-html (0.10.0)
slop (3.6.0) slop (3.6.0)
sprockets (3.7.1) sprockets (3.7.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
@@ -429,8 +454,8 @@ GEM
net-ssh (>= 2.8.0) net-ssh (>= 2.8.0)
statsd-instrument (2.1.2) statsd-instrument (2.1.2)
temple (0.8.0) temple (0.8.0)
terminal-table (1.8.0) terminal-table (1.7.3)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1.1)
thor (0.19.4) thor (0.19.4)
thread (0.2.2) thread (0.2.2)
thread_safe (0.3.6) thread_safe (0.3.6)
@@ -446,7 +471,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.4) unf_ext (0.0.7.4)
unicode-display_width (1.2.1) unicode-display_width (1.1.3)
uniform_notifier (1.10.0) uniform_notifier (1.10.0)
warden (1.2.7) warden (1.2.7)
rack (>= 1.0) rack (>= 1.0)
@@ -454,13 +479,10 @@ GEM
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff hashdiff
webpacker (1.2)
activesupport (>= 4.2)
multi_json (~> 1.2)
railties (>= 4.2)
websocket-driver (0.6.5) websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2) websocket-extensions (0.1.2)
whatlanguage (1.0.6)
xpath (2.0.0) xpath (2.0.0)
nokogiri (~> 1.3) nokogiri (~> 1.3)
@@ -468,87 +490,89 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
active_record_query_trace (~> 1.5) active_record_query_trace
addressable (~> 2.5) addressable
annotate (~> 2.7) autoprefixer-rails
aws-sdk (~> 2.9) aws-sdk (>= 2.0)
better_errors (~> 2.1) best_in_place (~> 3.0.1)
binding_of_caller (~> 0.7) better_errors
bootsnap (~> 0.3) binding_of_caller
brakeman (~> 3.6) browserify-rails
bullet (~> 5.5) bullet
bundler-audit (~> 0.5) capistrano (= 3.8.0)
capistrano (~> 3.8) capistrano-faster-assets (~> 1.0)
capistrano-rails (~> 1.2) capistrano-rails
capistrano-rbenv (~> 2.1) capistrano-rbenv
capistrano-yarn (~> 2.0) capistrano-yarn
capybara (~> 2.14) capybara
cld3 (~> 3.1) devise
devise (~> 4.2) devise-two-factor
devise-two-factor (~> 3.0) doorkeeper
doorkeeper (~> 4.2) dotenv-rails
dotenv-rails (~> 2.2) fabrication
fabrication (~> 2.16) faker
faker (~> 1.7) fast_blank
fast_blank (~> 1.0) font-awesome-rails
fuubar (~> 2.2) fuubar
goldfinger (~> 1.2) goldfinger
hamlit-rails (~> 0.2) hamlit-rails
hiredis (~> 0.6) hiredis
htmlentities (~> 4.3) htmlentities
http (~> 2.2) http
http_accept_language (~> 2.1) http_accept_language
httplog (~> 0.99) httplog
i18n-tasks (~> 0.9) i18n-tasks (~> 0.9.6)
kaminari (~> 1.0) jquery-rails
letter_opener (~> 1.4) kaminari
letter_opener_web (~> 1.3) letter_opener
link_header (~> 0.0) letter_opener_web
lograge (~> 0.5) link_header
microformats2 (~> 3.0) local_time
nokogiri (~> 1.7) lograge
oj (~> 3.0) microformats2
nokogiri
oj
ostatus2 (~> 2.0) ostatus2 (~> 2.0)
ox (~> 2.5) ox
paperclip (~> 5.1) paperclip (~> 5.1)
paperclip-av-transcoder (~> 0.6) paperclip-av-transcoder
parallel_tests (~> 2.14) pg
pg (~> 0.20) pghero
pghero (~> 1.7) pkg-config
pkg-config (~> 1.2) pry-rails
pry-rails (~> 0.3) puma
puma (~> 3.8) rabl
rabl (~> 0.13) rack-attack
rack-attack (~> 5.0) rack-cors
rack-cors (~> 0.4) rack-timeout
rack-timeout (~> 0.4) rails (~> 5.0.2)
rails (~> 5.0) rails-controller-testing
rails-controller-testing (~> 1.0) rails-i18n
rails-i18n (~> 5.0) rails-settings-cached
rails-settings-cached (~> 0.6) rails_12factor
redis (~> 3.3) react-rails
redis-namespace (~> 1.5) redis (~> 3.2)
redis-rails (~> 5.0) redis-rails
rqrcode (~> 0.10) rqrcode
rspec-rails (~> 3.6) rspec-rails
rspec-sidekiq (~> 3.0) rspec-sidekiq
rubocop (~> 0.48) rubocop
ruby-oembed (~> 0.12) ruby-oembed
sanitize (~> 4.4) sanitize
scss_lint (~> 0.53) sass-rails (~> 5.0)
sidekiq (~> 5.0) sidekiq
sidekiq-scheduler (~> 2.1) sidekiq-scheduler
sidekiq-unique-jobs (~> 5.0) sidekiq-unique-jobs
simple-navigation (~> 4.0) simple-navigation
simple_form (~> 3.4) simple_form
simplecov (~> 0.14) simplecov
sprockets-rails (~> 3.2) sprockets-rails
statsd-instrument (~> 2.1) statsd-instrument
twitter-text (~> 1.14) twitter-text
tzinfo-data (~> 1.2017) tzinfo-data
uglifier (~> 3.2) uglifier (>= 1.3.0)
webmock (~> 3.0) webmock
webpacker (~> 1.2) whatlanguage
RUBY VERSION RUBY VERSION
ruby 2.4.1p111 ruby 2.4.1p111

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

@@ -13,7 +13,7 @@ An alternative implementation of the GNU social project. Based on [ActivityStrea
Click on the screenshot to watch a demo of the UI: Click on the screenshot to watch a demo of the UI:
[![Screenshot](http://puu.sh/vMcvm/290f459dd4.jpg)][youtube_demo] [![Screenshot](https://i.imgur.com/T2q5V65.png)][youtube_demo]
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU [youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
@@ -48,10 +48,6 @@ If you would like, you can [support the development of this project on Patreon][
- **Deployable via Docker** - **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 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
## Development
Please follow the [development guide](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Development-guide.md) from the documentation repository.
## Deployment ## Deployment
There are guides in the documentation repository for [deploying on various platforms](https://github.com/tootsuite/documentation#running-mastodon). There are guides in the documentation repository for [deploying on various platforms](https://github.com/tootsuite/documentation#running-mastodon).

30
Vagrantfile vendored
View File

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

View File

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

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 339 KiB

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 258 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

View File

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 174 B

View File

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

View File

@@ -0,0 +1,9 @@
//= require jquery2
//= require jquery_ujs
//= require extras
//= require best_in_place
//= require local_time
$(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,4 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
import Immutable from 'immutable'; import Immutable from 'immutable';
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
@@ -37,14 +37,6 @@ 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_SUCCESS = 'ACCOUNT_TIMELINE_EXPAND_SUCCESS';
export const ACCOUNT_TIMELINE_EXPAND_FAIL = 'ACCOUNT_TIMELINE_EXPAND_FAIL'; export const ACCOUNT_TIMELINE_EXPAND_FAIL = 'ACCOUNT_TIMELINE_EXPAND_FAIL';
export const ACCOUNT_MEDIA_TIMELINE_FETCH_REQUEST = 'ACCOUNT_MEDIA_TIMELINE_FETCH_REQUEST';
export const ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS = 'ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS';
export const ACCOUNT_MEDIA_TIMELINE_FETCH_FAIL = 'ACCOUNT_MEDIA_TIMELINE_FETCH_FAIL';
export const ACCOUNT_MEDIA_TIMELINE_EXPAND_REQUEST = 'ACCOUNT_MEDIA_TIMELINE_EXPAND_REQUEST';
export const ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS = 'ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS';
export const ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL = 'ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL';
export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST'; export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST';
export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS'; export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS';
export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL'; export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL';
@@ -104,49 +96,24 @@ export function fetchAccountTimeline(id, replace = false) {
const ids = getState().getIn(['timelines', 'accounts_timelines', id, 'items'], Immutable.List()); const ids = getState().getIn(['timelines', 'accounts_timelines', id, 'items'], Immutable.List());
const newestId = ids.size > 0 ? ids.first() : null; const newestId = ids.size > 0 ? ids.first() : null;
let params = {}; let params = '';
let skipLoading = false; let skipLoading = false;
if (newestId !== null && !replace) { if (newestId !== null && !replace) {
params.since_id = newestId; params = `?since_id=${newestId}`;
skipLoading = true; skipLoading = true;
} }
dispatch(fetchAccountTimelineRequest(id, skipLoading)); dispatch(fetchAccountTimelineRequest(id, skipLoading));
api(getState).get(`/api/v1/accounts/${id}/statuses`, { params }).then(response => { api(getState).get(`/api/v1/accounts/${id}/statuses${params}`).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(fetchAccountTimelineSuccess(id, response.data, replace, skipLoading));
dispatch(fetchAccountTimelineSuccess(id, response.data, replace, skipLoading, next));
}).catch(error => { }).catch(error => {
dispatch(fetchAccountTimelineFail(id, error, skipLoading)); dispatch(fetchAccountTimelineFail(id, error, skipLoading));
}); });
}; };
}; };
export function fetchAccountMediaTimeline(id, replace = false) {
return (dispatch, getState) => {
const ids = getState().getIn(['timelines', 'accounts_media_timelines', id, 'items'], Immutable.List());
const newestId = ids.size > 0 ? ids.first() : null;
let params = { only_media: 'true', limit: 12 };
let skipLoading = false;
if (newestId !== null && !replace) {
params.since_id = newestId;
skipLoading = true;
}
dispatch(fetchAccountMediaTimelineRequest(id, skipLoading));
api(getState).get(`/api/v1/accounts/${id}/statuses`, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(fetchAccountMediaTimelineSuccess(id, response.data, replace, skipLoading, next));
}).catch(error => {
dispatch(fetchAccountMediaTimelineFail(id, error, skipLoading));
});
};
};
export function expandAccountTimeline(id) { export function expandAccountTimeline(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
const lastId = getState().getIn(['timelines', 'accounts_timelines', id, 'items'], Immutable.List()).last(); const lastId = getState().getIn(['timelines', 'accounts_timelines', id, 'items'], Immutable.List()).last();
@@ -156,8 +123,8 @@ export function expandAccountTimeline(id) {
api(getState).get(`/api/v1/accounts/${id}/statuses`, { api(getState).get(`/api/v1/accounts/${id}/statuses`, {
params: { params: {
limit: 10, limit: 10,
max_id: lastId, max_id: lastId
}, }
}).then(response => { }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandAccountTimelineSuccess(id, response.data, next)); dispatch(expandAccountTimelineSuccess(id, response.data, next));
@@ -167,38 +134,17 @@ export function expandAccountTimeline(id) {
}; };
}; };
export function expandAccountMediaTimeline(id) {
return (dispatch, getState) => {
const lastId = getState().getIn(['timelines', 'accounts_media_timelines', id, 'items'], Immutable.List()).last();
dispatch(expandAccountMediaTimelineRequest(id));
api(getState).get(`/api/v1/accounts/${id}/statuses`, {
params: {
limit: 12,
only_media: 'true',
max_id: lastId,
},
}).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandAccountMediaTimelineSuccess(id, response.data, next));
}).catch(error => {
dispatch(expandAccountMediaTimelineFail(id, error));
});
};
};
export function fetchAccountRequest(id) { export function fetchAccountRequest(id) {
return { return {
type: ACCOUNT_FETCH_REQUEST, type: ACCOUNT_FETCH_REQUEST,
id, id
}; };
}; };
export function fetchAccountSuccess(account) { export function fetchAccountSuccess(account) {
return { return {
type: ACCOUNT_FETCH_SUCCESS, type: ACCOUNT_FETCH_SUCCESS,
account, account
}; };
}; };
@@ -207,7 +153,7 @@ export function fetchAccountFail(id, error) {
type: ACCOUNT_FETCH_FAIL, type: ACCOUNT_FETCH_FAIL,
id, id,
error, error,
skipAlert: true, skipAlert: true
}; };
}; };
@@ -232,48 +178,48 @@ export function unfollowAccount(id) {
}).catch(error => { }).catch(error => {
dispatch(unfollowAccountFail(error)); dispatch(unfollowAccountFail(error));
}); });
}; }
}; };
export function followAccountRequest(id) { export function followAccountRequest(id) {
return { return {
type: ACCOUNT_FOLLOW_REQUEST, type: ACCOUNT_FOLLOW_REQUEST,
id, id
}; };
}; };
export function followAccountSuccess(relationship) { export function followAccountSuccess(relationship) {
return { return {
type: ACCOUNT_FOLLOW_SUCCESS, type: ACCOUNT_FOLLOW_SUCCESS,
relationship, relationship
}; };
}; };
export function followAccountFail(error) { export function followAccountFail(error) {
return { return {
type: ACCOUNT_FOLLOW_FAIL, type: ACCOUNT_FOLLOW_FAIL,
error, error
}; };
}; };
export function unfollowAccountRequest(id) { export function unfollowAccountRequest(id) {
return { return {
type: ACCOUNT_UNFOLLOW_REQUEST, type: ACCOUNT_UNFOLLOW_REQUEST,
id, id
}; };
}; };
export function unfollowAccountSuccess(relationship) { export function unfollowAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNFOLLOW_SUCCESS, type: ACCOUNT_UNFOLLOW_SUCCESS,
relationship, relationship
}; };
}; };
export function unfollowAccountFail(error) { export function unfollowAccountFail(error) {
return { return {
type: ACCOUNT_UNFOLLOW_FAIL, type: ACCOUNT_UNFOLLOW_FAIL,
error, error
}; };
}; };
@@ -281,18 +227,17 @@ export function fetchAccountTimelineRequest(id, skipLoading) {
return { return {
type: ACCOUNT_TIMELINE_FETCH_REQUEST, type: ACCOUNT_TIMELINE_FETCH_REQUEST,
id, id,
skipLoading, skipLoading
}; };
}; };
export function fetchAccountTimelineSuccess(id, statuses, replace, skipLoading, next) { export function fetchAccountTimelineSuccess(id, statuses, replace, skipLoading) {
return { return {
type: ACCOUNT_TIMELINE_FETCH_SUCCESS, type: ACCOUNT_TIMELINE_FETCH_SUCCESS,
id, id,
statuses, statuses,
replace, replace,
skipLoading, skipLoading
next,
}; };
}; };
@@ -302,43 +247,14 @@ export function fetchAccountTimelineFail(id, error, skipLoading) {
id, id,
error, error,
skipLoading, skipLoading,
skipAlert: error.response.status === 404, skipAlert: error.response.status === 404
};
};
export function fetchAccountMediaTimelineRequest(id, skipLoading) {
return {
type: ACCOUNT_MEDIA_TIMELINE_FETCH_REQUEST,
id,
skipLoading,
};
};
export function fetchAccountMediaTimelineSuccess(id, statuses, replace, skipLoading, next) {
return {
type: ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS,
id,
statuses,
replace,
skipLoading,
next,
};
};
export function fetchAccountMediaTimelineFail(id, error, skipLoading) {
return {
type: ACCOUNT_MEDIA_TIMELINE_FETCH_FAIL,
id,
error,
skipLoading,
skipAlert: error.response.status === 404,
}; };
}; };
export function expandAccountTimelineRequest(id) { export function expandAccountTimelineRequest(id) {
return { return {
type: ACCOUNT_TIMELINE_EXPAND_REQUEST, type: ACCOUNT_TIMELINE_EXPAND_REQUEST,
id, id
}; };
}; };
@@ -347,7 +263,7 @@ export function expandAccountTimelineSuccess(id, statuses, next) {
type: ACCOUNT_TIMELINE_EXPAND_SUCCESS, type: ACCOUNT_TIMELINE_EXPAND_SUCCESS,
id, id,
statuses, statuses,
next, next
}; };
}; };
@@ -355,31 +271,7 @@ export function expandAccountTimelineFail(id, error) {
return { return {
type: ACCOUNT_TIMELINE_EXPAND_FAIL, type: ACCOUNT_TIMELINE_EXPAND_FAIL,
id, id,
error, error
};
};
export function expandAccountMediaTimelineRequest(id) {
return {
type: ACCOUNT_MEDIA_TIMELINE_EXPAND_REQUEST,
id,
};
};
export function expandAccountMediaTimelineSuccess(id, statuses, next) {
return {
type: ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS,
id,
statuses,
next,
};
};
export function expandAccountMediaTimelineFail(id, error) {
return {
type: ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL,
id,
error,
}; };
}; };
@@ -411,7 +303,7 @@ export function unblockAccount(id) {
export function blockAccountRequest(id) { export function blockAccountRequest(id) {
return { return {
type: ACCOUNT_BLOCK_REQUEST, type: ACCOUNT_BLOCK_REQUEST,
id, id
}; };
}; };
@@ -419,35 +311,35 @@ export function blockAccountSuccess(relationship, statuses) {
return { return {
type: ACCOUNT_BLOCK_SUCCESS, type: ACCOUNT_BLOCK_SUCCESS,
relationship, relationship,
statuses, statuses
}; };
}; };
export function blockAccountFail(error) { export function blockAccountFail(error) {
return { return {
type: ACCOUNT_BLOCK_FAIL, type: ACCOUNT_BLOCK_FAIL,
error, error
}; };
}; };
export function unblockAccountRequest(id) { export function unblockAccountRequest(id) {
return { return {
type: ACCOUNT_UNBLOCK_REQUEST, type: ACCOUNT_UNBLOCK_REQUEST,
id, id
}; };
}; };
export function unblockAccountSuccess(relationship) { export function unblockAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNBLOCK_SUCCESS, type: ACCOUNT_UNBLOCK_SUCCESS,
relationship, relationship
}; };
}; };
export function unblockAccountFail(error) { export function unblockAccountFail(error) {
return { return {
type: ACCOUNT_UNBLOCK_FAIL, type: ACCOUNT_UNBLOCK_FAIL,
error, error
}; };
}; };
@@ -480,7 +372,7 @@ export function unmuteAccount(id) {
export function muteAccountRequest(id) { export function muteAccountRequest(id) {
return { return {
type: ACCOUNT_MUTE_REQUEST, type: ACCOUNT_MUTE_REQUEST,
id, id
}; };
}; };
@@ -488,35 +380,35 @@ export function muteAccountSuccess(relationship, statuses) {
return { return {
type: ACCOUNT_MUTE_SUCCESS, type: ACCOUNT_MUTE_SUCCESS,
relationship, relationship,
statuses, statuses
}; };
}; };
export function muteAccountFail(error) { export function muteAccountFail(error) {
return { return {
type: ACCOUNT_MUTE_FAIL, type: ACCOUNT_MUTE_FAIL,
error, error
}; };
}; };
export function unmuteAccountRequest(id) { export function unmuteAccountRequest(id) {
return { return {
type: ACCOUNT_UNMUTE_REQUEST, type: ACCOUNT_UNMUTE_REQUEST,
id, id
}; };
}; };
export function unmuteAccountSuccess(relationship) { export function unmuteAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNMUTE_SUCCESS, type: ACCOUNT_UNMUTE_SUCCESS,
relationship, relationship
}; };
}; };
export function unmuteAccountFail(error) { export function unmuteAccountFail(error) {
return { return {
type: ACCOUNT_UNMUTE_FAIL, type: ACCOUNT_UNMUTE_FAIL,
error, error
}; };
}; };
@@ -539,7 +431,7 @@ export function fetchFollowers(id) {
export function fetchFollowersRequest(id) { export function fetchFollowersRequest(id) {
return { return {
type: FOLLOWERS_FETCH_REQUEST, type: FOLLOWERS_FETCH_REQUEST,
id, id
}; };
}; };
@@ -548,7 +440,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
type: FOLLOWERS_FETCH_SUCCESS, type: FOLLOWERS_FETCH_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -556,7 +448,7 @@ export function fetchFollowersFail(id, error) {
return { return {
type: FOLLOWERS_FETCH_FAIL, type: FOLLOWERS_FETCH_FAIL,
id, id,
error, error
}; };
}; };
@@ -584,7 +476,7 @@ export function expandFollowers(id) {
export function expandFollowersRequest(id) { export function expandFollowersRequest(id) {
return { return {
type: FOLLOWERS_EXPAND_REQUEST, type: FOLLOWERS_EXPAND_REQUEST,
id, id
}; };
}; };
@@ -593,7 +485,7 @@ export function expandFollowersSuccess(id, accounts, next) {
type: FOLLOWERS_EXPAND_SUCCESS, type: FOLLOWERS_EXPAND_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -601,7 +493,7 @@ export function expandFollowersFail(id, error) {
return { return {
type: FOLLOWERS_EXPAND_FAIL, type: FOLLOWERS_EXPAND_FAIL,
id, id,
error, error
}; };
}; };
@@ -623,7 +515,7 @@ export function fetchFollowing(id) {
export function fetchFollowingRequest(id) { export function fetchFollowingRequest(id) {
return { return {
type: FOLLOWING_FETCH_REQUEST, type: FOLLOWING_FETCH_REQUEST,
id, id
}; };
}; };
@@ -632,7 +524,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
type: FOLLOWING_FETCH_SUCCESS, type: FOLLOWING_FETCH_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -640,7 +532,7 @@ export function fetchFollowingFail(id, error) {
return { return {
type: FOLLOWING_FETCH_FAIL, type: FOLLOWING_FETCH_FAIL,
id, id,
error, error
}; };
}; };
@@ -668,7 +560,7 @@ export function expandFollowing(id) {
export function expandFollowingRequest(id) { export function expandFollowingRequest(id) {
return { return {
type: FOLLOWING_EXPAND_REQUEST, type: FOLLOWING_EXPAND_REQUEST,
id, id
}; };
}; };
@@ -677,7 +569,7 @@ export function expandFollowingSuccess(id, accounts, next) {
type: FOLLOWING_EXPAND_SUCCESS, type: FOLLOWING_EXPAND_SUCCESS,
id, id,
accounts, accounts,
next, next
}; };
}; };
@@ -685,7 +577,7 @@ export function expandFollowingFail(id, error) {
return { return {
type: FOLLOWING_EXPAND_FAIL, type: FOLLOWING_EXPAND_FAIL,
id, id,
error, error
}; };
}; };
@@ -712,7 +604,7 @@ export function fetchRelationshipsRequest(ids) {
return { return {
type: RELATIONSHIPS_FETCH_REQUEST, type: RELATIONSHIPS_FETCH_REQUEST,
ids, ids,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -720,7 +612,7 @@ export function fetchRelationshipsSuccess(relationships) {
return { return {
type: RELATIONSHIPS_FETCH_SUCCESS, type: RELATIONSHIPS_FETCH_SUCCESS,
relationships, relationships,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -728,7 +620,7 @@ export function fetchRelationshipsFail(error) {
return { return {
type: RELATIONSHIPS_FETCH_FAIL, type: RELATIONSHIPS_FETCH_FAIL,
error, error,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -738,14 +630,14 @@ export function fetchFollowRequests() {
api(getState).get('/api/v1/follow_requests').then(response => { api(getState).get('/api/v1/follow_requests').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null))
}).catch(error => dispatch(fetchFollowRequestsFail(error))); }).catch(error => dispatch(fetchFollowRequestsFail(error)));
}; };
}; };
export function fetchFollowRequestsRequest() { export function fetchFollowRequestsRequest() {
return { return {
type: FOLLOW_REQUESTS_FETCH_REQUEST, type: FOLLOW_REQUESTS_FETCH_REQUEST
}; };
}; };
@@ -753,14 +645,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
return { return {
type: FOLLOW_REQUESTS_FETCH_SUCCESS, type: FOLLOW_REQUESTS_FETCH_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function fetchFollowRequestsFail(error) { export function fetchFollowRequestsFail(error) {
return { return {
type: FOLLOW_REQUESTS_FETCH_FAIL, type: FOLLOW_REQUESTS_FETCH_FAIL,
error, error
}; };
}; };
@@ -776,14 +668,14 @@ export function expandFollowRequests() {
api(getState).get(url).then(response => { api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null))
}).catch(error => dispatch(expandFollowRequestsFail(error))); }).catch(error => dispatch(expandFollowRequestsFail(error)));
}; };
}; };
export function expandFollowRequestsRequest() { export function expandFollowRequestsRequest() {
return { return {
type: FOLLOW_REQUESTS_EXPAND_REQUEST, type: FOLLOW_REQUESTS_EXPAND_REQUEST
}; };
}; };
@@ -791,14 +683,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
return { return {
type: FOLLOW_REQUESTS_EXPAND_SUCCESS, type: FOLLOW_REQUESTS_EXPAND_SUCCESS,
accounts, accounts,
next, next
}; };
}; };
export function expandFollowRequestsFail(error) { export function expandFollowRequestsFail(error) {
return { return {
type: FOLLOW_REQUESTS_EXPAND_FAIL, type: FOLLOW_REQUESTS_EXPAND_FAIL,
error, error
}; };
}; };
@@ -816,14 +708,14 @@ export function authorizeFollowRequest(id) {
export function authorizeFollowRequestRequest(id) { export function authorizeFollowRequestRequest(id) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST, type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
id, id
}; };
}; };
export function authorizeFollowRequestSuccess(id) { export function authorizeFollowRequestSuccess(id) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS, type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
id, id
}; };
}; };
@@ -831,7 +723,7 @@ export function authorizeFollowRequestFail(id, error) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_FAIL, type: FOLLOW_REQUEST_AUTHORIZE_FAIL,
id, id,
error, error
}; };
}; };
@@ -850,14 +742,14 @@ export function rejectFollowRequest(id) {
export function rejectFollowRequestRequest(id) { export function rejectFollowRequestRequest(id) {
return { return {
type: FOLLOW_REQUEST_REJECT_REQUEST, type: FOLLOW_REQUEST_REJECT_REQUEST,
id, id
}; };
}; };
export function rejectFollowRequestSuccess(id) { export function rejectFollowRequestSuccess(id) {
return { return {
type: FOLLOW_REQUEST_REJECT_SUCCESS, type: FOLLOW_REQUEST_REJECT_SUCCESS,
id, id
}; };
}; };
@@ -865,6 +757,6 @@ export function rejectFollowRequestFail(id, error) {
return { return {
type: FOLLOW_REQUEST_REJECT_FAIL, type: FOLLOW_REQUEST_REJECT_FAIL,
id, id,
error, error
}; };
}; };

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ export function fetchStatusCardRequest(id) {
return { return {
type: STATUS_CARD_FETCH_REQUEST, type: STATUS_CARD_FETCH_REQUEST,
id, id,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -37,7 +37,7 @@ export function fetchStatusCardSuccess(id, card) {
type: STATUS_CARD_FETCH_SUCCESS, type: STATUS_CARD_FETCH_SUCCESS,
id, id,
card, card,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -47,6 +47,6 @@ export function fetchStatusCardFail(id, error) {
id, id,
error, error,
skipLoading: true, skipLoading: true,
skipAlert: true, skipAlert: true
}; };
}; };

View File

@@ -35,7 +35,7 @@ export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
export function changeCompose(text) { export function changeCompose(text) {
return { return {
type: COMPOSE_CHANGE, type: COMPOSE_CHANGE,
text: text, text: text
}; };
}; };
@@ -43,7 +43,7 @@ export function replyCompose(status, router) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: COMPOSE_REPLY, type: COMPOSE_REPLY,
status: status, status: status
}); });
if (!getState().getIn(['compose', 'mounted'])) { if (!getState().getIn(['compose', 'mounted'])) {
@@ -54,7 +54,7 @@ export function replyCompose(status, router) {
export function cancelReplyCompose() { export function cancelReplyCompose() {
return { return {
type: COMPOSE_REPLY_CANCEL, type: COMPOSE_REPLY_CANCEL
}; };
}; };
@@ -62,7 +62,7 @@ export function mentionCompose(account, router) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ dispatch({
type: COMPOSE_MENTION, type: COMPOSE_MENTION,
account: account, account: account
}); });
if (!getState().getIn(['compose', 'mounted'])) { if (!getState().getIn(['compose', 'mounted'])) {
@@ -84,11 +84,11 @@ export function submitCompose() {
media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')), media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
sensitive: getState().getIn(['compose', 'sensitive']), sensitive: getState().getIn(['compose', 'sensitive']),
spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''), spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
visibility: getState().getIn(['compose', 'privacy']), visibility: getState().getIn(['compose', 'privacy'])
}, { }, {
headers: { headers: {
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']), 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey'])
}, }
}).then(function (response) { }).then(function (response) {
dispatch(submitComposeSuccess({ ...response.data })); dispatch(submitComposeSuccess({ ...response.data }));
@@ -112,21 +112,21 @@ export function submitCompose() {
export function submitComposeRequest() { export function submitComposeRequest() {
return { return {
type: COMPOSE_SUBMIT_REQUEST, type: COMPOSE_SUBMIT_REQUEST
}; };
}; };
export function submitComposeSuccess(status) { export function submitComposeSuccess(status) {
return { return {
type: COMPOSE_SUBMIT_SUCCESS, type: COMPOSE_SUBMIT_SUCCESS,
status: status, status: status
}; };
}; };
export function submitComposeFail(error) { export function submitComposeFail(error) {
return { return {
type: COMPOSE_SUBMIT_FAIL, type: COMPOSE_SUBMIT_FAIL,
error: error, error: error
}; };
}; };
@@ -144,7 +144,7 @@ export function uploadCompose(files) {
api(getState).post('/api/v1/media', data, { api(getState).post('/api/v1/media', data, {
onUploadProgress: function (e) { onUploadProgress: function (e) {
dispatch(uploadComposeProgress(e.loaded, e.total)); dispatch(uploadComposeProgress(e.loaded, e.total));
}, }
}).then(function (response) { }).then(function (response) {
dispatch(uploadComposeSuccess(response.data)); dispatch(uploadComposeSuccess(response.data));
}).catch(function (error) { }).catch(function (error) {
@@ -156,7 +156,7 @@ export function uploadCompose(files) {
export function uploadComposeRequest() { export function uploadComposeRequest() {
return { return {
type: COMPOSE_UPLOAD_REQUEST, type: COMPOSE_UPLOAD_REQUEST,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -164,7 +164,7 @@ export function uploadComposeProgress(loaded, total) {
return { return {
type: COMPOSE_UPLOAD_PROGRESS, type: COMPOSE_UPLOAD_PROGRESS,
loaded: loaded, loaded: loaded,
total: total, total: total
}; };
}; };
@@ -172,7 +172,7 @@ export function uploadComposeSuccess(media) {
return { return {
type: COMPOSE_UPLOAD_SUCCESS, type: COMPOSE_UPLOAD_SUCCESS,
media: media, media: media,
skipLoading: true, skipLoading: true
}; };
}; };
@@ -180,20 +180,20 @@ export function uploadComposeFail(error) {
return { return {
type: COMPOSE_UPLOAD_FAIL, type: COMPOSE_UPLOAD_FAIL,
error: error, error: error,
skipLoading: true, skipLoading: true
}; };
}; };
export function undoUploadCompose(media_id) { export function undoUploadCompose(media_id) {
return { return {
type: COMPOSE_UPLOAD_UNDO, type: COMPOSE_UPLOAD_UNDO,
media_id: media_id, media_id: media_id
}; };
}; };
export function clearComposeSuggestions() { export function clearComposeSuggestions() {
return { return {
type: COMPOSE_SUGGESTIONS_CLEAR, type: COMPOSE_SUGGESTIONS_CLEAR
}; };
}; };
@@ -203,8 +203,8 @@ export function fetchComposeSuggestions(token) {
params: { params: {
q: token, q: token,
resolve: false, resolve: false,
limit: 4, limit: 4
}, }
}).then(response => { }).then(response => {
dispatch(readyComposeSuggestions(token, response.data)); dispatch(readyComposeSuggestions(token, response.data));
}); });
@@ -215,7 +215,7 @@ export function readyComposeSuggestions(token, accounts) {
return { return {
type: COMPOSE_SUGGESTIONS_READY, type: COMPOSE_SUGGESTIONS_READY,
token, token,
accounts, accounts
}; };
}; };
@@ -227,20 +227,20 @@ export function selectComposeSuggestion(position, token, accountId) {
type: COMPOSE_SUGGESTION_SELECT, type: COMPOSE_SUGGESTION_SELECT,
position, position,
token, token,
completion, completion
}); });
}; };
}; };
export function mountCompose() { export function mountCompose() {
return { return {
type: COMPOSE_MOUNT, type: COMPOSE_MOUNT
}; };
}; };
export function unmountCompose() { export function unmountCompose() {
return { return {
type: COMPOSE_UNMOUNT, type: COMPOSE_UNMOUNT
}; };
}; };
@@ -252,21 +252,21 @@ export function changeComposeSensitivity() {
export function changeComposeSpoilerness() { export function changeComposeSpoilerness() {
return { return {
type: COMPOSE_SPOILERNESS_CHANGE, type: COMPOSE_SPOILERNESS_CHANGE
}; };
}; };
export function changeComposeSpoilerText(text) { export function changeComposeSpoilerText(text) {
return { return {
type: COMPOSE_SPOILER_TEXT_CHANGE, type: COMPOSE_SPOILER_TEXT_CHANGE,
text, text
}; };
}; };
export function changeComposeVisibility(value) { export function changeComposeVisibility(value) {
return { return {
type: COMPOSE_VISIBILITY_CHANGE, type: COMPOSE_VISIBILITY_CHANGE,
value, value
}; };
}; };
@@ -274,6 +274,6 @@ export function insertEmojiCompose(position, emoji) {
return { return {
type: COMPOSE_EMOJI_INSERT, type: COMPOSE_EMOJI_INSERT,
position, position,
emoji, emoji
}; };
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,13 +14,13 @@ export function initReport(account, status) {
return { return {
type: REPORT_INIT, type: REPORT_INIT,
account, account,
status, status
}; };
}; };
export function cancelReport() { export function cancelReport() {
return { return {
type: REPORT_CANCEL, type: REPORT_CANCEL
}; };
}; };
@@ -39,34 +39,34 @@ export function submitReport() {
api(getState).post('/api/v1/reports', { api(getState).post('/api/v1/reports', {
account_id: getState().getIn(['reports', 'new', 'account_id']), account_id: getState().getIn(['reports', 'new', 'account_id']),
status_ids: getState().getIn(['reports', 'new', 'status_ids']), status_ids: getState().getIn(['reports', 'new', 'status_ids']),
comment: getState().getIn(['reports', 'new', 'comment']), comment: getState().getIn(['reports', 'new', 'comment'])
}).then(response => dispatch(submitReportSuccess(response.data))).catch(error => dispatch(submitReportFail(error))); }).then(response => dispatch(submitReportSuccess(response.data))).catch(error => dispatch(submitReportFail(error)));
}; };
}; };
export function submitReportRequest() { export function submitReportRequest() {
return { return {
type: REPORT_SUBMIT_REQUEST, type: REPORT_SUBMIT_REQUEST
}; };
}; };
export function submitReportSuccess(report) { export function submitReportSuccess(report) {
return { return {
type: REPORT_SUBMIT_SUCCESS, type: REPORT_SUBMIT_SUCCESS,
report, report
}; };
}; };
export function submitReportFail(error) { export function submitReportFail(error) {
return { return {
type: REPORT_SUBMIT_FAIL, type: REPORT_SUBMIT_FAIL,
error, error
}; };
}; };
export function changeReportComment(comment) { export function changeReportComment(comment) {
return { return {
type: REPORT_COMMENT_CHANGE, type: REPORT_COMMENT_CHANGE,
comment, comment
}; };
}; };

View File

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

View File

@@ -6,14 +6,14 @@ export function changeSetting(key, value) {
return { return {
type: SETTING_CHANGE, type: SETTING_CHANGE,
key, key,
value, value
}; };
}; };
export function saveSettings() { export function saveSettings() {
return (_, getState) => { return (_, getState) => {
axios.put('/api/web/settings', { axios.put('/api/web/settings', {
data: getState().get('settings').toJS(), data: getState().get('settings').toJS()
}); });
}; };
}; };

View File

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

View File

@@ -12,6 +12,6 @@ export function hydrateStore(rawState) {
return { return {
type: STORE_HYDRATE, type: STORE_HYDRATE,
state, state
}; };
}; };

View File

@@ -1,4 +1,4 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api'
import Immutable from 'immutable'; import Immutable from 'immutable';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE'; export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
@@ -23,7 +23,7 @@ export function refreshTimelineSuccess(timeline, statuses, skipLoading, next) {
timeline, timeline,
statuses, statuses,
skipLoading, skipLoading,
next, next
}; };
}; };
@@ -35,7 +35,7 @@ export function updateTimeline(timeline, status) {
type: TIMELINE_UPDATE, type: TIMELINE_UPDATE,
timeline, timeline,
status, status,
references, references
}); });
}; };
}; };
@@ -51,7 +51,7 @@ export function deleteFromTimelines(id) {
id, id,
accountId, accountId,
references, references,
reblogOf, reblogOf
}); });
}; };
}; };
@@ -61,7 +61,7 @@ export function refreshTimelineRequest(timeline, id, skipLoading) {
type: TIMELINE_REFRESH_REQUEST, type: TIMELINE_REFRESH_REQUEST,
timeline, timeline,
id, id,
skipLoading, skipLoading
}; };
}; };
@@ -86,8 +86,6 @@ export function refreshTimeline(timeline, id = null) {
params = { ...params, since_id: newestId }; params = { ...params, since_id: newestId };
skipLoading = true; skipLoading = true;
} else if (getState().getIn(['timelines', timeline, 'loaded'])) {
skipLoading = true;
} }
dispatch(refreshTimelineRequest(timeline, id, skipLoading)); dispatch(refreshTimelineRequest(timeline, id, skipLoading));
@@ -106,7 +104,7 @@ export function refreshTimelineFail(timeline, error, skipLoading) {
type: TIMELINE_REFRESH_FAIL, type: TIMELINE_REFRESH_FAIL,
timeline, timeline,
error, error,
skipLoading, skipLoading
}; };
}; };
@@ -130,8 +128,8 @@ export function expandTimeline(timeline) {
params: { params: {
...params, ...params,
max_id: lastId, max_id: lastId,
limit: 10, limit: 10
}, }
}).then(response => { }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandTimelineSuccess(timeline, response.data, next ? next.uri : null)); dispatch(expandTimelineSuccess(timeline, response.data, next ? next.uri : null));
@@ -144,7 +142,7 @@ export function expandTimeline(timeline) {
export function expandTimelineRequest(timeline) { export function expandTimelineRequest(timeline) {
return { return {
type: TIMELINE_EXPAND_REQUEST, type: TIMELINE_EXPAND_REQUEST,
timeline, timeline
}; };
}; };
@@ -153,7 +151,7 @@ export function expandTimelineSuccess(timeline, statuses, next) {
type: TIMELINE_EXPAND_SUCCESS, type: TIMELINE_EXPAND_SUCCESS,
timeline, timeline,
statuses, statuses,
next, next
}; };
}; };
@@ -161,7 +159,7 @@ export function expandTimelineFail(timeline, error) {
return { return {
type: TIMELINE_EXPAND_FAIL, type: TIMELINE_EXPAND_FAIL,
timeline, timeline,
error, error
}; };
}; };
@@ -169,20 +167,20 @@ export function scrollTopTimeline(timeline, top) {
return { return {
type: TIMELINE_SCROLL_TOP, type: TIMELINE_SCROLL_TOP,
timeline, timeline,
top, top
}; };
}; };
export function connectTimeline(timeline) { export function connectTimeline(timeline) {
return { return {
type: TIMELINE_CONNECT, type: TIMELINE_CONNECT,
timeline, timeline
}; };
}; };
export function disconnectTimeline(timeline) { export function disconnectTimeline(timeline) {
return { return {
type: TIMELINE_DISCONNECT, type: TIMELINE_DISCONNECT,
timeline, timeline
}; };
}; };

View File

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

View File

@@ -1,4 +1,3 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Avatar from './avatar'; import Avatar from './avatar';
@@ -6,36 +5,33 @@ import DisplayName from './display_name';
import Permalink from './permalink'; import Permalink from './permalink';
import IconButton from './icon_button'; import IconButton from './icon_button';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
const messages = defineMessages({ const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' }, follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' }, requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }
}); });
class Account extends ImmutablePureComponent { class Account extends React.PureComponent {
static propTypes = { constructor (props, context) {
account: ImmutablePropTypes.map.isRequired, super(props, context);
me: PropTypes.number.isRequired, this.handleFollow = this.handleFollow.bind(this);
onFollow: PropTypes.func.isRequired, this.handleBlock = this.handleBlock.bind(this);
onBlock: PropTypes.func.isRequired, this.handleMute = this.handleMute.bind(this);
onMute: PropTypes.func.isRequired, }
intl: PropTypes.object.isRequired,
};
handleFollow = () => { handleFollow () {
this.props.onFollow(this.props.account); this.props.onFollow(this.props.account);
} }
handleBlock = () => { handleBlock () {
this.props.onBlock(this.props.account); this.props.onBlock(this.props.account);
} }
handleMute = () => { handleMute () {
this.props.onMute(this.props.account); this.props.onMute(this.props.account);
} }
@@ -55,7 +51,7 @@ class Account extends ImmutablePureComponent {
const muting = account.getIn(['relationship', 'muting']); const muting = account.getIn(['relationship', 'muting']);
if (requested) { if (requested) {
buttons = <IconButton disabled={true} icon='hourglass' title={intl.formatMessage(messages.requested)} />; buttons = <IconButton disabled={true} icon='hourglass' title={intl.formatMessage(messages.requested)} />
} else if (blocking) { } else if (blocking) {
buttons = <IconButton active={true} icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />; buttons = <IconButton active={true} icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
} else if (muting) { } else if (muting) {
@@ -83,4 +79,13 @@ class Account extends ImmutablePureComponent {
} }
Account.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
}
export default injectIntl(Account); export default injectIntl(Account);

View File

@@ -1,14 +1,9 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
class AttachmentList extends React.PureComponent { class AttachmentList extends React.PureComponent {
static propTypes = {
media: ImmutablePropTypes.list.isRequired,
};
render () { render () {
const { media } = this.props; const { media } = this.props;
@@ -28,7 +23,10 @@ class AttachmentList extends React.PureComponent {
</div> </div>
); );
} }
} }
AttachmentList.propTypes = {
media: ImmutablePropTypes.list.isRequired
};
export default AttachmentList; export default AttachmentList;

View File

@@ -1,10 +1,7 @@
import React from 'react';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { isRtl } from '../rtl'; import { isRtl } from '../rtl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
const textAtCursorMatchesToken = (str, caretPosition) => { const textAtCursorMatchesToken = (str, caretPosition) => {
let word; let word;
@@ -31,35 +28,25 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
} }
}; };
class AutosuggestTextarea extends ImmutablePureComponent { class AutosuggestTextarea extends React.Component {
static propTypes = { constructor (props, context) {
value: PropTypes.string, super(props, context);
suggestions: ImmutablePropTypes.list, this.state = {
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,
};
static defaultProps = {
autoFocus: true,
};
state = {
suggestionsHidden: false, suggestionsHidden: false,
selectedSuggestion: 0, selectedSuggestion: 0,
lastToken: null, lastToken: null,
tokenStart: 0, tokenStart: 0
}; };
this.onChange = this.onChange.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onBlur = this.onBlur.bind(this);
this.onSuggestionClick = this.onSuggestionClick.bind(this);
this.setTextarea = this.setTextarea.bind(this);
this.onPaste = this.onPaste.bind(this);
}
onChange = (e) => { onChange (e) {
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart); const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
if (token !== null && this.state.lastToken !== token) { if (token !== null && this.state.lastToken !== token) {
@@ -70,10 +57,14 @@ class AutosuggestTextarea extends ImmutablePureComponent {
this.props.onSuggestionsClearRequested(); this.props.onSuggestionsClearRequested();
} }
// auto-resize textarea
e.target.style.height = 'auto';
e.target.style.height = `${e.target.scrollHeight}px`;
this.props.onChange(e); this.props.onChange(e);
} }
onKeyDown = (e) => { onKeyDown (e) {
const { suggestions, disabled } = this.props; const { suggestions, disabled } = this.props;
const { selectedSuggestion, suggestionsHidden } = this.state; const { selectedSuggestion, suggestionsHidden } = this.state;
@@ -123,7 +114,7 @@ class AutosuggestTextarea extends ImmutablePureComponent {
this.props.onKeyDown(e); this.props.onKeyDown(e);
} }
onBlur = () => { onBlur () {
// If we hide the suggestions immediately, then this will prevent the // If we hide the suggestions immediately, then this will prevent the
// onClick for the suggestions themselves from firing. // onClick for the suggestions themselves from firing.
// Setting a short window for that to take place before hiding the // Setting a short window for that to take place before hiding the
@@ -133,8 +124,7 @@ class AutosuggestTextarea extends ImmutablePureComponent {
}, 100); }, 100);
} }
onSuggestionClick = (e) => { onSuggestionClick (suggestion, e) {
const suggestion = Number(e.currentTarget.getAttribute('data-index'));
e.preventDefault(); e.preventDefault();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
this.textarea.focus(); this.textarea.focus();
@@ -146,19 +136,19 @@ class AutosuggestTextarea extends ImmutablePureComponent {
} }
} }
setTextarea = (c) => { setTextarea (c) {
this.textarea = c; this.textarea = c;
} }
onPaste = (e) => { onPaste (e) {
if (e.clipboardData && e.clipboardData.files.length === 1) { if (e.clipboardData && e.clipboardData.files.length === 1) {
this.props.onPaste(e.clipboardData.files); this.props.onPaste(e.clipboardData.files)
e.preventDefault(); e.preventDefault();
} }
} }
render () { render () {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props; const { value, suggestions, disabled, placeholder, onKeyUp } = this.props;
const { suggestionsHidden, selectedSuggestion } = this.state; const { suggestionsHidden, selectedSuggestion } = this.state;
const style = { direction: 'ltr' }; const style = { direction: 'ltr' };
@@ -168,12 +158,12 @@ class AutosuggestTextarea extends ImmutablePureComponent {
return ( return (
<div className='autosuggest-textarea'> <div className='autosuggest-textarea'>
<Textarea <textarea
inputRef={this.setTextarea} ref={this.setTextarea}
className='autosuggest-textarea__textarea' className='autosuggest-textarea__textarea'
disabled={disabled} disabled={disabled}
placeholder={placeholder} placeholder={placeholder}
autoFocus={autoFocus} autoFocus={true}
value={value} value={value}
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
@@ -183,15 +173,14 @@ class AutosuggestTextarea extends ImmutablePureComponent {
style={style} style={style}
/> />
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}> <div style={{ display: (suggestions.size > 0 && !suggestionsHidden) ? 'block' : 'none' }} className='autosuggest-textarea__suggestions'>
{suggestions.map((suggestion, i) => ( {suggestions.map((suggestion, i) => (
<div <div
role='button' role='button'
tabIndex='0' tabIndex='0'
key={suggestion} key={suggestion}
data-index={suggestion}
className={`autosuggest-textarea__suggestions__item ${i === selectedSuggestion ? 'selected' : ''}`} className={`autosuggest-textarea__suggestions__item ${i === selectedSuggestion ? 'selected' : ''}`}
onClick={this.onSuggestionClick}> onClick={this.onSuggestionClick.bind(this, suggestion)}>
<AutosuggestAccountContainer id={suggestion} /> <AutosuggestAccountContainer id={suggestion} />
</div> </div>
))} ))}
@@ -200,6 +189,20 @@ class AutosuggestTextarea extends ImmutablePureComponent {
); );
} }
} };
AutosuggestTextarea.propTypes = {
value: 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,
};
export default AutosuggestTextarea; export default AutosuggestTextarea;

View File

@@ -0,0 +1,63 @@
import PropTypes from 'prop-types';
class Avatar extends React.PureComponent {
constructor (props, context) {
super(props, context);
this.state = {
hovering: false
};
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
}
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='account__avatar'
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
style={style}
/>
);
}
}
Avatar.propTypes = {
src: PropTypes.string.isRequired,
staticSrc: PropTypes.string,
size: PropTypes.number.isRequired,
style: PropTypes.object,
animate: PropTypes.bool
};
Avatar.defaultProps = {
animate: false
};
export default Avatar;

View File

@@ -0,0 +1,49 @@
import PropTypes from 'prop-types';
class Button extends React.PureComponent {
constructor (props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
}
handleClick (e) {
if (!this.props.disabled) {
this.props.onClick();
}
}
render () {
const style = {
display: this.props.block ? 'block' : 'inline-block',
width: this.props.block ? '100%' : 'auto',
padding: `0 ${this.props.size / 2.25}px`,
height: `${this.props.size}px`,
lineHeight: `${this.props.size}px`
};
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>
);
}
}
Button.propTypes = {
text: PropTypes.node,
onClick: PropTypes.func,
disabled: PropTypes.bool,
block: PropTypes.bool,
secondary: PropTypes.bool,
size: PropTypes.number,
style: PropTypes.object,
children: PropTypes.node
};
Button.defaultProps = {
size: 36
};
export default Button;

View File

@@ -1,6 +1,4 @@
import React from 'react'; import { Motion, spring } from 'react-motion';
import Motion from 'react-motion/lib/Motion';
import spring from 'react-motion/lib/spring';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const Collapsable = ({ fullHeight, isVisible, children }) => ( const Collapsable = ({ fullHeight, isVisible, children }) => (
@@ -16,7 +14,7 @@ const Collapsable = ({ fullHeight, isVisible, children }) => (
Collapsable.propTypes = { Collapsable.propTypes = {
fullHeight: PropTypes.number.isRequired, fullHeight: PropTypes.number.isRequired,
isVisible: PropTypes.bool.isRequired, isVisible: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired
}; };
export default Collapsable; export default Collapsable;

View File

@@ -1,14 +1,14 @@
import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
class ColumnBackButton extends React.PureComponent { class ColumnBackButton extends React.PureComponent {
static contextTypes = { constructor (props, context) {
router: PropTypes.object, super(props, context);
}; this.handleClick = this.handleClick.bind(this);
}
handleClick = () => { handleClick () {
if (window.history && window.history.length === 1) this.context.router.push("/"); if (window.history && window.history.length === 1) this.context.router.push("/");
else this.context.router.goBack(); else this.context.router.goBack();
} }
@@ -22,6 +22,10 @@ class ColumnBackButton extends React.PureComponent {
); );
} }
} };
ColumnBackButton.contextTypes = {
router: PropTypes.object
};
export default ColumnBackButton; export default ColumnBackButton;

View File

@@ -1,14 +1,14 @@
import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
class ColumnBackButtonSlim extends React.PureComponent { class ColumnBackButtonSlim extends React.PureComponent {
static contextTypes = { constructor (props, context) {
router: PropTypes.object, super(props, context);
}; this.handleClick = this.handleClick.bind(this);
}
handleClick = () => { handleClick () {
this.context.router.push('/'); this.context.router.push('/');
} }
@@ -22,7 +22,10 @@ class ColumnBackButtonSlim extends React.PureComponent {
</div> </div>
); );
} }
} }
ColumnBackButtonSlim.contextTypes = {
router: PropTypes.object
};
export default ColumnBackButtonSlim; export default ColumnBackButtonSlim;

View File

@@ -0,0 +1,56 @@
import { Motion, spring } from 'react-motion';
import PropTypes from 'prop-types';
class ColumnCollapsable extends React.PureComponent {
constructor (props, context) {
super(props, context);
this.state = {
collapsed: true
};
this.handleToggleCollapsed = this.handleToggleCollapsed.bind(this);
}
handleToggleCollapsed () {
const currentState = this.state.collapsed;
this.setState({ collapsed: !currentState });
if (!currentState && this.props.onCollapse) {
this.props.onCollapse();
}
}
render () {
const { icon, title, fullHeight, children } = this.props;
const { collapsed } = this.state;
const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable';
return (
<div className='column-collapsable'>
<div role='button' tabIndex='0' title={`${title}`} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}>
<i className={`fa fa-${icon}`} />
</div>
<Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : fullHeight, collapsed ? undefined : { stiffness: 150, damping: 9 }) }}>
{({ opacity, height }) =>
<div style={{ overflow: height === fullHeight ? 'auto' : 'hidden', height: `${height}px`, opacity: opacity / 100, maxHeight: '70vh' }}>
{children}
</div>
}
</Motion>
</div>
);
}
}
ColumnCollapsable.propTypes = {
icon: PropTypes.string.isRequired,
title: PropTypes.string,
fullHeight: PropTypes.number.isRequired,
children: PropTypes.node,
onCollapse: PropTypes.func
};
export default ColumnCollapsable;

View File

@@ -1,14 +1,9 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import escapeTextContentForBrowser from 'escape-html'; import escapeTextContentForBrowser from 'escape-html';
import emojify from '../emoji'; import emojify from '../emoji';
class DisplayName extends React.PureComponent { class DisplayName extends React.PureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
};
render () { render () {
const displayName = this.props.account.get('display_name').length === 0 ? this.props.account.get('username') : this.props.account.get('display_name'); const displayName = this.props.account.get('display_name').length === 0 ? this.props.account.get('username') : this.props.account.get('display_name');
const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) }; const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
@@ -20,6 +15,10 @@ class DisplayName extends React.PureComponent {
); );
} }
};
DisplayName.propTypes = {
account: ImmutablePropTypes.map.isRequired
} }
export default DisplayName; export default DisplayName;

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