├── log
└── .keep
├── tmp
└── .keep
├── vendor
└── .keep
├── lib
├── assets
│ └── .keep
├── tasks
│ └── db.rake
└── collation.rb
├── public
├── favicon.ico
├── assets
│ └── .keep
├── apple-touch-icon.png
├── apple-touch-icon-120x120.png
├── apple-touch-icon-precomposed.png
├── apple-touch-icon-120x120-precomposed.png
├── images
│ ├── icon.png
│ ├── logo.png
│ ├── icon_64.png
│ ├── icon_120.png
│ ├── icon_kero.jpg
│ ├── icon_large.png
│ ├── icon_small.png
│ ├── logo_large.png
│ ├── logo_white.png
│ ├── banner-background.jpg
│ └── logo_white_large.png
├── audios
│ ├── guitar
│ │ ├── e2.mp3
│ │ ├── e3.mp3
│ │ ├── e4.mp3
│ │ └── e5.mp3
│ ├── piano
│ │ ├── e2.mp3
│ │ ├── e3.mp3
│ │ ├── e4.mp3
│ │ └── e5.mp3
│ └── strings
│ │ ├── e2.mp3
│ │ ├── e3.mp3
│ │ ├── e4.mp3
│ │ └── e5.mp3
├── robots.txt
├── 500.html
├── 422.html
└── 404.html
├── .ruby-version
├── app
├── assets
│ ├── images
│ │ ├── .keep
│ │ └── favicon.ico
│ ├── config
│ │ └── manifest.js
│ ├── stylesheets
│ │ ├── application.css.sass
│ │ └── partials
│ │ │ └── fonts.sass
│ └── javascripts
│ │ └── application.js
├── helpers
│ └── application_helper.rb
├── frontend
│ ├── styles
│ │ ├── rails_admin.scss
│ │ ├── partials
│ │ │ ├── flash-message.sass
│ │ │ ├── footer.sass
│ │ │ ├── terms.sass
│ │ │ ├── variables.sass
│ │ │ ├── chord-colors.sass
│ │ │ ├── score-footer.sass
│ │ │ ├── fav.sass
│ │ │ ├── changelog.sass
│ │ │ ├── login.sass
│ │ │ ├── loading.sass
│ │ │ ├── tabbar.sass
│ │ │ ├── shared-buttons.sass
│ │ │ ├── navbar.sass
│ │ │ ├── user-page.sass
│ │ │ ├── notification.sass
│ │ │ ├── user-card.sass
│ │ │ └── score-header.sass
│ │ └── rechord.sass
│ ├── packs
│ │ ├── rails_admin.js
│ │ └── rechord.js
│ ├── components
│ │ ├── TitleControl
│ │ │ ├── validateTypes.js
│ │ │ └── index.js
│ │ ├── Score
│ │ │ ├── ScoreEditor
│ │ │ │ ├── validateTypes.js
│ │ │ │ └── changeScrollPosition.js
│ │ │ ├── validate.js
│ │ │ ├── ClearButton
│ │ │ │ └── index.js
│ │ │ ├── SetSampleButton
│ │ │ │ └── index.js
│ │ │ ├── LoopControl
│ │ │ │ └── index.js
│ │ │ ├── ClickControl
│ │ │ │ └── index.js
│ │ │ ├── InstrumentControl
│ │ │ │ └── index.js
│ │ │ ├── BpmControl
│ │ │ │ └── index.js
│ │ │ ├── CapoControl
│ │ │ │ └── index.js
│ │ │ ├── BeatControl
│ │ │ │ └── index.js
│ │ │ ├── VolumeControl
│ │ │ │ └── index.js
│ │ │ ├── KeyControl
│ │ │ │ └── index.js
│ │ │ └── UndoControl
│ │ │ │ └── index.js
│ │ ├── commons
│ │ │ ├── ErrorMessages.js
│ │ │ ├── ErrorBoundary.js
│ │ │ ├── HasAddonsField.js
│ │ │ ├── Notification
│ │ │ │ ├── DefaultNotification.js
│ │ │ │ ├── ReleaseNotification.js
│ │ │ │ ├── NotificationIcon.js
│ │ │ │ ├── index.js
│ │ │ │ └── FavNotification.js
│ │ │ ├── Field.js
│ │ │ ├── Slider.js
│ │ │ ├── HorizontalField.js
│ │ │ ├── SelectField.js
│ │ │ ├── LinkButton.js
│ │ │ ├── Button.js
│ │ │ ├── Footer
│ │ │ │ └── index.js
│ │ │ ├── FlashMessage.js
│ │ │ ├── LoginModal
│ │ │ │ └── index.js
│ │ │ └── ModalCard.js
│ │ ├── Routes
│ │ │ ├── User
│ │ │ │ ├── UserScoresList
│ │ │ │ │ └── index.js
│ │ │ │ ├── EditUser
│ │ │ │ │ └── validateTypes.js
│ │ │ │ └── DestroyUserModal
│ │ │ │ │ └── index.js
│ │ │ ├── FavsList
│ │ │ │ └── index.js
│ │ │ ├── ScoresList
│ │ │ │ └── index.js
│ │ │ ├── UsersList
│ │ │ │ └── index.js
│ │ │ ├── Changelog
│ │ │ │ ├── Version.js
│ │ │ │ └── index.js
│ │ │ ├── NewScore
│ │ │ │ └── RestoreModal
│ │ │ │ │ └── index.js
│ │ │ └── ShowScore
│ │ │ │ ├── DestroyScoreModal
│ │ │ │ └── index.js
│ │ │ │ ├── ScoreFooter
│ │ │ │ └── index.js
│ │ │ │ └── ScoreHeader
│ │ │ │ └── Author.js
│ │ ├── CardsList
│ │ │ ├── UsersResult
│ │ │ │ └── index.js
│ │ │ ├── SortSelect
│ │ │ │ └── index.js
│ │ │ ├── OptionCheckbox
│ │ │ │ └── index.js
│ │ │ ├── ScoresResult
│ │ │ │ └── index.js
│ │ │ └── cardsListUtils.js
│ │ ├── SharedButtons
│ │ │ └── ShareIcon
│ │ │ │ └── index.js
│ │ ├── index.js
│ │ └── StatusControl
│ │ │ └── index.js
│ ├── constants
│ │ ├── beats.js
│ │ ├── sampleScore.js
│ │ ├── index.js
│ │ ├── instruments.js
│ │ └── regex.js
│ ├── api
│ │ └── axios.js
│ ├── decorators
│ │ └── highlighter.js
│ ├── utils
│ │ ├── metaDescription.js
│ │ ├── browser-dependencies.js
│ │ ├── draftjsUtils.js
│ │ └── localStorageState.js
│ └── validator
│ │ ├── index.js
│ │ ├── FormWithValidate.js
│ │ └── translate.js
├── models
│ ├── application_record.rb
│ ├── ability.rb
│ ├── fav.rb
│ ├── maintenance_schedule.rb
│ ├── notification.rb
│ └── concerns
│ │ └── oauth_provider_formatter.rb
├── mailers
│ └── application_mailer.rb
├── views
│ ├── layouts
│ │ ├── _gtm_body.html.erb
│ │ ├── _google_ads.html.erb
│ │ ├── _ga.html.erb
│ │ ├── _gtm_head.html.erb
│ │ ├── application.html.slim
│ │ └── _meta.html.slim
│ └── top
│ │ ├── not_supported.html.slim
│ │ ├── index.html.slim
│ │ └── maintenance.html.slim
└── controllers
│ ├── statuses_controller.rb
│ ├── users
│ ├── scores_controller.rb
│ └── omniauth_callbacks_controller.rb
│ ├── application_controller.rb
│ ├── top_controller.rb
│ ├── concerns
│ └── search_params.rb
│ └── favs_controller.rb
├── .browserslistrc
├── .node-version
├── Procfile
├── .github
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .postcssrc.yml
├── config
├── initializers
│ ├── i18n.rb
│ ├── omniauth.rb
│ ├── mime_types.rb
│ ├── impression.rb
│ ├── application_controller_renderer.rb
│ ├── cookies_serializer.rb
│ ├── filter_parameter_logging.rb
│ ├── permissions_policy.rb
│ ├── carrierwave.rb
│ ├── wrap_parameters.rb
│ ├── backtrace_silencers.rb
│ ├── assets.rb
│ ├── inflections.rb
│ ├── content_security_policy.rb
│ └── exception_notification.rb
├── webpack
│ ├── environment.js
│ ├── test.js
│ ├── production.js
│ └── development.js
├── spring.rb
├── environment.rb
├── schedule.rb
├── version.yml
├── boot.rb
├── cable.yml
├── credentials.yml.enc
├── database.yml
├── application.rb
├── locales
│ └── en.yml
├── unicorn.rb
├── storage.yml
├── routes.rb
├── newrelic.yml
└── puma.rb
├── bin
├── bundle
├── rake
├── rails
├── webpack
├── webpack-dev-server
├── spring
├── yarn
├── fog
├── pry
├── yri
├── haml
├── puma
├── racc
├── sass
├── scss
├── thor
├── tilt
├── yard
├── oauth
├── rackup
├── slimrb
├── yardoc
├── byebug
├── coderay
├── dotenv
├── fission
├── listen
├── pumactl
├── unicorn
├── bootsnap
├── console
├── nokogiri
├── nrdebug
├── whenever
├── newrelic
├── rbvmomish
├── sprockets
├── httpclient
├── restclient
├── sass-convert
├── wheneverize
├── mongrel_rpm
├── newrelic_cmd
├── unicorn_rails
├── generate-api
├── update
└── setup
├── config.ru
├── db
├── migrate
│ ├── 20180205144032_add_remote_ip_to_score.rb
│ ├── 20180119050446_add_counter_cache_to_score.rb
│ ├── 20180119052259_add_counter_cache_to_user.rb
│ ├── 20171202020544_add_column_to_user.rb
│ ├── 20171202163814_rename_column_to_user.rb
│ ├── 20180131060629_add_column_to_score.rb
│ ├── 20180119043849_add_favs_count_to_scores.rb
│ ├── 20171116152249_create_users.rb
│ ├── 20180112052813_create_favs.rb
│ ├── 20171127012637_add_column_users.rb
│ ├── 20190125093520_create_maintenance_schedules.rb
│ ├── 20180620083642_create_notifications.rb
│ ├── 20171117024531_create_scores.rb
│ └── 20180112050744_create_impressions_table.rb
└── seeds.rb
├── Rakefile
├── postcss.config.js
├── .docker
└── docker-compose.yml
├── spec
└── frontend
│ └── decorators
│ └── scoreEditorDecorator.spec.js
├── README.md
├── .gitignore
├── .env.sample
├── Capfile
├── package.json
├── .eslintrc
└── circle.yml
/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.2.1
2 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 20.11.1
2 |
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-120x120-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec rails server
2 | js: bin/webpack-dev-server
3 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## したいこと
2 |
3 | ## 期待できる結果
4 |
5 | ## 実装メモ
6 |
--------------------------------------------------------------------------------
/.postcssrc.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | postcss-import: {}
3 | postcss-cssnext: {}
4 |
--------------------------------------------------------------------------------
/config/initializers/i18n.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.i18n.default_locale = :ja
2 |
--------------------------------------------------------------------------------
/app/frontend/styles/rails_admin.scss:
--------------------------------------------------------------------------------
1 | @import "rails_admin/src/rails_admin/styles/base";
2 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## スクリーンショット
2 |
3 | ## 改善
4 |
5 | ## 修正
6 |
7 | ## その他
8 |
--------------------------------------------------------------------------------
/public/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/icon.png
--------------------------------------------------------------------------------
/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/logo.png
--------------------------------------------------------------------------------
/public/images/icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/icon_64.png
--------------------------------------------------------------------------------
/public/audios/guitar/e2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/guitar/e2.mp3
--------------------------------------------------------------------------------
/public/audios/guitar/e3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/guitar/e3.mp3
--------------------------------------------------------------------------------
/public/audios/guitar/e4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/guitar/e4.mp3
--------------------------------------------------------------------------------
/public/audios/guitar/e5.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/guitar/e5.mp3
--------------------------------------------------------------------------------
/public/audios/piano/e2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/piano/e2.mp3
--------------------------------------------------------------------------------
/public/audios/piano/e3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/piano/e3.mp3
--------------------------------------------------------------------------------
/public/audios/piano/e4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/piano/e4.mp3
--------------------------------------------------------------------------------
/public/audios/piano/e5.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/piano/e5.mp3
--------------------------------------------------------------------------------
/public/images/icon_120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/icon_120.png
--------------------------------------------------------------------------------
/public/images/icon_kero.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/icon_kero.jpg
--------------------------------------------------------------------------------
/app/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/app/assets/images/favicon.ico
--------------------------------------------------------------------------------
/public/audios/strings/e2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/strings/e2.mp3
--------------------------------------------------------------------------------
/public/audios/strings/e3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/strings/e3.mp3
--------------------------------------------------------------------------------
/public/audios/strings/e4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/strings/e4.mp3
--------------------------------------------------------------------------------
/public/audios/strings/e5.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/audios/strings/e5.mp3
--------------------------------------------------------------------------------
/public/images/icon_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/icon_large.png
--------------------------------------------------------------------------------
/public/images/icon_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/icon_small.png
--------------------------------------------------------------------------------
/public/images/logo_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/logo_large.png
--------------------------------------------------------------------------------
/public/images/logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/logo_white.png
--------------------------------------------------------------------------------
/app/frontend/packs/rails_admin.js:
--------------------------------------------------------------------------------
1 | import "rails_admin/src/rails_admin/base";
2 | import "../styles/rails_admin.scss";
3 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 |
3 | module.exports = environment
4 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/public/images/banner-background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/banner-background.jpg
--------------------------------------------------------------------------------
/public/images/logo_white_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comorebi-notes/rechord/HEAD/public/images/logo_white_large.png
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | Spring.watch(
2 | ".ruby-version",
3 | ".rbenv-vars",
4 | "tmp/restart.txt",
5 | "tmp/caching-dev.txt"
6 | )
7 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | load File.expand_path("spring", __dir__)
3 | require_relative "../config/boot"
4 | require "rake"
5 | Rake.application.run
6 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/app/frontend/components/TitleControl/validateTypes.js:
--------------------------------------------------------------------------------
1 | export const validateTypes = [
2 | ["required"],
3 | ["maxLength", 40]
4 | ]
5 |
6 | export default validateTypes
7 |
--------------------------------------------------------------------------------
/config/schedule.rb:
--------------------------------------------------------------------------------
1 | set :output, "log/crontab.log"
2 | set :environment, :production
3 |
4 | every 1.day, at: "4:00 am", roles: [:db] do
5 | rake "backup:daily"
6 | end
7 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/ScoreEditor/validateTypes.js:
--------------------------------------------------------------------------------
1 | export const validateTypes = [
2 | ["required"],
3 | ["maxLength", 1024]
4 | ]
5 |
6 | export default validateTypes
7 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/flash-message.sass:
--------------------------------------------------------------------------------
1 | .flash-message
2 | margin-bottom: 2rem
3 | .notification
4 | padding: 1rem 1.5rem
5 | > *
6 | vertical-align: middle
7 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 | Rails.application.load_server
7 |
--------------------------------------------------------------------------------
/config/initializers/omniauth.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.middleware.use OmniAuth::Builder do
2 | configure do |config|
3 | config.full_host = ENV['RAILS_FULL_HOST']
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/db/migrate/20180205144032_add_remote_ip_to_score.rb:
--------------------------------------------------------------------------------
1 | class AddRemoteIpToScore < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :scores, :remote_ip, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/footer.sass:
--------------------------------------------------------------------------------
1 | footer.footer
2 | background-color: #532
3 | color: #fff
4 | a
5 | color: #fff
6 | opacity: .75
7 | &:hover, &:active
8 | opacity: .5
9 |
--------------------------------------------------------------------------------
/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | load File.expand_path("spring", __dir__)
3 | APP_PATH = File.expand_path('../config/application', __dir__)
4 | require_relative "../config/boot"
5 | require "rails/commands"
6 |
--------------------------------------------------------------------------------
/config/version.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | version: v2.0.0
3 |
4 | development:
5 | <<: *default
6 | test:
7 | <<: *default
8 | production:
9 | <<: *default
10 | staging:
11 | <<: *default
12 |
--------------------------------------------------------------------------------
/db/migrate/20180119050446_add_counter_cache_to_score.rb:
--------------------------------------------------------------------------------
1 | class AddCounterCacheToScore < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :scores, :views_count, :integer, default: 0
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20180119052259_add_counter_cache_to_user.rb:
--------------------------------------------------------------------------------
1 | class AddCounterCacheToUser < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :users, :scores_count, :integer, default: 0
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/frontend/constants/beats.js:
--------------------------------------------------------------------------------
1 | export const beats = {
2 | "2/4": [2, 4],
3 | "3/4": [3, 4],
4 | "4/4": [4, 4],
5 | "5/4": [5, 4],
6 | "6/4": [6, 4],
7 | "7/4": [7, 4]
8 | }
9 |
10 | export default beats
11 |
--------------------------------------------------------------------------------
/db/migrate/20171202020544_add_column_to_user.rb:
--------------------------------------------------------------------------------
1 | class AddColumnToUser < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :users, :email, :string
4 | add_column :users, :twitter, :string
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/ability.rb:
--------------------------------------------------------------------------------
1 | class Ability
2 | include CanCan::Ability
3 |
4 | def initialize(user)
5 | if user && user.admin?
6 | can :access, :rails_admin
7 | can :manage, :all
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require "bundler/setup" # Set up gems listed in the Gemfile.
4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/db/migrate/20171202163814_rename_column_to_user.rb:
--------------------------------------------------------------------------------
1 | class RenameColumnToUser < ActiveRecord::Migration[5.1]
2 | def change
3 | rename_column :users, :icon_url, :icon
4 | rename_column :users, :site_url, :site
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: rechord_production
11 |
--------------------------------------------------------------------------------
/db/migrate/20180131060629_add_column_to_score.rb:
--------------------------------------------------------------------------------
1 | class AddColumnToScore < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :scores, :capo, :integer, default: 0
4 | add_column :scores, :loop, :boolean, default: false
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/terms.sass:
--------------------------------------------------------------------------------
1 | .terms
2 | line-height: 1.8
3 | font-size: .85em
4 | ol
5 | margin-bottom: 1em
6 | &.terms-list
7 | > li
8 | margin-top: 2.5em
9 | h1:not(:first-child)
10 | margin-top: 2.5em
11 |
--------------------------------------------------------------------------------
/config/initializers/impression.rb:
--------------------------------------------------------------------------------
1 | # Use this hook to configure impressionist parameters
2 | #Impressionist.setup do |config|
3 | # Define ORM. Could be :active_record (default), :mongo_mapper or :mongoid
4 | # config.orm = :active_record
5 | #end
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/tasks/db.rake:
--------------------------------------------------------------------------------
1 | namespace :db do
2 | namespace :collation do
3 | task change: :environment do
4 | Collation.new.change_all
5 | end
6 | task rollback: :environment do
7 | Collation.new.rollback_all
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/views/layouts/_gtm_body.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/app/views/layouts/_google_ads.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/db/migrate/20180119043849_add_favs_count_to_scores.rb:
--------------------------------------------------------------------------------
1 | class AddFavsCountToScores < ActiveRecord::Migration[5.1]
2 | def self.up
3 | add_column :scores, :favs_count, :integer, null: false, default: 0
4 | end
5 |
6 | def self.down
7 | remove_column :scores, :favs_count
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20171116152249_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration[5.1]
2 | def change
3 | create_table :users do |t|
4 | t.string :provider
5 | t.string :uid
6 | t.string :name
7 | t.string :icon_url
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20180112052813_create_favs.rb:
--------------------------------------------------------------------------------
1 | class CreateFavs < ActiveRecord::Migration[5.1]
2 | def change
3 | create_table :favs do |t|
4 | t.references :user, index: true, foreign_key: true
5 | t.references :score, index: true, foreign_key: true
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [
5 | :password, :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
6 | ]
7 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('postcss-flexbugs-fixes'),
5 | require('postcss-preset-env')({
6 | autoprefixer: {
7 | flexbox: 'no-2009'
8 | },
9 | stage: 3
10 | }),
11 | require('postcss-cssnext')
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/ErrorMessages.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 |
3 | export default class ErrorMessages extends PureComponent {
4 | render() {
5 | const { error } = this.props
6 | return (
7 |
{error}
8 | )
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/frontend/api/axios.js:
--------------------------------------------------------------------------------
1 | import axios from "axios"
2 | import { document } from "../utils/browser-dependencies"
3 |
4 | axios.defaults.headers["X-Requested-With"] = "XMLHttpRequest"
5 | export default axios
6 |
7 | export const config = {
8 | headers: {
9 | "X-CSRF-Token": document.querySelector("head [name=csrf-token]").content
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | db:
4 | image: postgres:10.1
5 | ports:
6 | - 5432:5432
7 | environment:
8 | POSTGRES_USER: postgres
9 | volumes:
10 | - ./postgres/data:/var/lib/postgresql/data
11 | platform: linux/amd64
12 | redis:
13 | image: redis:latest
14 | ports:
15 | - 6379:6379
16 |
--------------------------------------------------------------------------------
/app/frontend/constants/sampleScore.js:
--------------------------------------------------------------------------------
1 | const sampleScore = [
2 | "# 小節を | で区切ってコードを入力します",
3 | "C|G|Am7|Em7",
4 | "F|Em7Am7|Dm7|G7",
5 | "# 行頭に # を入力するとメモが書けます",
6 | "FM7|G7",
7 | "# % で連打、 _ で休符、 = で直前のコードを伸ばします",
8 | "E7%_Am7|=G",
9 | "# / でオンコード(最低音)を指定できます",
10 | "FC/E|Dm7Dm7/G",
11 | "Cadd9"
12 | ].join("\n")
13 |
14 | export default sampleScore
15 |
--------------------------------------------------------------------------------
/app/frontend/decorators/highlighter.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import Highlighter from "react-highlight-words"
3 |
4 | export const highlighter = (highlightWords) => (text) => (
5 |
9 | )
10 |
11 | export default highlighter
12 |
--------------------------------------------------------------------------------
/db/migrate/20171127012637_add_column_users.rb:
--------------------------------------------------------------------------------
1 | class AddColumnUsers < ActiveRecord::Migration[5.1]
2 | def change
3 | add_column :users, :screen_name, :string
4 | add_column :users, :profile, :text
5 | add_column :users, :site_url, :string
6 | add_column :users, :admin, :boolean, default: false
7 | add_index :users, :name
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/views/layouts/_ga.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7 | # Character.create(name: 'Luke', movie: movies.first)
8 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/validate.js:
--------------------------------------------------------------------------------
1 | export const validate = (score, bpm) => {
2 | if (!score || score.length === 1) return false
3 | if (!bpm || Number(bpm) === 0) return false
4 |
5 | let hasError = false
6 |
7 | score.forEach((scoreItem) => {
8 | const { notes } = scoreItem
9 | if (!notes) hasError = true
10 | })
11 | return !hasError
12 | }
13 |
14 | export default validate
15 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/variables.sass:
--------------------------------------------------------------------------------
1 | // breakpoints
2 | $gap: 32px
3 | $tablet: 769px
4 | $desktop: 960px + (2 * $gap)
5 | $mobile: 1024px + (2 * $gap)
6 | $widescreen: 1152px + (2 * $gap)
7 | $fullhd: 1344px + (2 * $gap) !default
8 |
9 | // colors
10 | $primary: #a0ca30
11 | $info: #30b4d0
12 | $success: #53d12f
13 | $warning: #e4a020
14 | $link: #177fcb
15 | $danger: #e44020
16 |
--------------------------------------------------------------------------------
/db/migrate/20190125093520_create_maintenance_schedules.rb:
--------------------------------------------------------------------------------
1 | class CreateMaintenanceSchedules < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :maintenance_schedules do |t|
4 | t.string :title, null: false
5 | t.text :description, null: false
6 | t.datetime :start_time, null: false
7 | t.datetime :end_time, null: false
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/fav.rb:
--------------------------------------------------------------------------------
1 | class Fav < ApplicationRecord
2 | belongs_to :user
3 | belongs_to :score
4 |
5 | counter_culture :score
6 |
7 | validates :user_id, uniqueness: { scope: :score_id }
8 |
9 | after_destroy :destroy_notification_if_zero_fav
10 |
11 | private
12 |
13 | def destroy_notification_if_zero_fav
14 | Notification.destroy_if_exist(title: score.token) if score.favs_count == 1
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/User/UserScoresList/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import CardsList from "../../../CardsList"
3 |
4 | export default class UserScoresList extends Component {
5 | render() {
6 | return (
7 |
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/views/layouts/_gtm_head.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/config/initializers/permissions_policy.rb:
--------------------------------------------------------------------------------
1 | # Define an application-wide HTTP permissions policy. For further
2 | # information see https://developers.google.com/web/updates/2018/06/feature-policy
3 | #
4 | # Rails.application.config.permissions_policy do |f|
5 | # f.camera :none
6 | # f.gyroscope :none
7 | # f.microphone :none
8 | # f.usb :none
9 | # f.fullscreen :self
10 | # f.payment :self, "https://secure.example.com"
11 | # end
12 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/FavsList/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import CardsList from "../../CardsList"
3 |
4 | export default class FavsList extends Component {
5 | render() {
6 | return (
7 |
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/views/top/not_supported.html.slim:
--------------------------------------------------------------------------------
1 | section.hero.is-primary.is-fullheight
2 | .hero-body style="min-height: 100vh"
3 | .container.has-text-centered
4 | img src="/images/logo_white.png" width="160"
5 | h1.title style="margin-top: .4em; margin-bottom: 2em"
6 | | sorry... this browser isn't supported.
7 | h2.subtitle.is-6
8 | | 申し訳ありませんが、このブラウザは rechord に対応していません。
9 |
10 | - if ENV["UA_ID"].present?
11 | = render "layouts/ga"
12 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | hsXUs5TXvrKlB2UFHsKjmOX3EjWuyKjTG9gwz5RdyNi2sEYo4GP/qp3vE1YDHZzUEGLtTnR8FkiwZc8ccFOJW6LURTrd0oXSPHZAGK9/57DlWu2W/Qu2FrkfCPhUxFzVoz3kOCd1J/tNtsGGSS9XjsyyvxZe2jhLNYzcNz+8eu6iBwLiPBC/GMFP6LVDP5MW6cj64IwcmvmYNb9pM7J533K95bJzkcEt3pbeVK2dB7Hx/19mhDM6zdaihOLZE7lpI0TpuZD2FdECRRs9Gqaq3a1wlpiLAbhVHRyuaGn+tWAqOeShdOZBWCHgto8/Xgl43/s/rp9c7rLgU0d7PpYZcREKwhsD+zmT9erYa78x4yAg2MrCUgnOmQ4m4ycBTTW3R4bucWBr6GISYlGbpz4Z96qD7TS62rK6xfC5--acdQMJlxav6uiyzZ--oXso9tXsyGQyAwF93tcOvg==
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css.sass:
--------------------------------------------------------------------------------
1 | $primary: #a0ca30
2 | $info: #30b4d0
3 | $success: #53d12f
4 | $warning: #e4a020
5 | $link: #177fcb
6 | // $danger: #e44020
7 | // $dark: $grey-darker
8 | // $text: $grey-dark
9 |
10 | @import "font-awesome"
11 | @import "bulma"
12 | @import "../../../vendor/assets/bulma-slider.sass"
13 | @import "../../../vendor/assets/bulma-switch.sass"
14 | @import "../../../vendor/assets/bulma-checkradio.sass"
15 | @import "partials/*"
16 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/ErrorBoundary.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | export default class ErrorBoundary extends Component {
4 | constructor() {
5 | super()
6 | this.state = { hasError: false }
7 | }
8 | componentDidCatch = (error) => console.log(error) // eslint-disable-line
9 | render () {
10 | const { hasError } = this.state
11 | const { children } = this.props
12 | return hasError ? : children
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/User/EditUser/validateTypes.js:
--------------------------------------------------------------------------------
1 | export const validateTypes = {
2 | name: [["required"], ["maxLength", 16], ["format", /^[a-z0-9._-]*$/]],
3 | screenName: [["required"], ["maxLength", 32]],
4 | profile: [["maxLength", 256]],
5 | site: [["maxLength", 256], ["format", /^https?:\/\/([\w-]+\.)+[\w-]+((\/[\w- .?%&=]*)?)*$/]],
6 | twitter: [["maxLength", 16], ["format", /^[a-zA-Z0-9_]*$/]]
7 | }
8 |
9 | export default validateTypes
10 |
--------------------------------------------------------------------------------
/bin/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/webpack_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::WebpackRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/partials/fonts.sass:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap')
2 | @import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;700&display=swap')
3 | *
4 | font-family: 'Noto Sans JP', sans-serif
5 | #score-editor, .title-logo, .navbar, .tab-bar, .score-controls, .button, input
6 | *:not(.fa)
7 | font-family: 'Ubuntu', sans-serif
8 | #score-editor
9 | .comment
10 | font-family: 'Noto Sans JP', sans-serif
11 |
--------------------------------------------------------------------------------
/db/migrate/20180620083642_create_notifications.rb:
--------------------------------------------------------------------------------
1 | class CreateNotifications < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :notifications do |t|
4 | t.string :title, index: true, unique: true
5 | t.text :content
6 | t.json :params
7 | t.integer :template, default: 0
8 | t.integer :user_id, index: true
9 |
10 | t.timestamps
11 | end
12 |
13 | add_column :users, :last_read_at, :timestamp, default: -> { "NOW()" }
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/frontend/decorators/scoreEditorDecorator.spec.js:
--------------------------------------------------------------------------------
1 | import * as scoreEditorDecorator from '../../../app/frontend/decorators/scoreEditorDecorator'
2 |
3 | test('parse multi-line chord progression', () => {
4 | expect(scoreEditorDecorator.parseChordProgression('Gm/CF69|Gm/C\nAbGaug7|'))
5 | .toEqual(
6 | [
7 | [
8 | [['G', 'm/C'], ['F', '69']], [['G', 'm/C']]
9 | ],
10 | [
11 | [['Ab', ''], ['G', 'aug7']]
12 | ]
13 | ]
14 | )
15 | })
16 |
--------------------------------------------------------------------------------
/app/views/top/index.html.slim:
--------------------------------------------------------------------------------
1 | - content_for :pack do
2 | = javascript_pack_tag "rechord"
3 | = stylesheet_pack_tag "rechord"
4 |
5 | #rechord.rechord
6 |
7 | javascript:
8 | var data = {
9 | currentUser: #{raw current_user&.to_json || {}},
10 | currentVersion: #{raw Rails.configuration.preference["version"].to_json},
11 | notifications: #{raw @notifications&.to_json},
12 | flash: #{raw flash&.to_json || []},
13 | uaId: #{raw (ENV["UA_ID"] || "")&.to_json}
14 | }
15 |
--------------------------------------------------------------------------------
/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/dev_server_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::DevServerRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/maintenance_schedule.rb:
--------------------------------------------------------------------------------
1 | class MaintenanceSchedule < ApplicationRecord
2 | validates :title, presence: true
3 | validates :description, presence: true
4 | validates :start_time, presence: true
5 | validates :end_time, presence: true
6 |
7 | validate :validate_times
8 |
9 | scope :active, -> { where('start_time <= ?', Time.zone.now).where('end_time >= ?', Time.zone.now) }
10 |
11 | private
12 |
13 | def validate_times
14 | errors.add('開始時間は終了時間より前にしてください。') if start_time > end_time
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/controllers/statuses_controller.rb:
--------------------------------------------------------------------------------
1 | class StatusesController < ApplicationController
2 | def show
3 | render json: {
4 | currentUser: current_user || {},
5 | currentVersion: Rails.configuration.preference["version"],
6 | notifications: Notification.list_for_user(current_user&.id),
7 | maintenanceSchedules: MaintenanceSchedule.active
8 | }
9 | end
10 |
11 | private
12 |
13 | def status_params
14 | params.permit(:current_user_id, :current_version)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/HasAddonsField.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class HasAddonsField extends PureComponent {
5 | render() {
6 | const { customClass, customStyle, children } = this.props
7 | const fieldClass = classNames("field", "has-addons", { [customClass]: customClass })
8 | return (
9 |
10 | {children}
11 |
12 | )
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/config/initializers/carrierwave.rb:
--------------------------------------------------------------------------------
1 | CarrierWave.configure do |config|
2 | config.fog_credentials = {
3 | provider: "OpenStack",
4 | openstack_tenant: ENV["CONOHA_TENANT_NAME"],
5 | openstack_username: ENV["CONOHA_USERNAME"],
6 | openstack_api_key: ENV["CONOHA_API_PASSWORD"],
7 | openstack_auth_url: ENV["CONOHA_API_AUTH_URL"]
8 | }
9 | config.fog_directory = ENV["CONOHA_CONTAINER_NAME"]
10 | config.asset_host = ENV["CONOHA_ASSET_HOST"] + "/" + ENV["CONOHA_CONTAINER_NAME"]
11 | config.storage :fog
12 | end
13 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"])
3 | gem "bundler"
4 | require "bundler"
5 |
6 | # Load Spring without loading other gems in the Gemfile, for speed.
7 | Bundler.locked_gems&.specs&.find { |spec| spec.name == "spring" }&.tap do |spring|
8 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
9 | gem "spring", spring.version
10 | require "spring/binstub"
11 | rescue Gem::LoadError
12 | # Ignore when Spring is not installed.
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/ScoresList/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import CardsList from "../../CardsList"
3 |
4 | export default class ScoresList extends Component {
5 | render() {
6 | const options = [
7 | { key: "guest", label: "ゲストの投稿を含める" }
8 | ]
9 | return (
10 |
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/UsersList/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import CardsList from "../../CardsList"
3 |
4 | export default class UsersList extends Component {
5 | render() {
6 | const options = [
7 | { key: "no_scores", label: "スコアが無いユーザを含める" }
8 | ]
9 | return (
10 |
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/chord-colors.sass:
--------------------------------------------------------------------------------
1 | .score
2 | .A, .Gss, .Bbb
3 | color: #f65022
4 | .As, .Bb, .Cbb
5 | color: #f37800
6 | .B, .Ass, .Cb
7 | color: #feb140
8 | .C, .Bs, .Dbb
9 | color: #8fc31f
10 | .Cs, .Bss, .Db
11 | color: #49a944
12 | .D, .Css, .Ebb
13 | color: #009eca
14 | .Ds, .Eb, .Fbb
15 | color: #206ada
16 | .E, .Dss, .Fb
17 | color: #5020f7
18 | .F, .Es, .Gbb
19 | color: #8020c8
20 | .Fs, .Ess, .Gb
21 | color: #b21793
22 | .G, .Fss, .Abb
23 | color: #da008f
24 | .Gs, .Ab
25 | color: #ea305a
26 |
--------------------------------------------------------------------------------
/app/frontend/utils/metaDescription.js:
--------------------------------------------------------------------------------
1 | import { document } from "./browser-dependencies"
2 |
3 | const MAX_DESCRIPTION_LENGTH = 300
4 | const ORIGINAL_DESCRIPTION = document.querySelector('[name="description"]').content
5 |
6 | export const set = (text) => {
7 | const metatag = document.querySelector('[name="description"]')
8 | if (text && text.length > 0) {
9 | metatag.content = text.replace(/\n/g, "|").slice(0, MAX_DESCRIPTION_LENGTH)
10 | } else {
11 | metatag.content = ORIGINAL_DESCRIPTION
12 | }
13 | }
14 | export const reset = () => set(ORIGINAL_DESCRIPTION)
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [https://rechord.cc](https://rechord.cc)
4 |
5 | rechord は実際に演奏もできるコード進行共有サービスです。
6 |
7 | ## components
8 | - [Ruby on Rails](https://github.com/rails/rails)
9 | - [React](https://github.com/facebook/react)
10 | - [Tone.js](https://github.com/Tonejs/Tone.js/)
11 | - [tonal](https://github.com/danigb/tonal)
12 | - [Draft.js](https://github.com/facebook/draft-js)
13 | - [Bulma](https://github.com/jgthms/bulma)
14 |
15 | ## copyright
16 | Copyright © 2017 comorebi notes All Rights Reserved.
17 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
7 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
8 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]
9 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | adapter: postgresql
3 | encoding: unicode
4 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
5 | timeout: 5000
6 | database: <%= ENV['DATABASE_NAME'] %>
7 | host: <%= ENV['DATABASE_HOST'] %>
8 | port: <%= ENV['DATABASE_PORT'] %>
9 | username: <%= ENV['DATABASE_USERNAME'] %>
10 | password: <%= ENV['DATABASE_PASSWORD'] %>
11 |
12 | development:
13 | <<: *default
14 |
15 | production:
16 | <<: *default
17 |
18 | staging:
19 | <<: *default
20 |
21 | test:
22 | <<: *default
23 | database: <%= ENV['DATABASE_TEST'] %>
24 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
5 | select { |dir| File.expand_path(dir) != __dir__ }.
6 | product(["yarn", "yarn.cmd", "yarn.ps1"]).
7 | map { |dir, file| File.expand_path(file, dir) }.
8 | find { |file| File.executable?(file) }
9 |
10 | if yarn
11 | exec yarn, *ARGV
12 | else
13 | $stderr.puts "Yarn executable was not detected in the system."
14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
15 | exit 1
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/ClearButton/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import Field from "../../commons/Field"
4 | import Button from "../../commons/Button"
5 |
6 | export default class ClearButton extends Component {
7 | handleClearText = () => this.props.setInputText("")
8 | render() {
9 | const { disabled } = this.props
10 | return (
11 |
12 |
18 |
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/db/migrate/20171117024531_create_scores.rb:
--------------------------------------------------------------------------------
1 | class CreateScores < ActiveRecord::Migration[5.1]
2 | def change
3 | create_table :scores do |t|
4 | t.string :title, null: false
5 | t.text :content, null: false
6 | t.integer :status, default: 0
7 | t.integer :instrument, default: 0
8 | t.integer :beat, default: 3
9 | t.integer :bpm, default: 120
10 | t.boolean :click, default: false
11 | t.string :token
12 | t.references :user, foreign_key: true
13 |
14 | t.timestamps
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/controllers/users/scores_controller.rb:
--------------------------------------------------------------------------------
1 | class Users::ScoresController < ApplicationController
2 | include SearchParams
3 |
4 | def index
5 | user = User.find_by(name: params[:user_name])
6 | return unless user
7 |
8 | scores = user.scores_list(user_scores_list_params)
9 | total_count = scores.count
10 | scores = scores.page(params[:page] || 1)
11 |
12 | render json: {
13 | result: scores.as_json(include: :user, except: %i[remote_ip]),
14 | total_count: total_count,
15 | current_page: scores.current_page,
16 | total_pages: scores.total_pages
17 | }
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Notification/DefaultNotification.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import * as utils from "../../../utils"
3 |
4 | export default class DefaultNotification extends PureComponent {
5 | render() {
6 | const { data: { title, content, updated_at: updatedAt } } = this.props
7 | return (
8 |
9 |
10 |
{title}
11 |
14 |
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails/all"
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Rechord
10 | class Application < Rails::Application
11 | # Initialize configuration defaults for originally generated Rails version.
12 | config.load_defaults 6.1
13 |
14 | config.preference = config_for(:version)
15 |
16 | config.time_zone = 'Tokyo'
17 | config.active_record.default_timezone :local
18 |
19 | config.autoload_paths << Rails.root.join('lib')
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/Changelog/Version.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 |
3 | export default class Version extends PureComponent {
4 | render() {
5 | const { version, updated_at, children } = this.props
6 | return (
7 |
8 |
9 |
10 | {version}
11 |
12 |
13 | {updated_at}
14 |
15 |
16 |
17 | {children}
18 |
19 |
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Field.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class Field extends PureComponent {
5 | render() {
6 | const { customClass, customStyle, label, children } = this.props
7 | const fieldClass = classNames("field", { [customClass]: customClass })
8 | return (
9 |
10 | {label && (
11 |
14 | )}
15 |
16 | {children}
17 |
18 |
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/SetSampleButton/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import Field from "../../commons/Field"
4 | import Button from "../../commons/Button"
5 | import sampleScore from "../../../constants/sampleScore"
6 |
7 | export default class SetSampleButton extends Component {
8 | handleSetSample = () => this.props.setInputText(sampleScore)
9 | render() {
10 | const { disabled } = this.props
11 | return (
12 |
13 |
19 |
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/score-footer.sass:
--------------------------------------------------------------------------------
1 | .score-footer
2 | text-align: center
3 | .has-status-control
4 | display: inline-block
5 | .is-grouped
6 | justify-content: center
7 | .status-control
8 | font-weight: bold
9 | display: inline-block
10 | .current-status-description
11 | color: #888
12 | font-weight: normal
13 | font-size: .9em
14 | margin: .75em 0 .5em
15 | .save-control
16 | display: inline-block
17 | width: 100%
18 | .button
19 | width: 100%
20 | min-width: 150px
21 | max-width: 200px
22 | .notification
23 | margin-top: 1rem
24 | display: inline-block
25 |
26 | // @media screen and (max-width: $tablet - 1px)
27 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Slider.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class Slider extends PureComponent {
5 | render() {
6 | const { size, isFullwidth, step, min, max, value, onChange } = this.props
7 | const sliderClass = classNames("slider", {
8 | "is-fullwidth": isFullwidth,
9 | [`is-${size}`]: size
10 | })
11 | return (
12 |
21 | )
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/frontend/packs/rechord.js:
--------------------------------------------------------------------------------
1 | import "babel-polyfill" // for GoogleBot (Chrome 41)
2 |
3 | import React from "react"
4 | import { createRoot } from "react-dom/client"
5 | import Root from "../components"
6 | import { document } from "../utils/browser-dependencies"
7 | import "../styles/rechord.sass"
8 |
9 | const renderComponent = (id, component) => () => {
10 | const element = document.getElementById(id)
11 | if (element) {
12 | const root = createRoot(element)
13 | root.render(component)
14 | }
15 | }
16 | const setReact = (id, component) => (
17 | document.addEventListener("DOMContentLoaded", renderComponent(id, component))
18 | )
19 |
20 | setReact("rechord", )
21 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Notification/ReleaseNotification.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import releases from "../../../constants/releases"
3 | import * as utils from "../../../utils"
4 |
5 | export default class ReleaseNotification extends PureComponent {
6 | render() {
7 | const { data: { title, updated_at: updatedAt } } = this.props
8 | const version = releases.find(release => release.version === title)
9 | return (
10 |
11 |
12 |
{title} がリリースされました
13 | {version.content}
14 |
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.slim:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | title
5 | - base_title = "rechord - 演奏もできるコード進行共有サービス"
6 | - if @title.present?
7 | - title = "#{@title} | #{base_title}"
8 | - else
9 | - title = base_title
10 | = title
11 |
12 | = csrf_meta_tags
13 | / = render "layouts/gtm_head"
14 | = render "layouts/google_ads"
15 | = render "layouts/meta", title: title
16 |
17 | = stylesheet_link_tag "application", media: "all"
18 | = javascript_include_tag "application"
19 | = yield(:pack)
20 |
21 | / link href="https://use.fontawesome.com/releases/v5.0.3/css/all.css" rel="stylesheet"
22 |
23 | body
24 | / = render "layouts/gtm_body"
25 | = yield
26 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5 | // vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require_tree .
15 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/fav.sass:
--------------------------------------------------------------------------------
1 | .fav
2 | .icon.can-click
3 | i
4 | transform: translatez(0)
5 | -webkit-transform: translatez(0)
6 | transition: all .2s ease
7 | &:hover
8 | opacity: .6
9 | transform: scale(1.1)
10 | &.active
11 | font-weight: bold
12 | .icon.can-click
13 | color: #fa6060
14 | i
15 | transform: translatez(0)
16 | -webkit-transform: translatez(0)
17 | animation: fav-animation .2s linear 0s forwards
18 | &:not(.active)
19 | .icon.can-click:hover
20 | color: #fa6060
21 |
22 | @keyframes fav-animation
23 | 0%
24 | transform: scale(.5)
25 | color: #fa6060
26 | 5%
27 | transform: scale(1.4)
28 | 100%
29 | transform: scale(1)
30 |
--------------------------------------------------------------------------------
/app/frontend/validator/index.js:
--------------------------------------------------------------------------------
1 | const validate = (error, message) => (error ? message : false)
2 | const validates = {
3 | required: (value) => validate(!value || value.lenght === 0, "blank"),
4 | maxLength: (value, length) => validate(value.length > length, "too_long"),
5 | format: (value, regex) => validate(!regex.test(value), "invalid_format")
6 | }
7 |
8 | export const validator = ({ key, value, types, setState, errors }) => {
9 | const errorsArray = []
10 | types.forEach(type => {
11 | const [target, arg] = type
12 | const message = validates[target](value, arg)
13 | if (message) errorsArray.push(message)
14 | })
15 | return setState({ errors: Object.assign(errors, { [key]: errorsArray }) })
16 | }
17 |
18 | export default validator
19 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/changelog.sass:
--------------------------------------------------------------------------------
1 | .changelog
2 | .columns.version
3 | .column
4 | padding-top: 1rem
5 | padding-bottom: 2rem
6 | &:first-child
7 | text-align: right
8 | border-right: 1px solid #dbdbdb
9 | padding-right: 2em
10 | .title, .subtitle
11 | color: #444
12 | .subtitle
13 | margin-top: -1em
14 | font-size: 1em
15 | font-weight: normal
16 | @media screen and (max-width: $tablet - 1px)
17 | text-align: left
18 | border-right: none
19 | padding-right: 0
20 | padding-bottom: 0
21 | &:last-child
22 | font-size: .95em
23 | ul
24 | margin-top: .4em
25 | margin-bottom: .4em
26 |
--------------------------------------------------------------------------------
/app/frontend/constants/index.js:
--------------------------------------------------------------------------------
1 | export const STREAK_NOTE = "%"
2 | export const RESUME_NOTE = "="
3 | export const STOP_NOTE = "_"
4 | export const STOP_NOTE_2 = "N.C."
5 | export const START_MARKER = "<"
6 | export const END_MARKER = ">"
7 | export const MIN_BPM = 0
8 | export const MAX_BPM = 999
9 | export const MIN_CAPO = 0
10 | export const MAX_CAPO = 11
11 | export const MIN_VOLUME = 1
12 | export const MAX_VOLUME = 20
13 | export const DEFAULT_BEAT = "4/4"
14 | export const DEFAULT_BPM = 120
15 | export const DEFAULT_VOLUME = MAX_VOLUME - 5
16 | export const DEFAULT_INSTRUMENT_TYPE = "Piano"
17 |
--------------------------------------------------------------------------------
/app/frontend/components/CardsList/UsersResult/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import UserCard from "../../UserCard"
3 |
4 | export default class UsersResult extends Component {
5 | render() {
6 | const { users, word } = this.props
7 | return (
8 |
9 | {users.length > 0 ? (
10 | users.map(user => (
11 |
12 | ))
13 | ) : (
14 |
15 |
16 |
17 | No users.
18 |
19 |
20 |
21 | )}
22 |
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/LoopControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import HorizontalField from "../../commons/HorizontalField"
4 |
5 | export default class LoopControl extends Component {
6 | handleToggleLoop = (e) => {
7 | this.props.handleSetState({ loop: e.target.checked })
8 | }
9 | render() {
10 | const { loop } = this.props
11 | return (
12 |
13 |
21 |
22 |
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/frontend/components/CardsList/SortSelect/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import SelectField from "../../commons/SelectField"
3 | import * as utils from "../cardsListUtils"
4 |
5 | export default class SortSelect extends Component {
6 | handleChange = (e) => this.props.handleChangeOption("sort", e.target.value)
7 | render() {
8 | const { sort, type } = this.props
9 | return (
10 |
11 |
18 |
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/Changelog/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import Version from "./Version"
3 | import releases from "../../../constants/releases"
4 | import * as utils from "../../../utils"
5 |
6 | export default class Changelog extends PureComponent {
7 | componentDidMount = () => utils.setMeta("更新履歴", "", this.props.history)
8 | render() {
9 | return (
10 |
11 |
更新履歴
12 | {releases.slice().reverse().map(release => (
13 |
14 | {release.content}
15 |
16 | ))}
17 |
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/ClickControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import HorizontalField from "../../commons/HorizontalField"
4 |
5 | export default class ClickControl extends Component {
6 | handleToggleClick = (e) => {
7 | this.props.handleSetState({ enabledClick: e.target.checked })
8 | }
9 | render() {
10 | const { enabledClick } = this.props
11 | return (
12 |
13 |
21 |
22 |
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/bin/fog:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'fog' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("fog", "fog")
30 |
--------------------------------------------------------------------------------
/bin/pry:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'pry' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("pry", "pry")
30 |
--------------------------------------------------------------------------------
/bin/yri:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'yri' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("yard", "yri")
30 |
--------------------------------------------------------------------------------
/app/frontend/components/CardsList/OptionCheckbox/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | const isChecked = (value) => value === "true" || value === true
4 |
5 | export default class OptionCheckbox extends Component {
6 | handleChange = () => {
7 | const { option: { key }, value } = this.props
8 | this.props.handleChangeOption(key, !isChecked(value))
9 | }
10 | render() {
11 | const { option: { key, label }, value } = this.props
12 | const name = `${key}-checkbox`
13 | return (
14 |
15 |
23 |
26 |
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/frontend/components/SharedButtons/ShareIcon/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class ShareIcon extends PureComponent {
5 | render() {
6 | const { icon, color, large, customClass } = this.props
7 | const wrapperClass = classNames("icon", `is-${large ? "large" : "medium"}`, { [customClass]: customClass })
8 | const stackClass = classNames("fa-stack", { "fa-lg": large })
9 | const iconClass = classNames("fa", "fa-stack-1x", "fa-inverse", { [`fa-${icon}`]: icon })
10 | const colorStyle = color ? { color } : {}
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/bin/haml:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'haml' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("haml", "haml")
30 |
--------------------------------------------------------------------------------
/bin/puma:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'puma' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("puma", "puma")
30 |
--------------------------------------------------------------------------------
/bin/racc:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'racc' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("racc", "racc")
30 |
--------------------------------------------------------------------------------
/bin/sass:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'sass' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("sass", "sass")
30 |
--------------------------------------------------------------------------------
/bin/scss:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'scss' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("sass", "scss")
30 |
--------------------------------------------------------------------------------
/bin/thor:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'thor' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("thor", "thor")
30 |
--------------------------------------------------------------------------------
/bin/tilt:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'tilt' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("tilt", "tilt")
30 |
--------------------------------------------------------------------------------
/bin/yard:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'yard' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("yard", "yard")
30 |
--------------------------------------------------------------------------------
/bin/oauth:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'oauth' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("oauth", "oauth")
30 |
--------------------------------------------------------------------------------
/bin/rackup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'rackup' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("rack", "rackup")
30 |
--------------------------------------------------------------------------------
/bin/slimrb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'slimrb' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("slim", "slimrb")
30 |
--------------------------------------------------------------------------------
/bin/yardoc:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'yardoc' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("yard", "yardoc")
30 |
--------------------------------------------------------------------------------
/bin/byebug:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'byebug' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("byebug", "byebug")
30 |
--------------------------------------------------------------------------------
/bin/coderay:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'coderay' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("coderay", "coderay")
30 |
--------------------------------------------------------------------------------
/bin/dotenv:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'dotenv' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("dotenv", "dotenv")
30 |
--------------------------------------------------------------------------------
/bin/fission:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'fission' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("fission", "fission")
30 |
--------------------------------------------------------------------------------
/bin/listen:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'listen' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("listen", "listen")
30 |
--------------------------------------------------------------------------------
/bin/pumactl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'pumactl' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("puma", "pumactl")
30 |
--------------------------------------------------------------------------------
/bin/unicorn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'unicorn' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("unicorn", "unicorn")
30 |
--------------------------------------------------------------------------------
/bin/bootsnap:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bootsnap' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("bootsnap", "bootsnap")
30 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'console' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("fog-dnsimple", "console")
30 |
--------------------------------------------------------------------------------
/bin/nokogiri:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'nokogiri' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("nokogiri", "nokogiri")
30 |
--------------------------------------------------------------------------------
/bin/nrdebug:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'nrdebug' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("newrelic_rpm", "nrdebug")
30 |
--------------------------------------------------------------------------------
/bin/whenever:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'whenever' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("whenever", "whenever")
30 |
--------------------------------------------------------------------------------
/app/frontend/utils/browser-dependencies.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict, no-undef, no-restricted-globals */
2 |
3 | "use strict"
4 |
5 | const Window = window
6 | const Document = Window.document
7 | const Navigator = navigator
8 | const Location = Window.location
9 | const History = history
10 | const formData = FormData
11 | const audioContext = Window.AudioContext || Window.webkitAudioContext
12 |
13 | export { Window as window }
14 | export { Document as document }
15 | export { Navigator as navigator }
16 | export { Location as location }
17 | export { History as history }
18 | export { formData as FormData }
19 | export { audioContext as AudioContext }
20 |
21 | const LocalStorage = localStorage || {
22 | getItem: () => false,
23 | setItem: () => false,
24 | removeItem: () => false
25 | }
26 | export { LocalStorage as localStorage }
27 |
28 | /* eslint-enable */
29 |
--------------------------------------------------------------------------------
/bin/newrelic:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'newrelic' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("newrelic_rpm", "newrelic")
30 |
--------------------------------------------------------------------------------
/bin/rbvmomish:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'rbvmomish' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("rbvmomi", "rbvmomish")
30 |
--------------------------------------------------------------------------------
/bin/sprockets:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'sprockets' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("sprockets", "sprockets")
30 |
--------------------------------------------------------------------------------
/bin/httpclient:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'httpclient' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("httpclient", "httpclient")
30 |
--------------------------------------------------------------------------------
/bin/restclient:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'restclient' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("rest-client", "restclient")
30 |
--------------------------------------------------------------------------------
/bin/sass-convert:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'sass-convert' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("sass", "sass-convert")
30 |
--------------------------------------------------------------------------------
/bin/wheneverize:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'wheneverize' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("whenever", "wheneverize")
30 |
--------------------------------------------------------------------------------
/app/controllers/users/omniauth_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
2 | def twitter
3 | callback_from :twitter
4 | end
5 |
6 | def facebook
7 | callback_from :facebook
8 | end
9 |
10 | def google_oauth2
11 | callback_from :google_oauth2
12 | end
13 |
14 | def tumblr
15 | callback_from :tumblr
16 | end
17 |
18 | def github
19 | callback_from :github
20 | end
21 |
22 | private
23 |
24 | def callback_from(provider)
25 | user = User.find_or_create_from_auth(request.env["omniauth.auth"])
26 |
27 | if user.persisted?
28 | sign_in_and_redirect user, event: :authentication
29 | set_flash_message(:notice, :success, kind: provider) if is_navigational_format?
30 | else
31 | session["devise.#{provider}_data"] = request.env["omniauth.auth"]
32 | redirect_to root_path
33 | end
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/bin/mongrel_rpm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'mongrel_rpm' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("newrelic_rpm", "mongrel_rpm")
30 |
--------------------------------------------------------------------------------
/bin/newrelic_cmd:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'newrelic_cmd' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("newrelic_rpm", "newrelic_cmd")
30 |
--------------------------------------------------------------------------------
/bin/unicorn_rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'unicorn_rails' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("unicorn", "unicorn_rails")
30 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/HorizontalField.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class HorizontalField extends PureComponent {
5 | render() {
6 | const { label, customClass, customStyle, children } = this.props
7 | const fieldClass = classNames("field", "is-horizontal", { [customClass]: customClass })
8 | return (
9 |
10 | {label && (
11 |
12 |
15 |
16 | )}
17 |
18 |
19 |
20 | {children}
21 |
22 |
23 |
24 |
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/bin/generate-api:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'generate-api' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("google-api-client", "generate-api")
30 |
--------------------------------------------------------------------------------
/app/frontend/components/CardsList/ScoresResult/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import ScoreCard from "../../ScoreCard"
3 |
4 | export default class ScoresResult extends Component {
5 | render() {
6 | const { scores, word, noAuthor } = this.props
7 | return (
8 |
9 | {scores.length > 0 ? (
10 | scores.map(score => (
11 |
17 | ))
18 | ) : (
19 |
20 |
21 |
22 | No scores.
23 |
24 |
25 |
26 | )}
27 |
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/frontend/validator/FormWithValidate.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 | import { translate } from "./translate"
4 |
5 | export default class FormWithValidate extends PureComponent {
6 | render() {
7 | const { errorKey, target, errors, customStyle, children } = this.props
8 | const targetErrors = errors && errors[errorKey]
9 | const { className } = children.props
10 | const fieldClass = classNames(className, {
11 | "is-danger": targetErrors && targetErrors.length > 0
12 | })
13 | return (
14 |
15 | {React.cloneElement(children, { className: fieldClass })}
16 | {targetErrors && targetErrors.map(error => (
17 |
18 | {translate(target, errorKey, error)}
19 |
20 | ))}
21 |
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/SelectField.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class SelectField extends PureComponent {
5 | render() {
6 | const { icon, customClass, customStyle, children } = this.props
7 | const fieldClass = classNames("field", { [customClass]: customClass })
8 | const controlClass = classNames("control", { "has-icons-left": icon })
9 | const iconClass = classNames("fa", { [`fa-${icon}`]: icon })
10 | return (
11 |
12 |
13 |
14 | {children}
15 |
16 | {icon && (
17 |
18 |
19 |
20 | )}
21 |
22 |
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/InstrumentControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import SelectField from "../../commons/SelectField"
4 | import * as instruments from "../../../constants/instruments"
5 |
6 | export default class InstrumentControl extends Component {
7 | handleChangeInstrument = (e) => {
8 | this.props.handleSetState({ instrumentType: e.target.value })
9 | }
10 | render() {
11 | const { instrumentType, disabled } = this.props
12 | return (
13 |
14 |
25 |
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/frontend/constants/instruments.js:
--------------------------------------------------------------------------------
1 | export const types = (onload) => {
2 | const baseInstrument = ({ name, attack, release }) => (
3 | [{
4 | E2: "e2.mp3",
5 | E3: "e3.mp3",
6 | E4: "e4.mp3",
7 | E5: "e5.mp3"
8 | }, {
9 | attack: attack || 0,
10 | release: release || 0.5,
11 | baseUrl: `/audios/${name}/`,
12 | onload
13 | }]
14 | )
15 | return {
16 | Piano: baseInstrument({ name: "piano" }),
17 | Guitar: baseInstrument({ name: "guitar" }),
18 | Strings: baseInstrument({ name: "strings", release: 1 })
19 | }
20 | }
21 |
22 | export const click = {
23 | oscillator: {
24 | type: "square"
25 | },
26 | envelope: {
27 | attack: 0.005,
28 | decay: 0.2,
29 | sustain: 0.4,
30 | release: 0.4,
31 | },
32 | filterEnvelope: {
33 | attack: 0.005,
34 | decay: 0.1,
35 | sustain: 0.05,
36 | release: 0.4,
37 | baseFrequency: 300,
38 | octaves: 4
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/frontend/utils/draftjsUtils.js:
--------------------------------------------------------------------------------
1 | export const getSelectedText = (contentState, selection) => {
2 | const startKey = selection.getStartKey()
3 | const endKey = selection.getEndKey()
4 | const blocks = contentState.getBlockMap()
5 |
6 | let reachedEndOfBlock = false
7 | const selectedBlock = blocks
8 | .skipUntil((block) => block.getKey() === startKey)
9 | .takeUntil((block) => {
10 | if (reachedEndOfBlock) return true
11 | if (block.getKey() === endKey) reachedEndOfBlock = true
12 | return false
13 | })
14 |
15 | const selectedText = selectedBlock.map((block) => {
16 | const key = block.getKey()
17 | const text = block.getText()
18 | const start = (key === startKey) ? selection.getStartOffset() : 0
19 | const end = (key === endKey) ? selection.getEndOffset() : text.length
20 |
21 | return text.slice(start, end)
22 | }).join("\n")
23 |
24 | return selectedText
25 | }
26 |
27 | export default getSelectedText
28 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/NewScore/RestoreModal/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import ModalCard from "../../../commons/ModalCard"
3 |
4 | export default class RestoreModal extends Component {
5 | handleClick = () => {
6 | const { restoreState, handleSetState, setInputText } = this.props
7 | handleSetState(restoreState)
8 | setInputText(restoreState.inputText)
9 | this.hideModal()
10 | }
11 | hideModal = () => this.props.handleResetLocalStorage()
12 | render() {
13 | const { restoreState } = this.props
14 | const isActive = restoreState && restoreState.inputText.length > 0
15 | return (
16 |
24 | 前回作業したデータが残っています。
25 | 復元しますか?
26 |
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/LinkButton.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import { Link } from "react-router-dom"
3 | import classNames from "classnames"
4 |
5 | export default class LinkButton extends PureComponent {
6 | render() {
7 | const { to, color, size, icon, text, disabled, customClass, customStyle } = this.props
8 | const buttonClass = classNames("button", customClass, { [`is-${color}`]: color, [`is-${size}`]: size })
9 | const iconClass = classNames("fa", { [`fa-${icon}`]: icon })
10 | return (
11 |
17 | {icon && (
18 |
19 |
20 |
21 | )}
22 | {text && (
23 | {text}
24 | )}
25 |
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/BpmControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import HorizontalField from "../../commons/HorizontalField"
4 | import { MIN_BPM, MAX_BPM } from "../../../constants"
5 | import * as utils from "../../../utils"
6 |
7 | export default class BpmControl extends Component {
8 | handleChangeBpm = (e) => {
9 | this.props.handleSetState({ bpm: utils.valueInRange(e.target.value, MIN_BPM, MAX_BPM) })
10 | }
11 | handleBlur = () => {
12 | const { bpm } = this.props
13 | if (!bpm) this.props.handleSetState({ bpm: 0 })
14 | }
15 | render() {
16 | const { bpm } = this.props
17 | return (
18 |
19 |
28 |
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Button.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class Button extends PureComponent {
5 | render() {
6 | const { color, size, icon, text, disabled, rightIcon, customClass, onClick } = this.props
7 | const buttonClass = classNames("button", {
8 | [`is-${color}`]: color,
9 | [`is-${size}`]: size,
10 | [customClass]: customClass
11 | })
12 | const iconClass = classNames("fa", { [`fa-${icon}`]: icon })
13 | return (
14 |
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/views/top/maintenance.html.slim:
--------------------------------------------------------------------------------
1 | section.hero.is-primary.is-fullheight
2 | .hero-body style="min-height: 100vh"
3 | .container.has-text-centered
4 | = image_tag '/assets/images/logo_white.png', width: 160
5 | h1.title style="margin-top: .4em; margin-bottom: 2em"
6 | | sorry... rechord is under maintenance!
7 | h2.subtitle.is-6
8 | | 申し訳ありませんが、rechord は現在メンテナンス中です。
9 | br
10 | | メンテナンスの予定は以下の通りです。
11 | - @maintenance_schedules.each do |schedule|
12 | table.table.is-bordered.mt-3 style="margin: 3rem auto 0; min-width: 50%"
13 | tr
14 | th 目的
15 | td= schedule.title
16 | tr
17 | th 概要
18 | td= schedule.description.html_safe
19 | tr
20 | th 開始時間
21 | td= schedule.start_time.strftime('%Y年%m月%d日 %H時%M分')
22 | tr
23 | th 終了時間
24 | td= schedule.end_time.strftime('%Y年%m月%d日 %H時%M分')
25 |
26 | - if ENV['UA_ID'].present?
27 | = render 'layouts/ga'
28 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/CapoControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import HorizontalField from "../../commons/HorizontalField"
4 | import { MIN_CAPO, MAX_CAPO } from "../../../constants"
5 | import * as utils from "../../../utils"
6 |
7 | export default class CapoControl extends Component {
8 | handleChangeCapo = (e) => {
9 | this.props.handleSetState({ capo: utils.valueInRange(e.target.value, MIN_CAPO, MAX_CAPO) })
10 | }
11 | handleBlur = () => {
12 | const { capo } = this.props
13 | if (!capo) this.props.handleSetState({ capo: 0 })
14 | }
15 | render() {
16 | const { capo } = this.props
17 | return (
18 |
19 |
28 |
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/BeatControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import HorizontalField from "../../commons/HorizontalField"
4 | import SelectField from "../../commons/SelectField"
5 | import { beats } from "../../../constants/beats"
6 |
7 | export default class BeatControl extends Component {
8 | handleChangeBeat = (e) => {
9 | this.props.handleSetState({ beat: e.target.value })
10 | }
11 | render() {
12 | const { beat, disabled } = this.props
13 | return (
14 |
15 |
16 |
27 |
28 |
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 | /data/*
14 |
15 | # Ignore all logfiles and tempfiles.
16 | /log/*
17 | /tmp/*
18 | !/log/.keep
19 | !/tmp/.keep
20 |
21 | /node_modules
22 | /yarn-error.log
23 | /public/packs
24 | /public/packs-test
25 | /public/uploads
26 | /public/assets/*
27 | !/public/assets/.keep
28 |
29 | .byebug_history
30 |
31 | .env
32 | .rspec
33 | .DS_Store
34 | tags
35 | /.idea
36 | /.docker/postgres/*
37 |
38 | # Ignore master key for decrypting credentials and more.
39 | /config/master.key
40 |
41 | /public/packs
42 | /public/packs-test
43 | /node_modules
44 | /yarn-error.log
45 | yarn-debug.log*
46 | .yarn-integrity
47 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/login.sass:
--------------------------------------------------------------------------------
1 | .modal.login
2 | white-space: initial
3 | .modal-content
4 | width: 400px
5 | .button
6 | font-weight: bold
7 | display: block
8 | &:not(:last-child)
9 | margin-bottom: .8em
10 | .icon
11 | margin-right: .3em
12 | &.twitter
13 | background-color: #00aced
14 | &:hover
15 | background-color: darken(#00aced, 5%)
16 | &.facebook
17 | background-color: #3b5998
18 | &:hover
19 | background-color: darken(#3b5998, 5%)
20 | &.google
21 | background-color: #dd4b39
22 | &:hover
23 | background-color: darken(#dd4b39, 5%)
24 | &.tumblr
25 | background-color: #2c4762
26 | &:hover
27 | background-color: darken(#2c4762, 5%)
28 | &.github
29 | background-color: #24292e
30 | &:hover
31 | background-color: darken(#24292e, 5%)
32 |
33 | @media screen and (max-width: $tablet - 1px)
34 | .modal.login
35 | .modal-content
36 | width: calc(100% - 40px)
37 |
--------------------------------------------------------------------------------
/config/unicorn.rb:
--------------------------------------------------------------------------------
1 | rails_root = File.expand_path("../../", __FILE__)
2 | # rails_env = ENV["RAILS_ENV"] || "development"
3 |
4 | ENV["BUNDLE_GEMFILE"] = "#{rails_root}/Gemfile"
5 |
6 | worker_processes 2
7 | working_directory rails_root
8 | timeout 30
9 | preload_app true
10 |
11 | stderr_path File.expand_path("#{rails_root}/log/unicorn_stderr.log", __FILE__)
12 | stdout_path File.expand_path("#{rails_root}/log/unicorn_stdout.log", __FILE__)
13 |
14 | pid File.expand_path("#{rails_root}/tmp/pids/unicorn.pid", __FILE__)
15 |
16 | before_fork do |server, worker|
17 | defined?(ActiveRecord::Base) and
18 | ActiveRecord::Base.connection.disconnect!
19 |
20 | old_pid = "#{server.config[:pid]}.oldbin"
21 | if old_pid != server.pid
22 | begin
23 | sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
24 | Process.kill(sig, File.read(old_pid).to_i)
25 | rescue Errno::ENOENT, Errno::ESRCH
26 | end
27 | end
28 | end
29 |
30 | after_fork do |server, worker|
31 | defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
32 | end
33 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/VolumeControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import Slider from "../../commons/Slider"
4 | import { MIN_VOLUME, MAX_VOLUME } from "../../../constants"
5 | import * as utils from "../../../utils"
6 |
7 | export default class VolumeControl extends Component {
8 | handleChangeVolume = (e) => {
9 | this.props.handleSetState({ volume: utils.valueInRange(e.target.value, MIN_VOLUME, MAX_VOLUME) })
10 | }
11 | render() {
12 | const { volume } = this.props
13 | return (
14 |
15 |
16 |
17 |
18 |
26 |
27 |
28 |
29 |
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/frontend/constants/regex.js:
--------------------------------------------------------------------------------
1 | export const signature = "[#♯#b♭b]{0,2}"
2 | export const scales = "[CDEFGAB]"
3 | export const note = `${scales}${signature}`
4 | export const specialNotes = ["%", "=", "_", "<", ">", "N\\.C\\."]
5 | export const onChordSeparator = "(\\/|/|on)"
6 | export const chordTypes = "(?![#♯b♭])(?:(?!(on|l))[Ma-z0-9()(),\\-+#♯#b♭b△ΔΦφø])+"
7 |
8 | export const rootChord = new RegExp(`(${note})|${specialNotes.join("|")}`, "g")
9 | export const onChord = new RegExp(`${onChordSeparator}${note}`, "g")
10 | export const chordType = new RegExp(chordTypes, "g")
11 | export const chord = new RegExp(`^((${note}(${chordTypes})?(${onChordSeparator}${note})?)|${specialNotes.join("|")})$`)
12 | export const separator = /[||ll]/g
13 | export const whiteSpaces = /[ ]+/g
14 | export const comment = /^[#♯].*$/g
15 | export const startMarker = /^<.*$/g
16 | export const endMarker = /^>.*$/g
17 |
18 | export const markerLineTop = /[<>]/
19 | export const commentLineTop = /[#♯<>]/
20 |
21 | export const joinOnChord = new RegExp(`${onChordSeparator} (${note})`, "g")
22 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/loading.sass:
--------------------------------------------------------------------------------
1 | .loading-wrapper
2 | min-height: 200px
3 | &:before
4 | content: "loading..."
5 | display: flex
6 | justify-content: center
7 | align-items: center
8 | color: #aaa
9 | font-weight: bold
10 | position: absolute
11 | background-color: #fff
12 | width: calc(100% + 2em)
13 | height: calc(100% + 2em)
14 | top: -1em
15 | left: -1em
16 | z-index: 20
17 | opacity: .7
18 | &:after
19 | content: "\f110"
20 | position: absolute
21 | top: calc(50% - 1.4em)
22 | left: calc(50% - .52em)
23 | font: normal normal normal 14px/1 FontAwesome
24 | font-size: 3.17em
25 | color: #aaa
26 | z-index: 25
27 | text-rendering: auto
28 | -webkit-font-smoothing: antialiased
29 | -moz-osx-font-smoothing: grayscale
30 | -webkit-animation: spin 2s infinite linear
31 | animation: spin 1s infinite steps(8)
32 | opacity: .7
33 |
34 | @keyframes spin
35 | 0%
36 | -webkit-transform: rotate(0deg)
37 | transform: rotate(0deg)
38 | 100%
39 | -webkit-transform: rotate(359deg)
40 | transform: rotate(359deg)
41 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/tabbar.sass:
--------------------------------------------------------------------------------
1 | .tab-bar.tabs
2 | margin-bottom: 0
3 | .container
4 | border-bottom: 1px solid #dbdbdb
5 | display: flex
6 | align-items: center
7 | > div:first-child
8 | flex-grow: 1
9 | ul
10 | border: none
11 | margin-right: .5em
12 | a, span
13 | padding: .85em 1em
14 | a:hover
15 | background-color: #fafafa
16 | .disabled
17 | opacity: .5
18 | .icon
19 | margin: 0 .2em 0 0
20 | padding-left: .5em
21 | font-size: 1.1em
22 | .tab-label
23 | margin: 0
24 | padding: 0
25 | .field
26 | .input, .button, .icon
27 | font-size: .95rem
28 |
29 | @media screen and (max-width: $mobile - 1px)
30 | .tab-bar.tabs
31 | .container
32 | width: 100%
33 | .field
34 | margin-right: 1.5rem
35 |
36 | @media screen and (max-width: $tablet - 1px)
37 | .tab-bar.tabs
38 | .container
39 | ul
40 | .icon
41 | margin: 0
42 | padding: 0
43 | .tab-label
44 | display: none
45 | .field
46 | margin-right: .75rem
47 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Notification/NotificationIcon.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class NotificationIcon extends Component {
5 | render() {
6 | const { notifications, handleToggleNotification, customClass } = this.props
7 | const navbarItemClass = classNames("navbar-item", "notification-icon", { [customClass]: customClass })
8 |
9 | return notifications.length > 0 ? (
10 |
11 |
12 |
13 |
14 | {notifications.length}
15 |
16 |
17 |
18 | ) : (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------
1 | RAILS_FULL_HOST = 'http://localhost:3000'
2 |
3 | REDIS_URL = redis://127.0.0.1:6379
4 |
5 | BASIC_AUTHENTICATION_USERNAME = 'username'
6 | BASIC_AUTHENTICATION_PASSWORD = 'password'
7 |
8 | ### 普段はコメントアウト
9 | # NewRelic
10 | # NEWRELIC_LICENSE_KEY =
11 | # WEBHOOK_URL =
12 |
13 | # Gooogle Analytics
14 | # UA_ID =
15 |
16 | # 開発用DB
17 | DATABASE_NAME = rechord_development
18 | DATABASE_TEST = rechord_test
19 | DATABASE_HOST = localhost
20 | DATABASE_PORT = 5432
21 | DATABASE_USERNAME = postgres
22 | DATABASE_PASSWORD =
23 |
24 | # ConoHa Object Storage
25 | CONOHA_API_AUTH_URL =
26 | CONOHA_API_PASSWORD =
27 | CONOHA_ASSET_HOST =
28 | CONOHA_CONTAINER_NAME =
29 | CONOHA_TENANT_NAME =
30 | CONOHA_USERNAME =
31 |
32 | # omniauth
33 | TWITTER_KEY =
34 | TWITTER_SECRET =
35 | FACEBOOK_KEY =
36 | FACEBOOK_SECRET =
37 | GITHUB_KEY =
38 | GITHUB_SECRET =
39 | GOOGLE_CLIENT_ID =
40 | GOOGLE_CLIENT_SECRET =
41 | TUMBLR_KEY =
42 | TUMBLR_SECRET =
43 |
44 | # backup
45 | PG_DUMP_PATH =
46 | DROPBOX_ACCESS_TOKEN =
47 | BACKUP_FILES_PATH =
48 |
49 | # 本番用
50 | # RAILS_MASTER_KEY =
51 |
--------------------------------------------------------------------------------
/app/models/notification.rb:
--------------------------------------------------------------------------------
1 | class Notification < ApplicationRecord
2 | validates :title, length: { maximum: 128 }, uniqueness: true
3 | validates :content, length: { maximum: 2048 }
4 |
5 | scope :list_for_user, -> (user_id) {
6 | return if user_id.blank?
7 |
8 | user = User.find(user_id)
9 | return if user.blank?
10 |
11 | notifications = where(user_id: [user_id, nil])
12 | notifications = notifications.where("updated_at > ?", user.last_read_at) if user.last_read_at.present?
13 | notifications.order(updated_at: :desc).limit(20)
14 | }
15 |
16 | enum template: {
17 | default: 0,
18 | release: 1,
19 | fav: 2
20 | }
21 |
22 | class << self
23 | def create_or_update_by_fav(fav)
24 | notification = find_by(title: fav.score.token)
25 | if notification.present?
26 | notification.touch # updated_at だけ更新
27 | else
28 | notification = create(template: :fav, title: fav.score.token, user_id: fav.score.user_id)
29 | end
30 | end
31 |
32 | def destroy_if_exist(params)
33 | notification = find_by(params)
34 | notification.destroy! if notification.present?
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/ShowScore/DestroyScoreModal/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import ModalCard from "../../../commons/ModalCard"
3 | import * as api from "../../../../api"
4 | import * as path from "../../../../utils/path"
5 |
6 | export default class DestroyScoreModal extends Component {
7 | handleDestroyScore = () => {
8 | const { history, token, user: { name } } = this.props
9 | api.destroyScore(
10 | { token },
11 | () => history.push(path.user.show(name), { flash: ["success", "削除に成功しました。"] }),
12 | () => history.push(path.current, { flash: ["error", "削除に失敗しました。"] })
13 | )
14 | }
15 | hideModal = () => this.props.handleToggleDestroyModal()
16 | render() {
17 | const { active } = this.props
18 | return (
19 |
28 | 一度削除したスコアは復元できません。
29 | 本当にスコアを削除しますか?
30 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery with: :exception
3 |
4 | rescue_from CanCan::AccessDenied do |exception|
5 | redirect_to main_app.root_path
6 | end
7 |
8 | before_action :no_cache
9 | before_action :basic_authenticate if Rails.env.staging?
10 |
11 | private
12 |
13 | def authenticate_user!
14 | session[:user_return_to] = request.path_info
15 | redirect_to root_path unless user_signed_in?
16 | end
17 |
18 | def no_cache
19 | # ブラウザバックで JSON が表示されるのを防止
20 | if request.format.json?
21 | response.headers["Cache-Control"] = "no-cache, no-store"
22 | response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
23 | response.headers["Pragma"] = "no-cache"
24 | end
25 | end
26 |
27 | def basic_authenticate
28 | authenticate_or_request_with_http_basic do |username, password|
29 | username == ENV['BASIC_AUTHENTICATION_USERNAME'] && password == ENV['BASIC_AUTHENTICATION_PASSWORD']
30 | end
31 | end
32 |
33 | def after_sign_out_path_for(resource)
34 | root_path
35 | end
36 |
37 | def new_session_path(scope)
38 | root_path
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/frontend/validator/translate.js:
--------------------------------------------------------------------------------
1 | const keys = {
2 | score: {
3 | title: "タイトル",
4 | content: "スコア"
5 | },
6 | user: {
7 | name: "ID",
8 | screenName: "名前",
9 | profile: "プロフィール",
10 | icon: "アイコン",
11 | site: "サイトURL",
12 | twitter: "Twitter ID "
13 | }
14 | }
15 |
16 | const errorKey = (error) => {
17 | switch (true) {
18 | case (/You are not allowed to upload .* files, allowed types: jpg, jpeg, gif, png/).test(error):
19 | return "wrong_extention"
20 | case (/.*maybe it is not an image\?/).test(error):
21 | return "not_image"
22 | default:
23 | return error
24 | }
25 | }
26 |
27 | const errors = {
28 | blank: (key) => `${key}は必須項目です。`,
29 | too_long: (key) => `${key}が長すぎます。`,
30 | taken: (key) => `その${key}は既に使用されています。`,
31 | over_size: (key) => `${key}のファイルサイズは2MBが上限です。`,
32 | not_image: () => "画像ファイルではありません。",
33 | invalid_format: (key) => `${key}のフォーマットが正しくありません。`,
34 | wrong_extention: (key) => `${key}は .png, .jpg, .jpeg, .gif のみが使用可能です。`,
35 | }
36 |
37 | export const translate = (target, key, error) => errors[errorKey(error)](keys[target][key])
38 |
39 | export default translate
40 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "fileutils"
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path('..', __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to set up or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies
21 | system! 'bin/yarn'
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! 'bin/rails db:prepare'
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! 'bin/rails log:clear tmp:clear'
33 |
34 | puts "\n== Restarting application server =="
35 | system! 'bin/rails restart'
36 | end
37 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/User/DestroyUserModal/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import ModalCard from "../../../commons/ModalCard"
3 | import * as api from "../../../../api"
4 | import * as path from "../../../../utils/path"
5 | import { window } from "../../../../utils/browser-dependencies"
6 |
7 | export default class DestroyUserModal extends Component {
8 | handleDestroyUser = () => (
9 | api.destoryUser(
10 | { name: this.props.user.name },
11 | () => {
12 | window.location.href = path.root
13 | },
14 | () => this.props.history.push(path.current, { flash: ["error", "削除に失敗しました。"] })
15 | )
16 | )
17 | hideModal = () => this.props.handleToggleDestroyModal()
18 | render() {
19 | const { active } = this.props
20 | return (
21 |
30 | ユーザを削除すると、そのユーザで登録されたスコアもすべて削除されます。
31 | 本当にユーザを削除しますか?
32 |
33 | )
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/views/layouts/_meta.html.slim:
--------------------------------------------------------------------------------
1 | - description = "rechord はコード進行を入力するだけで誰でも簡単に演奏/共有することができる Web サービスです。"
2 | - base_url = "https://rechord.cc"
3 | - logo_url = "#{base_url}/images/logo.png"
4 |
5 | meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"
6 | meta name="format-detection" content="telephone=no"
7 |
8 | meta name="title" content=title
9 | meta name="keywords" content=""
10 | meta name="description" content=description
11 | meta name="author" content="comorebi notes"
12 | link rel="canonical" href=base_url
13 |
14 | meta property="og:site_name" content="rechord"
15 | meta property="og:title" content=title
16 | meta property="og:description" content=description
17 | meta property="og:type" content="website"
18 | meta property="og:url" content=request.url
19 | meta property="og:image" content=logo_url
20 | meta property="fb:app_id" content="325988221218874"
21 |
22 | meta name="twitter:card" content="summary"
23 | meta name="twitter:site" content="@rechord_cc"
24 | meta name="twitter:image" content=logo_url
25 |
26 | meta name="google-site-verification" content="Or2JzFxknGLTrrPfPDpFP9jVQFoMmZt15N5lAeLT1_0"
27 |
28 | = favicon_link_tag
29 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Footer/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import { Link } from "react-router-dom"
3 | import { Follow } from "react-twitter-widgets"
4 | import ShareButtons from "../../SharedButtons"
5 | import * as path from "../../../utils/path"
6 | import { location } from "../../../utils/browser-dependencies"
7 |
8 | export default class Footer extends PureComponent {
9 | render() {
10 | return (
11 |
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/KeyControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | import HasAddonsField from "../../commons/HasAddonsField"
4 | import Button from "../../commons/Button"
5 | import * as decorator from "../../../decorators/scoreEditorDecorator"
6 |
7 | export default class KeyControl extends Component {
8 | handleKeyChange = (operation) => {
9 | const { inputText, setInputText } = this.props
10 | setInputText(decorator.keyChange(inputText, operation))
11 | }
12 | handleKeyUp = () => this.handleKeyChange("up")
13 | handleKeyDown = () => this.handleKeyChange("down")
14 |
15 | render() {
16 | const { disabled } = this.props
17 | return (
18 |
19 |
20 |
27 |
28 |
29 |
36 |
37 |
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/UndoControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import { EditorState } from "draft-js"
3 |
4 | import HasAddonsField from "../../commons/HasAddonsField"
5 | import Button from "../../commons/Button"
6 |
7 | export default class UndoControl extends Component {
8 | handleUndo = () => {
9 | const prevEditorState = EditorState.undo(this.props.editorState)
10 | this.props.handleChangeEditorState(prevEditorState)
11 | }
12 | handleRedo = () => {
13 | const nextEditorState = EditorState.redo(this.props.editorState)
14 | this.props.handleChangeEditorState(nextEditorState)
15 | }
16 | render() {
17 | const { editorState, disabled } = this.props
18 | return (
19 |
20 |
21 |
26 |
27 |
28 |
33 |
34 |
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/collation.rb:
--------------------------------------------------------------------------------
1 | # http://319ring.net/blog/archives/2645/
2 |
3 | class Collation
4 | def initialize
5 | @connection = ActiveRecord::Base.connection
6 | end
7 |
8 | def change_all
9 | migration_base do |table, column|
10 | # 配列型に対応
11 | column_type = column.array ? "#{column.sql_type}[]" : column.sql_type
12 | @connection.execute "ALTER TABLE #{table} ALTER COLUMN #{column.name} TYPE #{column_type} COLLATE \"C\""
13 | end
14 | end
15 |
16 | def rollback_all
17 | migration_base do |table, column|
18 | # 配列型に対応
19 | column_type = column.array ? "#{column.sql_type}[]" : column.sql_type
20 | @connection.execute "ALTER TABLE #{table} ALTER COLUMN #{column.name} TYPE #{column_type}"
21 | end
22 | end
23 |
24 | private
25 | def migration_base
26 | @connection.tables.each do |table|
27 | begin
28 | model = Module.const_get(table.classify)
29 | rescue
30 | next
31 | end
32 | # 1からマイグレーションすると、schema cacheのせいで定義が古いまま。
33 | # 削除したり、リネームしたカラムを扱おうとして落ちるので、リセットする。
34 | model.connection.schema_cache.clear!
35 | model.reset_column_information
36 | model.columns.select {|column| column.type == :string || column.type == :text }.each do |column|
37 | yield(table, column)
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/app/frontend/components/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import { Router } from "react-router-dom"
3 | import { createBrowserHistory } from "history"
4 | import ReactGA from "react-ga"
5 |
6 | import Routes from "./Routes"
7 | import { window } from "../utils/browser-dependencies"
8 |
9 | export default class Rechord extends Component {
10 | constructor() {
11 | super()
12 | const { uaId } = window.data
13 | const history = createBrowserHistory()
14 | history.pushPageView = () => {}
15 |
16 | if (uaId.length > 0) {
17 | ReactGA.initialize(uaId)
18 | ReactGA.pageview(window.location.pathname)
19 | // history.listen(location => {
20 | // ReactGA.set({ page: location.pathname })
21 | // ReactGA.pageview(location.pathname)
22 | // })
23 |
24 | // ページタイトルはロード後に設定されるため、
25 | // 各 Component の componentDidMount で個別に設定
26 | history.pushPageView = () => {
27 | ReactGA.set({ page: window.location.pathname })
28 | ReactGA.pageview(window.location.pathname)
29 | }
30 | }
31 | this.state = { history }
32 | }
33 | render() {
34 | const { history } = this.state
35 | return (
36 |
37 |
38 |
39 | )
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/frontend/components/Score/ScoreEditor/changeScrollPosition.js:
--------------------------------------------------------------------------------
1 | import { document } from "../../../utils/browser-dependencies"
2 |
3 | let currentScrollWidth
4 | let currentClientHeight
5 | let isRightEnd
6 |
7 | export const setCurrentScrollPosition = () => {
8 | const editor = document.getElementById("score-editor")
9 | const editorContent = document.getElementsByClassName("public-DraftEditor-content")[0]
10 |
11 | const { scrollWidth, clientWidth, scrollLeft } = editor
12 | const { clientHeight } = editorContent.children[0]
13 |
14 | currentScrollWidth = scrollWidth
15 | currentClientHeight = clientHeight
16 | isRightEnd = scrollWidth === clientWidth + scrollLeft
17 | }
18 |
19 | export const changeScrollPosition = () => {
20 | const editor = document.getElementById("score-editor")
21 | const editorContent = document.getElementsByClassName("public-DraftEditor-content")[0]
22 |
23 | const { scrollWidth, clientWidth } = editor
24 | const { clientHeight } = editorContent.children[0]
25 |
26 | if (isRightEnd && scrollWidth > clientWidth && scrollWidth > currentScrollWidth) {
27 | // 右端での文字入力時にエディタが overscroll したらエディタを右端にスクロール
28 | editor.scrollLeft = scrollWidth - clientWidth
29 | } else if (clientHeight !== currentClientHeight) {
30 | // 文字入力時に高さが変わったらエディタを左端にスクロール
31 | editor.scrollLeft = 0
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | class OnlyAjaxRequest
2 | def matches?(request)
3 | request.xhr?
4 | end
5 | end
6 |
7 | Rails.application.routes.draw do
8 | mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
9 |
10 | devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }
11 | devise_scope :user do
12 | get "/users/logout", to: "devise/sessions#destroy", as: :destroy_user_session
13 | end
14 |
15 | resource :status, only: [:show], constraints: OnlyAjaxRequest.new
16 |
17 | resources :scores, {
18 | only: [:index, :show, :edit, :update, :create, :destroy],
19 | param: :token,
20 | constraints: OnlyAjaxRequest.new
21 | }
22 |
23 | resources :users, {
24 | only: [:index, :show, :update, :destroy],
25 | param: :name,
26 | constraints: OnlyAjaxRequest.new
27 | } do
28 | post :valid_name
29 | put :update_icon
30 | delete :remove_icon
31 | put :read
32 | resources :scores, only: [:index], controller: "users/scores"
33 | end
34 |
35 | resources :favs, only: [:index, :create, :destroy], constraints: OnlyAjaxRequest.new
36 |
37 | scope format: true, constraints: { format: /jpg|png|gif/ } do
38 | get "/*anything", to: proc { [404, {}, [""]] }
39 | end
40 |
41 | get "not_supported" => "top#not_supported"
42 | get "*path" => "top#index"
43 | root "top#index"
44 | end
45 |
--------------------------------------------------------------------------------
/app/controllers/top_controller.rb:
--------------------------------------------------------------------------------
1 | class TopController < ApplicationController
2 | before_action :set_title
3 | before_action :set_notifications
4 | before_action :check_browser_support, only: [:index]
5 | before_action :check_maintenance
6 |
7 | def index
8 | end
9 |
10 | def not_supported
11 | end
12 |
13 | def maintenance
14 | end
15 |
16 | private
17 |
18 | def set_title
19 | case request.fullpath
20 | when /\/users\/([^\/]*)(\/[^\/]*)*/
21 | user = User.friendly.find_by(name: $1)
22 | @title = user&.screen_name
23 | when /\/([^\/]{11})(\/[^\/]*)*/
24 | score = Score.friendly.find_by(token: $1)
25 | if score&.browsable?(current_user&.id) || score&.owner?(current_user&.id)
26 | @title = score&.title
27 | end
28 | end
29 | end
30 |
31 | def set_notifications
32 | @notifications = Notification.list_for_user(current_user&.id)
33 | end
34 |
35 | # IE11 非対応処理
36 | def check_browser_support
37 | ua = request.user_agent&.downcase
38 | if ua.present? && ((ua.include?('msie') && ua.exclude?('opera')) || ua.include?('trident/7'))
39 | redirect_to controller: :top, action: :not_supported
40 | end
41 | end
42 |
43 | def check_maintenance
44 | @maintenance_schedules = MaintenanceSchedule.active
45 | render :maintenance if @maintenance_schedules.present?
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Notification/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import ReleaseNotification from "./ReleaseNotification"
3 | import FavNotification from "./FavNotification"
4 | import DefaultNotification from "./DefaultNotification"
5 | import ModalCard from "../ModalCard"
6 |
7 | export default class Notification extends Component {
8 | render() {
9 | const { notifications, isActive, handleToggleNotification, handleClearNotification } = this.props
10 | const notificationTemplate = (notification) => {
11 | const params = { data: notification, key: notification.id, handleToggleNotification }
12 | switch (notification.template) {
13 | case "release": return
14 | case "fav": return
15 | default: return
16 | }
17 | }
18 | return (
19 |
29 | {isActive && notifications.map(notification => notificationTemplate(notification))}
30 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/controllers/concerns/search_params.rb:
--------------------------------------------------------------------------------
1 | module SearchParams
2 | extend ActiveSupport::Concern
3 |
4 | def scores_list_params
5 | { words: words, sort: scores_list_sort_option, order: order, options: scores_list_options }
6 | end
7 |
8 | def users_list_params
9 | { words: words, sort: users_list_sort_option, order: order, options: users_list_options }
10 | end
11 |
12 | def users_favs_list_params
13 | users_list_params
14 | end
15 |
16 | def user_scores_list_params
17 | scores_list_params.merge(
18 | owner: params[:user_name] == current_user&.name
19 | )
20 | end
21 |
22 | private
23 |
24 | def words
25 | params[:word]&.split(" ")
26 | end
27 |
28 | def order
29 | params[:sort]&.slice(/(asc|desc)$/) || "desc"
30 | end
31 |
32 | def sanitize_sort_option(options, default_option = "id")
33 | option = params[:sort]&.gsub(/_(asc|desc)$/, "")
34 | options.include?(option) ? option : default_option
35 | end
36 |
37 | def scores_list_sort_option
38 | sanitize_sort_option(%w(updated_at title views_count favs_count))
39 | end
40 |
41 | def users_list_sort_option
42 | sanitize_sort_option(%w(updated_at screen_name scores_count))
43 | end
44 |
45 | def scores_list_options
46 | { guest: params[:guest] == "true" }
47 | end
48 |
49 | def users_list_options
50 | { no_scores: params[:no_scores] == "true" }
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/app/controllers/favs_controller.rb:
--------------------------------------------------------------------------------
1 | class FavsController < ApplicationController
2 | include SearchParams
3 |
4 | before_action :authenticate!
5 |
6 | def index
7 | scores = current_user.favs_list(users_favs_list_params)
8 | total_count = scores.count
9 | scores = scores.page(params[:page] || 1)
10 |
11 | render json: {
12 | result: scores.as_json(include: :user),
13 | total_count: total_count,
14 | current_page: scores.current_page,
15 | total_pages: scores.total_pages
16 | }
17 | end
18 |
19 | def create
20 | fav = Fav.new(fav_params)
21 | if fav.save
22 | if fav.score.user_id.present? && current_user&.id != fav.score.user_id
23 | Notification.create_or_update_by_fav(fav)
24 | end
25 | render json: fav
26 | else
27 | render json: fav.errors.full_messages, status: :unprocessable_entity
28 | end
29 | end
30 |
31 | def destroy
32 | fav = Fav.find(params[:id])
33 | if fav.destroy
34 | head :ok
35 | else
36 | render json: fav.errors.full_messages, status: :unprocessable_entity
37 | end
38 | end
39 |
40 | private
41 |
42 | def fav_params
43 | params.require(:fav).permit(:user_id, :score_id)
44 | end
45 |
46 | def authenticate!
47 | unless user_signed_in?
48 | render json: "現在ログイン中のユーザは、この操作に対する権限がありません。", status: :unprocessable_entity
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/ShowScore/ScoreFooter/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import { Link } from "react-router-dom"
3 | import * as path from "../../../../utils/path"
4 | import * as localStorageState from "../../../../utils/localStorageState"
5 |
6 | export default class ScoreFooter extends Component {
7 | handleClick = () => localStorageState.set(this.props.currentState, "editScore")
8 | render() {
9 | const { token, handleToggleDestroyModal } = this.props
10 | const editPath = path.score.edit(token)
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Edit
20 |
21 |
22 |
23 |
29 |
30 |
31 |
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/shared-buttons.sass:
--------------------------------------------------------------------------------
1 | .shared-url
2 | a
3 | vertical-align: middle
4 | margin-right: .8em
5 | .shared-buttons
6 | display: flex
7 | justify-content: center
8 | padding-top: .3em
9 | .shared-button
10 | margin: .8rem
11 | margin-top: 0
12 | &:first-child
13 | margin-left: 0
14 | &:last-child
15 | margin-right: 0
16 | > div
17 | outline: 0
18 | cursor: pointer
19 | &:hover, &:active
20 | opacity: .8
21 | .fa-stack-1x
22 | font-size: .95em
23 | .fa-google-plus
24 | font-size: .9em
25 | .fa-envelope
26 | font-size: .85em
27 | &.as-show
28 | font-size: .85em
29 | .shared-button
30 | margin: 0 .75em 1.2em 0
31 | &:first-child
32 | margin-left: 0
33 | &:last-child
34 | margin-right: 0
35 | &.as-footer
36 | .shared-button
37 | .fa-stack
38 | .fa
39 | &:first-child
40 | color: #fff !important
41 | opacity: .7
42 | &:last-child
43 | color: #532 !important
44 |
45 | @media screen and (max-width: $tablet - 1px)
46 | .shared-url
47 | display: flex
48 | font-size: 1.1em
49 | flex-direction: column
50 | a
51 | margin-right: 0
52 | margin-bottom: .65em
53 | button
54 | align-self: center
55 | font-size: .85em
56 | .shared-buttons
57 | &.as-show
58 | justify-content: center
59 |
--------------------------------------------------------------------------------
/Capfile:
--------------------------------------------------------------------------------
1 | # Load DSL and set up stages
2 | require 'capistrano/setup'
3 |
4 | # Include default deployment tasks
5 | require 'capistrano/deploy'
6 |
7 | # Load the SCM plugin appropriate to your project:
8 | #
9 | # require 'capistrano/scm/hg'
10 | # install_plugin Capistrano::SCM::Hg
11 | # or
12 | # require 'capistrano/scm/svn'
13 | # install_plugin Capistrano::SCM::Svn
14 | # or
15 | require 'capistrano/scm/git'
16 | install_plugin Capistrano::SCM::Git
17 |
18 | # Include tasks from other gems included in your Gemfile
19 | #
20 | # For documentation on these, see for example:
21 | #
22 | # https://github.com/capistrano/rvm
23 | # https://github.com/capistrano/rbenv
24 | # https://github.com/capistrano/chruby
25 | # https://github.com/capistrano/bundler
26 | # https://github.com/capistrano/rails
27 | # https://github.com/capistrano/passenger
28 | #
29 | # require 'capistrano/rvm'
30 | # require 'capistrano/rbenv'
31 | # require 'capistrano/chruby'
32 | # require 'capistrano/bundler'
33 | # require 'capistrano/rails/assets'
34 | # require 'capistrano/rails/migrations'
35 | # require 'capistrano/passenger'
36 |
37 | require 'capistrano/rails'
38 | require 'capistrano/rbenv'
39 | require 'capistrano/bundler'
40 | require 'capistrano/puma'
41 | require 'whenever/capistrano'
42 | install_plugin Capistrano::Puma
43 |
44 | # Load custom tasks from `lib/capistrano/tasks` if you have any defined
45 | Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
46 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 | # # If you are using webpack-dev-server then specify webpack-dev-server host
15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
16 |
17 | # # Specify URI for violation reports
18 | # # policy.report_uri "/csp-violation-report-endpoint"
19 | # end
20 |
21 | # If you are using UJS then enable automatic nonce generation
22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
23 |
24 | # Set the nonce only to specific directives
25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
26 |
27 | # Report CSP violations to a specified URI
28 | # For further information see the following documentation:
29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
30 | # Rails.application.config.content_security_policy_report_only = true
31 |
--------------------------------------------------------------------------------
/app/frontend/components/TitleControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import Field from "../commons/Field"
3 | import { validateTypes } from "./validateTypes"
4 | import { validator } from "../../validator"
5 | import FormWithValidate from "../../validator/FormWithValidate"
6 |
7 | export default class TitleControl extends Component {
8 | constructor() {
9 | super()
10 | this.state = { touch: false }
11 | }
12 | validate = (value) => (
13 | validator({
14 | key: "title",
15 | types: validateTypes,
16 | setState: this.props.handleSetState,
17 | errors: this.props.errors,
18 | value,
19 | })
20 | )
21 | handleTouch = () => {
22 | this.setState({ touch: true })
23 | this.validate(this.props.title)
24 | }
25 | handleSetTitle = (e) => {
26 | const { touch } = this.state
27 | const { handleSetState } = this.props
28 | handleSetState({ title: e.target.value })
29 | if (touch) this.validate(e.target.value)
30 | }
31 | render() {
32 | const { title, errors } = this.props
33 | return (
34 |
35 |
36 |
44 |
45 |
46 | )
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/frontend/components/CardsList/cardsListUtils.js:
--------------------------------------------------------------------------------
1 | export const sortOptions = (type) => {
2 | const scoresOptions = [
3 | { value: "", label: "新着順" },
4 | { value: "updated_at", label: "更新順" },
5 | { value: "title_asc", label: "タイトル(昇順)" },
6 | { value: "title_desc", label: "タイトル(降順)" },
7 | { value: "views_count", label: "閲覧回数順" },
8 | { value: "favs_count", label: "いいね数順" }
9 | ]
10 | const usersOptions = [
11 | { value: "", label: "新着順" },
12 | { value: "updated_at", label: "更新順" },
13 | { value: "screen_name_asc", label: "名前(昇順)" },
14 | { value: "screen_name_desc", label: "名前(降順)" },
15 | { value: "scores_count", label: "スコア数順" }
16 | ]
17 | switch (type) {
18 | case "scores": return scoresOptions
19 | case "favs": return scoresOptions
20 | case "userScores": return scoresOptions
21 | case "users": return usersOptions
22 | default: return []
23 | }
24 | }
25 |
26 | export const setDefault = (_query, type) => {
27 | const query = _query
28 | switch (type) {
29 | case "scores":
30 | if (!query.word) query.word = ""
31 | if (!query.guest) query.guest = "true"
32 | return query
33 | case "favs":
34 | if (!query.word) query.word = ""
35 | return query
36 | case "userScores":
37 | if (!query.word) query.word = ""
38 | return query
39 | case "users":
40 | if (!query.word) query.word = ""
41 | if (!query.no_scores) query.no_scores = "true"
42 | return query
43 | default:
44 | return query
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/frontend/components/Routes/ShowScore/ScoreHeader/Author.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import { Link } from "react-router-dom"
3 | import * as path from "../../../../utils/path"
4 | import * as utils from "../../../../utils"
5 |
6 | export default class Author extends Component {
7 | render() {
8 | const { author, createdAt, updatedAt } = this.props
9 | const existAuthor = author && Object.keys(author).length > 0
10 | const authorPath = existAuthor && path.user.show(author.name)
11 | const iconBlock = (
12 |
13 |
14 |
15 | )
16 | return (
17 |
18 | {existAuthor ? (
19 |
{iconBlock}
20 | ) : (
21 | iconBlock
22 | )}
23 |
24 |
25 | {existAuthor ? (
26 |
27 | {author.screen_name}
28 |
29 | ) : (
30 | Guest User
31 | )}
32 |
33 |
36 | {createdAt !== updatedAt && (
37 |
40 | )}
41 |
42 |
43 | )
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/frontend/components/StatusControl/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 |
3 | export default class StatusControl extends Component {
4 | handleChangeStatus = (e) => {
5 | this.props.handleSetState({ status: e.target.value })
6 | }
7 | render() {
8 | const { status } = this.props
9 | const radioParams = [
10 | {
11 | label: "Public",
12 | description: 保存されたスコアは誰でも閲覧可能になります。
,
13 | value: "published"
14 | }, {
15 | label: "Private",
16 | description: (
17 |
18 | 保存されたスコアは非公開になります。
19 | あなた以外には見えず、検索にも表示されません。
20 |
21 | ),
22 | value: "closed"
23 | }
24 | ]
25 | const renderRadioComponent = (param) => (
26 |
27 |
36 |
37 |
38 | )
39 | const currentParam = radioParams.find(param => param.value === status) || {}
40 | return (
41 |
42 |
43 | {radioParams.map(renderRadioComponent)}
44 |
45 |
46 | {currentParam.description}
47 |
48 |
49 | )
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/FlashMessage.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class FlashMessage extends PureComponent {
5 | constructor() {
6 | super()
7 | this.state = { show: true }
8 | }
9 | componentWillReceiveProps() {
10 | this.setState({ show: true })
11 | }
12 | handleDelete = () => this.setState({ show: false })
13 | render() {
14 | const { show } = this.state
15 | const { flash } = this.props
16 | const [type, message] = flash
17 |
18 | const flashStyle = { display: (show ? "block" : "none") }
19 | const notificationClass = classNames("notification", {
20 | "is-success": (type === "success" || type === "notice"),
21 | "is-warning": type === "warning",
22 | "is-danger": (type === "error" || type === "alert")
23 | })
24 | const icon = {
25 | success: "fa-check-circle",
26 | notice: "fa-check-circle",
27 | warning: "fa-exclamation-circle",
28 | error: "fa-exclamation-triangle",
29 | alert: "fa-exclamation-triangle"
30 | }
31 | const iconClass = classNames("fa", "fa-lg", icon[type])
32 | return (
33 |
34 |
35 |
36 |
37 |
38 |
39 | {message}
40 |
41 |
42 | )
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/frontend/utils/localStorageState.js:
--------------------------------------------------------------------------------
1 | import { localStorage } from "./browser-dependencies"
2 |
3 | const localStorageKey = "rechordState"
4 | const commonKey = "rechord"
5 |
6 | export const get = (key) => {
7 | const state = localStorage.getItem(key || localStorageKey)
8 | if (!state) return false
9 | return JSON.parse(state || "{}")
10 | }
11 |
12 | export const set = (state, key) => {
13 | const { update, title, inputText, enabledClick, beat, bpm, capo, volume, loop, instrumentType, status } = state
14 | if (update) return false
15 |
16 | return localStorage.setItem(key || localStorageKey, JSON.stringify({
17 | title, inputText, enabledClick, beat, bpm, capo, volume, loop, instrumentType, status
18 | }))
19 | }
20 |
21 | export const remove = (key) => localStorage.removeItem(key || localStorageKey)
22 |
23 | export const visit = () => {
24 | const state = JSON.parse(localStorage.getItem(commonKey) || "{}")
25 | const newState = Object.assign(state, { isVisited: true })
26 | return localStorage.setItem(commonKey, JSON.stringify(newState))
27 | }
28 | export const isVisited = () => {
29 | const state = JSON.parse(localStorage.getItem(commonKey) || "{}")
30 | return state.isVisited
31 | }
32 |
33 | export const setCurrentVersion = (version) => {
34 | const state = JSON.parse(localStorage.getItem(commonKey) || "{}")
35 | const newState = Object.assign(state, { currentVersion: version })
36 | return localStorage.setItem(commonKey, JSON.stringify(newState))
37 | }
38 | export const getCurrentVersion = () => {
39 | const state = JSON.parse(localStorage.getItem(commonKey) || "{}")
40 | return state.currentVersion
41 | }
42 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/navbar.sass:
--------------------------------------------------------------------------------
1 | .navbar
2 | .navbar-brand
3 | .title-logo
4 | margin-left: -1rem
5 | .title
6 | color: #fff
7 | padding-bottom: 3px
8 | span
9 | margin-left: .5rem
10 | span, img
11 | vertical-align: middle
12 | span
13 | &:first-child
14 | margin-right: 8px
15 | opacity: .85
16 | .navbar-menu
17 | .current-user
18 | span
19 | margin-right: .8em
20 | font-size: .9em
21 | img
22 | max-height: 32px
23 | .navbar-end
24 | .navbar-item:last-child
25 | margin-right: -1rem
26 |
27 | @media screen and (max-width: $mobile - 1)
28 | .navbar
29 | .navbar-brand
30 | .title-logo
31 | margin-left: 0
32 |
33 | @media screen and (max-width: $desktop - 1)
34 | .navbar
35 | .button.is-primary.login-button
36 | width: 100%
37 | background-color: $primary
38 | border-color: transparent
39 | color: #fff
40 | .navbar-menu
41 | &.is-active
42 | position: absolute
43 | z-index: 30
44 | width: 100%
45 | .navbar-end
46 | text-align: center
47 | margin-bottom: .5em
48 | .current-user
49 | display: flex
50 | flex-direction: row-reverse
51 | align-items: center
52 | justify-content: center
53 | span
54 | margin-left: .8em
55 | .navbar-item:last-child
56 | margin-right: 0
57 |
58 | @media screen and (min-width: $desktop)
59 | .navbar
60 | .container
61 | .navbar-brand
62 | margin-left: 0
63 | .navbar-menu
64 | margin-right: 0
65 |
--------------------------------------------------------------------------------
/app/models/concerns/oauth_provider_formatter.rb:
--------------------------------------------------------------------------------
1 | class OauthProviderFormatter
2 | class << self
3 | def params_for_create_user(auth)
4 | case auth[:provider]
5 | when "twitter"
6 | {
7 | name: auth[:info][:nickname],
8 | screen_name: auth[:info][:name],
9 | profile: auth[:info][:description],
10 | icon: auth[:info][:image],
11 | site: auth[:info][:urls][:Website],
12 | twitter: auth[:info][:nickname]
13 | }
14 | when "facebook"
15 | {
16 | name: SecureRandom.urlsafe_base64(8),
17 | screen_name: auth[:info][:name],
18 | icon: auth[:info][:image],
19 | site: auth[:extra][:raw_info][:link],
20 | email: auth[:info][:email]
21 | }
22 | when "google_oauth2"
23 | {
24 | name: SecureRandom.urlsafe_base64(8),
25 | screen_name: auth[:info][:name],
26 | icon: auth[:info][:image],
27 | site: auth[:info][:urls]&.fetch(:google),
28 | email: auth[:info][:email]
29 | }
30 | when "tumblr"
31 | {
32 | name: auth[:info][:nickname],
33 | screen_name: auth[:info][:name],
34 | icon: auth[:info][:avatar]
35 | }
36 | when "github"
37 | {
38 | name: auth[:info][:nickname],
39 | screen_name: auth[:info][:nickname],
40 | icon: auth[:info][:image],
41 | site: auth[:info][:urls][:GitHub],
42 | email: auth[:info][:email]
43 | }
44 | end
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/config/newrelic.yml:
--------------------------------------------------------------------------------
1 | #
2 | # This file configures the New Relic Agent. New Relic monitors Ruby, Java,
3 | # .NET, PHP, Python, Node, and Go applications with deep visibility and low
4 | # overhead. For more information, visit www.newrelic.com.
5 | #
6 | # Generated December 20, 2017
7 | #
8 | # This configuration file is custom generated for comorebi notes
9 | #
10 | # For full documentation of agent configuration options, please refer to
11 | # https://docs.newrelic.com/docs/agents/ruby-agent/installation-configuration/ruby-agent-configuration
12 |
13 | common: &default_settings
14 | # Required license key associated with your New Relic account.
15 | license_key: <%= ENV['NEWRELIC_LICENSE_KEY'] %>
16 |
17 | # Your application name. Renaming here affects where data displays in New
18 | # Relic. For more details, see https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/renaming-applications
19 | app_name: rechord
20 |
21 | # To disable the agent regardless of other settings, uncomment the following:
22 | # agent_enabled: false
23 |
24 | # Logging level for log/newrelic_agent.log
25 | log_level: info
26 |
27 |
28 | # Environment-specific settings are in this section.
29 | # RAILS_ENV or RACK_ENV (as appropriate) is used to determine the environment.
30 | # If your application has other named environments, configure them here.
31 | development:
32 | <<: *default_settings
33 | app_name: rechord (Development)
34 |
35 | test:
36 | <<: *default_settings
37 | # It doesn't make sense to report to New Relic from automated test runs.
38 | monitor_mode: false
39 |
40 | staging:
41 | <<: *default_settings
42 | app_name: rechord (Staging)
43 |
44 | production:
45 | <<: *default_settings
46 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/user-page.sass:
--------------------------------------------------------------------------------
1 | .show-user
2 | .card.user-page
3 | box-shadow: none
4 | margin-bottom: 2em
5 | &.edit
6 | .card-content
7 | padding: 1.5rem .5rem 0
8 | input, textarea
9 | font-size: .9em
10 | .card-image
11 | padding: 0 1.5rem
12 | .icon-buttons
13 | display: flex
14 | margin-top: 1.5em
15 | > *
16 | flex-grow: 1
17 | > *:not(:last-child)
18 | margin-right: .5em
19 | &:last-child
20 | margin-bottom: 1em
21 | .file
22 | display: block
23 | .file-cta
24 | border-radius: 2px
25 | width: 100%
26 | justify-content: center
27 | .dummy-icon
28 | background-color: #ccc
29 | position: absolute
30 | top: 0
31 | left: 0
32 | right: 0
33 | bottom: 0
34 | border-radius: 50%
35 | .content
36 | font-size: .9em
37 | .url
38 | word-break: break-all
39 | .icon
40 | margin-right: .2em
41 | &:not(:last-of-type)
42 | margin-bottom: .4em
43 | .profile
44 | line-height: 1.7
45 | margin-bottom: 2em
46 | p
47 | margin-bottom: 0
48 | .scores
49 | .score-card
50 | .box
51 | .score-title
52 | white-space: normal
53 |
54 | @media screen and (max-width: $tablet - 1px)
55 | .show-user
56 | .card.user-page
57 | .card-image
58 | padding: 0
59 | .icon-buttons
60 | .file, .button
61 | &.is-small
62 | font-size: .85em
63 | .card-content
64 | padding: 1.5em 0
65 | &.edit
66 | .card-content
67 | padding: 0
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rechord",
3 | "private": true,
4 | "dependencies": {
5 | "@babel/preset-env": "^7.21.5",
6 | "@babel/preset-react": "^7.18.6",
7 | "@rails/webpacker": "5.4.4",
8 | "axios": "^0.17.1",
9 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
10 | "babel-polyfill": "^6.26.0",
11 | "chord-translator": "^0.1.0",
12 | "classnames": "^2.2.5",
13 | "draft-js": "^0.10.4",
14 | "history": "^4.7.2",
15 | "moji": "^0.5.1",
16 | "postcss-cssnext": "^3.1.1",
17 | "prop-types": "^15.8.1",
18 | "qs": "^6.5.1",
19 | "rails_admin": "3.1.2",
20 | "react": "^18.2.0",
21 | "react-dom": "^18.2.0",
22 | "react-ga": "^2.3.5",
23 | "react-highlight-words": "^0.10.0",
24 | "react-router-dom": "^4.2.2",
25 | "react-share": "^1.17.0",
26 | "react-twitter-widgets": "^1.7.1",
27 | "tonal": "2.1.2",
28 | "tone": "0.11.12",
29 | "webpack": "^4.46.0",
30 | "webpack-cli": "^3.3.12"
31 | },
32 | "devDependencies": {
33 | "@babel/core": "^7.2.2",
34 | "babel-eslint": "^10.0.1",
35 | "babel-jest": "^24.0.0",
36 | "eslint": "^5.9.0",
37 | "eslint-config-airbnb": "^17.1.0",
38 | "eslint-plugin-import": "^2.14.0",
39 | "eslint-plugin-jsx-a11y": "^6.1.2",
40 | "eslint-plugin-react": "^7.11.1",
41 | "jest": "23.6.0",
42 | "webpack-dev-server": "^3"
43 | },
44 | "scripts": {
45 | "lint": "eslint app/frontend --ext .js --ext .jsx",
46 | "test": "jest --no-cache ./spec/frontend"
47 | },
48 | "jest": {
49 | "testPathIgnorePatterns": [
50 | "/node_modules/",
51 | "/config/"
52 | ]
53 | },
54 | "resolutions": {
55 | "rails_admin/@fortawesome/fontawesome-free": "^5.15.0"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/notification.sass:
--------------------------------------------------------------------------------
1 | .notification.content
2 | time
3 | font-size: .9em
4 | color: #888
5 | margin-bottom: .8em
6 | & + p
7 | margin-top: .2em
8 | h3
9 | margin-top: .3em
10 | margin-bottom: .4em
11 | ul
12 | margin-top: .8em
13 | a
14 | color: $link
15 | strong
16 | margin: 0 .2em
17 | &:first-child
18 | margin-left: 0
19 | &:last-child
20 | margin-right: 0
21 | .loading-wrapper
22 | min-height: 50px
23 | &:before
24 | content: none
25 | &:after
26 | top: calc(50% - .5em)
27 |
28 | .notification-icon
29 | .icon
30 | width: 1.5rem
31 | .fa-layers
32 | display: inline-block
33 | position: relative
34 | height: 1em
35 | left: -.2em
36 | text-align: center
37 | vertical-align: -.125em
38 | .fa
39 | position: absolute
40 | top: 0
41 | right: 0
42 | left: 0
43 | bottom: 0
44 | margin: auto
45 | .fa-layers-counter
46 | display: inline-block
47 | box-sizing: border-box
48 | max-width: 5em
49 | min-width: 1.5em
50 | height: 1.5em
51 | line-height: 1
52 | position: absolute
53 | top: -0.75em
54 | left: .65em
55 | font-weight: bold
56 | padding: .25em
57 | border-radius: 1em
58 | text-align: center
59 | background-color: $danger
60 | color: #fff
61 | overflow: hidden
62 | text-overflow: ellipsis
63 | -webkit-transform: scale(.8)
64 | transform: scale(.8)
65 | -webkit-transform-origin: top right
66 | transform-origin: top right
67 | &.is-only-mobile
68 | position: absolute
69 | top: 1px
70 | right: 3.25rem
71 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/user-card.sass:
--------------------------------------------------------------------------------
1 | .users
2 | .user-card
3 | display: block
4 | .box
5 | display: flex
6 | justify-content: space-between
7 | transition: all .1s ease-in-out
8 | background-color: #fff
9 | .media
10 | width: 100%
11 | .screen-name
12 | color: $link
13 | font-weight: bold
14 | margin-bottom: .4em
15 | p
16 | margin-bottom: 0
17 | small
18 | font-weight: normal
19 | font-size: .7em
20 | .screen-name, .user-attributes
21 | transition: all .1s ease-in-out
22 | .user-attributes
23 | line-height: 1.5
24 | font-size: .9em
25 | strong
26 | font-size: 1.2em
27 | margin-left: .25em
28 | p
29 | margin-bottom: .8em
30 | .profile
31 | margin-bottom: 1em
32 | p
33 | font-size: .85em
34 | margin-bottom: 0
35 | nav
36 | align-items: center
37 | .control
38 | font-size: .9rem
39 | margin-top: .2em
40 | &:not(:last-child)
41 | margin-right: 1.2rem
42 | .icon
43 | margin-top: .1em
44 | margin-right: .2em
45 | &:hover, &:active
46 | .box
47 | background-color: #fafafa
48 | &.closed
49 | background-color: #e0e0e0
50 | &:not(:last-child)
51 | margin-bottom: 1.5rem
52 |
53 | .box.no-user
54 | box-shadow: none
55 | background-color: #f4f4f4
56 | padding: 2.4em
57 | .media-content
58 | text-align: center
59 | color: #888
60 | font-weight: bold
61 |
62 | @media screen and (max-width: $tablet - 1px)
63 | .users
64 | .user-card
65 | .box
66 | .image
67 | width: 64px
68 |
--------------------------------------------------------------------------------
/db/migrate/20180112050744_create_impressions_table.rb:
--------------------------------------------------------------------------------
1 | class CreateImpressionsTable < ActiveRecord::Migration[5.1]
2 | def self.up
3 | create_table :impressions, :force => true do |t|
4 | t.string :impressionable_type
5 | t.integer :impressionable_id
6 | t.integer :user_id
7 | t.string :controller_name
8 | t.string :action_name
9 | t.string :view_name
10 | t.string :request_hash
11 | t.string :ip_address
12 | t.string :session_hash
13 | t.text :message
14 | t.text :referrer
15 | t.text :params
16 | t.timestamps
17 | end
18 | add_index :impressions, [:impressionable_type, :message, :impressionable_id], :name => "impressionable_type_message_index", :unique => false, :length => {:message => 255 }
19 | add_index :impressions, [:impressionable_type, :impressionable_id, :request_hash], :name => "poly_request_index", :unique => false
20 | add_index :impressions, [:impressionable_type, :impressionable_id, :ip_address], :name => "poly_ip_index", :unique => false
21 | add_index :impressions, [:impressionable_type, :impressionable_id, :session_hash], :name => "poly_session_index", :unique => false
22 | add_index :impressions, [:controller_name,:action_name,:request_hash], :name => "controlleraction_request_index", :unique => false
23 | add_index :impressions, [:controller_name,:action_name,:ip_address], :name => "controlleraction_ip_index", :unique => false
24 | add_index :impressions, [:controller_name,:action_name,:session_hash], :name => "controlleraction_session_index", :unique => false
25 | add_index :impressions, [:impressionable_type, :impressionable_id, :params], :name => "poly_params_request_index", :unique => false, :length => {:params => 255 }
26 | add_index :impressions, :user_id
27 | end
28 |
29 | def self.down
30 | drop_table :impressions
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "parserOptions": {
4 | "ecmaVersion": 2017,
5 | "sourceType": "module",
6 | "ecmaFeatures": {
7 | "jsx": true
8 | }
9 | },
10 | "extends": "airbnb",
11 | "plugins": [
12 | "react"
13 | ],
14 | "rules": {
15 | "arrow-parens": 0,
16 | "camelcase": 0,
17 | "class-methods-use-this": 0,
18 | "comma-dangle": 0,
19 | "default-case": 0,
20 | "func-names": 0,
21 | "indent": [2, 2, { "SwitchCase": 1 }],
22 | "jsx-a11y/anchor-has-content": 0,
23 | "jsx-a11y/anchor-is-valid": 0,
24 | "jsx-a11y/click-events-have-key-events": 0,
25 | "jsx-a11y/label-has-associated-control": 0,
26 | "jsx-a11y/label-has-for": 0,
27 | "jsx-a11y/no-static-element-interactions": 0,
28 | "jsx-quotes": 0,
29 | "key-spacing": 0,
30 | "lines-between-class-members": 0,
31 | "max-len": ["error", { "code": 140, "comments": 140 }],
32 | "no-class-assign": 0,
33 | "no-else-return": 0,
34 | "no-fallthrough": 0,
35 | "no-irregular-whitespace": ["error", { "skipRegExps": true }],
36 | "no-multi-spaces": 0,
37 | "no-var": 1,
38 | "object-curly-newline": 0,
39 | "prefer-destructuring": ["error", { "object": true, "array": false }],
40 | "quotes": 0,
41 | "react/destructuring-assignment": 0,
42 | "react/jsx-filename-extension": 0,
43 | "react/jsx-one-expression-per-line": 0,
44 | "react/jsx-props-no-multi-spaces": 0,
45 | "react/no-array-index-key": 0,
46 | "react/no-multi-comp": 0,
47 | "react/prefer-stateless-function": 0,
48 | "react/prop-types": 0,
49 | "react/self-closing-comp": 0,
50 | "semi": 0,
51 | "space-before-function-paren": 0
52 | },
53 | "globals": {
54 | "window": true,
55 | "$": true,
56 | "before": true,
57 | "describe": true,
58 | "expect": true,
59 | "it": true
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | jobs:
4 | build:
5 | docker:
6 | - image: circleci/ruby:2.6.5-node
7 | environment:
8 | BUNDLE_JOBS: 3
9 | BUNDLE_RETRY: 3
10 | BUNDLE_PATH: vendor/bundle
11 | PGHOST: 127.0.0.1
12 | PGUSER: rechord
13 | RAILS_ENV: test
14 | - image: circleci/postgres:10.1-alpine
15 | environment:
16 | POSTGRES_USER: rechord
17 | POSTGRES_DB: rechord_test
18 | POSTGRES_PASSWORD: ""
19 |
20 | working_directory: ~/rechord
21 |
22 | steps:
23 | - checkout
24 |
25 | # bundle
26 | - run:
27 | name: Which bundler?
28 | command: bundle -v
29 | - restore_cache:
30 | keys:
31 | - rails-demo-bundle-v3-{{ checksum "Gemfile.lock" }}
32 | - run:
33 | name: Bundle Install
34 | command: bundle check || bundle install
35 | - save_cache:
36 | key: rails-demo-bundle-v3-{{ checksum "Gemfile.lock" }}
37 | paths:
38 | - vendor/bundle
39 |
40 | # yarn
41 | - restore_cache:
42 | keys:
43 | - rails-demo-yarn-v2-{{ checksum "yarn.lock" }}
44 | - rails-demo-yarn-v2-
45 | - run:
46 | name: yarn install
47 | command: curl -o- -L https://yarnpkg.com/install.sh; yarn install
48 | - save_cache:
49 | key: rails-demo-yarn-v2-{{ checksum "yarn.lock" }}
50 | paths:
51 | - ~/.cache/yarn
52 |
53 | # database
54 | - run:
55 | name: Database setup
56 | command: |
57 | bin/rails db:create
58 | bin/rails db:structure:load
59 |
60 | # test
61 | - run:
62 | name: check lint
63 | command: yarn lint
64 | - run:
65 | name: jest
66 | command: yarn test
67 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/LoginModal/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import classNames from "classnames"
3 |
4 | import * as path from "../../../utils/path"
5 |
6 | export default class LoginModal extends Component {
7 | render() {
8 | const { active, hideModal } = this.props
9 | const modalClass = classNames("modal", "login", { "is-active": active })
10 | const renderButton = ({ service, label }) => (
11 |
16 |
17 |
18 |
19 | {label}
20 |
21 | )
22 | const services = [
23 | { service: "twitter", label: "Twitter" },
24 | { service: "facebook", label: "Facebook" },
25 | { service: "google", label: "Google" },
26 | { service: "tumblr", label: "tumblr" },
27 | { service: "github", label: "GitHub" }
28 | ]
29 | return (
30 |
31 |
32 |
33 |
34 |
35 | Login / Register
36 |
37 | {services.map(renderButton)}
38 |
39 | ユーザ登録をすることで、投稿したスコアを管理したり、他人のスコアにいいねを付けたりすることができます。
40 |
41 |
42 | あなたが入力した外部サービスのパスワード等は、管理人を含め第三者に見られることはありません。
43 |
44 |
45 |
46 |
47 |
48 | )
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
9 | threads min_threads_count, max_threads_count
10 |
11 | # Specifies the `worker_timeout` threshold that Puma will use to wait before
12 | # terminating a worker in development environments.
13 | #
14 | worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
15 |
16 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
17 | #
18 | port ENV.fetch("PORT") { 3000 }
19 |
20 | # Specifies the `environment` that Puma will run in.
21 | #
22 | environment ENV.fetch("RAILS_ENV") { "development" }
23 |
24 | # Specifies the `pidfile` that Puma will use.
25 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
26 |
27 | # Specifies the number of `workers` to boot in clustered mode.
28 | # Workers are forked web server processes. If using threads and workers together
29 | # the concurrency of the application would be max `threads` * `workers`.
30 | # Workers do not work on JRuby or Windows (both of which do not support
31 | # processes).
32 | #
33 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
34 |
35 | # Use the `preload_app!` method when specifying a `workers` number.
36 | # This directive tells Puma to first boot the application and load code
37 | # before forking the application. This takes advantage of Copy On Write
38 | # process behavior so workers use less memory.
39 | #
40 | # preload_app!
41 |
42 | # Allow puma to be restarted by `rails restart` command.
43 | plugin :tmp_restart
44 |
--------------------------------------------------------------------------------
/app/frontend/styles/rechord.sass:
--------------------------------------------------------------------------------
1 | @import "partials/variables.sass"
2 | @import "partials/chord-colors.sass"
3 |
4 | @import "partials/loading.sass"
5 | @import "partials/fav.sass"
6 | @import "partials/flash-message.sass"
7 | @import "partials/navbar.sass"
8 | @import "partials/tabbar.sass"
9 | @import "partials/footer.sass"
10 | @import "partials/login.sass"
11 | @import "partials/notification.sass"
12 | @import "partials/shared-buttons.sass"
13 | @import "partials/about.sass"
14 | @import "partials/terms.sass"
15 | @import "partials/changelog.sass"
16 | @import "partials/search.sass"
17 | @import "partials/score-editor.sass"
18 | @import "partials/score-controls.sass"
19 | @import "partials/score-header.sass"
20 | @import "partials/score-footer.sass"
21 | @import "partials/score-card.sass"
22 | @import "partials/user-card.sass"
23 | @import "partials/user-page.sass"
24 |
25 | .user-icon
26 | border-radius: 50%
27 | &.has-border
28 | border: 8px solid #fff
29 | box-shadow: 0 0 0 2px #eaeaea
30 |
31 | .wide-button
32 | display: block
33 | width: 100%
34 |
35 | .animate-button
36 | transition: opacity .3s ease-in-out
37 |
38 | .message-body
39 | border-radius: 0
40 | &.bordered
41 | border: 1px solid #a0ca30
42 | border-radius: 4px
43 | &.has-icon
44 | display: flex
45 | align-items: center
46 | .icon
47 | opacity: .5
48 | margin-right: .6rem
49 |
50 | .twitter-tl
51 | margin: 3em auto 0
52 | width: max-content
53 |
54 | .can-click
55 | cursor: pointer
56 | pointer-events: all !important
57 |
58 | section.section
59 | padding-top: 2rem
60 | margin-bottom: 1em
61 |
62 | @media screen and (min-width: $tablet)
63 | .section.root-section
64 | display: flex
65 | flex-direction: column
66 |
67 | @media screen and (max-width: $mobile - 1)
68 | .section.root-section
69 | .container
70 | width: 100%
71 |
72 | @media screen and (max-width: $tablet - 1)
73 | .twitter-tl
74 | width: auto
75 |
--------------------------------------------------------------------------------
/config/initializers/exception_notification.rb:
--------------------------------------------------------------------------------
1 | require 'exception_notification/rails'
2 |
3 | ExceptionNotification.configure do |config|
4 | # Ignore additional exception types.
5 | # ActiveRecord::RecordNotFound, Mongoid::Errors::DocumentNotFound, AbstractController::ActionNotFound and ActionController::RoutingError are already added.
6 | # config.ignored_exceptions += %w{ActionView::TemplateError CustomError}
7 |
8 | # Adds a condition to decide when an exception must be ignored or not.
9 | # The ignore_if method can be invoked multiple times to add extra conditions.
10 | # config.ignore_if do |exception, options|
11 | # not Rails.env.production?
12 | # end
13 | config.ignore_if do |exception, options|
14 | not Rails.env.production?
15 | end
16 |
17 | # Notifiers =================================================================
18 |
19 | config.add_notifier :slack, webhook_url: ENV['WEBHOOK_URL'], channel: '#rechord_notification'
20 |
21 | # Email notifier sends notifications by email.
22 | # config.add_notifier :email, {
23 | # :email_prefix => "[ERROR] ",
24 | # :sender_address => %{"Notifier" },
25 | # :exception_recipients => %w{exceptions@example.com}
26 | # }
27 |
28 | # Campfire notifier sends notifications to your Campfire room. Requires 'tinder' gem.
29 | # config.add_notifier :campfire, {
30 | # :subdomain => 'my_subdomain',
31 | # :token => 'my_token',
32 | # :room_name => 'my_room'
33 | # }
34 |
35 | # HipChat notifier sends notifications to your HipChat room. Requires 'hipchat' gem.
36 | # config.add_notifier :hipchat, {
37 | # :api_token => 'my_token',
38 | # :room_name => 'my_room'
39 | # }
40 |
41 | # Webhook notifier sends notifications over HTTP protocol. Requires 'httparty' gem.
42 | # config.add_notifier :webhook, {
43 | # :url => 'http://example.com:5555/hubot/path',
44 | # :http_method => :post
45 | # }
46 |
47 | end
48 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/Notification/FavNotification.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import { Link } from "react-router-dom"
3 | import * as api from "../../../api"
4 | import * as path from "../../../utils/path"
5 | import * as utils from "../../../utils"
6 |
7 | export default class FavNotification extends Component {
8 | constructor() {
9 | super()
10 | this.state = { score: null, loading: true }
11 | }
12 | componentDidMount() {
13 | const deletedScore = { title: "削除済のスコア" }
14 | api.showScore(
15 | { token: this.props.data.title },
16 | ({ data: { score } }) => {
17 | this.setState({ score: score.status === "deleted" ? deletedScore : score, loading: false })
18 | },
19 | () => this.setState({ score: deletedScore, loading: false })
20 | )
21 | }
22 | render() {
23 | const { score, loading } = this.state
24 | const { data: { updated_at: updatedAt }, handleToggleNotification } = this.props
25 | const isActive = loading || (score && score.favs_count > 0)
26 | return isActive && (
27 |
28 |
29 | {loading ? (
30 |
31 | ) : (
32 |
33 |
34 |
35 |
36 |
37 | {score.token ? (
38 | {score.title}
39 | ) : (
40 | {score.title}
41 | )}
42 |
43 | が通算
44 | {score.favs_count}回
45 | いいねをされました。
46 |
47 | )}
48 |
49 | )
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/frontend/components/commons/ModalCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react"
2 | import classNames from "classnames"
3 |
4 | export default class ModalCard extends Component {
5 | render() {
6 | const {
7 | isActive, title, icon, children,
8 | hasButtons, yesButtonOnly, buttonColor, yesButtonLabel, noButtonLabel, handleClick, hideModal
9 | } = this.props
10 | const modalClass = classNames("modal", { "is-active": isActive })
11 | const iconClass = classNames("fa", `fa-${icon}`)
12 | const buttonClass = classNames("button", `is-${buttonColor || "success"}`)
13 |
14 | return (
15 |
16 |
17 |
18 | {title && (
19 |
28 | )}
29 |
32 | {hasButtons && (
33 |
43 | )}
44 |
45 |
46 |
47 | )
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/frontend/styles/partials/score-header.sass:
--------------------------------------------------------------------------------
1 | .score-header
2 | margin-bottom: .4rem
3 | .private-description
4 | font-size: .9em
5 | opacity: .5
6 | .icon
7 | margin-right: .5em
8 | .title
9 | font-size: 1.8em
10 | margin-bottom: 1.2rem
11 | .button
12 | display: block
13 | margin-bottom: 1rem
14 |
15 | .author
16 | display: flex
17 | align-items: center
18 | margin-bottom: .75em
19 | line-height: 1.3
20 | a
21 | color: #444
22 | &:hover
23 | color: #666
24 | figure
25 | margin-right: .8rem
26 | .created-at, .updated-at
27 | font-size: .8em
28 | color: #888
29 | .updated-at
30 | margin-left: .6em
31 | &:before
32 | content: "("
33 | &:after
34 | content: ")"
35 |
36 | .others
37 | display: flex
38 | align-items: center
39 | margin: .5em 0
40 | color: #666
41 | height: 2.25rem
42 | .separator
43 | color: #eee
44 | margin: 0 1.2em
45 | &:last-of-type
46 | margin-right: .4em
47 | .icon
48 | color: #666
49 | margin-right: .4em
50 | .button
51 | margin-bottom: 0
52 | margin-left: .4em
53 | color: #666
54 | &:active
55 | border-color: $link
56 | &:disabled
57 | &, .icon
58 | color: #666 !important
59 |
60 | @media screen and (max-width: $tablet - 1px)
61 | .score-header
62 | flex-direction: column
63 | .shared-buttons
64 | margin: 1.5em 0 0
65 | .shared-button
66 | font-size: 1.5em
67 | margin-bottom: 1em
68 | .fa-stack
69 | .fa:last-child
70 | left: .1em
71 | &.as-show
72 | .shared-button
73 | margin-top: .5em
74 | &:not(:last-child)
75 | margin-right: 1.2em
76 | .author
77 | figure.image
78 | width: 52px
79 | height: 52px
80 | p
81 | margin-bottom: .2em
82 | .created-at, .updated-at
83 | display: block
84 | .updated-at
85 | margin-left: 0
86 | &:before
87 | content: ""
88 | &:after
89 | content: ""
90 |
--------------------------------------------------------------------------------