diff --git a/Gemfile b/Gemfile index 82174f50..ef05fa73 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ source 'https://rubygems.org' +ruby "~> 2.3.0" gem 'rails', '4.2.7.1' gem 'puma', '~> 3.6.0' @@ -58,7 +59,7 @@ gem 'mixpanel-ruby' gem 'social-share-button' # Apps -gem 'easy_translate' +#gem 'easy_translate' gem 'levenshtein-ffi' # Social Logins @@ -66,9 +67,19 @@ gem 'omniauth-facebook' gem 'omniauth-twitter' gem 'omniauth-google-oauth2' +# Forum +gem 'thredded', '~> 0.13.2' +gem 'rails-ujs' +gem 'delayed_job_active_record' + +# Tech debt & hacks +gem 'binding_of_caller' # see has_changelog.rb + group :production do gem 'rails_12factor' gem 'uglifier', '>= 1.3.0' + + gem 'newrelic_rpm' end group :test, :production do @@ -78,7 +89,6 @@ end group :test do gem 'better_errors' - gem 'binding_of_caller' gem 'capybara' gem 'codeclimate-test-reporter', require: false gem 'coveralls', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 8beb41e0..366e2872 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,6 +26,8 @@ GEM erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) + active_record_union (1.2.0) + activerecord (>= 4.0) activejob (4.2.7.1) activesupport (= 4.2.7.1) globalid (>= 0.3.0) @@ -47,11 +49,17 @@ GEM ast (2.3.0) authority (3.2.2) activesupport (>= 3.0.0) + autoprefixer-rails (7.1.2.3) + execjs aws-sdk (1.66.0) aws-sdk-v1 (= 1.66.0) aws-sdk-v1 (1.66.0) json (~> 1.4) nokogiri (>= 1.4.4) + babel-source (5.8.35) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) + execjs (~> 2.0) bcrypt (3.1.11) better_errors (2.1.1) coderay (>= 1.0.0) @@ -94,6 +102,7 @@ GEM tins (>= 1.6.0, < 2) crack (0.4.3) safe_yaml (~> 1.0.0) + crass (1.0.2) cucumber (2.4.0) builder (>= 2.1.2) cucumber-core (~> 1.5.0) @@ -114,7 +123,14 @@ GEM database_cleaner (1.5.3) dateslices (0.0.4) rails (> 4) + db_text_search (0.2.2) + activerecord (>= 4.1.15, < 6.0) debug_inspector (0.0.2) + delayed_job (4.1.3) + activesupport (>= 3.0, < 5.2) + delayed_job_active_record (4.1.2) + activerecord (>= 3.0, < 5.2) + delayed_job (>= 3.0, < 5) devise (4.2.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -125,10 +141,6 @@ GEM docile (1.1.5) domain_name (0.5.20161021) unf (>= 0.0.5, < 1.0.0) - easy_translate (0.5.0) - json - thread - thread_safe erubis (2.7.0) execjs (2.7.0) factory_girl (4.7.0) @@ -138,9 +150,13 @@ GEM railties (>= 3.0.0) faraday (0.12.2) multipart-post (>= 1.2, < 3) + fast_blank (1.0.0) ffi (1.9.14) filesize (0.1.1) formatador (0.2.5) + friendly_id (5.2.1) + activerecord (>= 4.0.0) + gemoji (2.1.0) gherkin (4.0.0) globalid (0.3.7) activesupport (>= 4.1.0) @@ -162,14 +178,34 @@ GEM rubocop (~> 0.20) hashdiff (0.3.2) hashie (3.5.6) + html-pipeline (2.6.0) + activesupport (>= 2) + nokogiri (>= 1.4) + htmlentities (4.3.4) http-cookie (1.0.3) domain_name (~> 0.5) httparty (0.13.7) json (~> 1.8) multi_xml (>= 0.5.2) i18n (0.8.0) + inline_svg (1.2.2) + activesupport (>= 3.0) + nokogiri (>= 1.6) json (1.8.6) jwt (1.5.6) + kaminari (1.0.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.0.1) + kaminari-activerecord (= 1.0.1) + kaminari-core (= 1.0.1) + kaminari-actionview (1.0.1) + actionview + kaminari-core (= 1.0.1) + kaminari-activerecord (1.0.1) + activerecord + kaminari-core (= 1.0.1) + kaminari-core (1.0.1) + kramdown (1.14.0) levenshtein-ffi (1.1.0) ffi (~> 1.9) libv8 (3.16.14.15) @@ -195,14 +231,19 @@ GEM mini_portile2 (2.1.0) minitest (5.10.1) mixpanel-ruby (2.2.0) + moneta (1.0.0) multi_json (1.12.1) multi_test (0.1.2) multi_xml (0.6.0) multipart-post (2.0.0) + mustache (1.0.5) nenv (0.3.0) netrc (0.11.0) + newrelic_rpm (4.5.0.337) nokogiri (1.7.0.1) mini_portile2 (~> 2.1.0) + nokogumbo (1.4.13) + nokogiri notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) @@ -232,14 +273,23 @@ GEM omniauth-twitter (1.4.0) omniauth-oauth (~> 1.1) rack + onebox (1.8.16) + fast_blank (>= 1.0.0) + htmlentities (~> 4.3) + moneta (~> 1.0) + multi_json (~> 1.11) + mustache + nokogiri (~> 1.7) + sanitize orm_adapter (0.5.0) paperclip (4.2.4) activemodel (>= 3.2.0) activesupport (>= 3.2.0) cocaine (~> 0.5.5) mime-types - parser (2.3.1.4) - ast (~> 2.2) + parallel (1.12.0) + parser (2.4.0.2) + ast (~> 2.3) pg (0.19.0) powerpack (0.1.1) pry (0.10.4) @@ -249,6 +299,8 @@ GEM puma (3.6.0) puma-heroku (1.0.0) puma (~> 3.0) + pundit (1.1.0) + activesupport (>= 3.0.0) rack (1.6.5) rack-test (0.6.3) rack (>= 1.0) @@ -275,6 +327,8 @@ GEM rails-jquery-autocomplete (1.0.3) rails (>= 3.2) rails-perftest (0.0.6) + rails-ujs (0.1.0) + railties (>= 3.1) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging @@ -285,13 +339,15 @@ GEM activesupport (= 4.2.7.1) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rainbow (2.1.0) - rake (12.0.0) + rainbow (2.2.2) + rake + rake (12.1.0) raygun4ruby (1.1.11) httparty (~> 0.13.7) json rack rb-fsevent (0.9.7) + rb-gravatar (1.0.5) rb-inotify (0.9.7) ffi (>= 0.5.0) redcarpet (3.3.4) @@ -302,6 +358,7 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) + rinku (2.0.2) rmagick (2.13.4) rspec (3.5.0) rspec-core (~> 3.5.0) @@ -327,17 +384,22 @@ GEM rspec-mocks (~> 3.5.0) rspec-support (~> 3.5.0) rspec-support (3.5.0) - rubocop (0.43.0) - parser (>= 2.3.1.1, < 3.0) + rubocop (0.51.0) + parallel (~> 1.10) + parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) - rainbow (>= 1.99.1, < 3.0) + rainbow (>= 2.2.2, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-prof (0.15.9) - ruby-progressbar (1.8.1) + ruby-progressbar (1.9.0) ruby_dep (1.4.0) - rubyzip (1.2.0) + rubyzip (1.2.1) safe_yaml (1.0.4) + sanitize (4.5.0) + crass (~> 1.0.2) + nokogiri (>= 1.4.4) + nokogumbo (~> 1.4.1) sass (3.4.22) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -364,6 +426,10 @@ GEM sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) + sprockets-es6 (0.9.2) + babel-source (>= 5.8.11) + babel-transpiler + sprockets (>= 3.0.0) sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) @@ -377,9 +443,30 @@ GEM libv8 (~> 3.16.14.0) ref thor (0.19.4) - thread (0.2.2) thread_safe (0.3.5) + thredded (0.13.2) + active_record_union (>= 1.2.0) + autoprefixer-rails + db_text_search (~> 0.2.0) + friendly_id + gemoji (~> 2.1.0) + html-pipeline + htmlentities + inline_svg + kaminari + kramdown + nokogiri + onebox (~> 1.8, >= 1.8.13) + pundit (>= 1.1.0) + rails (>= 4.2.0) + rb-gravatar + rinku + sanitize + sass (>= 3.4.21) + sprockets-es6 + timeago_js tilt (2.0.5) + timeago_js (3.0.2) tins (1.12.0) tzinfo (1.2.2) thread_safe (~> 0.1) @@ -390,7 +477,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (1.1.1) + unicode-display_width (1.3.0) warden (1.2.6) rack (>= 1.0) webmock (2.3.2) @@ -419,8 +506,8 @@ DEPENDENCIES cucumber-rails database_cleaner dateslices + delayed_job_active_record devise - easy_translate factory_girl_rails filesize guard @@ -431,6 +518,7 @@ DEPENDENCIES medium-editor-rails meta-tags mixpanel-ruby + newrelic_rpm omniauth-facebook omniauth-google-oauth2 omniauth-twitter @@ -443,6 +531,7 @@ DEPENDENCIES rails (= 4.2.7.1) rails-jquery-autocomplete rails-perftest + rails-ujs rails_12factor raygun4ruby redcarpet @@ -461,9 +550,13 @@ DEPENDENCIES sqlite3 stripe therubyracer + thredded (~> 0.13.2) tzinfo-data uglifier (>= 1.3.0) webmock +RUBY VERSION + ruby 2.3.1p112 + BUNDLED WITH - 1.15.3 + 1.16.0.pre.3 diff --git a/README.rdoc b/README.rdoc index 33a2b43d..3b7c7f3c 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,12 +1,12 @@ -= notebook += Notebook.ai {Build Status}[https://travis-ci.org/indentlabs/notebook] {}[https://codeclimate.com/github/indentlabs/notebook] {}[https://codeclimate.com/github/indentlabs/notebook/coverage] {Inline docs}[http://inch-ci.org/github/indentlabs/notebook] - -== What is notebook? + +== What is Notebook.ai? see {live website}[http://notebook.ai/] - + notebook is a set of tools for writers, game designers, and roleplayers to create magnificent universes – and everything within them. From a simple interface in your browser, on your phone, or on your tablet, you can do everything you'd ever want to do while creating your own little (or big!) world. @@ -38,10 +38,9 @@ Install curl sudo apt-get install curl -Install rvm +Install rvm (or your favorite ruby version manager) \curl -sSL https://get.rvm.io | bash - source ~/.rvm/scripts/rvm Install ruby 2.3.1 @@ -50,13 +49,14 @@ Install ruby 2.3.1 rvm use 2.3.1 -Install rails 4.2.5 +Install rails 4.2.7 - gem install rails -v 4.2.5 + gem install rails -v 4.2.7 Install necessary libraries sudo apt-get install imagemagick libmagickwand-dev + sudo apt-get install libpq-dev Clone the code @@ -65,7 +65,7 @@ Clone the code Install gems bundle install - + Create database rake db:create @@ -73,16 +73,19 @@ Create database Run initial database migrations rake db:migrate + rake data_migrations:create_default_billing_plans + rake db:seed Finally, run the server with rails server - + You should now see a copy of the site running locally at http://localhost:3000/! == Running the notebook stack locally with Docker - install {Docker}[https://www.docker.com/products/overview] +- install {Docker Compose}[https://docs.docker.com/compose/install] - clone this git repo - cd into the root of this repo, and then run docker-compose up diff --git a/app/assets/javascripts/contributors.coffee b/app/assets/javascripts/contributors.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/app/assets/javascripts/contributors.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/documents.scss b/app/assets/stylesheets/contributors.scss similarity index 62% rename from app/assets/stylesheets/documents.scss rename to app/assets/stylesheets/contributors.scss index ec6aac28..d06e8cbb 100644 --- a/app/assets/stylesheets/documents.scss +++ b/app/assets/stylesheets/contributors.scss @@ -1,3 +1,3 @@ -// Place all the styles related to the Documents controller here. +// Place all the styles related to the Contributors controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/emails.scss b/app/assets/stylesheets/emails.scss deleted file mode 100644 index 35db24d0..00000000 --- a/app/assets/stylesheets/emails.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Emails controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/lab.scss b/app/assets/stylesheets/lab.scss deleted file mode 100644 index 7288a665..00000000 --- a/app/assets/stylesheets/lab.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Lab controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/navigator.css b/app/assets/stylesheets/navigator.css deleted file mode 100644 index 4bfbeb09..00000000 --- a/app/assets/stylesheets/navigator.css +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Navigator controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/voting.scss b/app/assets/stylesheets/voting.scss deleted file mode 100644 index 17469c52..00000000 --- a/app/assets/stylesheets/voting.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Voting controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/authorizers/attribute_authorizer.rb b/app/authorizers/attribute_authorizer.rb new file mode 100644 index 00000000..d427bcb8 --- /dev/null +++ b/app/authorizers/attribute_authorizer.rb @@ -0,0 +1,29 @@ +class AttributeAuthorizer < ApplicationAuthorizer + def self.creatable_by? user + [ + true + ].any? + end + + def readable_by? user + [ + PermissionService.user_owns_content?(user: user, content: resource), + PermissionService.user_owns_any_containing_universe?(user: user, content: resource), + PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource), + PermissionService.content_is_public?(content: resource), + PermissionService.content_is_in_a_public_universe?(content: resource) + ].any? + end + + def updatable_by? user + [ + PermissionService.user_owns_content?(user: user, content: resource), + ].any? + end + + def deletable_by? user + [ + PermissionService.user_owns_content?(user: user, content: resource), + ].any? + end +end diff --git a/app/authorizers/collective_content_authorizer.rb b/app/authorizers/collective_content_authorizer.rb index fb616053..cd77b85e 100644 --- a/app/authorizers/collective_content_authorizer.rb +++ b/app/authorizers/collective_content_authorizer.rb @@ -1,5 +1,10 @@ class CollectiveContentAuthorizer < ContentAuthorizer def self.creatable_by? user - user.active_billing_plans.any? { |plan| plan.allows_collective_content } || user.selected_billing_plan_id == 2 + return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email) + + [ + PermissionService.billing_plan_allows_collective_content?(user: user), + PermissionService.user_can_collaborate_in_universe_that_allows_collective_content?(user: user) + ].any? end end diff --git a/app/authorizers/content_authorizer.rb b/app/authorizers/content_authorizer.rb index 33f1abd7..c8d7f28d 100644 --- a/app/authorizers/content_authorizer.rb +++ b/app/authorizers/content_authorizer.rb @@ -1,19 +1,36 @@ class ContentAuthorizer < ApplicationAuthorizer def readable_by? user [ - resource.user_id == user.id, - resource.respond_to?(:privacy) && resource.privacy == 'public', - resource.respond_to?(:universe) && resource.universe.present? && resource.universe.privacy == 'public' + PermissionService.user_owns_any_containing_universe?(user: user, content: resource), + PermissionService.user_owns_content?(user: user, content: resource), + PermissionService.content_is_public?(content: resource), + PermissionService.content_is_in_a_public_universe?(content: resource), + PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource) ].any? end def updatable_by? user - #todo: Collaboration - resource.user_id == user.id + [ + PermissionService.user_owns_any_containing_universe?(user: user, content: resource), + PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource), + [ + PermissionService.content_has_no_containing_universe?(content: resource), + PermissionService.user_owns_content?(user: user, content: resource) + ].all? + ].any? end def deletable_by? user - #todo: Collaboration - resource.user_id == user.id + [ + PermissionService.user_owns_any_containing_universe?(user: user, content: resource), + [ + PermissionService.content_has_no_containing_universe?(content: resource), + PermissionService.user_owns_content?(user: user, content: resource) + ].all?, + [ + PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource), + PermissionService.user_owns_content?(user: user, content: resource) + ].all? + ].any? end end diff --git a/app/authorizers/core_content_authorizer.rb b/app/authorizers/core_content_authorizer.rb index 3a2459c9..0050fdf4 100644 --- a/app/authorizers/core_content_authorizer.rb +++ b/app/authorizers/core_content_authorizer.rb @@ -1,7 +1,9 @@ class CoreContentAuthorizer < ContentAuthorizer def self.creatable_by? user - active_billing_plans = user.active_billing_plans + return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email) - active_billing_plans.empty? || active_billing_plans.any? { |plan| plan.allows_core_content } + [ + PermissionService.billing_plan_allows_core_content?(user: user) + ].any? end end diff --git a/app/authorizers/extended_content_authorizer.rb b/app/authorizers/extended_content_authorizer.rb index e0b8c990..28c0b781 100644 --- a/app/authorizers/extended_content_authorizer.rb +++ b/app/authorizers/extended_content_authorizer.rb @@ -1,5 +1,10 @@ class ExtendedContentAuthorizer < ContentAuthorizer def self.creatable_by? user - user.active_billing_plans.any? { |plan| plan.allows_extended_content } || user.selected_billing_plan_id == 2 + return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email) + + [ + PermissionService.billing_plan_allows_extended_content?(user: user), + PermissionService.user_can_collaborate_in_universe_that_allows_extended_content?(user: user) + ].any? end end diff --git a/app/authorizers/universe_core_content_authorizer.rb b/app/authorizers/universe_core_content_authorizer.rb index 121cafac..793be96c 100644 --- a/app/authorizers/universe_core_content_authorizer.rb +++ b/app/authorizers/universe_core_content_authorizer.rb @@ -1,24 +1,31 @@ class UniverseCoreContentAuthorizer < CoreContentAuthorizer def self.creatable_by? user - if BillingPlan.find_by(stripe_plan_id: 'free-for-life') - return true if user.selected_billing_plan_id == BillingPlan.find_by(stripe_plan_id: 'free-for-life').id - end - - user.universes.count < (user.active_billing_plans.map(&:universe_limit).max || 5) + return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email) + + [ + PermissionService.user_has_fewer_owned_universes_than_plan_limit?(user: user), + PermissionService.user_is_on_premium_plan?(user: user) + ].any? end def readable_by? user [ - resource.user_id == user.id, - resource.privacy == 'public' - ].any? + PermissionService.content_is_public?(content: resource), + PermissionService.user_owns_content?(user: user, content: resource), + PermissionService.user_can_contribute_to_universe?(user: user, universe: resource) + ].any? end def updatable_by? user - resource.user_id == user.id + [ + PermissionService.user_owns_content?(user: user, content: resource), + PermissionService.user_can_contribute_to_universe?(user: user, universe: resource) + ].any? end def deletable_by? user - resource.user_id == user.id + [ + PermissionService.user_owns_content?(user: user, content: resource) + ].any? end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index defebcdd..b3d66ccc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,7 +6,8 @@ class ApplicationController < ActionController::Base if params[:universe] == 'all' session.delete(:universe_id) elsif params[:universe].is_a?(String) && params[:universe].to_i.to_s == params[:universe] - found_universe = Universe.find_by(user: current_user, id: params[:universe]) + found_universe = Universe.find_by(id: params[:universe]) + found_universe = nil unless current_user.universes.include?(found_universe) || current_user.contributable_universes.include?(found_universe) session[:universe_id] = found_universe.id if found_universe end end @@ -14,7 +15,8 @@ class ApplicationController < ActionController::Base before_action do if current_user && session[:universe_id] - @universe_scope = Universe.find_by(user: current_user, id: session[:universe_id]) + @universe_scope = Universe.find_by(id: session[:universe_id]) + @universe_scope = nil unless current_user.universes.include?(@universe_scope) || current_user.contributable_universes.include?(@universe_scope) else @universe_scope = nil end diff --git a/app/controllers/attribute_fields_controller.rb b/app/controllers/attribute_fields_controller.rb index ae52124b..6e627385 100644 --- a/app/controllers/attribute_fields_controller.rb +++ b/app/controllers/attribute_fields_controller.rb @@ -19,7 +19,7 @@ class AttributeFieldsController < ContentController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'created attribute', { 'content_type': params[:entity_type] - }) + }) if Rails.env.production? @content = AttributeField.new(label: content_params[:label]).tap do |f| f.attribute_category_id = category.id diff --git a/app/controllers/concerns/has_ownership.rb b/app/controllers/concerns/has_ownership.rb deleted file mode 100644 index aff96816..00000000 --- a/app/controllers/concerns/has_ownership.rb +++ /dev/null @@ -1,64 +0,0 @@ -# Module handles privacy and auth for controller content -module HasOwnership - extend ActiveSupport::Concern - - included do - before_action :require_ownership_or_sharing, only: [:show] - before_action :require_ownership, only: [:edit, :update, :destroy] - end - - module ClassMethods - def owner # TODO: is this even used? - user.id - end - end - - private - - # Ensures that only the owner can do this action - def require_ownership - model = content_type_from_controller(self.class) - redirect_if_not_owned model.find(params[:id]), send(redirect_path) - # rescue - # redirect_to '/500' unless Rails.env.development? - end - - # Unless this content is shared, ensure only the owner can do this action - def require_ownership_or_sharing - model = content_type_from_controller(self.class).find(params[:id]) - unless model.user == current_user || publicly_shared?(model) - if current_user - redirect_to send(redirect_path), notice: t(:no_do_permission) - else - redirect_to new_user_session_path, notice: t(:no_do_permission) - end - end - end - - # Redirect the user to redirect_path unless they own this content - def redirect_if_not_owned(object_to_check, redirect_path) - if current_user.nil? - redirect_to new_user_session_path, notice: t(:no_do_permission) - elsif current_user != object_to_check.user - redirect_to send(redirect_path), notice: t(:no_do_permission) - end - end - - def redirect_path - model = content_type_from_controller(self.class) - # TODO: proper pluralizing here - "#{model.to_s.underscore}s_path" - end - - def owned_by_current_user?(object) - current_user == object.user - end - - def publicly_shared?(object) - object_is_public = object.respond_to?(:privacy) && object.privacy.present? && object.privacy.casecmp('public').zero? - object_universe_is_public = object.respond_to?(:universe) && object.universe.present? && object.universe.privacy.casecmp('public').zero? - - # If either the content or the universe it's in are public, we treat the object as public - object_is_public || object_universe_is_public - end -end diff --git a/app/controllers/content_controller.rb b/app/controllers/content_controller.rb index e4c22707..392d7761 100644 --- a/app/controllers/content_controller.rb +++ b/app/controllers/content_controller.rb @@ -1,15 +1,25 @@ class ContentController < ApplicationController - include HasOwnership - before_action :authenticate_user!, only: [:index, :new, :create, :edit, :update, :destroy] def index - @content = content_type_from_controller(self.class) - .where(user_id: current_user.id) - .order(:name) + @content_type_class = content_type_from_controller(self.class) + pluralized_content_name = @content_type_class.name.downcase.pluralize + + if @universe_scope.present? && @content_type_class != Universe + @content = @universe_scope.send(pluralized_content_name) + else + @content = ( + current_user.send(pluralized_content_name) + + current_user.send("contributable_#{pluralized_content_name}") + ) + + unless @content_type_class == Universe + @content.concat(current_user.universes.flat_map { |universe| universe.send(pluralized_content_name) }) + end + end + + @content = @content.flatten.uniq.sort_by(&:name) - @content = @content.where(universe: @universe_scope) if @universe_scope.present? && @content.build.respond_to?(:universe) - @content ||= [] @questioned_content = @content.sample @question = @questioned_content.question unless @questioned_content.nil? @@ -24,6 +34,8 @@ class ContentController < ApplicationController # TODO: Secure this with content class whitelist lel @content = content_type.find(params[:id]) + return if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(@content.user.email) + if (current_user || User.new).can_read? @content @question = @content.question if current_user.present? and current_user == @content.user @@ -49,9 +61,9 @@ class ContentController < ApplicationController end else if current_user.present? - return redirect_to :back + return redirect_to :back, notice: "You don't have permission to view that content." else - return redirect_to root_path + return redirect_to root_path, notice: "You don't have permission to view that content." end end end @@ -75,7 +87,7 @@ class ContentController < ApplicationController .find(params[:id]) unless @content.updatable_by? current_user - return redirect_to :back + return redirect_to @content, notice: t(:no_do_permission) end respond_to do |format| @@ -92,6 +104,15 @@ class ContentController < ApplicationController return redirect_to :back end + # Even if a user can create content, we want to double check that they're either on a premium account or creating content in a universe owned by someone on premium + # unless current_user.on_premium_plan? + # containing_universe = Universe.find(content_params[:universe_id].to_i) + # unless content_params[:universe_id].present? && containing_universe && containing_universe.user.on_premium_plan? && current_user.contributable_universes.include?(containing_universe) + # return redirect_to send(content_type.name.downcase.pluralize + '_path'), + # notice: "Premium content must either be created by a user with a Premium account, or in a universe owned by a user with a Premium account." + # end + # end + Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'created content', { 'content_type': content_type.name }) if Rails.env.production? @@ -123,7 +144,21 @@ class ContentController < ApplicationController upload_files params['image_uploads'], content_type.name, @content.id end - if @content.update_attributes(content_params) + if @content.is_a?(Universe) && params.key?('contributors') && @content.user == current_user + params[:contributors][:email].reject(&:blank?).each do |email| + ContributorService.invite_contributor_to_universe(universe: @content, email: email) + end + end + + if @content.user == current_user + update_success = @content.update_attributes(content_params) + else + # Exclude fields only the real owner can edit + #todo move field list somewhere when it grows + update_success = @content.update_attributes(content_params.except!(:universe_id)) + end + + if update_success successful_response(@content, t(:update_success, model_name: humanized_model_name)) else failed_response('edit', :unprocessable_entity) @@ -165,7 +200,7 @@ class ContentController < ApplicationController @content = content_type.find(params[:id]) unless current_user.can_delete? @content - return redirect_to :back + return redirect_to :back, notice: "You don't have permission to do that!" end Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'deleted content', { diff --git a/app/controllers/contributors_controller.rb b/app/controllers/contributors_controller.rb new file mode 100644 index 00000000..2e2ad2d6 --- /dev/null +++ b/app/controllers/contributors_controller.rb @@ -0,0 +1,16 @@ +class ContributorsController < ApplicationController + def destroy + contributor = Contributor.find(params[:id]) + relevant_universe = Universe.find(contributor.universe_id) + + if contributor + contributor.destroy + + #todo send "you've been removed as a contributor" email + end + + # A 303 status is required here so the browser doesn't redirect with a DELETE action + # https://stackoverflow.com/questions/14598703/rails-redirect-after-delete-using-delete-instead-of-get + redirect_to universes_path, status: 303 + end +end diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb index 7c44ead2..b3b2cc75 100644 --- a/app/controllers/export_controller.rb +++ b/app/controllers/export_controller.rb @@ -46,6 +46,11 @@ class ExportController < ApplicationController send_data to_csv(current_user.races), filename: "races-#{Date.today}.csv" end + def floras_csv + report_to_mixpanel 'csv', 'flora' + send_data to_csv(current_user.floras), filename: "floras-#{Date.today}.csv" + end + def religions_csv report_to_mixpanel 'csv', 'religions' send_data to_csv(current_user.religions), filename: "religions-#{Date.today}.csv" diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index e7df9fb4..8d24f4a5 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -15,7 +15,7 @@ class RegistrationsController < Devise::RegistrationsController end def account_update_params - params.require(:user).permit(:name, :email, :password, :password_confirmation, :email_updates, :fluid_preference) + params.require(:user).permit(:name, :email, :username, :password, :password_confirmation, :email_updates, :fluid_preference) end protected @@ -25,6 +25,11 @@ class RegistrationsController < Devise::RegistrationsController end def add_account + # Tie any universe contributor invites with this email to this user + if resource.persisted? + Contributor.where(email: resource.email, user_id: nil).update_all(user_id: resource.id) + end + # If the user was created in the last 60 seconds, report it to Slack if resource.persisted? report_new_account_to_slack resource diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 4a5e1131..c972112d 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -52,6 +52,12 @@ class SubscriptionsController < ApplicationController end_date: Time.now.end_of_day + 5.years ) + unless Rails.env.test? + stripe_customer = Stripe::Customer.retrieve current_user.stripe_customer_id + stripe_subscription = stripe_customer.subscriptions.data[0] + stripe_subscription.save + end + report_subscription_change_to_slack current_user, old_billing_plan, new_billing_plan flash[:notice] = "You have been successfully downgraded to Starter." #todo proration/credit @@ -72,14 +78,14 @@ class SubscriptionsController < ApplicationController # Change subscription plan if they already have a payment method on file stripe_subscription.plan = new_plan_id begin - stripe_subscription.save + stripe_subscription.save unless Rails.env.test? rescue Stripe::CardError => e flash[:alert] = "We couldn't upgrade you to Premium because #{e.message.downcase} Please double check that your information is correct." return redirect_to :back end # If this is the first time this user is subscribing to Premium, gift them (and their referrer, if applicable) feature votes and space - existing_premium_subscriptions = current_user.subscriptions.where(billing_plan_id: [2, 3, 4, 5, 6]) + existing_premium_subscriptions = current_user.subscriptions.where(billing_plan_id: BillingPlan::PREMIUM_IDS) unless existing_premium_subscriptions.any? referring_user = current_user.referrer || current_user @@ -223,7 +229,7 @@ class SubscriptionsController < ApplicationController end # If this is the first time this user is subscribing to Premium, gift them (and their referrer, if applicable) feature votes and space - existing_premium_subscriptions = current_user.subscriptions.where(billing_plan_id: [2, 3, 4, 5, 6]) + existing_premium_subscriptions = current_user.subscriptions.where(billing_plan_id: BillingPlan::PREMIUM_IDS) unless existing_premium_subscriptions.any? referring_user = current_user.referrer || current_user @@ -323,14 +329,20 @@ class SubscriptionsController < ApplicationController delta = ":wave: Downgrade" end - active_subscriptions = Subscription.where('start_date < ?', Time.now).where('end_date > ?', Time.now) - total_subs_monthly = active_subscriptions.map(&:billing_plan).sum(&:monthly_cents).to_f / 100.0 + total_subscriptions = 0 + monthly_rev_cents = 0 + billing_plans_with_prices = BillingPlan.where.not(monthly_cents: 0).pluck(:id, :monthly_cents) + billing_plans_with_prices.each do |plan_id, monthly_cents| + users_on_this_plan = User.where(selected_billing_plan_id: plan_id).count + total_subscriptions += users_on_this_plan + monthly_rev_cents += monthly_cents * users_on_this_plan + end notifier.ping [ "#{delta} for #{user.email.split('@').first}@ (##{user.id})", "From: *#{from.name}* ($#{from.monthly_cents / 100.0}/month)", "To: *#{to.name}* (#{to.stripe_plan_id}) ($#{to.monthly_cents / 100.0}/month)", - "#{active_subscriptions.count} subscriptions total $#{'%.2f' % total_subs_monthly}/mo" + "#{total_subscriptions} subscriptions total $#{'%.2f' % (monthly_rev_cents / 100)}/mo" ].join("\n") end diff --git a/app/controllers/universes_controller.rb b/app/controllers/universes_controller.rb index 840a0f6a..b261041e 100644 --- a/app/controllers/universes_controller.rb +++ b/app/controllers/universes_controller.rb @@ -28,7 +28,7 @@ class UniversesController < ContentController :history, :privacy, :notes, :private_notes, - custom_attribute_values: [:name, :value] + custom_attribute_values: [:name, :value], ] end end diff --git a/app/helpers/contributors_helper.rb b/app/helpers/contributors_helper.rb new file mode 100644 index 00000000..2d0349f0 --- /dev/null +++ b/app/helpers/contributors_helper.rb @@ -0,0 +1,2 @@ +module ContributorsHelper +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 00000000..d25d8892 --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "from@example.com" + layout 'mailer' +end diff --git a/app/mailers/collaboration_mailer.rb b/app/mailers/collaboration_mailer.rb new file mode 100644 index 00000000..5bc1c99d --- /dev/null +++ b/app/mailers/collaboration_mailer.rb @@ -0,0 +1,13 @@ +class CollaborationMailer < ApplicationMailer + default from: "collaboration@notebook.ai" + + def contributor_invitation(inviter:, invite_email:, universe:) + @inviter = inviter + @universe = universe + + mail( + to: invite_email, + subject: "#{@inviter.name} requested your help collaborating on Notebook.ai." + ) + end +end diff --git a/app/models/attribute.rb b/app/models/attribute.rb index af42c602..c3272935 100644 --- a/app/models/attribute.rb +++ b/app/models/attribute.rb @@ -4,7 +4,7 @@ class Attribute < ActiveRecord::Base belongs_to :entity, polymorphic: true include Authority::Abilities - self.authorizer_name = 'CoreContentAuthorizer' + self.authorizer_name = 'AttributeAuthorizer' include HasPrivacy scope :is_public, -> { eager_load(:universe).where('universes.privacy = ? OR attributes.privacy = ?', 'public', 'public') } diff --git a/app/models/attribute_category.rb b/app/models/attribute_category.rb index 529c09c0..fa5e66f2 100644 --- a/app/models/attribute_category.rb +++ b/app/models/attribute_category.rb @@ -8,7 +8,7 @@ class AttributeCategory < ActiveRecord::Base include Serendipitous::Concern include Authority::Abilities - self.authorizer_name = 'CoreContentAuthorizer' + self.authorizer_name = 'AttributeAuthorizer' before_validation :ensure_name diff --git a/app/models/attribute_field.rb b/app/models/attribute_field.rb index 268470a0..880b91bf 100644 --- a/app/models/attribute_field.rb +++ b/app/models/attribute_field.rb @@ -9,7 +9,7 @@ class AttributeField < ActiveRecord::Base include Serendipitous::Concern include Authority::Abilities - self.authorizer_name = 'CoreContentAuthorizer' + self.authorizer_name = 'AttributeAuthorizer' attr_accessor :system diff --git a/app/models/billing_plan.rb b/app/models/billing_plan.rb index ad28a549..84d59b53 100644 --- a/app/models/billing_plan.rb +++ b/app/models/billing_plan.rb @@ -1,2 +1,3 @@ class BillingPlan < ActiveRecord::Base + PREMIUM_IDS = [2, 3, 4, 5, 6] end diff --git a/app/models/concerns/has_changelog.rb b/app/models/concerns/has_changelog.rb index 684918d8..ea4575e7 100644 --- a/app/models/concerns/has_changelog.rb +++ b/app/models/concerns/has_changelog.rb @@ -10,7 +10,7 @@ module HasChangelog after_create do ContentChangeEvent.create( - user: user, + user: find_current_user, changed_fields: changes, content_id: id, content_type: self.class.name, @@ -19,8 +19,9 @@ module HasChangelog end after_update do + # todo how to get current_user? ContentChangeEvent.create( - user: user, + user: find_current_user, changed_fields: changes, content_id: id, content_type: self.class.name, @@ -30,12 +31,24 @@ module HasChangelog before_destroy do ContentChangeEvent.create( - user: user, + user: find_current_user, changed_fields: changes, content_id: id, content_type: self.class.name, action: :deleted ) end + + private + + def find_current_user + (1..Kernel.caller.length).each do |n| + RubyVM::DebugInspector.open do |i| + current_user = eval "current_user rescue nil", i.frame_binding(n) + return current_user unless current_user.nil? + end + end + return nil + end end end diff --git a/app/models/concerns/has_content_groupers.rb b/app/models/concerns/has_content_groupers.rb index 862dc6c4..d5b18521 100644 --- a/app/models/concerns/has_content_groupers.rb +++ b/app/models/concerns/has_content_groupers.rb @@ -22,7 +22,7 @@ module HasContentGroupers } # } # Siblingships - has_many connecting_class_name + has_many connecting_class_name, dependent: :destroy # Siblings has_many relation, diff --git a/app/models/content_groupers/childrenship.rb b/app/models/content_groupers/childrenship.rb index 4dec0b84..48bf7cd9 100644 --- a/app/models/content_groupers/childrenship.rb +++ b/app/models/content_groupers/childrenship.rb @@ -39,6 +39,13 @@ class Childrenship < ActiveRecord::Base elsif gender == :female other_object.mothers.delete this_object + elsif gender.nil? + if other_object.fathers.include? this_object + other_object.fathers.delete this_object + + elsif other_object.mothers.include? this_object + other_object.mothers.delete this_object + end end end diff --git a/app/models/content_groupers/siblingship.rb b/app/models/content_groupers/siblingship.rb index aa269980..3133b944 100644 --- a/app/models/content_groupers/siblingship.rb +++ b/app/models/content_groupers/siblingship.rb @@ -16,6 +16,6 @@ class Siblingship < ActiveRecord::Base this_object = Character.find_by(id: self.character_id) other_object = Character.find_by(id: self.sibling_id) - other_object.siblings.delete this_object + other_object.siblings.delete(this_object) unless other_object.nil? end end diff --git a/app/models/content_types/universe.rb b/app/models/content_types/universe.rb index a5d40178..161e2e11 100644 --- a/app/models/content_types/universe.rb +++ b/app/models/content_types/universe.rb @@ -18,6 +18,7 @@ class Universe < ActiveRecord::Base self.authorizer_name = 'UniverseCoreContentAuthorizer' validates :name, presence: true + validates :user_id, presence: true belongs_to :user # Core content types @@ -36,6 +37,8 @@ class Universe < ActiveRecord::Base has_many :scenes has_many :groups + has_many :contributors, dependent: :destroy + scope :is_public, -> { where(privacy: 'public') } def content diff --git a/app/models/contributor.rb b/app/models/contributor.rb new file mode 100644 index 00000000..0d023851 --- /dev/null +++ b/app/models/contributor.rb @@ -0,0 +1,4 @@ +class Contributor < ActiveRecord::Base + belongs_to :universe + belongs_to :user +end diff --git a/app/models/user.rb b/app/models/user.rb index 3f620d55..8d260c26 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -14,11 +14,15 @@ class User < ActiveRecord::Base include HasContent include Authority::UserAbilities - validates :email, presence: true - #todo: We probably want a uniqueness constraint on email + validates_uniqueness_of :username, allow_nil: true, allow_blank: true has_many :subscriptions has_many :billing_plans, through: :subscriptions + def on_premium_plan? + return true + BillingPlan::PREMIUM_IDS.include?(self.selected_billing_plan_id) + end + has_many :image_uploads has_one :referral_code @@ -35,6 +39,22 @@ class User < ActiveRecord::Base has_many :omniauth_users + def contributable_universes + # todo email confirmation needs to happy for data safety / privacy (only verified emails) + contributor_by_email = Contributor.where(email: self.email).pluck(:universe_id) + contributor_by_user = Contributor.where(user: self).pluck(:universe_id) + + Universe.where(id: contributor_by_email + contributor_by_user) + end + [Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene, Flora].each do |content_type| + pluralized_content_type = content_type.name.downcase.pluralize + define_method "contributable_#{pluralized_content_type}" do + contributable_universes.flat_map do |universe| + universe.send(pluralized_content_type).where.not(user_id: self.id) + end + end + end + # TODO: Swap this out with a has_many when we transition from a scratchpad to users having multiple documents has_one :document @@ -43,7 +63,7 @@ class User < ActiveRecord::Base after_create :initialize_secure_code def createable_content_types - [Universe, Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene].select do |c| + [Universe, Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene, Flora].select do |c| can_create? c end end @@ -138,4 +158,12 @@ class User < ActiveRecord::Base :email ] end + + def forum_username + username = self.username.present? ? "@#{self.username}" : nil + username ||= self.name.present? ? self.name : nil + username ||= 'Anonymous Author' + + username + end end diff --git a/app/services/contributor_service.rb b/app/services/contributor_service.rb new file mode 100644 index 00000000..bc9b1f28 --- /dev/null +++ b/app/services/contributor_service.rb @@ -0,0 +1,39 @@ +class ContributorService < Service + def self.invite_contributor_to_universe universe:, email: + # First, look up whether a user already exists for this invite + related_user = User.find_by(email: email) + + # Create the Contributor object with or without a user + Contributor.create( + universe: universe, + email: email, + user: related_user + ) + + # If the user doesn't already have a Notebook.ai account, send them an invite + if related_user.nil? + send_invite_email_to(inviter: universe.user, email: email, universe: universe) + end + + # If the user does have a Notebook.ai account, send them an email letting them know they got added as a contributor + if related_user + send_contributor_notice_to(inviter: universe.user, email: email, universe: universe) + end + end + + def self.send_invite_email_to inviter:, email:, universe: + CollaborationMailer.contributor_invitation( + inviter: inviter, + invite_email: email, + universe: universe + ) + end + + def self.send_contributor_notice_to inviter:, email:, universe: + CollaborationMailer.contributor_invitation( + inviter: inviter, + invite_email: email, + universe: universe + ) + end +end \ No newline at end of file diff --git a/app/services/permission_service.rb b/app/services/permission_service.rb new file mode 100644 index 00000000..59ad64ea --- /dev/null +++ b/app/services/permission_service.rb @@ -0,0 +1,66 @@ +# A helper for more human-readable logic clauses to be used in permissions +class PermissionService < Service + def self.user_has_fewer_owned_universes_than_plan_limit?(user:) + user.universes.count < (user.active_billing_plans.map(&:universe_limit).max || 5) + end + + def self.user_owns_content?(user:, content:) + content.user == user + end + + def self.user_owns_any_containing_universe?(user:, content:) + content.respond_to?(:universe) && content.universe.present? && user_owns_content?(user: user, content: content.universe) + end + + def self.user_can_contribute_to_universe?(user:, universe:) + user.contributable_universes.pluck(:id).include?(universe.id) + end + + def self.content_is_public?(content:) + content.respond_to?(:privacy) && content.privacy == 'public' + end + + def self.content_is_in_a_public_universe?(content:) + content.respond_to?(:universe) && content.universe.present? && self.content_is_public?(content: content.universe) + end + + def self.user_can_contribute_to_containing_universe?(user:, content:) + content.universe.present? && user.contributable_universes.pluck(:id).include?(content.universe.id) + end + + def self.content_has_no_containing_universe?(content:) + content.universe.nil? + end + + def self.user_is_on_premium_plan?(user:) + user.on_premium_plan? + end + + def self.billing_plan_allows_core_content?(user:) + active_billing_plans = user.active_billing_plans + active_billing_plans.empty? || active_billing_plans.any?(&:allows_core_content) + end + + def self.billing_plan_allows_extended_content?(user:) + # todo remove second clause will billing plans are fixed + user.active_billing_plans.any?(&:allows_extended_content) || BillingPlan::PREMIUM_IDS.include?(user.selected_billing_plan_id) + end + + def self.user_can_collaborate_in_universe_that_allows_extended_content?(user:) + user.contributable_universes.any? do |universe| + billing_plan_allows_extended_content?(user: universe.user) + end + end + + def self.billing_plan_allows_collective_content?(user:) + user.active_billing_plans.any?(&:allows_collective_content) || BillingPlan::PREMIUM_IDS.include?(user.selected_billing_plan_id) + end + + def self.user_can_collaborate_in_universe_that_allows_collective_content?(user:) + user.contributable_universes.any? do |universe| + billing_plan_allows_collective_content?(user: universe.user) + end + end + + +end diff --git a/app/views/cards/intros/_content_type_intro.html.erb b/app/views/cards/intros/_content_type_intro.html.erb index dd750909..2fb004d2 100644 --- a/app/views/cards/intros/_content_type_intro.html.erb +++ b/app/views/cards/intros/_content_type_intro.html.erb @@ -5,7 +5,7 @@
- <%= image_tag "card-headers/#{content_name.pluralize}.jpg" %> + <%= image_tag "card-headers/#{content_name.pluralize}.jpg", height: 300, width: 300 %> Create <%= content_name == "magic" ? 'magic' : content_name.pluralize %>
@@ -13,4 +13,4 @@ <%= t("content_descriptions.#{content_name}") %>

-
\ No newline at end of file + diff --git a/app/views/collaboration_mailer/contributor_invitation.html.erb b/app/views/collaboration_mailer/contributor_invitation.html.erb new file mode 100644 index 00000000..7d22ce36 --- /dev/null +++ b/app/views/collaboration_mailer/contributor_invitation.html.erb @@ -0,0 +1,25 @@ + + + + + + +

Hello!

+ +

+ <%= @inviter.name %> is creating their own universes on Notebook.ai and has invited you to collaborate on their <%= link_to @universe.name, universe_path(@universe, referral_code: @universe.user.referral_code.code) %> universe. +

+

+ To get started, simply sign up or sign in to <%= link_to 'Notebook.ai', "https://www.notebook.ai/users/sign_in?referral_code=#{@universe.user.referral_code.code}" %> with this email address and look for "<%= @universe.name %>" under your Universes tab. +

+ +

+ Happy worldbuilding! +

+ +


+

+ Please don't respond to this email as this email address is not watched. If you have any questions, comments, concerns, or want to unsubscribe to future collaboration invitations, please send an email directly to andrew@indentlabs.com. Thank you for your understanding. +

+ + diff --git a/app/views/collaboration_mailer/contributor_invitation.text.erb b/app/views/collaboration_mailer/contributor_invitation.text.erb new file mode 100644 index 00000000..10df67c9 --- /dev/null +++ b/app/views/collaboration_mailer/contributor_invitation.text.erb @@ -0,0 +1,13 @@ +Hello! + +<%= @inviter.name %> is creating their own universes on Notebook.ai and has invited you to collaborate on their <%= @universe.name %> universe. + +To get started, simply sign up or sign in to https://www.notebook.ai/ with this email address and look for <%= @universe.name %> under your Universes tab. You can also navigate directly to the universe at https://www.notebook.ai<%= universe_path(@universe) %> and get started from there. + +If you are signing up for an account for the first time, you can use the following link to credit <%= @universe.user.name %> with a referral: <%= "https://www.notebook.ai/?referral_code=#{@universe.user.referral_code.code}" %> + +Happy worldbuilding! + +- + +Please don't respond to this email as the address is not watched. If you have any questions, comments, concerns, or want to unsubscribe to future collaboration invitations, please send an email directly to andrew@indentlabs.com. Thank you for your understanding. \ No newline at end of file diff --git a/app/views/content/contexts/_universe.html.erb b/app/views/content/contexts/_universe.html.erb index 5509f05e..1741e22b 100644 --- a/app/views/content/contexts/_universe.html.erb +++ b/app/views/content/contexts/_universe.html.erb @@ -1,4 +1,4 @@ -<% content_to_show = [Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene] %> +<% content_to_show = [Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene, Flora] %> <% content_to_show.each do |content_type| %> <% next if @content.send(content_type.name.downcase.pluralize).empty? %> diff --git a/app/views/content/display/_changelog.html.erb b/app/views/content/display/_changelog.html.erb index 75e0d92f..3ddb4392 100644 --- a/app/views/content/display/_changelog.html.erb +++ b/app/views/content/display/_changelog.html.erb @@ -3,6 +3,7 @@ \ No newline at end of file + diff --git a/app/views/content/display/_contributors.html.erb b/app/views/content/display/_contributors.html.erb new file mode 100644 index 00000000..ef50d55f --- /dev/null +++ b/app/views/content/display/_contributors.html.erb @@ -0,0 +1,37 @@ +<%# Usage: render partial: 'content/display/contributors', locals: { content: content } %> + + + +<%= render partial: 'content/form/contributors/leave', locals: { content: content } %> + + \ No newline at end of file diff --git a/app/views/content/display/_floating_action_buttons.html.erb b/app/views/content/display/_floating_action_buttons.html.erb index 230aef2d..b7387bee 100644 --- a/app/views/content/display/_floating_action_buttons.html.erb +++ b/app/views/content/display/_floating_action_buttons.html.erb @@ -1,7 +1,7 @@ <% editing = defined?(editing) && editing creating = defined?(creating) && creating - personal_content = (user_signed_in? && current_user.id == @content.user_id) || @content.new_record? + personal_content = @content.updatable_by?(current_user || User.new) || @content.new_record? %> <%# Primary FAB %> @@ -38,7 +38,7 @@ <% end %> -<% if @content.persisted? && editing %> +<% if @content.persisted? && editing && current_user.can_delete?(@content) %> <%= link_to @content, class: 'btn-floating btn-small halfway-fab waves-effect waves-light white delete left', style: 'z-index: 500;', diff --git a/app/views/content/display/_sidelinks.html.erb b/app/views/content/display/_sidelinks.html.erb index 9af6945b..bf9f23b5 100644 --- a/app/views/content/display/_sidelinks.html.erb +++ b/app/views/content/display/_sidelinks.html.erb @@ -27,6 +27,9 @@ # Show everything on create/edit forms elsif category.name == 'changelog' # Show changelog on show page + elsif category.name == 'contributors' + # Show contributors on show page if there are any to show + next if @content.contributors.empty? elsif category.name == 'gallery' # Only show gallery tab if the content has any images next if @content.image_uploads.empty? @@ -48,6 +51,9 @@ _panel" class="<%= 'red-text' if category.label == 'Overview' %>"> <%= category.icon %> <%= category.label.capitalize %> + <% if editing && category.label == 'Contributors' %> + + <% end %> <% end %> diff --git a/app/views/content/form/_contributors.html.erb b/app/views/content/form/_contributors.html.erb new file mode 100644 index 00000000..4ff84813 --- /dev/null +++ b/app/views/content/form/_contributors.html.erb @@ -0,0 +1,53 @@ +<%# Usage: render partial: 'content/form/contributors', locals: { content: content } %> + +

+ Universes can have an unlimited number of collaborators. +

+

+ When a user is added as a collaborator to a universe, they are allowed to: + +

+

+ +

+ They are NOT allowed to: + +

+

+ +<% if content.contributors.any? %> +

+ The following users are enabled as contributors to this universe. + <% if user_signed_in? && content.user == current_user %> + You may remove their contributor privileges at any time. + <% end %> +

+ + <%= render partial: 'content/display/contributors', locals: { content: content } %> +<% end %> + +<% if user_signed_in? && content.user == current_user %> +

+ You may also add additional contributors to this universe by inviting them by email below. +

+ +
+ <%= render partial: 'content/form/contributors/add', locals: { f: f, content: content } %> +
+
+ <%= link_to_add_association "invite another", f, + :contributors, + partial: 'content/form/contributors/add', + render_options: { locals: { f: f, content: content }} + %> +
+<% end %> diff --git a/app/views/content/form/_panel.html.erb b/app/views/content/form/_panel.html.erb index f0934d5f..a0ce2a0b 100644 --- a/app/views/content/form/_panel.html.erb +++ b/app/views/content/form/_panel.html.erb @@ -1,5 +1,6 @@
_panel" class="row panel"> + <%# todo: break this out into a partial %> <% if category.name == 'gallery' %>
<%= render partial: 'content/form/images/edit_list', locals: { content: content } %> @@ -8,7 +9,11 @@
Upload more images - You have <%= Filesize.from("#{current_user.upload_bandwidth_kb}KB").pretty %> of bandwidth remaining. + <% if current_user.upload_bandwidth_kb > 0 %> + You have <%= Filesize.from("#{current_user.upload_bandwidth_kb}KB").pretty %> of bandwidth remaining. + <% else %> + You have no upload bandwidth remaining. Upgrade to Premium or delete some existing images for more. + <% end %>
@@ -24,6 +29,10 @@
<% end %> + <% if category.name == 'contributors' %> + <%= render partial: 'content/form/contributors', locals: { f: f, content: content } %> + <% end %> + <% # Do some dynamic resizing of columns based on how many columns there are # TODO: move this into some service or something? Dunno, doesn't belong here. s_width = 12 @@ -54,7 +63,7 @@ <% if value.is_a?(ActiveRecord::Associations::CollectionProxy) %> <%# Relation-setting UI %> <% through_class = content.class.reflect_on_association(field.name).options[:through].to_s %> - <%= render 'content/form/relation_input', f: f, attribute: field.name, relation: through_class %> + <%= render 'content/form/relation_input', f: f, attribute: field.name, label: field.label, relation: through_class %> <% elsif field.name == 'archetype' %>
@@ -65,7 +74,46 @@ <% elsif field.name == 'universe_id' %>
<%= f.label field.name, field.label %>
- <%= f.select field.name, current_user.universes.sort_by(&:name).map { |u| [u.name, u.id] }.compact, include_blank: true, selected: (f.object.persisted? ? f.object.universe_id : session[:universe_id]) %> + <% if !f.object.persisted? || (f.object.persisted? && f.object.universe && f.object.universe.user == current_user) || f.object.universe_id.nil? %> + <%# todo not like this %> + <% + valid_universes = [] + show_premium_notice = false + + if content.is_a?(Character) || content.is_a?(Location) || content.is_a?(Item) + valid_universes += current_user.universes + valid_universes += current_user.contributable_universes + else + # Premium content + if current_user.on_premium_plan? + valid_universes += current_user.universes + else + show_premium_notice = true + end + + current_user.contributable_universes.each do |potential_universe| + if potential_universe.user.on_premium_plan? + valid_universes += [potential_universe] + end + end + end + %> + <%= + f.select field.name, + valid_universes.uniq.sort_by(&:name).map { |u| [u.name, u.id] }.compact, + include_blank: current_user.on_premium_plan? || content.is_a?(Character) || content.is_a?(Location) || content.is_a?(Item), + selected: (f.object.persisted? ? f.object.universe_id : session[:universe_id]) + %> + <% if show_premium_notice %> +
+ info + While on a <%= link_to 'Starter plan', subscription_path %>, you can only create premium content in universes you're a contributor to. +
+ <% end %> + <% else %> +
+ <%= link_to(f.object.universe.name, f.object.universe) if f.object.universe %> + <% end %>
<% elsif field.name == 'attribute_category_id' %> @@ -94,13 +142,27 @@
<% elsif field.name == 'private_notes' %> -
- <%= f.label field.name, field.label %> - <%= f.text_area field.name, class: "materialize-textarea", placeholder: 'Write as little or as much as you want!' %> -
- Private notes are always visible to only you, even if content is made public and shared. + <%# + This field is shown if and only if: + * A user is signed in AND + * That user is the owner of this universe, if it's a universe, or + * That user is the owner of this content's universe, if it's content + * That user is the owner of this content, if it's not in a universe + %> + <% if user_signed_in? && ( + (content.is_a?(Universe) && content.user == current_user) || + (content.respond_to?(:universe) && content.universe && content.universe.user == current_user) || + (content.respond_to?(:universe) && content.universe.nil? && content.user == current_user) + ) + %> +
+ <%= f.label field.name, field.label %> + <%= f.text_area field.name, class: "materialize-textarea", placeholder: 'Write as little or as much as you want!' %> +
+ Private notes are always visible to only you, even if content is made public and shared. +
-
+ <% end %> <% elsif field.name == 'item_type' %> <% potential_item_types = t('weapon_types') + t('shield_types') + t('axe_types') + t('bow_types') + diff --git a/app/views/content/form/_relation_input.html.erb b/app/views/content/form/_relation_input.html.erb index 58a99510..d8b93df7 100644 --- a/app/views/content/form/_relation_input.html.erb +++ b/app/views/content/form/_relation_input.html.erb @@ -5,7 +5,7 @@ <%= f.label attribute, I18n.translate( "attributes.#{f.object.class.name.downcase}.#{attribute}", scope: :activerecord, - default: attribute.humanize.capitalize + default: label.humanize.capitalize ) %>
diff --git a/app/views/content/form/contributors/_add.html.erb b/app/views/content/form/contributors/_add.html.erb new file mode 100644 index 00000000..65476a5e --- /dev/null +++ b/app/views/content/form/contributors/_add.html.erb @@ -0,0 +1,21 @@ +
+ <%= f.fields_for :contributors do |ff| %> +
+
+
+ <%= ff.label 'Contributor email address' %>
+ <%= ff.text_field :email, name: 'contributors[email][]' %> +
+
+ +
+
+
+ <%= link_to_remove_association f do %> + close + <% end %> +
+ +
+ <% end %> +
diff --git a/app/views/content/form/contributors/_leave.html.erb b/app/views/content/form/contributors/_leave.html.erb new file mode 100644 index 00000000..01917bbc --- /dev/null +++ b/app/views/content/form/contributors/_leave.html.erb @@ -0,0 +1,15 @@ +<%# Usage: render partial: 'content/form/contributors/leave', locals: { content: content } %> + +<% if user_signed_in? && content.contributors.pluck(:user_id).include?(current_user.id) %> +

+ You are a contributor to this universe. You may dismiss yourself as a contributor at any time, but will need to be reinvited to begin contributing again. + You will lose access to all content in this universe unless it is shared publicly, even if you created or edited it previously. +

+

+ <% my_contributor_object = Contributor.where(user: current_user, universe_id: content.id).first %> + <%= link_to 'Leave this universe', remove_contributor_path(my_contributor_object.id), + class: 'btn red lighten-3 black-text', + method: 'delete', + data: { confirm: "Are you sure? You will no longer have contributor access to this universe." } %> +

+<% end %> \ No newline at end of file diff --git a/app/views/content/index.html.erb b/app/views/content/index.html.erb index 7797f889..3640273f 100644 --- a/app/views/content/index.html.erb +++ b/app/views/content/index.html.erb @@ -1,6 +1,5 @@ <% - content_type_class = @content.build.class - content_type = content_type_class.name.downcase + content_type = @content_type_class.name.downcase %> <% content_for :sidebar_top do %> @@ -32,28 +31,28 @@ <% if @content.any? %>

- You've created <%= pluralize(@content.count, @content.content_name) %> + Your <%= pluralize(@content.count, content_type) %> <% if @content.count > 0 %> - <% if current_user.can_create? content_type_class %> + <% if current_user.can_create? @content_type_class %> - <%= link_to new_polymorphic_path(@content.build), class: "btn white #{content_type_class.color}-text" do %> + <%= link_to new_polymorphic_path(@content_type_class), class: "btn white #{@content_type_class.color}-text" do %> + - - <%= content_type_class.icon %> + + <%= @content_type_class.icon %> <% end %> <% else %> - <%= link_to subscription_path, class: "btn grey lighten-3 #{content_type_class.color}-text tooltipped", data: { + <%= link_to subscription_path, class: "btn grey lighten-3 #{@content_type_class.color}-text tooltipped", data: { position: 'bottom', - tooltip: "Upgrade to Premium to create more #{content_type_class.name.pluralize}", + tooltip: "Upgrade to Premium to create more #{@content_type_class.name.pluralize}", delay: 50 } do %> + - - <%= content_type_class.icon %> + + <%= @content_type_class.icon %> <% end %> @@ -61,25 +60,25 @@ <% end %>

- <%= render partial: 'content/list/list', locals: { content_list: @content, show_add_another_form: true } %> + <%= render partial: 'content/list/list', locals: { content_list: @content, content_type: @content_type_class, show_add_another_form: true } %> <% elsif @content.empty? %>
-

You haven't created any <%= @content.content_name.pluralize %> yet!

+

You haven't created any <%= content_type.pluralize %> yet!

- - <%= content_type_class.icon %> + + <%= @content_type_class.icon %>

<%= t("content_descriptions.#{content_type}") %>

- <% if current_user.can_create? content_type_class %> - <%= link_to "Create your first #{@content.content_name.humanize.downcase}", new_polymorphic_path(@content.build), class: "btn #{content_type_class.color}" %> + <% if current_user.can_create? @content_type_class %> + <%= link_to "Create your first #{content_type.humanize.downcase}", new_polymorphic_path(@content_type_class), class: "btn #{@content_type_class.color}" %> <% else %> - <%= link_to "Upgrade to Premium to create your first #{@content.content_name.humanize.downcase}", subscription_path %> + <%= link_to "Upgrade to Premium to create your first #{content_type.humanize.downcase}", subscription_path %> <% end %>
diff --git a/app/views/content/list/_list.html.erb b/app/views/content/list/_list.html.erb index 451f1306..58b0153f 100644 --- a/app/views/content/list/_list.html.erb +++ b/app/views/content/list/_list.html.erb @@ -1,6 +1,4 @@ -<% - content_type = content_list.any? ? content_list.first.class : content_list.build.class -%> +<%# Usage: render partial: 'content/list/list', locals: { content_list: @content, content_type: @content_type_class, show_add_another_form: true } %>
    <% if title.present? %> @@ -20,13 +18,32 @@ <%= link_to content.universe.name, content.universe %> <% end %> + <% if user_signed_in? && ( + (content.respond_to?(:universe) && content.universe && content.universe.user != current_user) || + (content.is_a?(Universe) && content.user != current_user) + ) + %> + + <% end %> + <% if user_signed_in? && ( + ( + content.respond_to?(:universe) && content.universe && content.universe.user == current_user && + content.user != current_user + ) || + (content.is_a?(Universe) && content.contributors.include?(current_user)) + ) + %> + + <% end %>

    <%= content.description %>

    - <% if current_user && current_user == content.user %> + <% if user_signed_in? && content.updatable_by?(current_user) %> " style="font-size: 80%"> mode_edit @@ -43,7 +60,7 @@ <% end %>

    - <% if current_user && current_user == content.user %> + <% if current_user && content.updatable_by?(current_user) %> <%= link_to edit_polymorphic_path(content), class: 'js-edit-hover' do %> edit <% end %> @@ -53,8 +70,10 @@ <% end %> <% if local_assigns[:show_add_another_form] && content_list.any? && current_user.can_create?(content_type) %> - <% unless content_type == Universe %> - <%= render partial: 'content/list/quick_add_form', locals: { content_type: content_list.build.class } %> + <% if content_type == Character || content_type == Location || content_type == Item || current_user.on_premium_plan? %> + <% unless content_type == Universe %> + <%= render partial: 'content/list/quick_add_form', locals: { content_type: content_type } %> + <% end %> <% end %> <% end %> diff --git a/app/views/content/show.html.erb b/app/views/content/show.html.erb index f205d8ce..eafd8a9f 100644 --- a/app/views/content/show.html.erb +++ b/app/views/content/show.html.erb @@ -33,8 +33,20 @@ <%= render partial: 'content/display/changelog', locals: { content: content } %> <% end %> + <% if category.name == 'contributors' %> +
    + <%= render partial: 'content/display/contributors', locals: { content: content } %> +
    + <% end %> <% category.attribute_fields.each do |attribute| %> - <% next if attribute.name.start_with?("private") && (current_user.nil? || @content.user != current_user) %> + <% if attribute.name.start_with?("private") %> + <% next unless user_signed_in? && ( + (content.is_a?(Universe) && content.user == current_user) || + (content.respond_to?(:universe) && content.universe && content.universe.user == current_user) || + (content.respond_to?(:universe) && content.universe.nil? && content.user == current_user) + ) + %> + <% end %> <% custom_field = false @@ -82,10 +94,10 @@ <% end %> - <% if current_user.nil? || current_user.id != @content.user_id %> + <% if current_user.nil? || current_user.id != @content.user_id && (@content.respond_to?(:universe) && @content.universe && @content.universe.user_id != current_user.id) %>

    - <%= @content.user.name %> created and maintains this universe on Notebook.ai. + This <%= @content.class.name.downcase %> was created by <%= @content.user.name %> on Notebook.ai<% if @content.is_a?(Universe) && @content.contributors.any? %> with <%= pluralize @content.contributors.count, 'contributor' %><% end %>.

    <%= link_to "See more from #{@content.user.name}", @content.user %> <%= link_to "Create your own universe", root_url %> diff --git a/app/views/contributors/create.html.erb b/app/views/contributors/create.html.erb new file mode 100644 index 00000000..619c099b --- /dev/null +++ b/app/views/contributors/create.html.erb @@ -0,0 +1,2 @@ +

    Contributors#create

    +

    Find me in app/views/contributors/create.html.erb

    diff --git a/app/views/contributors/destroy.html.erb b/app/views/contributors/destroy.html.erb new file mode 100644 index 00000000..c05a483d --- /dev/null +++ b/app/views/contributors/destroy.html.erb @@ -0,0 +1,2 @@ +

    Contributors#destroy

    +

    Find me in app/views/contributors/destroy.html.erb

    diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 527970d6..8bf21c4f 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -6,13 +6,18 @@

    Personal information

    - <%= f.label :name %>
    - <%= f.text_field :name %> + <%= f.label 'Name (visible on your profile and forum posts)' %>
    + <%= f.text_field :name, autofocus: true %>
    - <%= f.label :email %>
    - <%= f.email_field :email, autofocus: true %> + <%= f.label 'Email (always completely private)' %>
    + <%= f.email_field :email %> +
    + +
    + <%= f.label 'Username (users can @mention you with your username on the forums)' %>
    + <%= f.text_field :username %>
    @@ -54,7 +59,7 @@
    <%= f.check_box :email_updates %> <%= f.label :email_updates do %> - If this box is checked, you will receive occasional updates by email about new Notebook.ai features. + I want to receive occasional updates by email about new Notebook.ai features. <% end %>
    diff --git a/app/views/emails/one_click_unsubscribe.html.erb b/app/views/emails/one_click_unsubscribe.html.erb index eca05f68..e69de29b 100644 --- a/app/views/emails/one_click_unsubscribe.html.erb +++ b/app/views/emails/one_click_unsubscribe.html.erb @@ -1,2 +0,0 @@ -

    Emails#one_click_unsubscribe

    -

    Find me in app/views/emails/one_click_unsubscribe.html.erb

    diff --git a/app/views/export/index.html.erb b/app/views/export/index.html.erb index 35e7b155..b93cf774 100644 --- a/app/views/export/index.html.erb +++ b/app/views/export/index.html.erb @@ -37,8 +37,10 @@
  • <%= link_to "religions.csv", religions_csv_path %>
  • <%= link_to "magics.csv", magics_csv_path %>
  • <%= link_to "languages.csv", languages_csv_path %>
  • -
  • <%= link_to "scenes.csv", scenes_csv_path %>
  • <%= link_to "groups.csv", groups_csv_path %>
  • +
  • <%= link_to "floras.csv", floras_csv_path %>
  • + +
  • <%= link_to "scenes.csv", scenes_csv_path %>
diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index c4582218..f0bfffc4 100644 --- a/app/views/layouts/_footer.html.erb +++ b/app/views/layouts/_footer.html.erb @@ -6,7 +6,7 @@ add
    - <% [Universe, Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene].reverse.each do |content_type| %> + <% [Universe, Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene, Flora].reverse.each do |content_type| %> <% next unless current_user.can_create?(content_type) %>
  • <%= link_to new_polymorphic_path(content_type), class: "btn-floating #{content_type.color}" do %> diff --git a/app/views/layouts/_navbar.html.erb b/app/views/layouts/_navbar.html.erb index 12bda913..910d7a57 100644 --- a/app/views/layouts/_navbar.html.erb +++ b/app/views/layouts/_navbar.html.erb @@ -19,7 +19,7 @@
  • - <%= link_to privacy_policy_path do %> + <%= link_to main_app.privacy_policy_path do %> verified_user Privacy policy <% end %> @@ -28,7 +28,7 @@
      -
    • <%= link_to 'Sign in', new_user_session_path %>
    • -
    • <%= link_to 'Sign up', new_user_registration_path %>
    • +
    • <%= link_to 'Sign in', main_app.new_user_session_path %>
    • +
    • <%= link_to 'Sign up', main_app.new_user_registration_path %>
    • <%= link_to 'Report a problem', 'https://docs.google.com/forms/d/e/1FAIpQLSe0jnqJlcPJDqwogGere5j8-8F1nSGGYkzbsI-XkOeMnGwLrA/viewform', target: '_new' %>
    • <%= link_to 'Request a feature', 'https://docs.google.com/forms/d/e/1FAIpQLScOhSMdDqWqJu9HGPQdmd0R9s90_AcSo0g6_Nc1qNjQIbSaXA/viewform', target: '_new' %>
    • <%= link_to 'Leave feedback', 'https://docs.google.com/forms/d/e/1FAIpQLScZWEVMgm8hBWIIVj1LPzo0GqflmWUrLQlc4TAYqsaS087oAA/viewform', target: '_new' %>
    • -
    • <%= link_to 'Privacy policy', privacy_policy_path %>
    • +
    • <%= link_to 'Privacy policy', main_app.privacy_policy_path %>
  • + + <%= Universe.icon %> + <% [Character, Location, Item].each do |content_class| %> <%= content_class.icon %> @@ -98,15 +101,15 @@
  • check - Upload 1GB of images + Upload 10GB of images
  • check - Create all 10 page types + Create all 12 page types
  • - <% [Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene].each_with_index do |content_class, i| %> - <%= '
    '.html_safe if i % 5 == 0 && i != 0 %> + <% [Universe, Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Flora, Scene].each_with_index do |content_class, i| %> + <%= '
    '.html_safe if i % 4 == 0 && i != 0 %> <%= content_class.icon %> @@ -145,15 +148,15 @@
  • check - Upload 1GB of images + Upload 10GB of images
  • check - Create all 10 page types + Create all 12 page types
  • - <% [Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene].each_with_index do |content_class, i| %> - <%= '
    '.html_safe if i % 5 == 0 && i != 0 %> + <% [Universe, Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Flora, Scene].each_with_index do |content_class, i| %> + <%= '
    '.html_safe if i % 4 == 0 && i != 0 %> <%= content_class.icon %> @@ -192,15 +195,15 @@
  • check - Upload 1GB of images + Upload 10GB of images
  • check - Create all 10 page types + Create all 12 page types
  • - <% [Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Scene].each_with_index do |content_class, i| %> - <%= '
    '.html_safe if i % 5 == 0 && i != 0 %> + <% [Universe, Character, Location, Item, Creature, Race, Religion, Group, Magic, Language, Flora, Scene].each_with_index do |content_class, i| %> + <%= '
    '.html_safe if i % 4 == 0 && i != 0 %> <%= content_class.icon %> @@ -246,27 +249,7 @@
    -
    -
    -
    -
    Notebook.ai Community Drive
    -
    -
    -

    - For a limited time, we're holding a <%= link_to 'Community Drive', friends_landing_path %> for referring friends - to Notebook.ai. With each friend you refer that signs up for a Premium subscription, you'll gain +100 MB storage space, - two more entries in our upcoming raffle, and two more <%= link_to 'feature votes', community_voting_path %> to decide what - Notebook.ai's first community-first feature will be. -


    -
    - <%= link_to 'See community progress', friends_landing_path, class: 'btn blue' %> -
    -
    -
    -
    -
    -
    -
    +
    Your referral code & stats
    @@ -282,7 +265,7 @@

      <% referrals = current_user.referrals %> - <% converted_referrals = referrals.map(&:referree).select { |user| [2, 3, 4, 5, 6].include? user.selected_billing_plan_id } %> + <% converted_referrals = referrals.map(&:referree).select(&:on_premium_plan?) %>
    • <%= pluralize referrals.count, 'user' %> <%= referrals.count == 1 ? 'has' : 'have' %> signed up with your referral code.
    • <% if converted_referrals.any? %>
    • @@ -291,14 +274,6 @@ <% else %>
    • None have signed up for Premium yet, however.
    • <% end %> -
    • You've earned <%= pluralize current_user.votes.count, 'feature vote' %> and have voted <%= pluralize current_user.votes.where.not(votable_id: nil).count, 'time' %>. - <% if current_user.votes.where(votable_id: nil).any? %> - <%= link_to 'Vote now!', community_voting_path %> - <% else %> - <%= link_to 'See votes so far.', community_voting_path %> - <% end %> -
    • -
    • You've earned <%= pluralize current_user.raffle_entries.count, 'entry' %> in <%= link_to "Notebook.ai's raffle", friends_landing_path %>, happening on June 15th, 2017.
    @@ -433,4 +408,4 @@
    -
    \ No newline at end of file +
    diff --git a/app/views/thredded/categories/_category.html.erb b/app/views/thredded/categories/_category.html.erb new file mode 100644 index 00000000..344c3a57 --- /dev/null +++ b/app/views/thredded/categories/_category.html.erb @@ -0,0 +1 @@ +
  • <%= category.name %>
  • diff --git a/app/views/thredded/error_pages/forbidden.html.erb b/app/views/thredded/error_pages/forbidden.html.erb new file mode 100644 index 00000000..ac554df3 --- /dev/null +++ b/app/views/thredded/error_pages/forbidden.html.erb @@ -0,0 +1,6 @@ +<% content_for :thredded_page_title, @message %> +<% content_for :thredded_page_id, 'thredded--error-forbidden' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> +<%= thredded_page do %> +

    <%= @message %>

    +<% end %> diff --git a/app/views/thredded/error_pages/not_found.html.erb b/app/views/thredded/error_pages/not_found.html.erb new file mode 100644 index 00000000..d745413a --- /dev/null +++ b/app/views/thredded/error_pages/not_found.html.erb @@ -0,0 +1,6 @@ +<% content_for :thredded_page_title, @message %> +<% content_for :thredded_page_id, 'thredded--error-not-found' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> +<%= thredded_page do %> +

    <%= @message %>

    +<% end %> diff --git a/app/views/thredded/kaminari/_first_page.html.erb b/app/views/thredded/kaminari/_first_page.html.erb new file mode 100644 index 00000000..2682d341 --- /dev/null +++ b/app/views/thredded/kaminari/_first_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "First" page + - available local variables + url: url to the first page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, :remote => remote %> + diff --git a/app/views/thredded/kaminari/_gap.html.erb b/app/views/thredded/kaminari/_gap.html.erb new file mode 100644 index 00000000..bbb0f983 --- /dev/null +++ b/app/views/thredded/kaminari/_gap.html.erb @@ -0,0 +1,8 @@ +<%# Non-link tag that stands for skipped pages... + - available local variables + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<%= t('views.pagination.truncate').html_safe %> diff --git a/app/views/thredded/kaminari/_last_page.html.erb b/app/views/thredded/kaminari/_last_page.html.erb new file mode 100644 index 00000000..53d61307 --- /dev/null +++ b/app/views/thredded/kaminari/_last_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Last" page + - available local variables + url: url to the last page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, :remote => remote %> + diff --git a/app/views/thredded/kaminari/_next_page.html.erb b/app/views/thredded/kaminari/_next_page.html.erb new file mode 100644 index 00000000..4fc20712 --- /dev/null +++ b/app/views/thredded/kaminari/_next_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Next" page + - available local variables + url: url to the next page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, :rel => 'next', :remote => remote %> + diff --git a/app/views/thredded/kaminari/_page.html.erb b/app/views/thredded/kaminari/_page.html.erb new file mode 100644 index 00000000..582af7bc --- /dev/null +++ b/app/views/thredded/kaminari/_page.html.erb @@ -0,0 +1,12 @@ +<%# Link showing page number + - available local variables + page: a page object for "this" page + url: url to this page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless page.current?, page, url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} %> + diff --git a/app/views/thredded/kaminari/_paginator.html.erb b/app/views/thredded/kaminari/_paginator.html.erb new file mode 100644 index 00000000..88451781 --- /dev/null +++ b/app/views/thredded/kaminari/_paginator.html.erb @@ -0,0 +1,23 @@ +<%# The container tag + - available local variables + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote + paginator: the paginator that renders the pagination tags inside +-%> +<%= paginator.render do -%> + +<% end -%> diff --git a/app/views/thredded/kaminari/_prev_page.html.erb b/app/views/thredded/kaminari/_prev_page.html.erb new file mode 100644 index 00000000..9c4aff49 --- /dev/null +++ b/app/views/thredded/kaminari/_prev_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Previous" page + - available local variables + url: url to the previous page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> + + <%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, :rel => 'prev', :remote => remote %> + diff --git a/app/views/thredded/messageboard_groups/new.html.erb b/app/views/thredded/messageboard_groups/new.html.erb new file mode 100644 index 00000000..ffad52ed --- /dev/null +++ b/app/views/thredded/messageboard_groups/new.html.erb @@ -0,0 +1,28 @@ +<% content_for :thredded_page_title, t('thredded.messageboard_group.create') %> +<% content_for :thredded_page_id, 'thredded--messageboard-groups-new' %> +<% content_for :thredded_breadcrumbs do %> +
      + +
    +<% end %> + +<%= thredded_page do %> +
    + <%= form_for @messageboard_group, html: { class: 'thredded--form' } do |f| %> +
      +
    • + <%= f.label :name %> + <%= f.text_field :name, required: true %> +
    • + +
    • + <%= f.submit t('thredded.messageboard_group.create'), + class: 'thredded--form--submit', + 'data-disable-with' => t('thredded.messageboard_group.form.create_btn_submitting') %> +
    • +
    + <% end %> +
    +<% end %> diff --git a/app/views/thredded/messageboards/_form.html.erb b/app/views/thredded/messageboards/_form.html.erb new file mode 100644 index 00000000..a50a9c34 --- /dev/null +++ b/app/views/thredded/messageboards/_form.html.erb @@ -0,0 +1,24 @@ +<%= form_for messageboard, html: { class: 'thredded--form' } do |f| %> +
      +
    • + <%= f.label t('thredded.messageboard.form.title_label') %> + <%= f.text_field :name, required: true %> +
    • +
    • + <%= f.label t('thredded.messageboard.form.description_label') %> + <%= f.text_field :description %> +
    • +
    • + <%= f.label t('thredded.messageboard.form.messageboard_group_id_label') %> + <%= f.collection_select :messageboard_group_id, Thredded::MessageboardGroup.all, :id, :name, + include_blank: t('thredded.messageboard.form.no_group') %> +
    • +
    • + <%= f.submit @messageboard.persisted? ? t('thredded.messageboard.update') : t('thredded.messageboard.create'), + class: 'thredded--form--submit', + 'data-disable-with' => @messageboard.persisted? ? + t('thredded.messageboard.form.update_btn_submitting') : + t('thredded.messageboard.form.create_btn_submitting') %> +
    • +
    +<% end %> diff --git a/app/views/thredded/messageboards/_messageboard.html.erb b/app/views/thredded/messageboards/_messageboard.html.erb new file mode 100644 index 00000000..c9335acd --- /dev/null +++ b/app/views/thredded/messageboards/_messageboard.html.erb @@ -0,0 +1,24 @@ +<% if policy(messageboard).read? %> + <%= view_hooks.messageboards_index.messageboard.render self, messageboard: messageboard do %> + <%= link_to messageboard_topics_path(messageboard), class: 'thredded--messageboard' do %> +
    +

    <%= messageboard.name %>

    +

    + <%= t 'thredded.messageboard.topics_and_posts_counts', + topics_count: number_with_delimiter(messageboard.topics_count), + posts_count: number_with_delimiter(messageboard.posts_count) %> +

    +
    + +

    <%= messageboard.description %>

    + + <% if messageboard.last_topic %> + + <% end %> + <% end %> + <% end %> +<% end %> diff --git a/app/views/thredded/messageboards/edit.html.erb b/app/views/thredded/messageboards/edit.html.erb new file mode 100644 index 00000000..27790eb4 --- /dev/null +++ b/app/views/thredded/messageboards/edit.html.erb @@ -0,0 +1,15 @@ +<% content_for :thredded_page_title, t('thredded.nav.edit_messageboard') %> +<% content_for :thredded_page_id, 'thredded--messageboard-edit' %> +<% content_for :thredded_breadcrumbs do %> +
      + +
    +<% end %> + +<%= thredded_page do %> +
    + <%= render 'form', messageboard: @messageboard %> +
    +<% end %> diff --git a/app/views/thredded/messageboards/index.html.erb b/app/views/thredded/messageboards/index.html.erb new file mode 100644 index 00000000..8267d616 --- /dev/null +++ b/app/views/thredded/messageboards/index.html.erb @@ -0,0 +1,34 @@ +<% content_for :thredded_page_title, t('thredded.messageboard.index.page_title') %> +<% content_for :thredded_page_id, 'thredded--messageboards-index' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> +<%= thredded_page do %> + <%= view_hooks.messageboards_index.container.render self, groups: @groups do %> +
    + <%= view_hooks.messageboards_index.list.render self, groups: @groups do %> + <% @groups.each do |group| %> + <% if group.name.present? %> +

    <%= group.name %>

    + <% end %> +
    + <%= view_hooks.messageboards_index.group.render self, group: group do %> + <%= render group.messageboards %> + <% end %> +
    + <% end %> + <% end %> + +
    + <% if policy(Thredded::Messageboard.new).create? %> + + <%= t('thredded.messageboard.create') %> + + <% end %> + <% if policy(Thredded::MessageboardGroup.new).create? %> + + <%= t('thredded.messageboard_group.create') %> + + <% end %> +
    +
    + <% end %> +<% end %> diff --git a/app/views/thredded/messageboards/new.html.erb b/app/views/thredded/messageboards/new.html.erb new file mode 100644 index 00000000..404a2dd7 --- /dev/null +++ b/app/views/thredded/messageboards/new.html.erb @@ -0,0 +1,15 @@ +<% content_for :thredded_page_title, t('thredded.messageboard.create') %> +<% content_for :thredded_page_id, 'thredded--messageboards-new' %> +<% content_for :thredded_breadcrumbs do %> +
      + +
    +<% end %> + +<%= thredded_page do %> +
    + <%= render 'form', messageboard: @messageboard %> +
    +<% end %> diff --git a/app/views/thredded/moderation/_nav.html.erb b/app/views/thredded/moderation/_nav.html.erb new file mode 100644 index 00000000..5bfebc29 --- /dev/null +++ b/app/views/thredded/moderation/_nav.html.erb @@ -0,0 +1,19 @@ +<% content_for :thredded_main_navigation do %> + +<% end %> diff --git a/app/views/thredded/moderation/_post.html.erb b/app/views/thredded/moderation/_post.html.erb new file mode 100644 index 00000000..4a91d80e --- /dev/null +++ b/app/views/thredded/moderation/_post.html.erb @@ -0,0 +1,19 @@ +<% post, content = post_and_content if local_assigns.key?(:post_and_content) %> +<%= content_tag :article, id: dom_id(post), class: 'thredded--post thredded--post-moderation' do %> + <%= render 'thredded/posts_common/actions', post: post %> + <%= render 'thredded/posts_common/header_with_user_and_topic', + post: post, + post_user_link: if post.user + link_to(post.user.thredded_display_name, user_moderation_path(post.user.id)) + else + content_tag :em, t('thredded.null_user_name') + end + %> + <%= content || render('thredded/posts/content', post: post) %> + <% if post.blocked? %> +

    + <%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %> +

    + <% end %> + <%= render 'post_moderation_actions', post: post %> +<% end %> diff --git a/app/views/thredded/moderation/_post_moderation_actions.html.erb b/app/views/thredded/moderation/_post_moderation_actions.html.erb new file mode 100644 index 00000000..0f3f5bee --- /dev/null +++ b/app/views/thredded/moderation/_post_moderation_actions.html.erb @@ -0,0 +1,12 @@ +
    + <% unless post.approved? %> + <%= button_to t('thredded.moderation.approve_btn'), moderate_post_path, + class: 'thredded--button', + params: { id: post.to_model.id, moderation_state: 'approved' } %> + <% end %> + <% unless post.blocked? %> + <%= button_to t('thredded.moderation.block_btn'), moderate_post_path, + class: 'thredded--button', + params: { id: post.to_model.id, moderation_state: 'blocked' } %> + <% end %> +
    diff --git a/app/views/thredded/moderation/_post_moderation_record.html.erb b/app/views/thredded/moderation/_post_moderation_record.html.erb new file mode 100644 index 00000000..1baf9bcf --- /dev/null +++ b/app/views/thredded/moderation/_post_moderation_record.html.erb @@ -0,0 +1,46 @@ +<% + record = post_moderation_record + post = record.post + if post + post_view = Thredded::PostView.new(post, policy(post)) + end + moderation_state_notice_args = { + moderator: user_link(record.moderator), + time_ago: time_ago(record.created_at) + } +%> +
    +
    +

    + <% if record.approved? %> + <%= t('thredded.moderation.post_approved_html', moderation_state_notice_args) %> + <% elsif record.blocked? %> + <%= t('thredded.moderation.post_blocked_html', moderation_state_notice_args) %> + <% end %> +

    + <% if post && post.content != record.post_content %> +

    + <%= t('thredded.moderation.posts_content_changed_since_moderation_html', post_url: post_permalink_path(post)) %> +

    + <% end %> + <%= content_tag :em, t('thredded.moderation.post_deleted_notice') unless post %> +
    +
    + <% post_user_link = capture do %> + <% if record.post_user %> + <%= link_to record.post_user.thredded_display_name, user_moderation_path(record.post_user.id) %> + <% else %> + <%= safe_join [record.post_user_name, content_tag(:em, t('thredded.null_user_name'))].compact, ', ' %> + <% end %> + <% end %> + <% if post %> + <%= render 'thredded/posts_common/header_with_user_and_topic', post: post_view, post_user_link: post_user_link %> + <% else %> +

    <%= post_user_link %>

    + <% end %> +
    + <%= Thredded::ContentFormatter.new(self).format_content(record.post_content) %> +
    +
    + <%= render 'post_moderation_actions', post: post if post %> +
    diff --git a/app/views/thredded/moderation/_user_moderation_state.html.erb b/app/views/thredded/moderation/_user_moderation_state.html.erb new file mode 100644 index 00000000..6ef07cce --- /dev/null +++ b/app/views/thredded/moderation/_user_moderation_state.html.erb @@ -0,0 +1,3 @@ +<% user_detail = user.thredded_user_detail %> +<%= user_detail.moderation_state.to_s.humanize %> +<%= time_ago user_detail.moderation_state_changed_at, default: '' %> diff --git a/app/views/thredded/moderation/_user_post.html.erb b/app/views/thredded/moderation/_user_post.html.erb new file mode 100644 index 00000000..7bfad672 --- /dev/null +++ b/app/views/thredded/moderation/_user_post.html.erb @@ -0,0 +1,12 @@ +<% post, content = post_and_content if local_assigns.key?(:post_and_content) %> +<%= content_tag :article, id: dom_id(post), class: 'thredded--post thredded--post-moderation' do %> + <%= render 'thredded/posts_common/actions', post: post %> + <%= render 'thredded/posts_common/header_with_topic', post: post %> + <%= content || render('thredded/posts/content', post: post) %> + <% if post.blocked? %> +

    + <%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %> +

    + <% end %> + <%= render 'post_moderation_actions', post: post %> +<% end %> diff --git a/app/views/thredded/moderation/_users_search_form.html.erb b/app/views/thredded/moderation/_users_search_form.html.erb new file mode 100644 index 00000000..1ee61172 --- /dev/null +++ b/app/views/thredded/moderation/_users_search_form.html.erb @@ -0,0 +1,13 @@ +<%= form_tag users_moderation_path, + method: 'get', + class: 'thredded--form thredded--navigation--search thredded--navigation--search--right', + 'data-thredded-turboform' => true do %> + <%= label_tag :q, t('thredded.moderation.search_users.form_label') %> + <%= text_field_tag :q, @query, + type: 'search', + required: true, + # If there are no results the user will likely want to change the query, so auto-focus. + autofocus: @query.presence && !@users.presence, + placeholder: t('thredded.moderation.search_users.form_placeholder') %> + +<% end %> diff --git a/app/views/thredded/moderation/activity.html.erb b/app/views/thredded/moderation/activity.html.erb new file mode 100644 index 00000000..083deade --- /dev/null +++ b/app/views/thredded/moderation/activity.html.erb @@ -0,0 +1,20 @@ +<% content_for :thredded_page_title, t('thredded.nav.moderation') %> +<% content_for :thredded_page_id, 'thredded--moderation-activity' %> +<%= render 'nav' %> + +<%= thredded_page do %> +
    +

    <%= t 'thredded.recent_activity' %>

    + <% if @last_moderated_record %> +
    + <%= render 'post_moderation_record', post_moderation_record: @last_moderated_record %> +
    + <% end %> + <% if @posts.present? %> + <%= render_posts @posts, + partial: 'thredded/moderation/post', + content_partial: 'thredded/posts/content' %> + <%= paginate @posts %> + <% end %> +
    +<% end %> diff --git a/app/views/thredded/moderation/history.html.erb b/app/views/thredded/moderation/history.html.erb new file mode 100644 index 00000000..2e678c98 --- /dev/null +++ b/app/views/thredded/moderation/history.html.erb @@ -0,0 +1,13 @@ +<% content_for :thredded_page_title, + safe_join([t('thredded.nav.moderation'), t('thredded.nav.moderation_history')], ': ') %> +<% content_for :thredded_page_id, 'thredded--moderation-history' %> +<%= render 'nav' %> + +<%= thredded_page do %> +
    + <% if @post_moderation_records.present? %> + <%= render partial: 'post_moderation_record', collection: @post_moderation_records %> + <%= paginate @post_moderation_records %> + <% end %> +
    +<% end %> diff --git a/app/views/thredded/moderation/pending.html.erb b/app/views/thredded/moderation/pending.html.erb new file mode 100644 index 00000000..8653715e --- /dev/null +++ b/app/views/thredded/moderation/pending.html.erb @@ -0,0 +1,24 @@ +<% content_for :thredded_page_title, t('thredded.nav.moderation') %> +<% content_for :thredded_page_id, 'thredded--pending-moderation' %> +<%= render 'nav' %> + +<%= thredded_page do %> +
    + <% if @last_moderated_record %> +
    + <%= render 'post_moderation_record', post_moderation_record: @last_moderated_record %> +
    + <% end %> + <% if @posts.present? %> + <%= render_posts @posts, + partial: 'thredded/moderation/post', + content_partial: 'thredded/posts/content' %> + <%= paginate @posts %> + <% else %> +
    +

    <%= t 'thredded.moderation.pending.empty.title' %>

    +

    <%= t 'thredded.moderation.pending.empty.content' %>

    +
    + <% end %> +
    +<% end %> diff --git a/app/views/thredded/moderation/user.html.erb b/app/views/thredded/moderation/user.html.erb new file mode 100644 index 00000000..d7b7c10d --- /dev/null +++ b/app/views/thredded/moderation/user.html.erb @@ -0,0 +1,54 @@ +<% + user = @user + user_detail = user.thredded_user_detail +%> +<% content_for :thredded_page_title, t('thredded.nav.moderation') %> +<% content_for :thredded_page_id, 'thredded--moderation-user' %> +<%= render 'nav' %> +<%= thredded_page do %> +
    + <%= view_hooks.moderation_user_page.user_title.render self, user: user do %> +

    + <%= image_tag Thredded.avatar_url.call(user), class: 'thredded--user--avatar' %><%= user.thredded_display_name %> +

    + <% end %> + <%= view_hooks.moderation_user_page.user_info.render self, user: user do %> +
      + <%= view_hooks.moderation_user_page.user_info_list_items.render self, user: user do %> +
    • <%= t 'thredded.users.user_since_html', time_ago: time_ago(user.created_at) %>
    • + <% if user_detail.last_seen_at %> +
    • <%= t 'thredded.users.last_active_html', time_ago: time_ago(user_detail.last_seen_at) %>
    • + <% end %> + <% if user_detail.topics_count > 0 %> +
    • <%= t 'thredded.users.started_topics_count', count: user_detail.topics_count %>
    • + <% end %> + <% if user_detail.posts_count > 0 %> +
    • <%= t 'thredded.users.posts_count', count: user_detail.posts_count %>
    • + <% end %> +
    • <%= render 'user_moderation_state', user: @user %>
    • + <% end %> +
    + <% end %> + <%= view_hooks.moderation_user_page.user_moderation_actions.render self, user: user do %> +
    + <% unless user_detail.approved? %> + <%= button_to t('thredded.moderation.approve_btn'), moderate_user_path, + class: 'thredded--button', + params: { id: user.to_model.id, moderation_state: 'approved' } %> + <% end %> + <% unless user_detail.blocked? %> + <%= button_to t('thredded.moderation.block_btn'), moderate_user_path, + class: 'thredded--button', + params: { id: user.to_model.id, moderation_state: 'blocked' } %> + <% end %> +
    + <% end %> + <% if @posts.present? %> +

    <%= t 'thredded.users.recent_activity' %>

    + <%= render_posts @posts, + partial: 'thredded/moderation/user_post', + content_partial: 'thredded/posts/content' %> + <%= paginate @posts %> + <% end %> +
    +<% end %> diff --git a/app/views/thredded/moderation/users.html.erb b/app/views/thredded/moderation/users.html.erb new file mode 100644 index 00000000..7ec691be --- /dev/null +++ b/app/views/thredded/moderation/users.html.erb @@ -0,0 +1,41 @@ +<% content_for :thredded_page_title, t('thredded.nav.moderation') %> +<% content_for :thredded_page_id, 'thredded--moderation-users' %> +<%= render 'nav' %> + +<%= thredded_page do %> +
    + <% if @users.present? %> + <% if @query.present? %> +

    + <%= t 'thredded.moderation.search_users.results_message', query: "'#{@query}'" %> +

    + <% end %> + + + + + + + + + + <% @users.each do |user| %> + + + + + <% end %> + +
    <%= page_entries_info @users %>
    UserModeration state
    + <%= link_to user.thredded_display_name, user_moderation_path(user.id), class: 'thredded--link' %> + + <%= render 'user_moderation_state', user: user %> +
    + <%= paginate @users %> + <% else %> +

    + <%= t 'thredded.moderation.search_users.no_results_message', query: "'#{@query}'" %> +

    + <% end %> +
    +<% end %> diff --git a/app/views/thredded/post_mailer/post_notification.html.erb b/app/views/thredded/post_mailer/post_notification.html.erb new file mode 100644 index 00000000..657f4b62 --- /dev/null +++ b/app/views/thredded/post_mailer/post_notification.html.erb @@ -0,0 +1,23 @@ + diff --git a/app/views/thredded/post_mailer/post_notification.text.erb b/app/views/thredded/post_mailer/post_notification.text.erb new file mode 100644 index 00000000..0a226443 --- /dev/null +++ b/app/views/thredded/post_mailer/post_notification.text.erb @@ -0,0 +1,10 @@ +<%= @post.content %> + +--- + +This email was sent to you because you are following this topic +"<%= @post.postable.title %>". Go here to view the conversation: +<%= post_permalink_url @post %> + +To unsubscribe from these emails, update your preferences here: + <%= edit_messageboard_preferences_url @post.messageboard %> diff --git a/app/views/thredded/post_previews/preview.html.erb b/app/views/thredded/post_previews/preview.html.erb new file mode 100644 index 00000000..fa2dc28d --- /dev/null +++ b/app/views/thredded/post_previews/preview.html.erb @@ -0,0 +1 @@ +<%= @post.filtered_content(self) %> diff --git a/app/views/thredded/post_previews/update.html.erb b/app/views/thredded/post_previews/update.html.erb new file mode 100644 index 00000000..fa2dc28d --- /dev/null +++ b/app/views/thredded/post_previews/update.html.erb @@ -0,0 +1 @@ +<%= @post.filtered_content(self) %> diff --git a/app/views/thredded/posts/_content.html.erb b/app/views/thredded/posts/_content.html.erb new file mode 100644 index 00000000..e7e97058 --- /dev/null +++ b/app/views/thredded/posts/_content.html.erb @@ -0,0 +1 @@ +<%= render 'thredded/posts_common/content', post: post, options: local_assigns[:options] || {} %> diff --git a/app/views/thredded/posts/_form.html.erb b/app/views/thredded/posts/_form.html.erb new file mode 100644 index 00000000..09f0d31d --- /dev/null +++ b/app/views/thredded/posts/_form.html.erb @@ -0,0 +1,5 @@ +<%= render 'thredded/posts_common/form', + post: post, + content_label: t('thredded.posts.form.content_label'), + button_text: button_text, + button_submitting_text: local_assigns.key?(:button_submitting_text) ? button_submitting_text : nil %> diff --git a/app/views/thredded/posts/_post.html.erb b/app/views/thredded/posts/_post.html.erb new file mode 100644 index 00000000..0bdf18a9 --- /dev/null +++ b/app/views/thredded/posts/_post.html.erb @@ -0,0 +1,13 @@ +<% post, content = post_and_content if local_assigns.key?(:post_and_content) %> +<%= content_tag :article, id: dom_id(post), class: "thredded--post thredded--#{post.read_state}--post" do %> + <%= render 'thredded/posts_common/actions', post: post, actions: local_assigns[:actions] %> + <%= render 'thredded/posts_common/header', post: post %> + <%= content || render('thredded/posts/content', post: post) %> + <% if post.pending_moderation? && !Thredded.content_visible_while_pending_moderation %> +

    <%= t 'thredded.posts.pending_moderation_notice' %>

    + <% elsif post.blocked? && post.can_moderate? %> +

    + <%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %> +

    + <% end %> +<% end %> diff --git a/app/views/thredded/posts/_user.html.erb b/app/views/thredded/posts/_user.html.erb new file mode 100644 index 00000000..b401c5f2 --- /dev/null +++ b/app/views/thredded/posts/_user.html.erb @@ -0,0 +1,3 @@ + + <%= post.user_posts_count %> posts + diff --git a/app/views/thredded/posts/edit.html.erb b/app/views/thredded/posts/edit.html.erb new file mode 100644 index 00000000..89c8e419 --- /dev/null +++ b/app/views/thredded/posts/edit.html.erb @@ -0,0 +1,16 @@ +<% content_for :thredded_page_title, t('thredded.nav.edit_post') %> +<% content_for :thredded_page_id, 'thredded--edit-post' %> +<% content_for :thredded_breadcrumbs do %> +
      +
    • <%= link_to t('thredded.nav.edit_post'), edit_post_path(@post_form.post) %>
    • +
    +<% end %> + +<%= thredded_page do %> +
    + <%= render 'thredded/posts/form', + post: @post_form, + button_text: t('thredded.posts.form.update_btn'), + button_submitting_text: t('thredded.posts.form.update_btn_submitting')%> +
    +<% end %> diff --git a/app/views/thredded/posts/new.html.erb b/app/views/thredded/posts/new.html.erb new file mode 100644 index 00000000..bcb533a1 --- /dev/null +++ b/app/views/thredded/posts/new.html.erb @@ -0,0 +1,15 @@ +<% content_for :thredded_page_title, @post_form.topic.title %> +<% content_for :thredded_page_id, 'thredded--topic--new-post' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> + +<%= thredded_page do %> +
    +
    +

    <%= t('thredded.posts.form.title_label') %>

    + <%= render 'thredded/posts/form', + post: @post_form, + button_text: t('thredded.posts.form.create_btn'), + button_submitting_text: t('thredded.posts.form.create_btn_submitting') %> +
    +
    +<% end %> diff --git a/app/views/thredded/posts_common/_actions.html.erb b/app/views/thredded/posts_common/_actions.html.erb new file mode 100644 index 00000000..98751421 --- /dev/null +++ b/app/views/thredded/posts_common/_actions.html.erb @@ -0,0 +1,28 @@ +<% actions ||= {} %> +<% actions_html = capture do %> + <%= view_hooks.post_common.actions.render self, post: post do %> + <% if actions[:quote] && post.can_reply? %> + <%= render 'thredded/posts_common/actions/quote', post: post %> + <% end %> + <% if post.can_update? %> + <%= render 'thredded/posts_common/actions/edit', post: post %> + <% end %> + <% if post.can_destroy? %> + <%= render 'thredded/posts_common/actions/delete', post: post %> + <% end %> + <% if post.read_state %> + <%= view_hooks.post_common.mark_as_unread.render self, post: post do %> + <%= render 'thredded/posts_common/actions/mark_as_unread', post: post %> + <% end %> + <% end %> + <% end %> +<% end %> + +<%- if actions_html.present? %> +
    + <%= inline_svg 'thredded/three-dot-menu.svg', class: 'thredded--post--dropdown--toggle' %> +
    + <%= actions_html %> +
    +
    +<% end %> diff --git a/app/views/thredded/posts_common/_content.html.erb b/app/views/thredded/posts_common/_content.html.erb new file mode 100644 index 00000000..fff36c61 --- /dev/null +++ b/app/views/thredded/posts_common/_content.html.erb @@ -0,0 +1,3 @@ +
    + <%= post.filtered_content(self, local_assigns[:options] || {}) %> +
    diff --git a/app/views/thredded/posts_common/_form.html.erb b/app/views/thredded/posts_common/_form.html.erb new file mode 100644 index 00000000..cae0fdf0 --- /dev/null +++ b/app/views/thredded/posts_common/_form.html.erb @@ -0,0 +1,22 @@ +<%# locals: post, content_label, button_text, button_submitting_text. %> +<%= form_for post, + url: post.submit_path, + as: :post, + html: { + class: 'thredded--form thredded--post-form', + 'data-thredded-post-form' => true, + 'data-autocomplete-url' => autocomplete_users_path, + 'data-autocomplete-min-length' => Thredded.autocomplete_min_length, + } do |form| %> +
      + <%= render 'thredded/posts_common/form/content', + form: form, content_label: content_label, preview_url: post.preview_path %> +
    • + <% button_submitting_text ||= + post.persisted? ? t('thredded.form.update_btn_submitting') : t('thredded.form.create_btn_submitting') %> + +
    • +
    +<% end %> diff --git a/app/views/thredded/posts_common/_header.html.erb b/app/views/thredded/posts_common/_header.html.erb new file mode 100644 index 00000000..67cbc0eb --- /dev/null +++ b/app/views/thredded/posts_common/_header.html.erb @@ -0,0 +1,8 @@ +<%# @param post [Thredded::PostView] %> +
    + <%= image_tag post.avatar_url, class: 'thredded--post--avatar' if post.user %> +

    <%= user_link post.user %>

    + + <%= time_ago post.created_at %> + +
    diff --git a/app/views/thredded/posts_common/_header_with_topic.html.erb b/app/views/thredded/posts_common/_header_with_topic.html.erb new file mode 100644 index 00000000..f1b0f781 --- /dev/null +++ b/app/views/thredded/posts_common/_header_with_topic.html.erb @@ -0,0 +1,15 @@ +<%# @param post [Thredded::PostView] %> +<% topic = post.to_model.postable %> +
    +

    + <%= + topic_link = link_to(topic.title, post.permalink_path) + if topic.first_post == post.to_model + t 'thredded.users.started_topic_html', topic_link: topic_link + else + t 'thredded.users.posted_in_topic_html', topic_link: topic_link + end + %> +

    +

    <%= time_ago post.created_at %>

    +
    diff --git a/app/views/thredded/posts_common/_header_with_user_and_topic.html.erb b/app/views/thredded/posts_common/_header_with_user_and_topic.html.erb new file mode 100644 index 00000000..2077a409 --- /dev/null +++ b/app/views/thredded/posts_common/_header_with_user_and_topic.html.erb @@ -0,0 +1,18 @@ +<%# @param post [Thredded::PostView] %> +<%# @param post_user_link [String] optional %> +<% topic = post.to_model.postable %> +<% post_user_link ||= user_link(post.user) %> +
    + <%= image_tag post.avatar_url, class: 'thredded--post--avatar' if post.user %> +

    + <%= + topic_link = link_to(topic.title, post.permalink_path) + if topic.first_post == post.to_model + t 'thredded.users.user_started_topic_html', user_link: post_user_link, topic_link: topic_link + else + t 'thredded.users.user_posted_in_topic_html', user_link: post_user_link, topic_link: topic_link + end + %> +

    +

    <%= time_ago post.created_at %>

    +
    diff --git a/app/views/thredded/posts_common/actions/_delete.html.erb b/app/views/thredded/posts_common/actions/_delete.html.erb new file mode 100644 index 00000000..06b646f5 --- /dev/null +++ b/app/views/thredded/posts_common/actions/_delete.html.erb @@ -0,0 +1,4 @@ +<%= button_to t('thredded.posts.delete'), post.destroy_path, + method: :delete, + class: 'thredded--post--delete thredded--post--dropdown--actions--item', + data: {confirm: I18n.t('thredded.posts.delete_confirm')} %> diff --git a/app/views/thredded/posts_common/actions/_edit.html.erb b/app/views/thredded/posts_common/actions/_edit.html.erb new file mode 100644 index 00000000..98b1eee7 --- /dev/null +++ b/app/views/thredded/posts_common/actions/_edit.html.erb @@ -0,0 +1,3 @@ +<%= link_to t('thredded.posts.edit'), post.edit_path, + class: 'thredded--post--edit thredded--post--dropdown--actions--item', + rel: 'nofollow' %> diff --git a/app/views/thredded/posts_common/actions/_mark_as_unread.html.erb b/app/views/thredded/posts_common/actions/_mark_as_unread.html.erb new file mode 100644 index 00000000..413c53a1 --- /dev/null +++ b/app/views/thredded/posts_common/actions/_mark_as_unread.html.erb @@ -0,0 +1,2 @@ +<%= button_to(t('thredded.topics.mark_as_unread'), post.mark_unread_path, method: :post, class: + 'thredded--post--mark-as-unread thredded--post--dropdown--actions--item') %> diff --git a/app/views/thredded/posts_common/actions/_quote.html.erb b/app/views/thredded/posts_common/actions/_quote.html.erb new file mode 100644 index 00000000..3644b39a --- /dev/null +++ b/app/views/thredded/posts_common/actions/_quote.html.erb @@ -0,0 +1,4 @@ +<%= link_to t('thredded.posts.quote_btn'), url_for(post.quote_url_params), + 'data-thredded-quote-post' => post.quote_path, + class: 'thredded--post--quote thredded--post--dropdown--actions--item', + rel: 'nofollow' %> diff --git a/app/controllers/concerns/.keep b/app/views/thredded/posts_common/form/_after_content.html.erb similarity index 100% rename from app/controllers/concerns/.keep rename to app/views/thredded/posts_common/form/_after_content.html.erb diff --git a/app/views/thredded/posts_common/form/_before_content.html.erb b/app/views/thredded/posts_common/form/_before_content.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/thredded/posts_common/form/_content.html.erb b/app/views/thredded/posts_common/form/_content.html.erb new file mode 100644 index 00000000..dbc86392 --- /dev/null +++ b/app/views/thredded/posts_common/form/_content.html.erb @@ -0,0 +1,7 @@ +<%= render 'thredded/posts_common/form/content_field', + form: form, + content_label: content_label %> + +<%= render 'thredded/posts_common/form/preview_area', + form: form, + preview_url: preview_url %> diff --git a/app/views/thredded/posts_common/form/_content_field.html.erb b/app/views/thredded/posts_common/form/_content_field.html.erb new file mode 100644 index 00000000..9d7b0dfb --- /dev/null +++ b/app/views/thredded/posts_common/form/_content_field.html.erb @@ -0,0 +1,8 @@ +
  • + <%= form.label :content, content_label %> + <%= view_hooks.post_form.content_text_area.render self, form: form, content_label: content_label do %> + <%= render 'thredded/posts_common/form/before_content', form: form %> + <%= form.text_area :content, {rows: 5, required: true} %> + <%= render 'thredded/posts_common/form/after_content', form: form %> + <% end %> +
  • diff --git a/app/views/thredded/posts_common/form/_preview_area.html.erb b/app/views/thredded/posts_common/form/_preview_area.html.erb new file mode 100644 index 00000000..9f0a23e6 --- /dev/null +++ b/app/views/thredded/posts_common/form/_preview_area.html.erb @@ -0,0 +1,16 @@ +<%= view_hooks.post_form.preview_area.render self, form: form, preview_url: preview_url do %> +
  • + + +
    +

    <%= t 'thredded.form.preview' %>

    +
    +
    +
  • +<% end %> diff --git a/app/views/thredded/preferences/_form.html.erb b/app/views/thredded/preferences/_form.html.erb new file mode 100644 index 00000000..e50b78fb --- /dev/null +++ b/app/views/thredded/preferences/_form.html.erb @@ -0,0 +1,95 @@ +<%# @type preferences [Thredded::UserPreferencesForm] %> +<%= form_for(preferences, method: :patch, url: preferences.update_path, html: { + class: 'thredded--form thredded--notification-preferences-form', + 'data-thredded-user-preferences-form' => true +}) do |f| %> +
      +
    • + <%= f.label :auto_follow_topics do %> + <%= f.check_box :auto_follow_topics, + 'data-thredded-update-checkbox-on-change' => + 'user_preferences_form[messageboard_auto_follow_topics]' %> + <%= t 'thredded.preferences.form.auto_follow_topics.label' %> +

      + <%= t 'thredded.preferences.form.auto_follow_topics.hint' %> +

      + <% end %> +
    • +
    • + <%= f.label :follow_topics_on_mention do %> + <%= f.check_box :follow_topics_on_mention, 'data-thredded-bound-messageboard-pref' => 'user_preferences_form[messageboard_follow_topics_on_mention]' %> + <%= t 'thredded.preferences.form.follow_topics_on_mention.label' %> +

      + <%= t 'thredded.preferences.form.follow_topics_on_mention.hint' %> +

      + <% end %> +
    • + <% if Thredded.notifiers.present? %> +
    • + + <%= f.fields_for :notifications_for_followed_topics, preferences.notifications_for_followed_topics do |fn| %> + <%= fn.label :enabled do %> + <%= fn.hidden_field :notifier_key %> + <%= fn.check_box :enabled, + 'data-thredded-bound-messageboard-pref' => + "user_preferences_form[messageboard_notifications_for_followed_topics_attributes][#{fn.index}][enabled]" %> + <%= fn.object.notifier_human_name %> + <%- end %> + <%- end %> +
    • +
    • + + <%= f.fields_for :notifications_for_private_topics, preferences.notifications_for_private_topics do |fn| %> + <%= fn.label :enabled do %> + <%= fn.hidden_field :notifier_key %> + <%= fn.check_box :enabled %> + <%= fn.object.notifier_human_name %> + <% end %> + <% end %> +
    • + <% end %> +
    + <% if preferences.messageboard %> +

    + <%= t 'thredded.preferences.messageboard_preferences_title_html', messageboard: messageboard.name %> +

    +
      +
    • + <%= f.label :messageboard_auto_follow_topics do %> + <%= f.check_box :messageboard_auto_follow_topics %> + <%= t 'thredded.preferences.form.messageboard_auto_follow_topics.label' %> +

      + <%= t 'thredded.preferences.form.messageboard_auto_follow_topics.hint' %> +

      + <% end %> +
    • +
    • + <%= f.label :messageboard_follow_topics_on_mention do %> + <%= f.check_box :messageboard_follow_topics_on_mention %> + <%= t 'thredded.preferences.form.messageboard_follow_topics_on_mention.label' %> +

      + <%= t 'thredded.preferences.form.messageboard_follow_topics_on_mention.hint' %> +

      + <% end %> +
    • + <% if Thredded.notifiers.present? %> +
    • + + <%= f.fields_for :messageboard_notifications_for_followed_topics, + preferences.messageboard_notifications_for_followed_topics do |fn| %> + <%= fn.label :enabled do %> + <%= fn.hidden_field :notifier_key %> + <%= fn.hidden_field :messageboard_id %> + <%= fn.check_box :enabled %> + <%= fn.object.notifier_human_name %> + <% end %> + <%- end %> +
    • + <% end %> +
    + <% end %> + +<% end %> diff --git a/app/views/thredded/preferences/_messageboards_nav.html.erb b/app/views/thredded/preferences/_messageboards_nav.html.erb new file mode 100644 index 00000000..953012ef --- /dev/null +++ b/app/views/thredded/preferences/_messageboards_nav.html.erb @@ -0,0 +1,8 @@ +<% preferences.messageboard_groups.each do |group| %> +

    <%= group.name %>

    +
    + <%= render partial: 'thredded/preferences/messageboards_nav_item', + collection: group.messageboards, + as: :messageboard %> +
    +<% end %> diff --git a/app/views/thredded/preferences/_messageboards_nav_item.html.erb b/app/views/thredded/preferences/_messageboards_nav_item.html.erb new file mode 100644 index 00000000..6deb96b3 --- /dev/null +++ b/app/views/thredded/preferences/_messageboards_nav_item.html.erb @@ -0,0 +1,2 @@ +<%= link_to messageboard.name, edit_messageboard_preferences_path(messageboard), + class: 'thredded--preferences--messageboards-nav--item thredded--messageboard' %> diff --git a/app/views/thredded/preferences/edit.html.erb b/app/views/thredded/preferences/edit.html.erb new file mode 100644 index 00000000..5388b824 --- /dev/null +++ b/app/views/thredded/preferences/edit.html.erb @@ -0,0 +1,20 @@ +<% content_for :thredded_page_title, t('thredded.preferences.edit.page_title') %> +<% content_for :thredded_page_id, 'thredded--preferences' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> + +<%= thredded_page do %> +
    +
    +

    <%= t 'thredded.preferences.global_preferences_title' %>

    + <%= render 'thredded/preferences/form', preferences: @preferences %> +
    + <% unless preferences.messageboard %> +
    +

    + <%= t 'thredded.preferences.messageboard_preferences_nav_title' %> +

    + <%= render 'thredded/preferences/messageboards_nav', preferences: @preferences %> +
    + <% end %> +
    +<% end %> diff --git a/app/views/thredded/private_post_previews/preview.html.erb b/app/views/thredded/private_post_previews/preview.html.erb new file mode 100644 index 00000000..4bc2d273 --- /dev/null +++ b/app/views/thredded/private_post_previews/preview.html.erb @@ -0,0 +1 @@ +<%= @private_post.filtered_content(self) %> diff --git a/app/views/thredded/private_post_previews/update.html.erb b/app/views/thredded/private_post_previews/update.html.erb new file mode 100644 index 00000000..4bc2d273 --- /dev/null +++ b/app/views/thredded/private_post_previews/update.html.erb @@ -0,0 +1 @@ +<%= @private_post.filtered_content(self) %> diff --git a/app/views/thredded/private_posts/_content.html.erb b/app/views/thredded/private_posts/_content.html.erb new file mode 100644 index 00000000..e7e97058 --- /dev/null +++ b/app/views/thredded/private_posts/_content.html.erb @@ -0,0 +1 @@ +<%= render 'thredded/posts_common/content', post: post, options: local_assigns[:options] || {} %> diff --git a/app/views/thredded/private_posts/_form.html.erb b/app/views/thredded/private_posts/_form.html.erb new file mode 100644 index 00000000..1f2fbf8c --- /dev/null +++ b/app/views/thredded/private_posts/_form.html.erb @@ -0,0 +1,6 @@ +<%# TODO: For private topics, only autocomplete users in this private topic %> +<%= render 'thredded/posts_common/form', + post: post, + button_text: t('thredded.private_posts.form.create_btn'), + button_submitting_text: t('thredded.private_posts.form.create_btn_submitting'), + content_label: t('thredded.private_posts.form.content_label') %> diff --git a/app/views/thredded/private_posts/_private_post.html.erb b/app/views/thredded/private_posts/_private_post.html.erb new file mode 100644 index 00000000..6595cb36 --- /dev/null +++ b/app/views/thredded/private_posts/_private_post.html.erb @@ -0,0 +1,6 @@ +<% private_post, content = post_and_content if local_assigns.key?(:post_and_content) %> +<%= content_tag :article, id: dom_id(private_post), class: 'thredded--post' do %> + <%= render 'thredded/posts_common/actions', post: private_post, actions: local_assigns[:actions] %> + <%= render 'thredded/posts_common/header', post: private_post %> + <%= content || render('thredded/private_posts/content', post: post) %> +<% end %> diff --git a/app/views/thredded/private_posts/edit.html.erb b/app/views/thredded/private_posts/edit.html.erb new file mode 100644 index 00000000..89c8e419 --- /dev/null +++ b/app/views/thredded/private_posts/edit.html.erb @@ -0,0 +1,16 @@ +<% content_for :thredded_page_title, t('thredded.nav.edit_post') %> +<% content_for :thredded_page_id, 'thredded--edit-post' %> +<% content_for :thredded_breadcrumbs do %> +
      +
    • <%= link_to t('thredded.nav.edit_post'), edit_post_path(@post_form.post) %>
    • +
    +<% end %> + +<%= thredded_page do %> +
    + <%= render 'thredded/posts/form', + post: @post_form, + button_text: t('thredded.posts.form.update_btn'), + button_submitting_text: t('thredded.posts.form.update_btn_submitting')%> +
    +<% end %> diff --git a/app/views/thredded/private_posts/new.html.erb b/app/views/thredded/private_posts/new.html.erb new file mode 100644 index 00000000..22e47e4e --- /dev/null +++ b/app/views/thredded/private_posts/new.html.erb @@ -0,0 +1,11 @@ +<% private_topic = @post_form.topic %> +<% content_for :thredded_page_title, private_topic.title %> +<% content_for :thredded_page_id, 'thredded--private-topic--new_post' %> +<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %> + +<%= thredded_page do %> +
    + <%= render 'thredded/private_posts/form', + post: @post_form %> +
    +<% end %> diff --git a/app/views/thredded/private_topic_mailer/message_notification.html.erb b/app/views/thredded/private_topic_mailer/message_notification.html.erb new file mode 100644 index 00000000..bd43d36c --- /dev/null +++ b/app/views/thredded/private_topic_mailer/message_notification.html.erb @@ -0,0 +1,20 @@ + diff --git a/app/views/thredded/private_topic_mailer/message_notification.text.erb b/app/views/thredded/private_topic_mailer/message_notification.text.erb new file mode 100644 index 00000000..1aeb6959 --- /dev/null +++ b/app/views/thredded/private_topic_mailer/message_notification.text.erb @@ -0,0 +1,12 @@ +<%= @post.user.thredded_display_name %> said: +<%= @post.content %> +--- + +This email was sent to you because <%= @topic.user.thredded_display_name %> +included you in the private topic "<%= @topic.title %>". + +Go here to view the conversation: + <%= private_post_permalink_url(@post) %> + +To unsubscribe from these emails, update your preferences here: + <%= edit_preferences_url %> diff --git a/app/views/thredded/private_topic_previews/preview.html.erb b/app/views/thredded/private_topic_previews/preview.html.erb new file mode 100644 index 00000000..4bc2d273 --- /dev/null +++ b/app/views/thredded/private_topic_previews/preview.html.erb @@ -0,0 +1 @@ +<%= @private_post.filtered_content(self) %> diff --git a/app/views/thredded/private_topics/_breadcrumbs.html.erb b/app/views/thredded/private_topics/_breadcrumbs.html.erb new file mode 100644 index 00000000..9475c271 --- /dev/null +++ b/app/views/thredded/private_topics/_breadcrumbs.html.erb @@ -0,0 +1,4 @@ +
      +
    • <%= link_to t('thredded.nav.all_messageboards'), messageboards_path -%>
    • +
    • <%= link_to t('thredded.nav.private_topics'), private_topics_path -%>
    • +
    diff --git a/app/views/thredded/private_topics/_form.html.erb b/app/views/thredded/private_topics/_form.html.erb new file mode 100644 index 00000000..59713acb --- /dev/null +++ b/app/views/thredded/private_topics/_form.html.erb @@ -0,0 +1,39 @@ +<%= form_for private_topic, + url: private_topic.submit_path, + html: { + class: "thredded--form thredded--new-private-topic-form #{local_assigns[:css_class]}", + 'data-thredded-topic-form' => true, + # TODO: only autocomplete users in this private topic + 'data-autocomplete-url' => autocomplete_users_path, + 'data-autocomplete-min-length' => Thredded.autocomplete_min_length, + } do |form| %> + +
      +
    • + <%= form.label :title, t('thredded.private_topics.form.title_label') %> + <%= form.text_field :title, placeholder: placeholder, required: true %> +
    • +
    • + <%= form.label :user_names, t('thredded.private_topics.form.users_label') %> + <%= form.text_area :user_names, + placeholder: t('thredded.private_topics.form.users_placeholder'), + 'data-thredded-users-select' => true, + 'data-autocomplete-url' => autocomplete_users_path, + 'data-autocomplete-min-length' => Thredded.autocomplete_min_length, + rows: 1 %> +
    • + + <%= render 'thredded/posts_common/form/content', + form: form, + content_label: t('thredded.private_topics.form.content_label'), + preview_url: private_topic.preview_path %> + +
    • + +
    • +
    +<% end %> + diff --git a/app/views/thredded/private_topics/_header.html.erb b/app/views/thredded/private_topics/_header.html.erb new file mode 100644 index 00000000..9fe042cd --- /dev/null +++ b/app/views/thredded/private_topics/_header.html.erb @@ -0,0 +1,17 @@ +
    +

    <%= topic.title %>

    + + <%= t 'thredded.topics.started_by_html', + time_ago: time_ago(topic.created_at), + user: user_link(topic.user) %> + + + <%= render partial: 'thredded/private_topics/header/participant', + collection: (topic.users - [topic.user]) %> + + <% if topic.can_update? %> + <%= link_to t('thredded.private_topics.edit'), topic.edit_path, + class: 'thredded--topic-header--edit-topic', + rel: 'nofollow' %> + <% end %> +
    diff --git a/app/views/thredded/private_topics/_no_private_topics.html.erb b/app/views/thredded/private_topics/_no_private_topics.html.erb new file mode 100644 index 00000000..8c1eb892 --- /dev/null +++ b/app/views/thredded/private_topics/_no_private_topics.html.erb @@ -0,0 +1,6 @@ +
    +

    <%= t 'thredded.private_topics.no_private_topics.title' %>

    + + <%= t 'thredded.private_topics.no_private_topics.create_btn' %> + +
    diff --git a/app/views/thredded/private_topics/_private_topic.html.erb b/app/views/thredded/private_topics/_private_topic.html.erb new file mode 100644 index 00000000..5c4938ab --- /dev/null +++ b/app/views/thredded/private_topics/_private_topic.html.erb @@ -0,0 +1,23 @@ +<%= content_tag :article, + id: dom_id(private_topic), + class: ['thredded--topics--topic', *topic_css_classes(private_topic)] do %> +
    <%= private_topic.posts_count %>
    + +

    + <%= link_to private_topic.title, private_topic.path %> +

    + + + <%= time_ago private_topic.last_post_at %> + + <%= render partial: 'thredded/private_topics/private_topic/participant', + collection: [private_topic.last_user, *(private_topic.users - [private_topic.last_user])] %> + + + + + <%= time_ago private_topic.created_at %> + <%= user_link private_topic.user %> + +<% end %> + diff --git a/app/views/thredded/private_topics/edit.html.erb b/app/views/thredded/private_topics/edit.html.erb new file mode 100644 index 00000000..89e7a3ce --- /dev/null +++ b/app/views/thredded/private_topics/edit.html.erb @@ -0,0 +1,35 @@ +<% content_for :thredded_page_title, t('thredded.nav.edit_private_topic') %> +<% content_for :thredded_page_id, 'thredded--edit-private-topic' %> +<% content_for :thredded_breadcrumbs do %> +
      +
    • <%= link_to t('thredded.nav.all_messageboards'), messageboards_path %>
    • +
    • <%= link_to t('thredded.nav.private_topics'), private_topics_path %>
    • +
    • <%= link_to @private_topic.title, topic_path(@private_topic) %>
    • +
    • <%= link_to t('thredded.nav.edit_private_topic'), edit_private_topic_path(@private_topic) %>
    • +
    +<% end %> + + +<%= thredded_page do %> +
    + <%= form_for @private_topic, + url: private_topic_path(@private_topic), + html: { class: 'thredded--form', 'data-thredded-topic-form' => true } do |form| %> +
      +
    • + <%= form.label :title, t('thredded.private_topics.form.title_label') %> + <%= form.text_field :title, + placeholder: t('thredded.private_topics.form.title_placeholder_new'), + autofocus: true, + required: true %> +
    • +
    • + +
    • +
    + <% end %> +
    +<% end %> diff --git a/app/views/thredded/private_topics/header/_participant.html.erb b/app/views/thredded/private_topics/header/_participant.html.erb new file mode 100644 index 00000000..cc8d4f26 --- /dev/null +++ b/app/views/thredded/private_topics/header/_participant.html.erb @@ -0,0 +1 @@ +<%= user_link participant %> diff --git a/app/views/thredded/private_topics/index.html.erb b/app/views/thredded/private_topics/index.html.erb new file mode 100644 index 00000000..6f8a926d --- /dev/null +++ b/app/views/thredded/private_topics/index.html.erb @@ -0,0 +1,32 @@ +<% content_for :thredded_page_title, t('thredded.nav.private_topics') %> +<% content_for :thredded_page_id, 'thredded--private-topics-index' %> +<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %> + +<%= thredded_page do %> + <%= content_tag :section, + class: 'thredded--main-section thredded--private-topics', + 'data-thredded-topics' => true do %> + + <% if @private_topics.empty? -%> + <%= render 'thredded/private_topics/no_private_topics' %> + <% else -%> + <%= render 'thredded/private_topics/form', + private_topic: @new_private_topic, + css_class: 'thredded--is-compact', + placeholder: t('thredded.private_topics.form.title_placeholder_start') if @new_private_topic %> + + <%= render @private_topics %> + + <% unless unread_private_topics_count.zero? %> + <%= link_to t('thredded.nav.mark_all_read'), + mark_all_private_topics_read_path, + class: 'thredded--button thredded--button-light thredded--button-wide', + method: :put -%> + <% end %> + <% end -%> + <% end %> + +
    + <%= paginate @private_topics %> +
    +<% end %> diff --git a/app/views/thredded/private_topics/new.html.erb b/app/views/thredded/private_topics/new.html.erb new file mode 100644 index 00000000..89c935ce --- /dev/null +++ b/app/views/thredded/private_topics/new.html.erb @@ -0,0 +1,11 @@ +<% content_for :thredded_page_title, 'Create a New Private Topic' %> +<% content_for :thredded_page_id, 'thredded--new-private-topic' %> +<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %> + +<%= thredded_page do %> +
    + <%= render 'thredded/private_topics/form', + private_topic: @private_topic, + placeholder: t('thredded.private_topics.form.title_placeholder_new') if @private_topic %> +
    +<% end %> diff --git a/app/views/thredded/private_topics/private_topic/_participant.html.erb b/app/views/thredded/private_topics/private_topic/_participant.html.erb new file mode 100644 index 00000000..14624fe4 --- /dev/null +++ b/app/views/thredded/private_topics/private_topic/_participant.html.erb @@ -0,0 +1 @@ +<%= user_link participant %> diff --git a/app/views/thredded/private_topics/show.html.erb b/app/views/thredded/private_topics/show.html.erb new file mode 100644 index 00000000..8a2309de --- /dev/null +++ b/app/views/thredded/private_topics/show.html.erb @@ -0,0 +1,28 @@ +<% private_topic = @posts.topic %> +<% content_for :thredded_page_title, private_topic.title %> +<% content_for :thredded_page_id, 'thredded--private-topic-show' %> +<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %> + +<%= thredded_page do %> + <%= content_tag :section, + id: dom_id(private_topic), + class: ['thredded--main-section', 'thredded--topic', *topic_css_classes(private_topic)] do %> + <%= render 'thredded/private_topics/header', topic: private_topic %> + <%= view_hooks.posts_common.pagination_top.render(self, posts: @posts) do %> +
    <%= paginate @posts %>
    + <% end %> + <%= render_posts @posts, + partial: 'thredded/private_posts/private_post', + content_partial: 'thredded/private_posts/content', + locals: { actions: { quote: true } } %> + <%= view_hooks.posts_common.pagination_bottom.render(self, posts: @posts) do %> +
    <%= paginate @posts %>
    + <% end %> + + <% if policy(@new_post.post).create? %> + <%= render 'thredded/private_posts/form', + topic: private_topic, + post: @new_post %> + <% end %> + <% end %> +<% end %> diff --git a/app/views/thredded/search/_form.html.erb b/app/views/thredded/search/_form.html.erb new file mode 100644 index 00000000..a35db94e --- /dev/null +++ b/app/views/thredded/search/_form.html.erb @@ -0,0 +1,13 @@ +<%= form_tag search_path(messageboard), + method: 'get', + class: 'thredded--form thredded--navigation--search thredded--navigation--search-topics', + 'data-thredded-turboform' => true do %> + <%= label_tag :q, t('thredded.search.form.label') %> + <%= text_field_tag :q, @query, + type: 'search', + required: true, + # If there are no results the user will likely want to change the query, so auto-focus. + autofocus: @query.presence && !@topics.presence, + placeholder: t('thredded.search.form.placeholder') %> + +<% end %> diff --git a/app/views/thredded/shared/_breadcrumbs.html.erb b/app/views/thredded/shared/_breadcrumbs.html.erb new file mode 100644 index 00000000..a39bc580 --- /dev/null +++ b/app/views/thredded/shared/_breadcrumbs.html.erb @@ -0,0 +1,6 @@ +
      +
    • <%= link_to t('thredded.nav.all_messageboards'), messageboards_path %>
    • + <% if messageboard_or_nil.try(:persisted?) %> +
    • <%= link_to messageboard.name, messageboard_topics_path(messageboard) %>
    • + <% end %> +
    diff --git a/app/views/thredded/shared/_content_moderation_blocked_state.html.erb b/app/views/thredded/shared/_content_moderation_blocked_state.html.erb new file mode 100644 index 00000000..7bae4425 --- /dev/null +++ b/app/views/thredded/shared/_content_moderation_blocked_state.html.erb @@ -0,0 +1,8 @@ +<% # @param moderation_record [Thredded::PostModerationRecord, nil] %> +<% if moderation_record %> + <%= t 'thredded.content_moderation_states.content_blocked_notice_with_record_html', + moderator: user_link(moderation_record.moderator), + time_ago: time_ago(moderation_record.created_at) %> +<% else %> + <%= t 'thredded.content_moderation_states.content_blocked_notice' %> +<% end %> diff --git a/app/views/thredded/shared/_currently_online.html.erb b/app/views/thredded/shared/_currently_online.html.erb new file mode 100644 index 00000000..80069fc2 --- /dev/null +++ b/app/views/thredded/shared/_currently_online.html.erb @@ -0,0 +1,18 @@ +<% if (users = active_users).present? %> + +<% end %> diff --git a/app/views/thredded/shared/_flash_messages.html.erb b/app/views/thredded/shared/_flash_messages.html.erb new file mode 100644 index 00000000..09dbd2bc --- /dev/null +++ b/app/views/thredded/shared/_flash_messages.html.erb @@ -0,0 +1,7 @@ +<% [:success, :error, :notice, :alert].each do |key| %> + <% if flash[key] %> +
    + <%= flash[key] %> +
    + <% end %> +<% end %> diff --git a/app/views/thredded/shared/_header.html.erb b/app/views/thredded/shared/_header.html.erb new file mode 100644 index 00000000..cee228a6 --- /dev/null +++ b/app/views/thredded/shared/_header.html.erb @@ -0,0 +1,3 @@ +
    + <%= render 'thredded/shared/nav' %> +
    diff --git a/app/views/thredded/shared/_nav.html.erb b/app/views/thredded/shared/_nav.html.erb new file mode 100644 index 00000000..3ae70124 --- /dev/null +++ b/app/views/thredded/shared/_nav.html.erb @@ -0,0 +1,17 @@ + diff --git a/app/views/thredded/shared/_page.html.erb b/app/views/thredded/shared/_page.html.erb new file mode 100644 index 00000000..d9f5a7e6 --- /dev/null +++ b/app/views/thredded/shared/_page.html.erb @@ -0,0 +1,15 @@ +<%= content_tag(:div, id: 'thredded--container', class: thredded_container_classes, data: thredded_container_data) do %> + <%= render 'thredded/shared/header' %> + <%= render 'thredded/shared/flash_messages' %> + <%= yield :thredded_page_content %> + <%= render 'thredded/shared/currently_online' %> +<%- end %> + +<%# If thredded JS is loaded via an [async] script, the JS may +run before or after DOMContentLoaded. Expose a flag to Thredded +so it can initialize correctly. %> + diff --git a/app/views/thredded/shared/nav/_bar.html.erb b/app/views/thredded/shared/nav/_bar.html.erb new file mode 100644 index 00000000..ba3530d8 --- /dev/null +++ b/app/views/thredded/shared/nav/_bar.html.erb @@ -0,0 +1,37 @@ + + + diff --git a/app/views/thredded/shared/nav/_moderation.html.erb b/app/views/thredded/shared/nav/_moderation.html.erb new file mode 100644 index 00000000..bd5dda12 --- /dev/null +++ b/app/views/thredded/shared/nav/_moderation.html.erb @@ -0,0 +1,14 @@ +<% if moderatable_messageboards_ids.present? %> + <% current = current_page_moderation? %> +
  • + <%= link_to (current ? messageboards_path : pending_moderation_path), rel: 'nofollow' do %> + <%= inline_svg 'thredded/moderation.svg', + class: 'thredded--icon', + title: t('thredded.nav.moderation') %> + <%= t 'thredded.nav.moderation' %> + <% if posts_pending_moderation_count > 0 %> + <%= posts_pending_moderation_count %> + <% end %> + <% end %> +
  • +<% end %> diff --git a/app/views/thredded/shared/nav/_notification_preferences.html.erb b/app/views/thredded/shared/nav/_notification_preferences.html.erb new file mode 100644 index 00000000..1fa97f93 --- /dev/null +++ b/app/views/thredded/shared/nav/_notification_preferences.html.erb @@ -0,0 +1,8 @@ +<% current = current_page_preferences? %> +
  • + <%= link_to current ? (messageboard ? messageboard_topics_path(messageboard) : messageboards_path) + : edit_preferences_path(messageboard), rel: 'nofollow' do %> + <%= inline_svg 'thredded/settings.svg', class: 'thredded--icon', title: t('thredded.nav.settings') %> + <%= t('thredded.nav.settings') %> + <% end %> +
  • diff --git a/app/views/thredded/shared/nav/_private_topics.html.erb b/app/views/thredded/shared/nav/_private_topics.html.erb new file mode 100644 index 00000000..8944acac --- /dev/null +++ b/app/views/thredded/shared/nav/_private_topics.html.erb @@ -0,0 +1,12 @@ +<% current = current_page_private_topics? %> +
  • + <%= link_to (current ? messageboards_path : private_topics_path), rel: 'nofollow' do %> + <%= inline_svg 'thredded/private-messages.svg', + class: 'thredded--icon', + title: t('thredded.nav.private_topics') %> + <%= t('thredded.nav.private_topics') %> + <% if unread_private_topics_count > 0 -%> + <%= unread_private_topics_count %> + <% end -%> + <% end -%> +
  • diff --git a/app/views/thredded/shared/nav/_standalone.html.erb b/app/views/thredded/shared/nav/_standalone.html.erb new file mode 100644 index 00000000..543eb762 --- /dev/null +++ b/app/views/thredded/shared/nav/_standalone.html.erb @@ -0,0 +1,12 @@ +
  • + <% resource_name = Thredded.user_class_name.underscore %> + <% if thredded_signed_in? %> + <%= link_to main_app.send(:"destroy_#{resource_name}_session_path") do %> + Sign Out + <% end %> + <% else %> + <%= link_to main_app.send(:"new_#{resource_name}_session_path") do %> + Sign In / Register + <% end %> + <% end %> +
  • diff --git a/app/views/thredded/shared/nav/_standalone_profile.html.erb b/app/views/thredded/shared/nav/_standalone_profile.html.erb new file mode 100644 index 00000000..0d9d8b05 --- /dev/null +++ b/app/views/thredded/shared/nav/_standalone_profile.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to thredded_current_user.thredded_display_name, user_path(thredded_current_user) %> +
  • diff --git a/app/views/thredded/shared/preview.html.erb b/app/views/thredded/shared/preview.html.erb new file mode 100644 index 00000000..1549e9b0 --- /dev/null +++ b/app/views/thredded/shared/preview.html.erb @@ -0,0 +1,10 @@ +<% content_for :thredded_page_title, t('thredded.form.preview') %> +<% content_for :thredded_page_id, 'thredded--post-preview' %> + +<%# The noscript version of a post preview %> +<%= thredded_page do %> +

    <%= t 'thredded.form.preview' %>

    +
    + <%= @preview_content %> +
    +<% end %> diff --git a/app/views/thredded/theme_previews/_section_title.html.erb b/app/views/thredded/theme_previews/_section_title.html.erb new file mode 100644 index 00000000..1cab479a --- /dev/null +++ b/app/views/thredded/theme_previews/_section_title.html.erb @@ -0,0 +1,3 @@ +

    + <%= label %> +

    diff --git a/app/views/thredded/theme_previews/show.html.erb b/app/views/thredded/theme_previews/show.html.erb new file mode 100644 index 00000000..625a4416 --- /dev/null +++ b/app/views/thredded/theme_previews/show.html.erb @@ -0,0 +1,97 @@ +<% content_for :thredded_page_title, 'Theme Preview' %> +<% content_for :thredded_page_id, 'thredded--theme' %> +<% content_for :thredded_breadcrumbs do %> +
      +
    • <%= link_to 'Theme Preview', theme_preview_path %>
    • +
    +<% end %> + +<%= thredded_page do %> + <%= render 'section_title', label: 'messageboards#index', href: messageboards_path %> + <%= content_tag :section, class: 'thredded--thredded--main-section thredded--messageboards' do %> + <%= render @messageboards %> + + <% end %> + + + <%= render 'section_title', label: 'topics#index', href: messageboard_topics_path(@messageboard) %> + <%= content_tag :section, class: 'thredded--thredded--main-section topics' do %> + <%= render 'thredded/topics/form', + topic: @new_topic, + css_class: 'thredded--is-compact', + placeholder: 'Start a New Topic' %> + <%= render @topics %> + <% end %> +
    + <%= paginate @topics %> +
    +
    + + + <%= render 'section_title', label: 'topics#show', href: @topic.path %> + <%= content_tag :section, id: dom_id(@topic), class: ['thredded--thredded--main-section', topic_css_classes(@topic)] do %> + <%= render 'thredded/topics/header', topic: @topic %> + <%= render @posts %> + <%= render 'thredded/posts/form', + post: @new_post, + button_text: t('thredded.posts.form.create_btn') + %> + <% end %> + + <%= render 'section_title', label: 'topics#new', href: new_messageboard_topic_path(@messageboard) %> + <%= render 'thredded/topics/form', + topic: @new_topic, + placeholder: 'Start a New Topic' %> + + <%= render 'section_title', label: 'posts#edit', href: edit_messageboard_topic_post_path(@messageboard, @post.postable, @post) %> + <%= content_tag :section, class: 'thredded--thredded--main-section posts-form' do %> +

    Edit Post

    + + <%= render 'thredded/posts/form', + post: @post_form, + button_text: t('thredded.posts.form.update_btn') + %> + <% end %> + + <%= render 'section_title', label: 'private_topics#index', href: private_topics_path %> + <%= content_tag :section, class: 'thredded--thredded--main-section private-topics' do %> +

    If the user has no private topics:

    + <%= render 'thredded/private_topics/no_private_topics' %> + +
    + +

    If the user has private topics:

    + <%= render 'thredded/private_topics/form', + private_topic: @new_private_topic, + css_class: 'thredded--is-compact', + placeholder: t('thredded.private_topics.form.title_placeholder_start') %> + <%= render @private_topics %> + <% end %> + + <%= render 'section_title', label: 'private_topics#new', href: new_private_topic_path %> +
    + <%= render 'thredded/private_topics/form', + private_topic: @new_private_topic, + placeholder: t('thredded.private_topics.form.title_placeholder_new') %> +
    + + <%= render 'section_title', label: 'private_topics#show', href: private_topic_path(@private_topic) %> +
    + <%= render 'thredded/private_topics/header', topic: @private_topic %> + <%= render @private_posts %> + <%= render 'thredded/private_posts/form', + post: @private_post_form, + button_text: t('thredded.private_posts.form.create_btn') + %> +
    + + <%= render 'section_title', label: 'preferences#edit', href: edit_messageboard_preferences_path(@messageboard) %> + <%= content_tag :section, class: 'thredded--thredded--main-section preferences' do %> +

    <%= t 'thredded.preferences.global_preferences_title' %>

    + <%= render 'thredded/preferences/form', preferences: @preferences %> + <% end %> + + <%= render 'thredded/shared/currently_online' %> +<% end %> diff --git a/app/views/thredded/topic_previews/preview.html.erb b/app/views/thredded/topic_previews/preview.html.erb new file mode 100644 index 00000000..fa2dc28d --- /dev/null +++ b/app/views/thredded/topic_previews/preview.html.erb @@ -0,0 +1 @@ +<%= @post.filtered_content(self) %> diff --git a/app/views/thredded/topics/_followers.html.erb b/app/views/thredded/topics/_followers.html.erb new file mode 100644 index 00000000..2e1eb29b --- /dev/null +++ b/app/views/thredded/topics/_followers.html.erb @@ -0,0 +1,12 @@ +<% if Thredded.show_topic_followers %> +
    + <% if topic.followers.present? %> + <%= t('thredded.topics.followed_by')%> + <% topic.followers.each do |user| %> + <%= user_mention(user) %> + <% end %> + <% else %> + <%= t('thredded.topics.followed_by_noone')%> + <% end %> +
    +<% end %> diff --git a/app/views/thredded/topics/_form.html.erb b/app/views/thredded/topics/_form.html.erb new file mode 100644 index 00000000..30bd380d --- /dev/null +++ b/app/views/thredded/topics/_form.html.erb @@ -0,0 +1,32 @@ +<%= form_for topic, + url: topic.submit_path, + html: { + class: "thredded--form thredded--new-topic-form #{local_assigns[:css_class]}", + 'data-thredded-topic-form' => true, + 'data-autocomplete-url' => autocomplete_users_path, + 'data-autocomplete-min-length' => Thredded.autocomplete_min_length, + } do |form| %> +
      +
    • + <%= form.label :title, t('thredded.topics.form.title_label') %> + <%= form.text_field :title, placeholder: placeholder, required: true %> +
    • + + <% if form.object.category_options.any? %> +
    • + <%= form.select :category_ids, + form.object.category_options, + {}, + { multiple: true, 'data-placeholder' => t('thredded.topics.form.categories_placeholder') } %> +
    • + <% end %> + + <%= render 'thredded/posts_common/form/content', + form: form, + content_label: t('thredded.topics.form.content_label'), + preview_url: topic.preview_path %> + <%= render 'thredded/topics/topic_form_admin_options', form: form %> + +
    • <%= form.submit t('thredded.topics.form.create_btn'), class: 'thredded--form--submit' %>
    • +
    +<% end %> diff --git a/app/views/thredded/topics/_header.html.erb b/app/views/thredded/topics/_header.html.erb new file mode 100644 index 00000000..d6211f8e --- /dev/null +++ b/app/views/thredded/topics/_header.html.erb @@ -0,0 +1,32 @@ +
    +

    <%= topic.title %>

    + + <%= t 'thredded.topics.started_by_html', + time_ago: time_ago(topic.created_at), + user: user_link(topic.user) %> + + <% if topic.can_update? %> + <%= link_to t('thredded.topics.edit'), topic.edit_path, + class: 'thredded--topic-header--edit-topic', + rel: 'nofollow' %> + <% end %> + <% if thredded_current_user %> + <% if topic.followed? %> + + <% else %> + + <% end %> + <% end %> + <%= render partial: 'thredded/topics/followers', locals: {topic: topic} %> +
    diff --git a/app/views/thredded/topics/_sticky_topics_divider.html.erb b/app/views/thredded/topics/_sticky_topics_divider.html.erb new file mode 100644 index 00000000..b62b7026 --- /dev/null +++ b/app/views/thredded/topics/_sticky_topics_divider.html.erb @@ -0,0 +1 @@ +
    diff --git a/app/views/thredded/topics/_topic.html.erb b/app/views/thredded/topics/_topic.html.erb new file mode 100644 index 00000000..62f0bb26 --- /dev/null +++ b/app/views/thredded/topics/_topic.html.erb @@ -0,0 +1,46 @@ +<%= content_tag :article, + id: dom_id(topic), + class: ['thredded--topics--topic', topic_css_classes(topic)], + data: {topic: topic.id, messageboard: topic.messageboard_id} do %> +
    <%= topic.posts_count %>
    + + + +

    + <%= link_to topic.title, topic.path %> +

    + + <% if topic.categories.any? %> +
      + <%= render topic.categories %> +
    + <% end %> + + + <%= time_ago topic.last_post_at %> + <%= user_link topic.last_user %> + + + + <%= time_ago topic.created_at %> + <%= user_link topic.user %> + + + <% if topic.blocked? && topic.can_moderate? %> + + <%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: topic.last_moderation_record %> + + <% end %> +<% end %> + +<% if !topic_iteration.last? && topic.sticky? && !topics[topic_counter + 1].sticky? %> + <%= render 'thredded/topics/sticky_topics_divider' %> +<% end %> diff --git a/app/views/thredded/topics/_topic_form_admin_options.html.erb b/app/views/thredded/topics/_topic_form_admin_options.html.erb new file mode 100644 index 00000000..520d586b --- /dev/null +++ b/app/views/thredded/topics/_topic_form_admin_options.html.erb @@ -0,0 +1,12 @@ +<% topic = form.object.respond_to?(:topic) ? form.object.topic : form.object %> +<% if policy(topic).moderate? %> +
  • + <%= form.label :locked do %> + <%= form.check_box :locked %> <%= t 'thredded.topics.locked.label' %> + <% end %> + + <%= form.label :sticky do %> + <%= form.check_box :sticky %> <%= t 'thredded.topics.sticky.label' %> + <% end %> +
  • +<% end %> diff --git a/app/views/thredded/topics/edit.html.erb b/app/views/thredded/topics/edit.html.erb new file mode 100644 index 00000000..0e341882 --- /dev/null +++ b/app/views/thredded/topics/edit.html.erb @@ -0,0 +1,50 @@ +<% content_for :thredded_page_title, t('thredded.nav.edit_topic') %> +<% content_for :thredded_page_id, 'thredded--edit-topic' %> +<% content_for :thredded_breadcrumbs do %> +
      +
    • <%= link_to t('thredded.nav.all_messageboards'), messageboards_path %>
    • +
    • <%= link_to messageboard.name, @edit_topic.messageboard_path %>
    • +
    • <%= link_to @edit_topic.title, @edit_topic.path %>
    • +
    • <%= link_to t('thredded.nav.edit_topic'), @edit_topic.edit_path %>
    • +
    +<% end %> + +<%= thredded_page do %> +
    + <%= form_for @edit_topic, + url: @edit_topic.path, + html: { class: 'thredded--form thredded--is-expanded', 'data-thredded-topic-form' => true } do |form| %> +
      + +
    • + <%= form.label :title, t('thredded.topics.form.title_label') %> + <%= form.text_field :title, + placeholder: t('thredded.topics.form.title_placeholder'), + autofocus: true, + required: true %> +
    • + + <% if form.object.category_options.any? %> +
    • + <%= form.select :category_ids, form.object.category_options, {}, + multiple: true, + 'data-placeholder' => t('thredded.topics.form.categories_placeholder') %> +
    • + <% end %> + +
    • + <%= form.label :messageboard_id, t('thredded.topics.form.messageboard_label') %> + <%= form.select :messageboard_id, form.object.messageboard_options %> +
    • + + <%= render 'thredded/topics/topic_form_admin_options', form: form %> + +
    • + +
    • + <% end %> +
    +
    +<% end %> diff --git a/app/views/thredded/topics/index.html.erb b/app/views/thredded/topics/index.html.erb new file mode 100644 index 00000000..ecd378db --- /dev/null +++ b/app/views/thredded/topics/index.html.erb @@ -0,0 +1,30 @@ +<% content_for :thredded_page_title, messageboard.name %> +<% content_for :thredded_page_id, 'thredded--topics-index' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> + +<%= thredded_page do %> +
    + <%= inline_svg 'thredded/follow.svg', id: 'thredded-follow-icon', title: nil %> + <%= inline_svg 'thredded/unfollow.svg', id: 'thredded-unfollow-icon' %> +
    + + <%= content_tag :section, class: 'thredded--main-section thredded--topics', 'data-thredded-topics' => true do %> + <%= render 'thredded/topics/form', + topic: @new_topic, + css_class: 'thredded--is-compact', + placeholder: t('thredded.topics.form.title_placeholder_start') if @new_topic %> + <%= render partial: 'thredded/topics/topic', collection: @topics, locals: {topics: @topics} %> + <% end %> + +
    + <%= paginate @topics %> +
    + +
    + <% if policy(messageboard).update? %> + + <%= t('thredded.nav.edit_messageboard') %> + + <% end %> +
    +<% end %> diff --git a/app/views/thredded/topics/new.html.erb b/app/views/thredded/topics/new.html.erb new file mode 100644 index 00000000..a32b105b --- /dev/null +++ b/app/views/thredded/topics/new.html.erb @@ -0,0 +1,12 @@ +<% content_for :thredded_page_title, 'Create a New Topic' %> +<% content_for :thredded_page_id, 'thredded--new-topic' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> + +<%= thredded_page do %> +
    + <%= render 'thredded/topics/form', + topic: @new_topic, + css_class: 'thredded--is-expanded', + placeholder: t('thredded.topics.form.title_placeholder_start') %> +
    +<% end %> diff --git a/app/views/thredded/topics/search.html.erb b/app/views/thredded/topics/search.html.erb new file mode 100644 index 00000000..7b1b1544 --- /dev/null +++ b/app/views/thredded/topics/search.html.erb @@ -0,0 +1,39 @@ +<% content_for :thredded_page_title, t('thredded.topics.search.page_title') %> +<% content_for :thredded_page_id, 'thredded--topic-search-results' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> + +<%= thredded_page do %> + <% if @topics.present? %> +

    + <% if messageboard_or_nil %> + <%= t 'thredded.topics.search.results_in_messageboard_message_html', + query: @query, messageboard: messageboard.name %> +
    + + <%= t('thredded.topics.search.search_in_all_messageboards_btn') -%> + + <% else %> + <%= t 'thredded.topics.search.results_message_html', query: @query %> + <% end %> +

    +
    + <%= render @topics %> +
    +
    + <%= paginate @topics %> +
    + <% else %> +

    + <% if messageboard_or_nil %> + <%= t 'thredded.topics.search.no_results_in_messageboard_message_html', + query: @query, messageboard: messageboard.name -%> +
    + + <%= t('thredded.topics.search.search_in_all_messageboards_btn') -%> + + <% else %> + <%= t 'thredded.topics.search.no_results_message_html', query: @query %> + <% end %> +

    + <% end %> +<% end %> diff --git a/app/views/thredded/topics/show.html.erb b/app/views/thredded/topics/show.html.erb new file mode 100644 index 00000000..2f7fd2d2 --- /dev/null +++ b/app/views/thredded/topics/show.html.erb @@ -0,0 +1,42 @@ +<% topic = @posts.topic %> +<% content_for :thredded_page_title, topic.title %> +<% content_for :thredded_page_id, 'thredded--topic-show' %> +<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %> + +<%= thredded_page do %> + <%= content_tag :section, + id: dom_id(topic), + class: ['thredded--main-section', 'thredded--topic', *topic_css_classes(topic)] do %> + + <%= render 'thredded/topics/header', topic: topic %> + <%= view_hooks.posts_common.pagination_top.render(self, posts: @posts) do %> +
    <%= paginate @posts %>
    + <% end %> + <%= render_posts @posts, + partial: 'thredded/posts/post', + content_partial: 'thredded/posts/content', + locals: { actions: { quote: true } } %> + <%= view_hooks.posts_common.pagination_bottom.render(self, posts: @posts) do %> +
    <%= paginate @posts %>
    + <% end %> + + <% if policy(@new_post.post).create? %> +
    +

    <%= t('thredded.posts.form.title_label') %>

    + <%= render 'thredded/posts/form', + post: @new_post, + button_text: t('thredded.posts.form.create_btn'), + button_submitting_text: t('thredded.posts.form.create_btn_submitting') %> +
    + <% end %> + + <% if topic.can_destroy? %> +
    + <%= button_to t('thredded.topics.delete_topic'), topic.destroy_path, method: :delete, + form_class: 'thredded--topic-delete-form', + class: 'thredded--button', + 'data-confirm' => t('thredded.topics.delete_confirm') %> +
    + <% end %> + <% end %> +<% end %> diff --git a/app/views/thredded/users/_badge.html.erb b/app/views/thredded/users/_badge.html.erb new file mode 100644 index 00000000..eb0a7224 --- /dev/null +++ b/app/views/thredded/users/_badge.html.erb @@ -0,0 +1,9 @@ +<% badge_style = 'padding: 3px 5px; font-size: 70%; font-weight: normal;' %> + +<% if user.selected_billing_plan_id == 2 %> + Beta Tester +<% elsif user.selected_billing_plan_id == 3 %> + Early Adopter +<% elsif [4, 5, 6].include? user.selected_billing_plan_id %> + Premium Supporter +<% end %> diff --git a/app/views/thredded/users/_link.html.erb b/app/views/thredded/users/_link.html.erb new file mode 100644 index 00000000..1fd6761c --- /dev/null +++ b/app/views/thredded/users/_link.html.erb @@ -0,0 +1,11 @@ +<% if user %> + <% if !user.thredded_anonymous? %> +<%= user.thredded_display_name %> +<%= render partial: 'thredded/users/badge', locals: { user: user } %> + <% else %> +<%= user.thredded_display_name %> +<%= render partial: 'thredded/users/badge', locals: { user: user } %> + <% end %> +<% else %> +<%= t 'thredded.null_user_name' %> +<% end %> diff --git a/app/views/thredded/users/_post.html.erb b/app/views/thredded/users/_post.html.erb new file mode 100644 index 00000000..76fb9427 --- /dev/null +++ b/app/views/thredded/users/_post.html.erb @@ -0,0 +1,6 @@ +<% post, content = post_and_content if local_assigns.key?(:post_and_content) %> +<%= content_tag :article, id: dom_id(post), class: 'thredded--post' do %> + <%= render 'thredded/posts_common/actions', post: post %> + <%= render 'thredded/posts_common/header_with_topic', post: post %> + <%= content || render('thredded/posts/content', post: post) %> +<% end %> diff --git a/app/views/thredded/users/_posts.html.erb b/app/views/thredded/users/_posts.html.erb new file mode 100644 index 00000000..a01c8d1e --- /dev/null +++ b/app/views/thredded/users/_posts.html.erb @@ -0,0 +1,7 @@ +<%# + This partial can be called from the parent app to render the user's posts. + The parent view context that renders this partial must include Thredded::ApplicationHelper. + TODO: Use a Cell instead. https://github.com/apotonick/cells +%> +<%# @param posts [Thredded::PostsPageView] %> +<%= render_posts posts, partial: 'thredded/users/post', content_partial: 'thredded/posts/content' %> diff --git a/app/views/universes/_picker.html.erb b/app/views/universes/_picker.html.erb index a342b9a9..87042b1d 100644 --- a/app/views/universes/_picker.html.erb +++ b/app/views/universes/_picker.html.erb @@ -20,8 +20,8 @@
  • <%= link_to "All universes", url_for(params.merge(:universe => :all)) %>
  • <% if current_user %>
  • - <% current_user.universes.sort_by(&:name).each do |universe| %> -
  • <%= link_to universe.name, "?universe=#{universe.id}" %>
  • + <% (current_user.universes + current_user.contributable_universes).sort_by(&:name).each do |universe| %> +
  • <%= link_to universe.name + (universe.user == current_user ? '' : ' (contributor)'), "?universe=#{universe.id}" %>
  • <% end %> <% end %> -
\ No newline at end of file + diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb deleted file mode 100644 index 1afe0651..00000000 --- a/app/views/users/_form.html.erb +++ /dev/null @@ -1,50 +0,0 @@ -<%= form_for @user, :url => signup_process_path, :html => { :class => 'form-horizontal' } do |f| %> -
- <% if @user.errors.messages.length > 0 %> -
- Oops! There was a problem. See the high-tech bug report below and please try again!
-
    - <% @user.errors.messages.keys.each do |key| %> -
  • <%= key.capitalize %>: <%= @user.errors.messages[key] %>
  • - <% end %> -
-
- <% end %> - -
- <%= f.label :name, :class => 'control-label' %> -
- <% if session[:anon_user] %> - <%= f.text_field :name, :class => 'text_field', :value => '' %> - <% else %> - <%= f.text_field :name, :class => 'text_field' %> - <% end %> - This is the username you will log in with. Spaces are allowed. -
-
-
- <%= f.label :email, :class => 'control-label' %> -
- <% if session[:anon_user] %> - <%= f.text_field :email, :class => 'text_field', :value => '' %> - <% else %> - <%= f.text_field :email, :class => 'text_field' %> - <% end %> -
-
-
- <%= f.label :password, :class => 'control-label' %> -
- <%= f.password_field :password, :class => 'text_field' %> -
-
- -
-
- -
- <%= f.submit nil, :class => 'btn btn-primary' %> - <%= link_to t('.cancel', :default => t("helpers.links.cancel")), root_url, :class => 'btn' %> -
-
-<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 8e1ae1dd..78351c24 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -22,7 +22,7 @@ content_jsonld = { tabs = %w( universes characters locations items - creatures races religions magics languages groups scenes + creatures races religions magics languages groups floras scenes ) tab_content_list = {} @@ -43,6 +43,12 @@ content_jsonld = {
<%= @user.name %>
+ <% if user_signed_in? && current_user.id == @user.id %> +
+ <%= link_to 'Edit your profile', edit_user_registration_path(current_user) %> +
+ <% end %> +
Profile Pictureclose

Profile pictures are powered by <%= link_to 'Gravatar', 'https://gravatar.com/', target: "_blank" %>, using the email you provided in your <%= link_to 'account settings', edit_user_registration_path %>.

@@ -76,6 +82,14 @@ content_jsonld = {
<% end %> + <% if @user.username.present? %> +
+
+ <%= link_to 'Forums', thredded_path %> username: <%= @user.username %> +
+
+ <% end %> + <%#
Author links coming soon @@ -159,4 +173,4 @@ content_jsonld = { break # Only do this for the first tab with content we see end end -%> \ No newline at end of file +%> diff --git a/bin/delayed_job b/bin/delayed_job new file mode 100755 index 00000000..edf19598 --- /dev/null +++ b/bin/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize diff --git a/config/application.rb b/config/application.rb index f07e9743..bad9f190 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,6 +25,9 @@ module Notebook # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de + # Use delayed_job to process jobs + config.active_job.queue_adapter = :delayed_job + # Filter sensitive parameters out of logs config.filter_parameters << :password config.filter_parameters << :password_confirmation diff --git a/config/attributes/character.yml b/config/attributes/character.yml index 82c45e78..d30051c1 100644 --- a/config/attributes/character.yml +++ b/config/attributes/character.yml @@ -32,14 +32,14 @@ :label: Eye Color - :name: race :label: Race + - :name: races + :label: Linked Races - :name: skintone :label: Skin Tone - :name: bodytype :label: Body Type - :name: identmarks :label: Identifying Marks - :name: races - :label: Races :nature: :label: Nature :icon: fingerprint @@ -110,6 +110,8 @@ :label: Siblings - :name: children :label: Children + - :name: pets + :label: Pets :gallery: :label: Gallery :icon: photo_library diff --git a/config/attributes/universe.yml b/config/attributes/universe.yml index fd26ab1f..4b16cdef 100644 --- a/config/attributes/universe.yml +++ b/config/attributes/universe.yml @@ -44,3 +44,6 @@ :attributes: - :name: privacy :label: Privacy +:contributors: + :label: Contributors + :icon: group_add \ No newline at end of file diff --git a/config/initializers/thredded.rb b/config/initializers/thredded.rb new file mode 100644 index 00000000..5a161c7b --- /dev/null +++ b/config/initializers/thredded.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +# Thredded configuration + +# ==> User Configuration +# The name of the class your app uses for your users. +# By default the engine will use 'User' but if you have another name +# for your user class - change it here. +Thredded.user_class = 'User' + +# User name column, used in @mention syntax and *must* be unique. +# This is the column used to search for users' names if/when someone is @ mentioned. +Thredded.user_name_column = :username + +# User display name method, by default thredded uses the user_name_column defined above +# You may want to use :to_s or some more elaborate method +Thredded.user_display_name_method = :forum_username + +# The path (or URL) you will use to link to your users' profiles. +# When linking to a user, Thredded will use this lambda to spit out +# the path or url to your user. This lambda is evaluated in the view context. +Thredded.user_path = lambda do |user| + user_path = :"#{Thredded.user_class_name.underscore}_path" + main_app.respond_to?(user_path) ? main_app.send(user_path, user) : "/users/#{user.to_param}" +end + +# This method is used by Thredded controllers and views to fetch the currently signed-in user +Thredded.current_user_method = :"current_#{Thredded.user_class_name.underscore}" + +# User avatar URL. rb-gravatar gem is used by default: +Thredded.avatar_url = ->(user) { Gravatar.src(user.email, 128, 'mm') } + +# ==> Database Configuration +# By default, thredded uses integers for record ID route constraints. +# For integer based IDs (default): +# Thredded.routes_id_constraint = /[1-9]\d*/ +# For UUID based IDs (example): +# Thredded.routes_id_constraint = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ + +# ==> Permissions Configuration +# By default, thredded uses a simple permission model, where all the users can post to all message boards, +# and admins and moderators are determined by a flag on the users table. + +# The name of the moderator flag column on the users table. +Thredded.moderator_column = :forum_administrator +# The name of the admin flag column on the users table. +Thredded.admin_column = :forum_administrator + +# Whether posts and topics pending moderation are visible to regular users. +Thredded.content_visible_while_pending_moderation = true + +# Whether users that are following a topic are listed on topic page. +Thredded.show_topic_followers = false + +# This model can be customized further by overriding a handful of methods on the User model. +# For more information, see app/models/thredded/user_extender.rb. + +# ==> Ordering configuration + +# How to calculate the position of messageboards in a list: +# :position (default) set the position manually (new messageboards go to the bottom, by creation timestamp) +# :last_post_at_desc most recent post first +# :topics_count_desc most topics first +Thredded.messageboards_order = :last_post_at_desc + +# ==> Email Configuration +# Email "From:" field will use the following +# Thredded.email_from = 'no-reply@example.com' + +# Emails going out will prefix the "Subject:" with the following string +# Thredded.email_outgoing_prefix = '[My Forum] ' +# +# The parent mailer for all Thredded mailers +# Thredded.parent_mailer = 'ActionMailer::Base' + +# ==> View Configuration +# Set the layout for rendering the thredded views. +Thredded.layout = 'layouts/forum' + +# ==> URLs +# How Thredded generates URL slugs from text. + +# Default: +# Thredded.slugifier = ->(input) { input.parameterize } + +# If your forum is in a language other than English, you might want to use the babosa gem instead +# Thredded.slugifier = ->(input) { Babosa::Identifier.new(input).normalize.transliterate(:russian).to_s } + +# ==> Post Content Formatting +# Customize the way Thredded handles post formatting. + +# Change the default html-pipeline filters used by thredded. +# E.g. to replace default emoji filter with your own: +# Thredded::ContentFormatter.after_markup_filters[ +# Thredded::ContentFormatter.after_markup_filters.index(HTML::Pipeline::EmojiFilter)] = MyEmojiFilter + +# Change the HTML sanitization settings used by Thredded. +# See the Sanitize docs for more information on the underlying library: https://github.com/rgrove/sanitize/#readme +# E.g. to allow a custom element : +# Thredded::ContentFormatter.whitelist[:elements] += %w(custom-element) + +# ==> User autocompletion (Private messages and @-mentions) +# Thredded.autocomplete_min_length = 2 lower to 1 if have 1-letter names -- increase if you want + +# ==> Error Handling +# By default Thredded just renders a flash alert on errors such as Topic not found, or Login required. +# Below is an example of overriding the default behavior on LoginRequired: +# +# Rails.application.config.to_prepare do +# Thredded::ApplicationController.module_eval do +# rescue_from Thredded::Errors::LoginRequired do |exception| +# @message = exception.message +# render template: 'sessions/new', status: :forbidden +# end +# end +# end + +# ==> View hooks +# +# Customize the UI before/after/replacing individual components. +# See the full list of view hooks and their arguments by running: +# +# $ grep view_hooks -R --include '*.html.erb' "$(bundle show thredded)" +# +# Rails.application.config.to_prepare do +# Thredded.view_hooks.post_form.content_text_area.config.before do |form:, **args| +# # This is called in the Thredded view context, so all Thredded helpers and URLs are accessible here directly. +# 'hi' +# end +# end + +# ==> Topic following +# +# By default, a user will be subscribed to a topic they've created. Uncomment this to not subscribe them: +# +# Thredded.auto_follow_when_creating_topic = false +# +# By default, a user will be subscribed to (follow) a topic they post in. Uncomment this to not subscribe them: +# +# Thredded.auto_follow_when_posting_in_topic = false +# +# By default, a user will be subscribed to the topic they get @-mentioned in. +# Individual users can disable this in the Notification Settings. +# To change the default for all users, simply change the default value of the `follow_topics_on_mention` column +# of the `thredded_user_preferences` and `thredded_user_messageboard_preferences` tables. + +# ==> Notifiers +# +# Change how users can choose to be notified, by adding notifiers here, or removing the initializer altogether +# +# default: +# Thredded.notifiers = [Thredded::EmailNotifier.new] +# +# none: +# Thredded.notifiers = [] +# +# add in (must install separate gem (under development) as well): +# Thredded.notifiers = [Thredded::EmailNotifier.new, Thredded::PushoverNotifier.new(ENV['PUSHOVER_APP_ID'])] diff --git a/config/locales/en.yml b/config/locales/en.yml index faebd123..703400b7 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -23,6 +23,18 @@ en: cta: get_yours: Get Yours + writers: + header: For writers + body: Authors use Notebook.ai to create rich, immersive worlds to set their novels, stories, and plays in. + button: Learn more + roleplayers: + header: For roleplayers + body: Dungeon masters use Notebook.ai to build worlds for their campaigns that can be shared with players. + button: Learn more + designers: + header: For designers + body: Game design teams use Notebook.ai to collaborate and keep every aspect of their world in sync. + button: Learn more benefits: creativity: @@ -100,7 +112,7 @@ en: price: $9/month universes_html: Plan unlimited universes - upload_html: Upload up to 1GB of images + upload_html: Upload up to 10GB of images characters_html: Plan unlimited characters locations_html: Plan unlimited locations items_html: Plan unlimited items @@ -111,6 +123,7 @@ en: magics_html: Plan unlimited magic races_html: Plan unlimited races religions_html: Plan unlimited religions + floras_html: Plan unlimited flora scenes_html: Plan unlimited scenes footer: @@ -137,6 +150,7 @@ en: group: Group magic: Magic language: Language + flora: Flora scene: Scene @@ -419,7 +433,7 @@ en: content_descriptions: universe: > - Track multiple stories simultaneously by adding your characters and other ideas to the universe they + Track multiple stories simultaneously by organizing your ideas into just the universe they belong in. character: > From your heroes to the little guys that live in the background, everyone plays a role in strong @@ -464,6 +478,40 @@ en: location_update_upload_map_error: > Location was updated, but your map did not upload. Please try again. + thredded: + nav: + all_messageboards: Notebook.ai Forums + settings: Settings + private_topics: Inbox + search: + form: + placeholder: Search Discussions + preferences: + global_preferences_title: Preferences + messageboard_preferences_nav_title: Per-category Settings + messageboard: + create: Create a New Discussion Board + topics_and_posts_counts: "%{topics_count} threads / %{posts_count} posts" + index: + page_title: Discussions + topics: + follow: Follow this discussion + form: + create_btn: Create New Discussion + title_placeholder_start: Start a New Discussion + following: + auto: You are following this discussion because auto-follow is enabled. + manual: You are following this discussion. + mentioned: You are following this discussion because you were mentioned on it. + posted: You are following this discussion because you posted to it. + posts: + delete: Delete reply + delete_confirm: Are you sure you want to delete this reply? + deleted_notice: Your reply has been deleted. + form: + create_btn: Post reply + title_label: Reply to this discussion + body_types: - Delicate - Flat diff --git a/config/newrelic.yml b/config/newrelic.yml new file mode 100644 index 00000000..c0e5b9b4 --- /dev/null +++ b/config/newrelic.yml @@ -0,0 +1,45 @@ +# +# This file configures the New Relic Agent. New Relic monitors Ruby, Java, +# .NET, PHP, Python and Node applications with deep visibility and low +# overhead. For more information, visit www.newrelic.com. +# +# Generated October 27, 2017 +# +# This configuration file is custom generated for app49251381@heroku.com +# +# For full documentation of agent configuration options, please refer to +# https://docs.newrelic.com/docs/agents/ruby-agent/installation-configuration/ruby-agent-configuration + +common: &default_settings + # Required license key associated with your New Relic account. + license_key: c714fab9361f4ce4b58f45464317068870539418 + + # Your application name. Renaming here affects where data displays in New + # Relic. For more details, see https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/renaming-applications + app_name: My Application + + # To disable the agent regardless of other settings, uncomment the following: + # agent_enabled: false + + # Logging level for log/newrelic_agent.log + log_level: info + + +# Environment-specific settings are in this section. +# RAILS_ENV or RACK_ENV (as appropriate) is used to determine the environment. +# If your application has other named environments, configure them here. +development: + <<: *default_settings + app_name: My Application (Development) + +test: + <<: *default_settings + # It doesn't make sense to report to New Relic from automated test runs. + monitor_mode: false + +staging: + <<: *default_settings + app_name: My Application (Staging) + +production: + <<: *default_settings diff --git a/config/routes.rb b/config/routes.rb index 84971224..095b3f00 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,10 +1,11 @@ # rubocop:disable LineLength Rails.application.routes.draw do - devise_for :users, controllers: { registrations: 'registrations', omniauth_callbacks: 'users/omniauth_callbacks' } resources :users + delete 'contributor/:id/remove', to: 'contributors#destroy', as: :remove_contributor + get '/unsubscribe/emails/:code', to: 'emails#one_click_unsubscribe' # Main pages @@ -143,6 +144,7 @@ Rails.application.routes.draw do get '/items.csv', to: 'export#items_csv', as: :items_csv get '/creatures.csv', to: 'export#creatures_csv', as: :creatures_csv get '/races.csv', to: 'export#races_csv', as: :races_csv + get '/floras.csv', to: 'export#floras_csv', as: :floras_csv get '/religions.csv', to: 'export#religions_csv', as: :religions_csv get '/magics.csv', to: 'export#magics_csv', as: :magics_csv get '/languages.csv', to: 'export#languages_csv', as: :languages_csv @@ -215,6 +217,7 @@ Rails.application.routes.draw do end resources :omniauth_accounts, only: [:destroy] + mount Thredded::Engine => '/forum' end # rubocop:enable LineLength diff --git a/db/migrate/20161021230205_religious_locationship.rb b/db/migrate/20161021230205_create_religious_locationships.rb similarity index 74% rename from db/migrate/20161021230205_religious_locationship.rb rename to db/migrate/20161021230205_create_religious_locationships.rb index c1e84742..214002ed 100644 --- a/db/migrate/20161021230205_religious_locationship.rb +++ b/db/migrate/20161021230205_create_religious_locationships.rb @@ -1,4 +1,4 @@ -class ReligiousLocationship < ActiveRecord::Migration +class CreateReligiousLocationships < ActiveRecord::Migration def change create_table :religious_locationships do |t| t.integer :religion_id diff --git a/db/migrate/20170724114620_add_username_to_user.rb b/db/migrate/20170724114620_add_username_to_user.rb new file mode 100644 index 00000000..d1b7ea37 --- /dev/null +++ b/db/migrate/20170724114620_add_username_to_user.rb @@ -0,0 +1,6 @@ +class AddUsernameToUser < ActiveRecord::Migration + def change + add_column :users, :username, :string + add_index :users, :username, unique: true + end +end diff --git a/db/migrate/20170724114723_add_forum_admin_flag.rb b/db/migrate/20170724114723_add_forum_admin_flag.rb new file mode 100644 index 00000000..a2e1f2ce --- /dev/null +++ b/db/migrate/20170724114723_add_forum_admin_flag.rb @@ -0,0 +1,5 @@ +class AddForumAdminFlag < ActiveRecord::Migration + def change + add_column :users, :forum_admin, :string, default: false + end +end diff --git a/db/migrate/20170724114833_create_thredded.thredded.rb b/db/migrate/20170724114833_create_thredded.thredded.rb new file mode 100644 index 00000000..88777d77 --- /dev/null +++ b/db/migrate/20170724114833_create_thredded.thredded.rb @@ -0,0 +1,257 @@ +# This migration comes from thredded (originally 20160329231848) +# frozen_string_literal: true + +require 'thredded/base_migration' + +# rubocop:disable Metrics/ClassLength +# rubocop:disable Metrics/MethodLength +class CreateThredded < Thredded::BaseMigration + def change + unless table_exists?(:friendly_id_slugs) + # The user might have installed FriendlyId separately already. + create_table :friendly_id_slugs do |t| + t.string :slug, null: false + t.integer :sluggable_id, null: false + t.string :sluggable_type, limit: 50 + t.string :scope + t.datetime :created_at + end + add_index :friendly_id_slugs, :sluggable_id + add_index :friendly_id_slugs, %i[slug sluggable_type], length: { slug: 140, sluggable_type: 50 } + add_index :friendly_id_slugs, %i[slug sluggable_type scope], + length: { slug: 70, sluggable_type: 50, scope: 70 }, + unique: true + add_index :friendly_id_slugs, :sluggable_type + end + + create_table :thredded_categories do |t| + t.references :messageboard, null: false, index: false + t.string :name, limit: 191, null: false + t.string :description, limit: 255 + t.timestamps null: false + t.string :slug, limit: 191, null: false + t.index %i[messageboard_id slug], name: :index_thredded_categories_on_messageboard_id_and_slug, unique: true + t.index [:messageboard_id], name: :index_thredded_categories_on_messageboard_id + end + DbTextSearch::CaseInsensitive.add_index connection, :thredded_categories, :name, name: :thredded_categories_name_ci + + create_table :thredded_messageboards do |t| + t.string :name, limit: 191, null: false + t.string :slug, limit: 191 + t.text :description + t.integer :topics_count, default: 0 + t.integer :posts_count, default: 0 + t.integer :position, null: false + t.references :last_topic, index: false + t.references :messageboard_group, index: false + t.timestamps null: false + t.index [:messageboard_group_id], name: :index_thredded_messageboards_on_messageboard_group_id + t.index [:slug], name: :index_thredded_messageboards_on_slug + end + + create_table :thredded_posts do |t| + t.references :user, type: user_id_type, index: false + t.text :content, limit: 65_535 + t.string :ip, limit: 255 + t.string :source, limit: 255, default: 'web' + t.references :postable, null: false, index: false + t.references :messageboard, null: false, index: false + t.integer :moderation_state, null: false + t.timestamps null: false + t.index %i[moderation_state updated_at], + order: { updated_at: :asc }, + name: :index_thredded_posts_for_display + t.index [:messageboard_id], name: :index_thredded_posts_on_messageboard_id + t.index [:postable_id], name: :index_thredded_posts_on_postable_id + t.index [:postable_id], name: :index_thredded_posts_on_postable_id_and_postable_type + t.index [:user_id], name: :index_thredded_posts_on_user_id + end + DbTextSearch::FullText.add_index connection, :thredded_posts, :content, name: :thredded_posts_content_fts + + create_table :thredded_private_posts do |t| + t.references :user, type: user_id_type, index: false + t.text :content, limit: 65_535 + t.references :postable, null: false, index: false + t.string :ip, limit: 255 + t.timestamps null: false + end + + create_table :thredded_private_topics do |t| + t.references :user, type: user_id_type, index: false + t.references :last_user, index: false + t.string :title, limit: 255, null: false + t.string :slug, limit: 191, null: false + t.integer :posts_count, default: 0 + t.string :hash_id, limit: 191, null: false + t.datetime :last_post_at + t.timestamps null: false + t.index [:hash_id], name: :index_thredded_private_topics_on_hash_id + t.index [:slug], name: :index_thredded_private_topics_on_slug + end + + create_table :thredded_private_users do |t| + t.references :private_topic, index: false + t.references :user, type: user_id_type, index: false + t.timestamps null: false + t.index [:private_topic_id], name: :index_thredded_private_users_on_private_topic_id + t.index [:user_id], name: :index_thredded_private_users_on_user_id + end + + create_table :thredded_topic_categories do |t| + t.references :topic, null: false, index: false + t.references :category, null: false, index: false + t.index [:category_id], name: :index_thredded_topic_categories_on_category_id + t.index [:topic_id], name: :index_thredded_topic_categories_on_topic_id + end + + create_table :thredded_topics do |t| + t.references :user, type: user_id_type, index: false + t.references :last_user, index: false + t.string :title, limit: 255, null: false + t.string :slug, limit: 191, null: false + t.references :messageboard, null: false, index: false + t.integer :posts_count, default: 0, null: false + t.boolean :sticky, default: false, null: false + t.boolean :locked, default: false, null: false + t.string :hash_id, limit: 191, null: false + t.string :type, limit: 191 + t.integer :moderation_state, null: false + t.datetime :last_post_at + t.timestamps null: false + t.index %i[moderation_state sticky updated_at], + order: { sticky: :desc, updated_at: :desc }, + name: :index_thredded_topics_for_display + t.index [:hash_id], name: :index_thredded_topics_on_hash_id + t.index [:slug], name: :index_thredded_topics_on_slug, unique: true + t.index [:messageboard_id], name: :index_thredded_topics_on_messageboard_id + t.index [:user_id], name: :index_thredded_topics_on_user_id + end + DbTextSearch::FullText.add_index connection, :thredded_topics, :title, name: :thredded_topics_title_fts + + create_table :thredded_user_details do |t| + t.references :user, type: user_id_type, null: false, index: false + t.datetime :latest_activity_at + t.integer :posts_count, default: 0 + t.integer :topics_count, default: 0 + t.datetime :last_seen_at + t.integer :moderation_state, null: false, default: 0 # pending_moderation + t.timestamp :moderation_state_changed_at + t.timestamps null: false + t.index %i[moderation_state moderation_state_changed_at], + order: { moderation_state_changed_at: :desc }, + name: :index_thredded_user_details_for_moderations + t.index %i[latest_activity_at], name: :index_thredded_user_details_on_latest_activity_at + t.index %i[user_id], name: :index_thredded_user_details_on_user_id + end + + create_table :thredded_messageboard_users do |t| + t.references :thredded_user_detail, null: false, index: false + t.references :thredded_messageboard, null: false, index: false + t.datetime :last_seen_at, null: false + t.index %i[thredded_messageboard_id thredded_user_detail_id], + name: :index_thredded_messageboard_users_primary + t.index %i[thredded_messageboard_id last_seen_at], + name: :index_thredded_messageboard_users_for_recently_active + end + add_foreign_key :thredded_messageboard_users, :thredded_user_details, + column: :thredded_user_detail_id, on_delete: :cascade + add_foreign_key :thredded_messageboard_users, :thredded_messageboards, + column: :thredded_messageboard_id, on_delete: :cascade + + create_table :thredded_user_preferences do |t| + t.references :user, type: user_id_type, null: false, index: false + t.boolean :follow_topics_on_mention, default: true, null: false + t.boolean :auto_follow_topics, default: false, null: false + t.timestamps null: false + t.index [:user_id], name: :index_thredded_user_preferences_on_user_id + end + + create_table :thredded_user_messageboard_preferences do |t| + t.references :user, type: user_id_type, null: false, index: false + t.references :messageboard, null: false, index: false + t.boolean :follow_topics_on_mention, default: true, null: false + t.boolean :auto_follow_topics, default: false, null: false + t.timestamps null: false + t.index %i[user_id messageboard_id], + name: :thredded_user_messageboard_preferences_user_id_messageboard_id, + unique: true + end + + %i[topic private_topic].each do |topics_table| + table_name = :"thredded_user_#{topics_table}_read_states" + create_table table_name do |t| + t.references :user, type: user_id_type, null: false, index: false + t.references :postable, null: false, index: false + t.integer :page, default: 1, null: false + t.timestamp :read_at, null: false + t.index %i[user_id postable_id], name: :"#{table_name}_user_postable", unique: true + end + end + + create_table :thredded_messageboard_groups do |t| + t.string :name + t.integer :position, null: false + t.timestamps null: false + end + + create_table :thredded_user_topic_follows do |t| + t.references :user, type: user_id_type, null: false, index: false + t.references :topic, null: false, index: false + t.datetime :created_at, null: false + t.integer :reason, limit: 1 + t.index %i[user_id topic_id], name: :thredded_user_topic_follows_user_topic, unique: true + end + + create_table :thredded_post_moderation_records do |t| + t.references :post, index: false + t.references :messageboard, index: false + t.text :post_content, limit: 65_535 + t.references :post_user, index: false + t.text :post_user_name + t.references :moderator, index: false + t.integer :moderation_state, null: false + t.integer :previous_moderation_state, null: false + t.timestamp :created_at, null: false + t.index %i[messageboard_id created_at], + order: { created_at: :desc }, + name: :index_thredded_moderation_records_for_display + end + + create_table :thredded_notifications_for_private_topics do |t| + t.references :user, null: false, index: false, type: user_id_type + t.string :notifier_key, null: false, limit: 90 + t.boolean :enabled, default: true, null: false + t.index %i[user_id notifier_key], + name: 'thredded_notifications_for_private_topics_unique', unique: true + end + create_table :thredded_notifications_for_followed_topics do |t| + t.references :user, null: false, index: false, type: user_id_type + t.string :notifier_key, null: false, limit: 90 + t.boolean :enabled, default: true, null: false + t.index %i[user_id notifier_key], + name: 'thredded_notifications_for_followed_topics_unique', unique: true + end + create_table :thredded_messageboard_notifications_for_followed_topics do |t| + t.references :user, null: false, index: false, type: user_id_type + t.references :messageboard, null: false, index: false + t.string :notifier_key, null: false, limit: 90 + t.boolean :enabled, default: true, null: false + t.index %i[user_id messageboard_id notifier_key], + name: 'thredded_messageboard_notifications_for_followed_topics_unique', unique: true + end + + create_table :thredded_user_post_notifications do |t| + t.references :user, null: false, index: false, type: user_id_type + t.references :post, null: false, index: false + t.datetime :notified_at, null: false + t.index :post_id, name: :index_thredded_user_post_notifications_on_post_id + t.index %i[user_id post_id], name: :index_thredded_user_post_notifications_on_user_id_and_post_id, unique: true + end + add_foreign_key :thredded_user_post_notifications, + Thredded.user_class.table_name, column: :user_id, on_delete: :cascade + add_foreign_key :thredded_user_post_notifications, + :thredded_posts, column: :post_id, on_delete: :cascade + end +end +# rubocop:enable Metrics/MethodLength +# rubocop:enable Metrics/ClassLength diff --git a/db/migrate/20170811145534_create_contributors.rb b/db/migrate/20170811145534_create_contributors.rb new file mode 100644 index 00000000..d7c3ee64 --- /dev/null +++ b/db/migrate/20170811145534_create_contributors.rb @@ -0,0 +1,11 @@ +class CreateContributors < ActiveRecord::Migration + def change + create_table :contributors do |t| + t.references :universe, index: true, foreign_key: true + t.string :email + t.references :user, index: true, foreign_key: true + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20171028220829_add_forum_admin_flag_again.rb b/db/migrate/20171028220829_add_forum_admin_flag_again.rb new file mode 100644 index 00000000..c5e3f757 --- /dev/null +++ b/db/migrate/20171028220829_add_forum_admin_flag_again.rb @@ -0,0 +1,5 @@ +class AddForumAdminFlagAgain < ActiveRecord::Migration + def change + add_column :users, :forum_administrator, :boolean, null: false, default: false + end +end diff --git a/db/migrate/20171028221518_remove_forum_admin_flag.rb b/db/migrate/20171028221518_remove_forum_admin_flag.rb new file mode 100644 index 00000000..257d364a --- /dev/null +++ b/db/migrate/20171028221518_remove_forum_admin_flag.rb @@ -0,0 +1,5 @@ +class RemoveForumAdminFlag < ActiveRecord::Migration + def change + remove_column :users, :forum_admin + end +end diff --git a/db/migrate/20171028230258_create_delayed_jobs.rb b/db/migrate/20171028230258_create_delayed_jobs.rb new file mode 100644 index 00000000..27fdcf6c --- /dev/null +++ b/db/migrate/20171028230258_create_delayed_jobs.rb @@ -0,0 +1,22 @@ +class CreateDelayedJobs < ActiveRecord::Migration + def self.up + create_table :delayed_jobs, force: true do |table| + table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue + table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually. + table.text :handler, null: false # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.string :queue # The name of the queue this job is in + table.timestamps null: true + end + + add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority" + end + + def self.down + drop_table :delayed_jobs + end +end diff --git a/db/schema.rb b/db/schema.rb index 5e35710f..49c0079b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170824183110) do +ActiveRecord::Schema.define(version: 20171028230258) do create_table "archenemyships", force: :cascade do |t| t.integer "user_id" @@ -234,6 +234,22 @@ ActiveRecord::Schema.define(version: 20170824183110) do t.integer "user_id" end + create_table "delayed_jobs", force: :cascade do |t| + t.integer "priority", default: 0, null: false + t.integer "attempts", default: 0, null: false + t.text "handler", null: false + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority" + create_table "documents", force: :cascade do |t| t.integer "user_id" t.text "body" @@ -829,10 +845,13 @@ ActiveRecord::Schema.define(version: 20170824183110) do t.integer "upload_bandwidth_kb", default: 50000 t.string "secure_code" t.boolean "fluid_preference" + t.string "username" + t.boolean "forum_administrator", default: false, null: false end add_index "users", ["email"], name: "index_users_on_email", unique: true add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + add_index "users", ["username"], name: "index_users_on_username", unique: true create_table "votables", force: :cascade do |t| t.string "name" diff --git a/lib/tasks/data_migrations.rake b/lib/tasks/data_migrations.rake index 8de590b4..ce45db8e 100644 --- a/lib/tasks/data_migrations.rake +++ b/lib/tasks/data_migrations.rake @@ -56,8 +56,8 @@ namespace :data_migrations do ] # Premium - puts "Setting premium users to 1GB" - puts User.where(selected_billing_plan_id: premium_ids).update_all(upload_bandwidth_kb: 1_000_000) # 1GB + puts "Setting premium users to 10GB" + puts User.where(selected_billing_plan_id: premium_ids).update_all(upload_bandwidth_kb: 10_000_000) # 10GB # Starter puts "Setting starter users to 50MB" @@ -68,4 +68,4 @@ namespace :data_migrations do puts "Setting beta users to 250MB" puts User.where(selected_billing_plan_id: beta_id).update_all(upload_bandwidth_kb: 250_000) # 250MB end -end \ No newline at end of file +end diff --git a/lib/tasks/datafill.rake b/lib/tasks/datafill.rake new file mode 100644 index 00000000..eb3b4a5c --- /dev/null +++ b/lib/tasks/datafill.rake @@ -0,0 +1,32 @@ +namespace :datafill do + desc "Create 500 randomized characters" + task characters: :environment do + owner = User.last + + COLORS = ['Red', 'Green', 'Blue', 'Orange', 'White', 'Black', 'Yellow', 'Purple'] + + puts "Creating 500 characters for user #{owner.email}." + 500.times do + print '.' + character = Character.create( + user: owner, + name: [ + 'Alex', 'Bob', 'Carol', 'David', 'Evan', 'Fred', 'George', 'Harry', 'Isaac', 'Jacob', 'Kevin', 'Lars', 'Man', 'Nelly', 'OJ', 'Peter', + 'Dr. Q', 'Rusty', 'Steve', 'Ulysses', 'Victor', 'Wayne', 'Professor X', 'Zed' + ].sample + ' ' + COLORS.sample, + role: ['Protagonist', 'Antagonist', 'Foil', 'Supporting Character', 'Background Character'].sample, + gender: ['Male', 'Female', 'Other'].sample, + age: (1..100).to_a.sample, + height: "#{(1..7).to_a.sample}'#{(1..11).to_a.sample}\"", + weight: (50..350).to_a.sample, + haircolor: COLORS.sample, + eyecolor: COLORS.sample, + skintone: COLORS.sample, + fave_color: COLORS.sample, + ) + character.change_events.update_all(user_id: owner.id) + end + puts + puts "Done." + end +end diff --git a/public/emoji/bowtie.png b/public/emoji/bowtie.png new file mode 100644 index 00000000..28ff0c78 Binary files /dev/null and b/public/emoji/bowtie.png differ diff --git a/public/emoji/feelsgood.png b/public/emoji/feelsgood.png new file mode 100644 index 00000000..bad80a6b Binary files /dev/null and b/public/emoji/feelsgood.png differ diff --git a/public/emoji/finnadie.png b/public/emoji/finnadie.png new file mode 100644 index 00000000..05ba8ac5 Binary files /dev/null and b/public/emoji/finnadie.png differ diff --git a/public/emoji/fu.png b/public/emoji/fu.png new file mode 100644 index 00000000..61a3fee8 Binary files /dev/null and b/public/emoji/fu.png differ diff --git a/public/emoji/goberserk.png b/public/emoji/goberserk.png new file mode 100644 index 00000000..59a742aa Binary files /dev/null and b/public/emoji/goberserk.png differ diff --git a/public/emoji/godmode.png b/public/emoji/godmode.png new file mode 100644 index 00000000..7e75ab20 Binary files /dev/null and b/public/emoji/godmode.png differ diff --git a/public/emoji/hurtrealbad.png b/public/emoji/hurtrealbad.png new file mode 100644 index 00000000..146ef1a6 Binary files /dev/null and b/public/emoji/hurtrealbad.png differ diff --git a/public/emoji/metal.png b/public/emoji/metal.png new file mode 100644 index 00000000..94f1fda2 Binary files /dev/null and b/public/emoji/metal.png differ diff --git a/public/emoji/neckbeard.png b/public/emoji/neckbeard.png new file mode 100644 index 00000000..15108fc9 Binary files /dev/null and b/public/emoji/neckbeard.png differ diff --git a/public/emoji/octocat.png b/public/emoji/octocat.png new file mode 100644 index 00000000..9d74d902 Binary files /dev/null and b/public/emoji/octocat.png differ diff --git a/public/emoji/rage1.png b/public/emoji/rage1.png new file mode 100644 index 00000000..dd2c84f9 Binary files /dev/null and b/public/emoji/rage1.png differ diff --git a/public/emoji/rage2.png b/public/emoji/rage2.png new file mode 100644 index 00000000..f792e063 Binary files /dev/null and b/public/emoji/rage2.png differ diff --git a/public/emoji/rage3.png b/public/emoji/rage3.png new file mode 100644 index 00000000..58764cbc Binary files /dev/null and b/public/emoji/rage3.png differ diff --git a/public/emoji/rage4.png b/public/emoji/rage4.png new file mode 100644 index 00000000..c726c94a Binary files /dev/null and b/public/emoji/rage4.png differ diff --git a/public/emoji/shipit.png b/public/emoji/shipit.png new file mode 100644 index 00000000..a58a47f6 Binary files /dev/null and b/public/emoji/shipit.png differ diff --git a/public/emoji/suspect.png b/public/emoji/suspect.png new file mode 100644 index 00000000..58e8921c Binary files /dev/null and b/public/emoji/suspect.png differ diff --git a/public/emoji/trollface.png b/public/emoji/trollface.png new file mode 100644 index 00000000..cce7c758 Binary files /dev/null and b/public/emoji/trollface.png differ diff --git a/public/emoji/unicode/0023-20e3.png b/public/emoji/unicode/0023-20e3.png new file mode 100644 index 00000000..6765d7d3 Binary files /dev/null and b/public/emoji/unicode/0023-20e3.png differ diff --git a/public/emoji/unicode/0030-20e3.png b/public/emoji/unicode/0030-20e3.png new file mode 100644 index 00000000..15e7446c Binary files /dev/null and b/public/emoji/unicode/0030-20e3.png differ diff --git a/public/emoji/unicode/0031-20e3.png b/public/emoji/unicode/0031-20e3.png new file mode 100644 index 00000000..2d1f9f8c Binary files /dev/null and b/public/emoji/unicode/0031-20e3.png differ diff --git a/public/emoji/unicode/0032-20e3.png b/public/emoji/unicode/0032-20e3.png new file mode 100644 index 00000000..c191f8a3 Binary files /dev/null and b/public/emoji/unicode/0032-20e3.png differ diff --git a/public/emoji/unicode/0033-20e3.png b/public/emoji/unicode/0033-20e3.png new file mode 100644 index 00000000..55644c99 Binary files /dev/null and b/public/emoji/unicode/0033-20e3.png differ diff --git a/public/emoji/unicode/0034-20e3.png b/public/emoji/unicode/0034-20e3.png new file mode 100644 index 00000000..14782ba2 Binary files /dev/null and b/public/emoji/unicode/0034-20e3.png differ diff --git a/public/emoji/unicode/0035-20e3.png b/public/emoji/unicode/0035-20e3.png new file mode 100644 index 00000000..794321aa Binary files /dev/null and b/public/emoji/unicode/0035-20e3.png differ diff --git a/public/emoji/unicode/0036-20e3.png b/public/emoji/unicode/0036-20e3.png new file mode 100644 index 00000000..56880556 Binary files /dev/null and b/public/emoji/unicode/0036-20e3.png differ diff --git a/public/emoji/unicode/0037-20e3.png b/public/emoji/unicode/0037-20e3.png new file mode 100644 index 00000000..354e89ae Binary files /dev/null and b/public/emoji/unicode/0037-20e3.png differ diff --git a/public/emoji/unicode/0038-20e3.png b/public/emoji/unicode/0038-20e3.png new file mode 100644 index 00000000..7bdb4223 Binary files /dev/null and b/public/emoji/unicode/0038-20e3.png differ diff --git a/public/emoji/unicode/0039-20e3.png b/public/emoji/unicode/0039-20e3.png new file mode 100644 index 00000000..8006cc90 Binary files /dev/null and b/public/emoji/unicode/0039-20e3.png differ diff --git a/public/emoji/unicode/00a9.png b/public/emoji/unicode/00a9.png new file mode 100644 index 00000000..d59f580a Binary files /dev/null and b/public/emoji/unicode/00a9.png differ diff --git a/public/emoji/unicode/00ae.png b/public/emoji/unicode/00ae.png new file mode 100644 index 00000000..e5394109 Binary files /dev/null and b/public/emoji/unicode/00ae.png differ diff --git a/public/emoji/unicode/1f004.png b/public/emoji/unicode/1f004.png new file mode 100644 index 00000000..f51ce65f Binary files /dev/null and b/public/emoji/unicode/1f004.png differ diff --git a/public/emoji/unicode/1f0cf.png b/public/emoji/unicode/1f0cf.png new file mode 100644 index 00000000..4c78f361 Binary files /dev/null and b/public/emoji/unicode/1f0cf.png differ diff --git a/public/emoji/unicode/1f170.png b/public/emoji/unicode/1f170.png new file mode 100644 index 00000000..4908a44f Binary files /dev/null and b/public/emoji/unicode/1f170.png differ diff --git a/public/emoji/unicode/1f171.png b/public/emoji/unicode/1f171.png new file mode 100644 index 00000000..8742b3d2 Binary files /dev/null and b/public/emoji/unicode/1f171.png differ diff --git a/public/emoji/unicode/1f17e.png b/public/emoji/unicode/1f17e.png new file mode 100644 index 00000000..d85f9fb9 Binary files /dev/null and b/public/emoji/unicode/1f17e.png differ diff --git a/public/emoji/unicode/1f17f.png b/public/emoji/unicode/1f17f.png new file mode 100644 index 00000000..c24af81c Binary files /dev/null and b/public/emoji/unicode/1f17f.png differ diff --git a/public/emoji/unicode/1f18e.png b/public/emoji/unicode/1f18e.png new file mode 100644 index 00000000..2a522204 Binary files /dev/null and b/public/emoji/unicode/1f18e.png differ diff --git a/public/emoji/unicode/1f191.png b/public/emoji/unicode/1f191.png new file mode 100644 index 00000000..15ac6752 Binary files /dev/null and b/public/emoji/unicode/1f191.png differ diff --git a/public/emoji/unicode/1f192.png b/public/emoji/unicode/1f192.png new file mode 100644 index 00000000..937dcd79 Binary files /dev/null and b/public/emoji/unicode/1f192.png differ diff --git a/public/emoji/unicode/1f193.png b/public/emoji/unicode/1f193.png new file mode 100644 index 00000000..c886cf24 Binary files /dev/null and b/public/emoji/unicode/1f193.png differ diff --git a/public/emoji/unicode/1f194.png b/public/emoji/unicode/1f194.png new file mode 100644 index 00000000..47437a76 Binary files /dev/null and b/public/emoji/unicode/1f194.png differ diff --git a/public/emoji/unicode/1f195.png b/public/emoji/unicode/1f195.png new file mode 100644 index 00000000..28d1570e Binary files /dev/null and b/public/emoji/unicode/1f195.png differ diff --git a/public/emoji/unicode/1f196.png b/public/emoji/unicode/1f196.png new file mode 100644 index 00000000..2ca180ae Binary files /dev/null and b/public/emoji/unicode/1f196.png differ diff --git a/public/emoji/unicode/1f197.png b/public/emoji/unicode/1f197.png new file mode 100644 index 00000000..6433d1a9 Binary files /dev/null and b/public/emoji/unicode/1f197.png differ diff --git a/public/emoji/unicode/1f198.png b/public/emoji/unicode/1f198.png new file mode 100644 index 00000000..e3e16ef7 Binary files /dev/null and b/public/emoji/unicode/1f198.png differ diff --git a/public/emoji/unicode/1f199.png b/public/emoji/unicode/1f199.png new file mode 100644 index 00000000..829219a8 Binary files /dev/null and b/public/emoji/unicode/1f199.png differ diff --git a/public/emoji/unicode/1f19a.png b/public/emoji/unicode/1f19a.png new file mode 100644 index 00000000..86363885 Binary files /dev/null and b/public/emoji/unicode/1f19a.png differ diff --git a/public/emoji/unicode/1f1e8-1f1f3.png b/public/emoji/unicode/1f1e8-1f1f3.png new file mode 100644 index 00000000..b30dcc53 Binary files /dev/null and b/public/emoji/unicode/1f1e8-1f1f3.png differ diff --git a/public/emoji/unicode/1f1e9-1f1ea.png b/public/emoji/unicode/1f1e9-1f1ea.png new file mode 100644 index 00000000..16a28548 Binary files /dev/null and b/public/emoji/unicode/1f1e9-1f1ea.png differ diff --git a/public/emoji/unicode/1f1ea-1f1f8.png b/public/emoji/unicode/1f1ea-1f1f8.png new file mode 100644 index 00000000..71b30bff Binary files /dev/null and b/public/emoji/unicode/1f1ea-1f1f8.png differ diff --git a/public/emoji/unicode/1f1eb-1f1f7.png b/public/emoji/unicode/1f1eb-1f1f7.png new file mode 100644 index 00000000..6311c911 Binary files /dev/null and b/public/emoji/unicode/1f1eb-1f1f7.png differ diff --git a/public/emoji/unicode/1f1ec-1f1e7.png b/public/emoji/unicode/1f1ec-1f1e7.png new file mode 100644 index 00000000..2a62c7a0 Binary files /dev/null and b/public/emoji/unicode/1f1ec-1f1e7.png differ diff --git a/public/emoji/unicode/1f1ee-1f1f9.png b/public/emoji/unicode/1f1ee-1f1f9.png new file mode 100644 index 00000000..70bc9f32 Binary files /dev/null and b/public/emoji/unicode/1f1ee-1f1f9.png differ diff --git a/public/emoji/unicode/1f1ef-1f1f5.png b/public/emoji/unicode/1f1ef-1f1f5.png new file mode 100644 index 00000000..b786efbb Binary files /dev/null and b/public/emoji/unicode/1f1ef-1f1f5.png differ diff --git a/public/emoji/unicode/1f1f0-1f1f7.png b/public/emoji/unicode/1f1f0-1f1f7.png new file mode 100644 index 00000000..b4c0c1b6 Binary files /dev/null and b/public/emoji/unicode/1f1f0-1f1f7.png differ diff --git a/public/emoji/unicode/1f1f7-1f1fa.png b/public/emoji/unicode/1f1f7-1f1fa.png new file mode 100644 index 00000000..55fcf354 Binary files /dev/null and b/public/emoji/unicode/1f1f7-1f1fa.png differ diff --git a/public/emoji/unicode/1f1fa-1f1f8.png b/public/emoji/unicode/1f1fa-1f1f8.png new file mode 100644 index 00000000..38137669 Binary files /dev/null and b/public/emoji/unicode/1f1fa-1f1f8.png differ diff --git a/public/emoji/unicode/1f201.png b/public/emoji/unicode/1f201.png new file mode 100644 index 00000000..3bef28c9 Binary files /dev/null and b/public/emoji/unicode/1f201.png differ diff --git a/public/emoji/unicode/1f202.png b/public/emoji/unicode/1f202.png new file mode 100644 index 00000000..387f098b Binary files /dev/null and b/public/emoji/unicode/1f202.png differ diff --git a/public/emoji/unicode/1f21a.png b/public/emoji/unicode/1f21a.png new file mode 100644 index 00000000..25f694ed Binary files /dev/null and b/public/emoji/unicode/1f21a.png differ diff --git a/public/emoji/unicode/1f22f.png b/public/emoji/unicode/1f22f.png new file mode 100644 index 00000000..6557f567 Binary files /dev/null and b/public/emoji/unicode/1f22f.png differ diff --git a/public/emoji/unicode/1f232.png b/public/emoji/unicode/1f232.png new file mode 100644 index 00000000..f550a573 Binary files /dev/null and b/public/emoji/unicode/1f232.png differ diff --git a/public/emoji/unicode/1f233.png b/public/emoji/unicode/1f233.png new file mode 100644 index 00000000..c05f5cff Binary files /dev/null and b/public/emoji/unicode/1f233.png differ diff --git a/public/emoji/unicode/1f234.png b/public/emoji/unicode/1f234.png new file mode 100644 index 00000000..03ab0d87 Binary files /dev/null and b/public/emoji/unicode/1f234.png differ diff --git a/public/emoji/unicode/1f235.png b/public/emoji/unicode/1f235.png new file mode 100644 index 00000000..5df1cb87 Binary files /dev/null and b/public/emoji/unicode/1f235.png differ diff --git a/public/emoji/unicode/1f236.png b/public/emoji/unicode/1f236.png new file mode 100644 index 00000000..cd8fb3f6 Binary files /dev/null and b/public/emoji/unicode/1f236.png differ diff --git a/public/emoji/unicode/1f237.png b/public/emoji/unicode/1f237.png new file mode 100644 index 00000000..e4dfe5aa Binary files /dev/null and b/public/emoji/unicode/1f237.png differ diff --git a/public/emoji/unicode/1f238.png b/public/emoji/unicode/1f238.png new file mode 100644 index 00000000..fc4a9901 Binary files /dev/null and b/public/emoji/unicode/1f238.png differ diff --git a/public/emoji/unicode/1f239.png b/public/emoji/unicode/1f239.png new file mode 100644 index 00000000..2148253f Binary files /dev/null and b/public/emoji/unicode/1f239.png differ diff --git a/public/emoji/unicode/1f23a.png b/public/emoji/unicode/1f23a.png new file mode 100644 index 00000000..ba946d3f Binary files /dev/null and b/public/emoji/unicode/1f23a.png differ diff --git a/public/emoji/unicode/1f250.png b/public/emoji/unicode/1f250.png new file mode 100644 index 00000000..e79af784 Binary files /dev/null and b/public/emoji/unicode/1f250.png differ diff --git a/public/emoji/unicode/1f251.png b/public/emoji/unicode/1f251.png new file mode 100644 index 00000000..2d200903 Binary files /dev/null and b/public/emoji/unicode/1f251.png differ diff --git a/public/emoji/unicode/1f300.png b/public/emoji/unicode/1f300.png new file mode 100644 index 00000000..6c49f64b Binary files /dev/null and b/public/emoji/unicode/1f300.png differ diff --git a/public/emoji/unicode/1f301.png b/public/emoji/unicode/1f301.png new file mode 100644 index 00000000..3c7b8b04 Binary files /dev/null and b/public/emoji/unicode/1f301.png differ diff --git a/public/emoji/unicode/1f302.png b/public/emoji/unicode/1f302.png new file mode 100644 index 00000000..072c5c21 Binary files /dev/null and b/public/emoji/unicode/1f302.png differ diff --git a/public/emoji/unicode/1f303.png b/public/emoji/unicode/1f303.png new file mode 100644 index 00000000..d4de585f Binary files /dev/null and b/public/emoji/unicode/1f303.png differ diff --git a/public/emoji/unicode/1f304.png b/public/emoji/unicode/1f304.png new file mode 100644 index 00000000..ebc3db14 Binary files /dev/null and b/public/emoji/unicode/1f304.png differ diff --git a/public/emoji/unicode/1f305.png b/public/emoji/unicode/1f305.png new file mode 100644 index 00000000..ec58dcc9 Binary files /dev/null and b/public/emoji/unicode/1f305.png differ diff --git a/public/emoji/unicode/1f306.png b/public/emoji/unicode/1f306.png new file mode 100644 index 00000000..7cb178a2 Binary files /dev/null and b/public/emoji/unicode/1f306.png differ diff --git a/public/emoji/unicode/1f307.png b/public/emoji/unicode/1f307.png new file mode 100644 index 00000000..91ca2a40 Binary files /dev/null and b/public/emoji/unicode/1f307.png differ diff --git a/public/emoji/unicode/1f308.png b/public/emoji/unicode/1f308.png new file mode 100644 index 00000000..6b1faa03 Binary files /dev/null and b/public/emoji/unicode/1f308.png differ diff --git a/public/emoji/unicode/1f309.png b/public/emoji/unicode/1f309.png new file mode 100644 index 00000000..495b06c3 Binary files /dev/null and b/public/emoji/unicode/1f309.png differ diff --git a/public/emoji/unicode/1f30a.png b/public/emoji/unicode/1f30a.png new file mode 100644 index 00000000..f8d520cd Binary files /dev/null and b/public/emoji/unicode/1f30a.png differ diff --git a/public/emoji/unicode/1f30b.png b/public/emoji/unicode/1f30b.png new file mode 100644 index 00000000..9b434539 Binary files /dev/null and b/public/emoji/unicode/1f30b.png differ diff --git a/public/emoji/unicode/1f30c.png b/public/emoji/unicode/1f30c.png new file mode 100644 index 00000000..901090a1 Binary files /dev/null and b/public/emoji/unicode/1f30c.png differ diff --git a/public/emoji/unicode/1f30d.png b/public/emoji/unicode/1f30d.png new file mode 100644 index 00000000..44ce5ecb Binary files /dev/null and b/public/emoji/unicode/1f30d.png differ diff --git a/public/emoji/unicode/1f30e.png b/public/emoji/unicode/1f30e.png new file mode 100644 index 00000000..97d71767 Binary files /dev/null and b/public/emoji/unicode/1f30e.png differ diff --git a/public/emoji/unicode/1f30f.png b/public/emoji/unicode/1f30f.png new file mode 100644 index 00000000..95ec357c Binary files /dev/null and b/public/emoji/unicode/1f30f.png differ diff --git a/public/emoji/unicode/1f310.png b/public/emoji/unicode/1f310.png new file mode 100644 index 00000000..b1986466 Binary files /dev/null and b/public/emoji/unicode/1f310.png differ diff --git a/public/emoji/unicode/1f311.png b/public/emoji/unicode/1f311.png new file mode 100644 index 00000000..540239b1 Binary files /dev/null and b/public/emoji/unicode/1f311.png differ diff --git a/public/emoji/unicode/1f312.png b/public/emoji/unicode/1f312.png new file mode 100644 index 00000000..c8f13dd3 Binary files /dev/null and b/public/emoji/unicode/1f312.png differ diff --git a/public/emoji/unicode/1f313.png b/public/emoji/unicode/1f313.png new file mode 100644 index 00000000..f38c2369 Binary files /dev/null and b/public/emoji/unicode/1f313.png differ diff --git a/public/emoji/unicode/1f314.png b/public/emoji/unicode/1f314.png new file mode 100644 index 00000000..dd8c4845 Binary files /dev/null and b/public/emoji/unicode/1f314.png differ diff --git a/public/emoji/unicode/1f315.png b/public/emoji/unicode/1f315.png new file mode 100644 index 00000000..8ff657a2 Binary files /dev/null and b/public/emoji/unicode/1f315.png differ diff --git a/public/emoji/unicode/1f316.png b/public/emoji/unicode/1f316.png new file mode 100644 index 00000000..8e324ec5 Binary files /dev/null and b/public/emoji/unicode/1f316.png differ diff --git a/public/emoji/unicode/1f317.png b/public/emoji/unicode/1f317.png new file mode 100644 index 00000000..355e3c3f Binary files /dev/null and b/public/emoji/unicode/1f317.png differ diff --git a/public/emoji/unicode/1f318.png b/public/emoji/unicode/1f318.png new file mode 100644 index 00000000..30387780 Binary files /dev/null and b/public/emoji/unicode/1f318.png differ diff --git a/public/emoji/unicode/1f319.png b/public/emoji/unicode/1f319.png new file mode 100644 index 00000000..afdb450d Binary files /dev/null and b/public/emoji/unicode/1f319.png differ diff --git a/public/emoji/unicode/1f31a.png b/public/emoji/unicode/1f31a.png new file mode 100644 index 00000000..b9aff7a0 Binary files /dev/null and b/public/emoji/unicode/1f31a.png differ diff --git a/public/emoji/unicode/1f31b.png b/public/emoji/unicode/1f31b.png new file mode 100644 index 00000000..85ae2ce7 Binary files /dev/null and b/public/emoji/unicode/1f31b.png differ diff --git a/public/emoji/unicode/1f31c.png b/public/emoji/unicode/1f31c.png new file mode 100644 index 00000000..9ece82df Binary files /dev/null and b/public/emoji/unicode/1f31c.png differ diff --git a/public/emoji/unicode/1f31d.png b/public/emoji/unicode/1f31d.png new file mode 100644 index 00000000..94395a40 Binary files /dev/null and b/public/emoji/unicode/1f31d.png differ diff --git a/public/emoji/unicode/1f31e.png b/public/emoji/unicode/1f31e.png new file mode 100644 index 00000000..ee276636 Binary files /dev/null and b/public/emoji/unicode/1f31e.png differ diff --git a/public/emoji/unicode/1f31f.png b/public/emoji/unicode/1f31f.png new file mode 100644 index 00000000..8b40ff4c Binary files /dev/null and b/public/emoji/unicode/1f31f.png differ diff --git a/public/emoji/unicode/1f320.png b/public/emoji/unicode/1f320.png new file mode 100644 index 00000000..097a8424 Binary files /dev/null and b/public/emoji/unicode/1f320.png differ diff --git a/public/emoji/unicode/1f330.png b/public/emoji/unicode/1f330.png new file mode 100644 index 00000000..066fb6bf Binary files /dev/null and b/public/emoji/unicode/1f330.png differ diff --git a/public/emoji/unicode/1f331.png b/public/emoji/unicode/1f331.png new file mode 100644 index 00000000..f0eb5a6b Binary files /dev/null and b/public/emoji/unicode/1f331.png differ diff --git a/public/emoji/unicode/1f332.png b/public/emoji/unicode/1f332.png new file mode 100644 index 00000000..ae8ad103 Binary files /dev/null and b/public/emoji/unicode/1f332.png differ diff --git a/public/emoji/unicode/1f333.png b/public/emoji/unicode/1f333.png new file mode 100644 index 00000000..9bb16bdf Binary files /dev/null and b/public/emoji/unicode/1f333.png differ diff --git a/public/emoji/unicode/1f334.png b/public/emoji/unicode/1f334.png new file mode 100644 index 00000000..d534785e Binary files /dev/null and b/public/emoji/unicode/1f334.png differ diff --git a/public/emoji/unicode/1f335.png b/public/emoji/unicode/1f335.png new file mode 100644 index 00000000..5a2c3cc7 Binary files /dev/null and b/public/emoji/unicode/1f335.png differ diff --git a/public/emoji/unicode/1f337.png b/public/emoji/unicode/1f337.png new file mode 100644 index 00000000..b3ee1102 Binary files /dev/null and b/public/emoji/unicode/1f337.png differ diff --git a/public/emoji/unicode/1f338.png b/public/emoji/unicode/1f338.png new file mode 100644 index 00000000..e0315549 Binary files /dev/null and b/public/emoji/unicode/1f338.png differ diff --git a/public/emoji/unicode/1f339.png b/public/emoji/unicode/1f339.png new file mode 100644 index 00000000..3479fbcb Binary files /dev/null and b/public/emoji/unicode/1f339.png differ diff --git a/public/emoji/unicode/1f33a.png b/public/emoji/unicode/1f33a.png new file mode 100644 index 00000000..32a3774c Binary files /dev/null and b/public/emoji/unicode/1f33a.png differ diff --git a/public/emoji/unicode/1f33b.png b/public/emoji/unicode/1f33b.png new file mode 100644 index 00000000..d9bad194 Binary files /dev/null and b/public/emoji/unicode/1f33b.png differ diff --git a/public/emoji/unicode/1f33c.png b/public/emoji/unicode/1f33c.png new file mode 100644 index 00000000..55a97353 Binary files /dev/null and b/public/emoji/unicode/1f33c.png differ diff --git a/public/emoji/unicode/1f33d.png b/public/emoji/unicode/1f33d.png new file mode 100644 index 00000000..fe5d8b12 Binary files /dev/null and b/public/emoji/unicode/1f33d.png differ diff --git a/public/emoji/unicode/1f33e.png b/public/emoji/unicode/1f33e.png new file mode 100644 index 00000000..a9bba5c2 Binary files /dev/null and b/public/emoji/unicode/1f33e.png differ diff --git a/public/emoji/unicode/1f33f.png b/public/emoji/unicode/1f33f.png new file mode 100644 index 00000000..de1ff1b7 Binary files /dev/null and b/public/emoji/unicode/1f33f.png differ diff --git a/public/emoji/unicode/1f340.png b/public/emoji/unicode/1f340.png new file mode 100644 index 00000000..f2014bea Binary files /dev/null and b/public/emoji/unicode/1f340.png differ diff --git a/public/emoji/unicode/1f341.png b/public/emoji/unicode/1f341.png new file mode 100644 index 00000000..4e9b4720 Binary files /dev/null and b/public/emoji/unicode/1f341.png differ diff --git a/public/emoji/unicode/1f342.png b/public/emoji/unicode/1f342.png new file mode 100644 index 00000000..d49f9c17 Binary files /dev/null and b/public/emoji/unicode/1f342.png differ diff --git a/public/emoji/unicode/1f343.png b/public/emoji/unicode/1f343.png new file mode 100644 index 00000000..801e578e Binary files /dev/null and b/public/emoji/unicode/1f343.png differ diff --git a/public/emoji/unicode/1f344.png b/public/emoji/unicode/1f344.png new file mode 100644 index 00000000..5eeed8e7 Binary files /dev/null and b/public/emoji/unicode/1f344.png differ diff --git a/public/emoji/unicode/1f345.png b/public/emoji/unicode/1f345.png new file mode 100644 index 00000000..a129700b Binary files /dev/null and b/public/emoji/unicode/1f345.png differ diff --git a/public/emoji/unicode/1f346.png b/public/emoji/unicode/1f346.png new file mode 100644 index 00000000..566d6a84 Binary files /dev/null and b/public/emoji/unicode/1f346.png differ diff --git a/public/emoji/unicode/1f347.png b/public/emoji/unicode/1f347.png new file mode 100644 index 00000000..0f9f007a Binary files /dev/null and b/public/emoji/unicode/1f347.png differ diff --git a/public/emoji/unicode/1f348.png b/public/emoji/unicode/1f348.png new file mode 100644 index 00000000..11c13cbb Binary files /dev/null and b/public/emoji/unicode/1f348.png differ diff --git a/public/emoji/unicode/1f349.png b/public/emoji/unicode/1f349.png new file mode 100644 index 00000000..fc212be7 Binary files /dev/null and b/public/emoji/unicode/1f349.png differ diff --git a/public/emoji/unicode/1f34a.png b/public/emoji/unicode/1f34a.png new file mode 100644 index 00000000..fc9d4f82 Binary files /dev/null and b/public/emoji/unicode/1f34a.png differ diff --git a/public/emoji/unicode/1f34b.png b/public/emoji/unicode/1f34b.png new file mode 100644 index 00000000..9814dc95 Binary files /dev/null and b/public/emoji/unicode/1f34b.png differ diff --git a/public/emoji/unicode/1f34c.png b/public/emoji/unicode/1f34c.png new file mode 100644 index 00000000..a0563afb Binary files /dev/null and b/public/emoji/unicode/1f34c.png differ diff --git a/public/emoji/unicode/1f34d.png b/public/emoji/unicode/1f34d.png new file mode 100644 index 00000000..d6f8e287 Binary files /dev/null and b/public/emoji/unicode/1f34d.png differ diff --git a/public/emoji/unicode/1f34e.png b/public/emoji/unicode/1f34e.png new file mode 100644 index 00000000..08aa17b9 Binary files /dev/null and b/public/emoji/unicode/1f34e.png differ diff --git a/public/emoji/unicode/1f34f.png b/public/emoji/unicode/1f34f.png new file mode 100644 index 00000000..337205cd Binary files /dev/null and b/public/emoji/unicode/1f34f.png differ diff --git a/public/emoji/unicode/1f350.png b/public/emoji/unicode/1f350.png new file mode 100644 index 00000000..f24aca8c Binary files /dev/null and b/public/emoji/unicode/1f350.png differ diff --git a/public/emoji/unicode/1f351.png b/public/emoji/unicode/1f351.png new file mode 100644 index 00000000..ee2139ec Binary files /dev/null and b/public/emoji/unicode/1f351.png differ diff --git a/public/emoji/unicode/1f352.png b/public/emoji/unicode/1f352.png new file mode 100644 index 00000000..8d3e044f Binary files /dev/null and b/public/emoji/unicode/1f352.png differ diff --git a/public/emoji/unicode/1f353.png b/public/emoji/unicode/1f353.png new file mode 100644 index 00000000..13eb827a Binary files /dev/null and b/public/emoji/unicode/1f353.png differ diff --git a/public/emoji/unicode/1f354.png b/public/emoji/unicode/1f354.png new file mode 100644 index 00000000..9f1a3fdf Binary files /dev/null and b/public/emoji/unicode/1f354.png differ diff --git a/public/emoji/unicode/1f355.png b/public/emoji/unicode/1f355.png new file mode 100644 index 00000000..460367d0 Binary files /dev/null and b/public/emoji/unicode/1f355.png differ diff --git a/public/emoji/unicode/1f356.png b/public/emoji/unicode/1f356.png new file mode 100644 index 00000000..d6b311b6 Binary files /dev/null and b/public/emoji/unicode/1f356.png differ diff --git a/public/emoji/unicode/1f357.png b/public/emoji/unicode/1f357.png new file mode 100644 index 00000000..43ad8596 Binary files /dev/null and b/public/emoji/unicode/1f357.png differ diff --git a/public/emoji/unicode/1f358.png b/public/emoji/unicode/1f358.png new file mode 100644 index 00000000..954c901e Binary files /dev/null and b/public/emoji/unicode/1f358.png differ diff --git a/public/emoji/unicode/1f359.png b/public/emoji/unicode/1f359.png new file mode 100644 index 00000000..04f8a880 Binary files /dev/null and b/public/emoji/unicode/1f359.png differ diff --git a/public/emoji/unicode/1f35a.png b/public/emoji/unicode/1f35a.png new file mode 100644 index 00000000..f4773ede Binary files /dev/null and b/public/emoji/unicode/1f35a.png differ diff --git a/public/emoji/unicode/1f35b.png b/public/emoji/unicode/1f35b.png new file mode 100644 index 00000000..7983c706 Binary files /dev/null and b/public/emoji/unicode/1f35b.png differ diff --git a/public/emoji/unicode/1f35c.png b/public/emoji/unicode/1f35c.png new file mode 100644 index 00000000..78dc7d53 Binary files /dev/null and b/public/emoji/unicode/1f35c.png differ diff --git a/public/emoji/unicode/1f35d.png b/public/emoji/unicode/1f35d.png new file mode 100644 index 00000000..08de243f Binary files /dev/null and b/public/emoji/unicode/1f35d.png differ diff --git a/public/emoji/unicode/1f35e.png b/public/emoji/unicode/1f35e.png new file mode 100644 index 00000000..7e7c6375 Binary files /dev/null and b/public/emoji/unicode/1f35e.png differ diff --git a/public/emoji/unicode/1f35f.png b/public/emoji/unicode/1f35f.png new file mode 100644 index 00000000..cfef6696 Binary files /dev/null and b/public/emoji/unicode/1f35f.png differ diff --git a/public/emoji/unicode/1f360.png b/public/emoji/unicode/1f360.png new file mode 100644 index 00000000..32117fa9 Binary files /dev/null and b/public/emoji/unicode/1f360.png differ diff --git a/public/emoji/unicode/1f361.png b/public/emoji/unicode/1f361.png new file mode 100644 index 00000000..2d042aeb Binary files /dev/null and b/public/emoji/unicode/1f361.png differ diff --git a/public/emoji/unicode/1f362.png b/public/emoji/unicode/1f362.png new file mode 100644 index 00000000..73add1c7 Binary files /dev/null and b/public/emoji/unicode/1f362.png differ diff --git a/public/emoji/unicode/1f363.png b/public/emoji/unicode/1f363.png new file mode 100644 index 00000000..0d179bd9 Binary files /dev/null and b/public/emoji/unicode/1f363.png differ diff --git a/public/emoji/unicode/1f364.png b/public/emoji/unicode/1f364.png new file mode 100644 index 00000000..c8c284bf Binary files /dev/null and b/public/emoji/unicode/1f364.png differ diff --git a/public/emoji/unicode/1f365.png b/public/emoji/unicode/1f365.png new file mode 100644 index 00000000..a8f22614 Binary files /dev/null and b/public/emoji/unicode/1f365.png differ diff --git a/public/emoji/unicode/1f366.png b/public/emoji/unicode/1f366.png new file mode 100644 index 00000000..871ce097 Binary files /dev/null and b/public/emoji/unicode/1f366.png differ diff --git a/public/emoji/unicode/1f367.png b/public/emoji/unicode/1f367.png new file mode 100644 index 00000000..0d0b382c Binary files /dev/null and b/public/emoji/unicode/1f367.png differ diff --git a/public/emoji/unicode/1f368.png b/public/emoji/unicode/1f368.png new file mode 100644 index 00000000..190be016 Binary files /dev/null and b/public/emoji/unicode/1f368.png differ diff --git a/public/emoji/unicode/1f369.png b/public/emoji/unicode/1f369.png new file mode 100644 index 00000000..ccf86912 Binary files /dev/null and b/public/emoji/unicode/1f369.png differ diff --git a/public/emoji/unicode/1f36a.png b/public/emoji/unicode/1f36a.png new file mode 100644 index 00000000..653edb25 Binary files /dev/null and b/public/emoji/unicode/1f36a.png differ diff --git a/public/emoji/unicode/1f36b.png b/public/emoji/unicode/1f36b.png new file mode 100644 index 00000000..c7ec19d0 Binary files /dev/null and b/public/emoji/unicode/1f36b.png differ diff --git a/public/emoji/unicode/1f36c.png b/public/emoji/unicode/1f36c.png new file mode 100644 index 00000000..33722f23 Binary files /dev/null and b/public/emoji/unicode/1f36c.png differ diff --git a/public/emoji/unicode/1f36d.png b/public/emoji/unicode/1f36d.png new file mode 100644 index 00000000..ba55e709 Binary files /dev/null and b/public/emoji/unicode/1f36d.png differ diff --git a/public/emoji/unicode/1f36e.png b/public/emoji/unicode/1f36e.png new file mode 100644 index 00000000..9f843b4c Binary files /dev/null and b/public/emoji/unicode/1f36e.png differ diff --git a/public/emoji/unicode/1f36f.png b/public/emoji/unicode/1f36f.png new file mode 100644 index 00000000..73278898 Binary files /dev/null and b/public/emoji/unicode/1f36f.png differ diff --git a/public/emoji/unicode/1f370.png b/public/emoji/unicode/1f370.png new file mode 100644 index 00000000..efeb9b4b Binary files /dev/null and b/public/emoji/unicode/1f370.png differ diff --git a/public/emoji/unicode/1f371.png b/public/emoji/unicode/1f371.png new file mode 100644 index 00000000..c6d99e89 Binary files /dev/null and b/public/emoji/unicode/1f371.png differ diff --git a/public/emoji/unicode/1f372.png b/public/emoji/unicode/1f372.png new file mode 100644 index 00000000..6e80b4a9 Binary files /dev/null and b/public/emoji/unicode/1f372.png differ diff --git a/public/emoji/unicode/1f373.png b/public/emoji/unicode/1f373.png new file mode 100644 index 00000000..c3de6ae4 Binary files /dev/null and b/public/emoji/unicode/1f373.png differ diff --git a/public/emoji/unicode/1f374.png b/public/emoji/unicode/1f374.png new file mode 100644 index 00000000..8ba4bc65 Binary files /dev/null and b/public/emoji/unicode/1f374.png differ diff --git a/public/emoji/unicode/1f375.png b/public/emoji/unicode/1f375.png new file mode 100644 index 00000000..3ece0b70 Binary files /dev/null and b/public/emoji/unicode/1f375.png differ diff --git a/public/emoji/unicode/1f376.png b/public/emoji/unicode/1f376.png new file mode 100644 index 00000000..1f69907e Binary files /dev/null and b/public/emoji/unicode/1f376.png differ diff --git a/public/emoji/unicode/1f377.png b/public/emoji/unicode/1f377.png new file mode 100644 index 00000000..82b0f000 Binary files /dev/null and b/public/emoji/unicode/1f377.png differ diff --git a/public/emoji/unicode/1f378.png b/public/emoji/unicode/1f378.png new file mode 100644 index 00000000..28b45ea5 Binary files /dev/null and b/public/emoji/unicode/1f378.png differ diff --git a/public/emoji/unicode/1f379.png b/public/emoji/unicode/1f379.png new file mode 100644 index 00000000..55ca9eed Binary files /dev/null and b/public/emoji/unicode/1f379.png differ diff --git a/public/emoji/unicode/1f37a.png b/public/emoji/unicode/1f37a.png new file mode 100644 index 00000000..cd78bed7 Binary files /dev/null and b/public/emoji/unicode/1f37a.png differ diff --git a/public/emoji/unicode/1f37b.png b/public/emoji/unicode/1f37b.png new file mode 100644 index 00000000..cc5e4ab5 Binary files /dev/null and b/public/emoji/unicode/1f37b.png differ diff --git a/public/emoji/unicode/1f37c.png b/public/emoji/unicode/1f37c.png new file mode 100644 index 00000000..1b2cfe5e Binary files /dev/null and b/public/emoji/unicode/1f37c.png differ diff --git a/public/emoji/unicode/1f380.png b/public/emoji/unicode/1f380.png new file mode 100644 index 00000000..63ee5ba5 Binary files /dev/null and b/public/emoji/unicode/1f380.png differ diff --git a/public/emoji/unicode/1f381.png b/public/emoji/unicode/1f381.png new file mode 100644 index 00000000..552cfdc2 Binary files /dev/null and b/public/emoji/unicode/1f381.png differ diff --git a/public/emoji/unicode/1f382.png b/public/emoji/unicode/1f382.png new file mode 100644 index 00000000..36e8edcb Binary files /dev/null and b/public/emoji/unicode/1f382.png differ diff --git a/public/emoji/unicode/1f383.png b/public/emoji/unicode/1f383.png new file mode 100644 index 00000000..1f7667ea Binary files /dev/null and b/public/emoji/unicode/1f383.png differ diff --git a/public/emoji/unicode/1f384.png b/public/emoji/unicode/1f384.png new file mode 100644 index 00000000..d813b959 Binary files /dev/null and b/public/emoji/unicode/1f384.png differ diff --git a/public/emoji/unicode/1f385.png b/public/emoji/unicode/1f385.png new file mode 100644 index 00000000..a2240c07 Binary files /dev/null and b/public/emoji/unicode/1f385.png differ diff --git a/public/emoji/unicode/1f386.png b/public/emoji/unicode/1f386.png new file mode 100644 index 00000000..b4eccd57 Binary files /dev/null and b/public/emoji/unicode/1f386.png differ diff --git a/public/emoji/unicode/1f387.png b/public/emoji/unicode/1f387.png new file mode 100644 index 00000000..4aabd7e0 Binary files /dev/null and b/public/emoji/unicode/1f387.png differ diff --git a/public/emoji/unicode/1f388.png b/public/emoji/unicode/1f388.png new file mode 100644 index 00000000..a4d3207b Binary files /dev/null and b/public/emoji/unicode/1f388.png differ diff --git a/public/emoji/unicode/1f389.png b/public/emoji/unicode/1f389.png new file mode 100644 index 00000000..7411b526 Binary files /dev/null and b/public/emoji/unicode/1f389.png differ diff --git a/public/emoji/unicode/1f38a.png b/public/emoji/unicode/1f38a.png new file mode 100644 index 00000000..bd293e3d Binary files /dev/null and b/public/emoji/unicode/1f38a.png differ diff --git a/public/emoji/unicode/1f38b.png b/public/emoji/unicode/1f38b.png new file mode 100644 index 00000000..47334641 Binary files /dev/null and b/public/emoji/unicode/1f38b.png differ diff --git a/public/emoji/unicode/1f38c.png b/public/emoji/unicode/1f38c.png new file mode 100644 index 00000000..2ffbb262 Binary files /dev/null and b/public/emoji/unicode/1f38c.png differ diff --git a/public/emoji/unicode/1f38d.png b/public/emoji/unicode/1f38d.png new file mode 100644 index 00000000..fc858d0f Binary files /dev/null and b/public/emoji/unicode/1f38d.png differ diff --git a/public/emoji/unicode/1f38e.png b/public/emoji/unicode/1f38e.png new file mode 100644 index 00000000..47ce3390 Binary files /dev/null and b/public/emoji/unicode/1f38e.png differ diff --git a/public/emoji/unicode/1f38f.png b/public/emoji/unicode/1f38f.png new file mode 100644 index 00000000..540164e8 Binary files /dev/null and b/public/emoji/unicode/1f38f.png differ diff --git a/public/emoji/unicode/1f390.png b/public/emoji/unicode/1f390.png new file mode 100644 index 00000000..efacf5dd Binary files /dev/null and b/public/emoji/unicode/1f390.png differ diff --git a/public/emoji/unicode/1f391.png b/public/emoji/unicode/1f391.png new file mode 100644 index 00000000..14361988 Binary files /dev/null and b/public/emoji/unicode/1f391.png differ diff --git a/public/emoji/unicode/1f392.png b/public/emoji/unicode/1f392.png new file mode 100644 index 00000000..edfb19ae Binary files /dev/null and b/public/emoji/unicode/1f392.png differ diff --git a/public/emoji/unicode/1f393.png b/public/emoji/unicode/1f393.png new file mode 100644 index 00000000..2e811b09 Binary files /dev/null and b/public/emoji/unicode/1f393.png differ diff --git a/public/emoji/unicode/1f3a0.png b/public/emoji/unicode/1f3a0.png new file mode 100644 index 00000000..765d2c0a Binary files /dev/null and b/public/emoji/unicode/1f3a0.png differ diff --git a/public/emoji/unicode/1f3a1.png b/public/emoji/unicode/1f3a1.png new file mode 100644 index 00000000..54a1dcfa Binary files /dev/null and b/public/emoji/unicode/1f3a1.png differ diff --git a/public/emoji/unicode/1f3a2.png b/public/emoji/unicode/1f3a2.png new file mode 100644 index 00000000..9180b986 Binary files /dev/null and b/public/emoji/unicode/1f3a2.png differ diff --git a/public/emoji/unicode/1f3a3.png b/public/emoji/unicode/1f3a3.png new file mode 100644 index 00000000..d84609c3 Binary files /dev/null and b/public/emoji/unicode/1f3a3.png differ diff --git a/public/emoji/unicode/1f3a4.png b/public/emoji/unicode/1f3a4.png new file mode 100644 index 00000000..ce19a2bb Binary files /dev/null and b/public/emoji/unicode/1f3a4.png differ diff --git a/public/emoji/unicode/1f3a5.png b/public/emoji/unicode/1f3a5.png new file mode 100644 index 00000000..9c143840 Binary files /dev/null and b/public/emoji/unicode/1f3a5.png differ diff --git a/public/emoji/unicode/1f3a6.png b/public/emoji/unicode/1f3a6.png new file mode 100644 index 00000000..a990ccf9 Binary files /dev/null and b/public/emoji/unicode/1f3a6.png differ diff --git a/public/emoji/unicode/1f3a7.png b/public/emoji/unicode/1f3a7.png new file mode 100644 index 00000000..ad83000e Binary files /dev/null and b/public/emoji/unicode/1f3a7.png differ diff --git a/public/emoji/unicode/1f3a8.png b/public/emoji/unicode/1f3a8.png new file mode 100644 index 00000000..d45212b0 Binary files /dev/null and b/public/emoji/unicode/1f3a8.png differ diff --git a/public/emoji/unicode/1f3a9.png b/public/emoji/unicode/1f3a9.png new file mode 100644 index 00000000..7d27134d Binary files /dev/null and b/public/emoji/unicode/1f3a9.png differ diff --git a/public/emoji/unicode/1f3aa.png b/public/emoji/unicode/1f3aa.png new file mode 100644 index 00000000..4af8719a Binary files /dev/null and b/public/emoji/unicode/1f3aa.png differ diff --git a/public/emoji/unicode/1f3ab.png b/public/emoji/unicode/1f3ab.png new file mode 100644 index 00000000..cdacf1a7 Binary files /dev/null and b/public/emoji/unicode/1f3ab.png differ diff --git a/public/emoji/unicode/1f3ac.png b/public/emoji/unicode/1f3ac.png new file mode 100644 index 00000000..4e1dc111 Binary files /dev/null and b/public/emoji/unicode/1f3ac.png differ diff --git a/public/emoji/unicode/1f3ad.png b/public/emoji/unicode/1f3ad.png new file mode 100644 index 00000000..899fbe5a Binary files /dev/null and b/public/emoji/unicode/1f3ad.png differ diff --git a/public/emoji/unicode/1f3ae.png b/public/emoji/unicode/1f3ae.png new file mode 100644 index 00000000..59d45bae Binary files /dev/null and b/public/emoji/unicode/1f3ae.png differ diff --git a/public/emoji/unicode/1f3af.png b/public/emoji/unicode/1f3af.png new file mode 100644 index 00000000..0438fe54 Binary files /dev/null and b/public/emoji/unicode/1f3af.png differ diff --git a/public/emoji/unicode/1f3b0.png b/public/emoji/unicode/1f3b0.png new file mode 100644 index 00000000..26f11483 Binary files /dev/null and b/public/emoji/unicode/1f3b0.png differ diff --git a/public/emoji/unicode/1f3b1.png b/public/emoji/unicode/1f3b1.png new file mode 100644 index 00000000..c2c710d4 Binary files /dev/null and b/public/emoji/unicode/1f3b1.png differ diff --git a/public/emoji/unicode/1f3b2.png b/public/emoji/unicode/1f3b2.png new file mode 100644 index 00000000..4136e78e Binary files /dev/null and b/public/emoji/unicode/1f3b2.png differ diff --git a/public/emoji/unicode/1f3b3.png b/public/emoji/unicode/1f3b3.png new file mode 100644 index 00000000..13d8ece2 Binary files /dev/null and b/public/emoji/unicode/1f3b3.png differ diff --git a/public/emoji/unicode/1f3b4.png b/public/emoji/unicode/1f3b4.png new file mode 100644 index 00000000..cc46a6a1 Binary files /dev/null and b/public/emoji/unicode/1f3b4.png differ diff --git a/public/emoji/unicode/1f3b5.png b/public/emoji/unicode/1f3b5.png new file mode 100644 index 00000000..68b261bc Binary files /dev/null and b/public/emoji/unicode/1f3b5.png differ diff --git a/public/emoji/unicode/1f3b6.png b/public/emoji/unicode/1f3b6.png new file mode 100644 index 00000000..a13147fa Binary files /dev/null and b/public/emoji/unicode/1f3b6.png differ diff --git a/public/emoji/unicode/1f3b7.png b/public/emoji/unicode/1f3b7.png new file mode 100644 index 00000000..011559a7 Binary files /dev/null and b/public/emoji/unicode/1f3b7.png differ diff --git a/public/emoji/unicode/1f3b8.png b/public/emoji/unicode/1f3b8.png new file mode 100644 index 00000000..2b7fa43c Binary files /dev/null and b/public/emoji/unicode/1f3b8.png differ diff --git a/public/emoji/unicode/1f3b9.png b/public/emoji/unicode/1f3b9.png new file mode 100644 index 00000000..93647a4a Binary files /dev/null and b/public/emoji/unicode/1f3b9.png differ diff --git a/public/emoji/unicode/1f3ba.png b/public/emoji/unicode/1f3ba.png new file mode 100644 index 00000000..8d4703fc Binary files /dev/null and b/public/emoji/unicode/1f3ba.png differ diff --git a/public/emoji/unicode/1f3bb.png b/public/emoji/unicode/1f3bb.png new file mode 100644 index 00000000..0dba5ba2 Binary files /dev/null and b/public/emoji/unicode/1f3bb.png differ diff --git a/public/emoji/unicode/1f3bc.png b/public/emoji/unicode/1f3bc.png new file mode 100644 index 00000000..0c927d32 Binary files /dev/null and b/public/emoji/unicode/1f3bc.png differ diff --git a/public/emoji/unicode/1f3bd.png b/public/emoji/unicode/1f3bd.png new file mode 100644 index 00000000..0d68bba0 Binary files /dev/null and b/public/emoji/unicode/1f3bd.png differ diff --git a/public/emoji/unicode/1f3be.png b/public/emoji/unicode/1f3be.png new file mode 100644 index 00000000..278d904e Binary files /dev/null and b/public/emoji/unicode/1f3be.png differ diff --git a/public/emoji/unicode/1f3bf.png b/public/emoji/unicode/1f3bf.png new file mode 100644 index 00000000..c97de3ed Binary files /dev/null and b/public/emoji/unicode/1f3bf.png differ diff --git a/public/emoji/unicode/1f3c0.png b/public/emoji/unicode/1f3c0.png new file mode 100644 index 00000000..ef694bec Binary files /dev/null and b/public/emoji/unicode/1f3c0.png differ diff --git a/public/emoji/unicode/1f3c1.png b/public/emoji/unicode/1f3c1.png new file mode 100644 index 00000000..ead4a68d Binary files /dev/null and b/public/emoji/unicode/1f3c1.png differ diff --git a/public/emoji/unicode/1f3c2.png b/public/emoji/unicode/1f3c2.png new file mode 100644 index 00000000..aeda5c8d Binary files /dev/null and b/public/emoji/unicode/1f3c2.png differ diff --git a/public/emoji/unicode/1f3c3.png b/public/emoji/unicode/1f3c3.png new file mode 100644 index 00000000..1ecfd905 Binary files /dev/null and b/public/emoji/unicode/1f3c3.png differ diff --git a/public/emoji/unicode/1f3c4.png b/public/emoji/unicode/1f3c4.png new file mode 100644 index 00000000..b067e8cb Binary files /dev/null and b/public/emoji/unicode/1f3c4.png differ diff --git a/public/emoji/unicode/1f3c6.png b/public/emoji/unicode/1f3c6.png new file mode 100644 index 00000000..95d3b63f Binary files /dev/null and b/public/emoji/unicode/1f3c6.png differ diff --git a/public/emoji/unicode/1f3c7.png b/public/emoji/unicode/1f3c7.png new file mode 100644 index 00000000..e3bbaec1 Binary files /dev/null and b/public/emoji/unicode/1f3c7.png differ diff --git a/public/emoji/unicode/1f3c8.png b/public/emoji/unicode/1f3c8.png new file mode 100644 index 00000000..0e4e168f Binary files /dev/null and b/public/emoji/unicode/1f3c8.png differ diff --git a/public/emoji/unicode/1f3c9.png b/public/emoji/unicode/1f3c9.png new file mode 100644 index 00000000..f8db67d7 Binary files /dev/null and b/public/emoji/unicode/1f3c9.png differ diff --git a/public/emoji/unicode/1f3ca.png b/public/emoji/unicode/1f3ca.png new file mode 100644 index 00000000..d3878a06 Binary files /dev/null and b/public/emoji/unicode/1f3ca.png differ diff --git a/public/emoji/unicode/1f3e0.png b/public/emoji/unicode/1f3e0.png new file mode 100644 index 00000000..95b9ee09 Binary files /dev/null and b/public/emoji/unicode/1f3e0.png differ diff --git a/public/emoji/unicode/1f3e1.png b/public/emoji/unicode/1f3e1.png new file mode 100644 index 00000000..eccbfe94 Binary files /dev/null and b/public/emoji/unicode/1f3e1.png differ diff --git a/public/emoji/unicode/1f3e2.png b/public/emoji/unicode/1f3e2.png new file mode 100644 index 00000000..3f20b564 Binary files /dev/null and b/public/emoji/unicode/1f3e2.png differ diff --git a/public/emoji/unicode/1f3e3.png b/public/emoji/unicode/1f3e3.png new file mode 100644 index 00000000..43b59e30 Binary files /dev/null and b/public/emoji/unicode/1f3e3.png differ diff --git a/public/emoji/unicode/1f3e4.png b/public/emoji/unicode/1f3e4.png new file mode 100644 index 00000000..0f65b145 Binary files /dev/null and b/public/emoji/unicode/1f3e4.png differ diff --git a/public/emoji/unicode/1f3e5.png b/public/emoji/unicode/1f3e5.png new file mode 100644 index 00000000..c05c4937 Binary files /dev/null and b/public/emoji/unicode/1f3e5.png differ diff --git a/public/emoji/unicode/1f3e6.png b/public/emoji/unicode/1f3e6.png new file mode 100644 index 00000000..1faa8777 Binary files /dev/null and b/public/emoji/unicode/1f3e6.png differ diff --git a/public/emoji/unicode/1f3e7.png b/public/emoji/unicode/1f3e7.png new file mode 100644 index 00000000..c2846e79 Binary files /dev/null and b/public/emoji/unicode/1f3e7.png differ diff --git a/public/emoji/unicode/1f3e8.png b/public/emoji/unicode/1f3e8.png new file mode 100644 index 00000000..d29f276a Binary files /dev/null and b/public/emoji/unicode/1f3e8.png differ diff --git a/public/emoji/unicode/1f3e9.png b/public/emoji/unicode/1f3e9.png new file mode 100644 index 00000000..44d7db82 Binary files /dev/null and b/public/emoji/unicode/1f3e9.png differ diff --git a/public/emoji/unicode/1f3ea.png b/public/emoji/unicode/1f3ea.png new file mode 100644 index 00000000..671696c2 Binary files /dev/null and b/public/emoji/unicode/1f3ea.png differ diff --git a/public/emoji/unicode/1f3eb.png b/public/emoji/unicode/1f3eb.png new file mode 100644 index 00000000..afd922bf Binary files /dev/null and b/public/emoji/unicode/1f3eb.png differ diff --git a/public/emoji/unicode/1f3ec.png b/public/emoji/unicode/1f3ec.png new file mode 100644 index 00000000..68d959c5 Binary files /dev/null and b/public/emoji/unicode/1f3ec.png differ diff --git a/public/emoji/unicode/1f3ed.png b/public/emoji/unicode/1f3ed.png new file mode 100644 index 00000000..64046347 Binary files /dev/null and b/public/emoji/unicode/1f3ed.png differ diff --git a/public/emoji/unicode/1f3ee.png b/public/emoji/unicode/1f3ee.png new file mode 100644 index 00000000..18730ad5 Binary files /dev/null and b/public/emoji/unicode/1f3ee.png differ diff --git a/public/emoji/unicode/1f3ef.png b/public/emoji/unicode/1f3ef.png new file mode 100644 index 00000000..f225ab21 Binary files /dev/null and b/public/emoji/unicode/1f3ef.png differ diff --git a/public/emoji/unicode/1f3f0.png b/public/emoji/unicode/1f3f0.png new file mode 100644 index 00000000..8229b8a8 Binary files /dev/null and b/public/emoji/unicode/1f3f0.png differ diff --git a/public/emoji/unicode/1f400.png b/public/emoji/unicode/1f400.png new file mode 100644 index 00000000..1c463dfd Binary files /dev/null and b/public/emoji/unicode/1f400.png differ diff --git a/public/emoji/unicode/1f401.png b/public/emoji/unicode/1f401.png new file mode 100644 index 00000000..2d777e5e Binary files /dev/null and b/public/emoji/unicode/1f401.png differ diff --git a/public/emoji/unicode/1f402.png b/public/emoji/unicode/1f402.png new file mode 100644 index 00000000..f7669802 Binary files /dev/null and b/public/emoji/unicode/1f402.png differ diff --git a/public/emoji/unicode/1f403.png b/public/emoji/unicode/1f403.png new file mode 100644 index 00000000..3bcde3ed Binary files /dev/null and b/public/emoji/unicode/1f403.png differ diff --git a/public/emoji/unicode/1f404.png b/public/emoji/unicode/1f404.png new file mode 100644 index 00000000..594c9215 Binary files /dev/null and b/public/emoji/unicode/1f404.png differ diff --git a/public/emoji/unicode/1f405.png b/public/emoji/unicode/1f405.png new file mode 100644 index 00000000..b0c7d8dc Binary files /dev/null and b/public/emoji/unicode/1f405.png differ diff --git a/public/emoji/unicode/1f406.png b/public/emoji/unicode/1f406.png new file mode 100644 index 00000000..8abfc4a2 Binary files /dev/null and b/public/emoji/unicode/1f406.png differ diff --git a/public/emoji/unicode/1f407.png b/public/emoji/unicode/1f407.png new file mode 100644 index 00000000..5bc993e7 Binary files /dev/null and b/public/emoji/unicode/1f407.png differ diff --git a/public/emoji/unicode/1f408.png b/public/emoji/unicode/1f408.png new file mode 100644 index 00000000..977c992c Binary files /dev/null and b/public/emoji/unicode/1f408.png differ diff --git a/public/emoji/unicode/1f409.png b/public/emoji/unicode/1f409.png new file mode 100644 index 00000000..e399d60e Binary files /dev/null and b/public/emoji/unicode/1f409.png differ diff --git a/public/emoji/unicode/1f40a.png b/public/emoji/unicode/1f40a.png new file mode 100644 index 00000000..7435d5ab Binary files /dev/null and b/public/emoji/unicode/1f40a.png differ diff --git a/public/emoji/unicode/1f40b.png b/public/emoji/unicode/1f40b.png new file mode 100644 index 00000000..4af657b2 Binary files /dev/null and b/public/emoji/unicode/1f40b.png differ diff --git a/public/emoji/unicode/1f40c.png b/public/emoji/unicode/1f40c.png new file mode 100644 index 00000000..e75e69a8 Binary files /dev/null and b/public/emoji/unicode/1f40c.png differ diff --git a/public/emoji/unicode/1f40d.png b/public/emoji/unicode/1f40d.png new file mode 100644 index 00000000..ef58933e Binary files /dev/null and b/public/emoji/unicode/1f40d.png differ diff --git a/public/emoji/unicode/1f40e.png b/public/emoji/unicode/1f40e.png new file mode 100644 index 00000000..4d09c64d Binary files /dev/null and b/public/emoji/unicode/1f40e.png differ diff --git a/public/emoji/unicode/1f40f.png b/public/emoji/unicode/1f40f.png new file mode 100644 index 00000000..5ea7bfbc Binary files /dev/null and b/public/emoji/unicode/1f40f.png differ diff --git a/public/emoji/unicode/1f410.png b/public/emoji/unicode/1f410.png new file mode 100644 index 00000000..4be9cf30 Binary files /dev/null and b/public/emoji/unicode/1f410.png differ diff --git a/public/emoji/unicode/1f411.png b/public/emoji/unicode/1f411.png new file mode 100644 index 00000000..c7277d28 Binary files /dev/null and b/public/emoji/unicode/1f411.png differ diff --git a/public/emoji/unicode/1f412.png b/public/emoji/unicode/1f412.png new file mode 100644 index 00000000..64070359 Binary files /dev/null and b/public/emoji/unicode/1f412.png differ diff --git a/public/emoji/unicode/1f413.png b/public/emoji/unicode/1f413.png new file mode 100644 index 00000000..fab23ad3 Binary files /dev/null and b/public/emoji/unicode/1f413.png differ diff --git a/public/emoji/unicode/1f414.png b/public/emoji/unicode/1f414.png new file mode 100644 index 00000000..6d25c0ef Binary files /dev/null and b/public/emoji/unicode/1f414.png differ diff --git a/public/emoji/unicode/1f415.png b/public/emoji/unicode/1f415.png new file mode 100644 index 00000000..c7f6a24a Binary files /dev/null and b/public/emoji/unicode/1f415.png differ diff --git a/public/emoji/unicode/1f416.png b/public/emoji/unicode/1f416.png new file mode 100644 index 00000000..fec3374d Binary files /dev/null and b/public/emoji/unicode/1f416.png differ diff --git a/public/emoji/unicode/1f417.png b/public/emoji/unicode/1f417.png new file mode 100644 index 00000000..8196ad4a Binary files /dev/null and b/public/emoji/unicode/1f417.png differ diff --git a/public/emoji/unicode/1f418.png b/public/emoji/unicode/1f418.png new file mode 100644 index 00000000..5ca04570 Binary files /dev/null and b/public/emoji/unicode/1f418.png differ diff --git a/public/emoji/unicode/1f419.png b/public/emoji/unicode/1f419.png new file mode 100644 index 00000000..52ce64b4 Binary files /dev/null and b/public/emoji/unicode/1f419.png differ diff --git a/public/emoji/unicode/1f41a.png b/public/emoji/unicode/1f41a.png new file mode 100644 index 00000000..3145b564 Binary files /dev/null and b/public/emoji/unicode/1f41a.png differ diff --git a/public/emoji/unicode/1f41b.png b/public/emoji/unicode/1f41b.png new file mode 100644 index 00000000..c2eaf7a7 Binary files /dev/null and b/public/emoji/unicode/1f41b.png differ diff --git a/public/emoji/unicode/1f41c.png b/public/emoji/unicode/1f41c.png new file mode 100644 index 00000000..b92d1cc1 Binary files /dev/null and b/public/emoji/unicode/1f41c.png differ diff --git a/public/emoji/unicode/1f41d.png b/public/emoji/unicode/1f41d.png new file mode 100644 index 00000000..f5373395 Binary files /dev/null and b/public/emoji/unicode/1f41d.png differ diff --git a/public/emoji/unicode/1f41e.png b/public/emoji/unicode/1f41e.png new file mode 100644 index 00000000..222577ca Binary files /dev/null and b/public/emoji/unicode/1f41e.png differ diff --git a/public/emoji/unicode/1f41f.png b/public/emoji/unicode/1f41f.png new file mode 100644 index 00000000..dc2a3f52 Binary files /dev/null and b/public/emoji/unicode/1f41f.png differ diff --git a/public/emoji/unicode/1f420.png b/public/emoji/unicode/1f420.png new file mode 100644 index 00000000..a6d73498 Binary files /dev/null and b/public/emoji/unicode/1f420.png differ diff --git a/public/emoji/unicode/1f421.png b/public/emoji/unicode/1f421.png new file mode 100644 index 00000000..a1d47cb7 Binary files /dev/null and b/public/emoji/unicode/1f421.png differ diff --git a/public/emoji/unicode/1f422.png b/public/emoji/unicode/1f422.png new file mode 100644 index 00000000..04d1d968 Binary files /dev/null and b/public/emoji/unicode/1f422.png differ diff --git a/public/emoji/unicode/1f423.png b/public/emoji/unicode/1f423.png new file mode 100644 index 00000000..005a5551 Binary files /dev/null and b/public/emoji/unicode/1f423.png differ diff --git a/public/emoji/unicode/1f424.png b/public/emoji/unicode/1f424.png new file mode 100644 index 00000000..9be8d293 Binary files /dev/null and b/public/emoji/unicode/1f424.png differ diff --git a/public/emoji/unicode/1f425.png b/public/emoji/unicode/1f425.png new file mode 100644 index 00000000..39c25bc7 Binary files /dev/null and b/public/emoji/unicode/1f425.png differ diff --git a/public/emoji/unicode/1f426.png b/public/emoji/unicode/1f426.png new file mode 100644 index 00000000..e6be8c02 Binary files /dev/null and b/public/emoji/unicode/1f426.png differ diff --git a/public/emoji/unicode/1f427.png b/public/emoji/unicode/1f427.png new file mode 100644 index 00000000..d8edbcb8 Binary files /dev/null and b/public/emoji/unicode/1f427.png differ diff --git a/public/emoji/unicode/1f428.png b/public/emoji/unicode/1f428.png new file mode 100644 index 00000000..e17bd3cf Binary files /dev/null and b/public/emoji/unicode/1f428.png differ diff --git a/public/emoji/unicode/1f429.png b/public/emoji/unicode/1f429.png new file mode 100644 index 00000000..adac80bd Binary files /dev/null and b/public/emoji/unicode/1f429.png differ diff --git a/public/emoji/unicode/1f42a.png b/public/emoji/unicode/1f42a.png new file mode 100644 index 00000000..c8c7b9ff Binary files /dev/null and b/public/emoji/unicode/1f42a.png differ diff --git a/public/emoji/unicode/1f42b.png b/public/emoji/unicode/1f42b.png new file mode 100644 index 00000000..496c186a Binary files /dev/null and b/public/emoji/unicode/1f42b.png differ diff --git a/public/emoji/unicode/1f42c.png b/public/emoji/unicode/1f42c.png new file mode 100644 index 00000000..9326077a Binary files /dev/null and b/public/emoji/unicode/1f42c.png differ diff --git a/public/emoji/unicode/1f42d.png b/public/emoji/unicode/1f42d.png new file mode 100644 index 00000000..8ff162e2 Binary files /dev/null and b/public/emoji/unicode/1f42d.png differ diff --git a/public/emoji/unicode/1f42e.png b/public/emoji/unicode/1f42e.png new file mode 100644 index 00000000..12e1ab6c Binary files /dev/null and b/public/emoji/unicode/1f42e.png differ diff --git a/public/emoji/unicode/1f42f.png b/public/emoji/unicode/1f42f.png new file mode 100644 index 00000000..d6cc84a3 Binary files /dev/null and b/public/emoji/unicode/1f42f.png differ diff --git a/public/emoji/unicode/1f430.png b/public/emoji/unicode/1f430.png new file mode 100644 index 00000000..5cb3ef6f Binary files /dev/null and b/public/emoji/unicode/1f430.png differ diff --git a/public/emoji/unicode/1f431.png b/public/emoji/unicode/1f431.png new file mode 100644 index 00000000..09b9ef79 Binary files /dev/null and b/public/emoji/unicode/1f431.png differ diff --git a/public/emoji/unicode/1f432.png b/public/emoji/unicode/1f432.png new file mode 100644 index 00000000..e5e556bd Binary files /dev/null and b/public/emoji/unicode/1f432.png differ diff --git a/public/emoji/unicode/1f433.png b/public/emoji/unicode/1f433.png new file mode 100644 index 00000000..5bb113e4 Binary files /dev/null and b/public/emoji/unicode/1f433.png differ diff --git a/public/emoji/unicode/1f434.png b/public/emoji/unicode/1f434.png new file mode 100644 index 00000000..78d580ad Binary files /dev/null and b/public/emoji/unicode/1f434.png differ diff --git a/public/emoji/unicode/1f435.png b/public/emoji/unicode/1f435.png new file mode 100644 index 00000000..6964cf4d Binary files /dev/null and b/public/emoji/unicode/1f435.png differ diff --git a/public/emoji/unicode/1f436.png b/public/emoji/unicode/1f436.png new file mode 100644 index 00000000..389a02bf Binary files /dev/null and b/public/emoji/unicode/1f436.png differ diff --git a/public/emoji/unicode/1f437.png b/public/emoji/unicode/1f437.png new file mode 100644 index 00000000..f7f273c7 Binary files /dev/null and b/public/emoji/unicode/1f437.png differ diff --git a/public/emoji/unicode/1f438.png b/public/emoji/unicode/1f438.png new file mode 100644 index 00000000..cfe11b18 Binary files /dev/null and b/public/emoji/unicode/1f438.png differ diff --git a/public/emoji/unicode/1f439.png b/public/emoji/unicode/1f439.png new file mode 100644 index 00000000..ada9c310 Binary files /dev/null and b/public/emoji/unicode/1f439.png differ diff --git a/public/emoji/unicode/1f43a.png b/public/emoji/unicode/1f43a.png new file mode 100644 index 00000000..c60c9689 Binary files /dev/null and b/public/emoji/unicode/1f43a.png differ diff --git a/public/emoji/unicode/1f43b.png b/public/emoji/unicode/1f43b.png new file mode 100644 index 00000000..f5afe920 Binary files /dev/null and b/public/emoji/unicode/1f43b.png differ diff --git a/public/emoji/unicode/1f43c.png b/public/emoji/unicode/1f43c.png new file mode 100644 index 00000000..a794fb17 Binary files /dev/null and b/public/emoji/unicode/1f43c.png differ diff --git a/public/emoji/unicode/1f43d.png b/public/emoji/unicode/1f43d.png new file mode 100644 index 00000000..38d61244 Binary files /dev/null and b/public/emoji/unicode/1f43d.png differ diff --git a/public/emoji/unicode/1f43e.png b/public/emoji/unicode/1f43e.png new file mode 100644 index 00000000..89b9fec9 Binary files /dev/null and b/public/emoji/unicode/1f43e.png differ diff --git a/public/emoji/unicode/1f440.png b/public/emoji/unicode/1f440.png new file mode 100644 index 00000000..dc2216f6 Binary files /dev/null and b/public/emoji/unicode/1f440.png differ diff --git a/public/emoji/unicode/1f442.png b/public/emoji/unicode/1f442.png new file mode 100644 index 00000000..2bbbf10c Binary files /dev/null and b/public/emoji/unicode/1f442.png differ diff --git a/public/emoji/unicode/1f443.png b/public/emoji/unicode/1f443.png new file mode 100644 index 00000000..ad17c16c Binary files /dev/null and b/public/emoji/unicode/1f443.png differ diff --git a/public/emoji/unicode/1f444.png b/public/emoji/unicode/1f444.png new file mode 100644 index 00000000..826ed110 Binary files /dev/null and b/public/emoji/unicode/1f444.png differ diff --git a/public/emoji/unicode/1f445.png b/public/emoji/unicode/1f445.png new file mode 100644 index 00000000..b0bab120 Binary files /dev/null and b/public/emoji/unicode/1f445.png differ diff --git a/public/emoji/unicode/1f446.png b/public/emoji/unicode/1f446.png new file mode 100644 index 00000000..196d109a Binary files /dev/null and b/public/emoji/unicode/1f446.png differ diff --git a/public/emoji/unicode/1f447.png b/public/emoji/unicode/1f447.png new file mode 100644 index 00000000..658c6d91 Binary files /dev/null and b/public/emoji/unicode/1f447.png differ diff --git a/public/emoji/unicode/1f448.png b/public/emoji/unicode/1f448.png new file mode 100644 index 00000000..fee9cac4 Binary files /dev/null and b/public/emoji/unicode/1f448.png differ diff --git a/public/emoji/unicode/1f449.png b/public/emoji/unicode/1f449.png new file mode 100644 index 00000000..b04e2849 Binary files /dev/null and b/public/emoji/unicode/1f449.png differ diff --git a/public/emoji/unicode/1f44a.png b/public/emoji/unicode/1f44a.png new file mode 100644 index 00000000..2d41fd37 Binary files /dev/null and b/public/emoji/unicode/1f44a.png differ diff --git a/public/emoji/unicode/1f44b.png b/public/emoji/unicode/1f44b.png new file mode 100644 index 00000000..e78402eb Binary files /dev/null and b/public/emoji/unicode/1f44b.png differ diff --git a/public/emoji/unicode/1f44c.png b/public/emoji/unicode/1f44c.png new file mode 100644 index 00000000..3177439d Binary files /dev/null and b/public/emoji/unicode/1f44c.png differ diff --git a/public/emoji/unicode/1f44d.png b/public/emoji/unicode/1f44d.png new file mode 100644 index 00000000..3a43ecae Binary files /dev/null and b/public/emoji/unicode/1f44d.png differ diff --git a/public/emoji/unicode/1f44e.png b/public/emoji/unicode/1f44e.png new file mode 100644 index 00000000..e44c0421 Binary files /dev/null and b/public/emoji/unicode/1f44e.png differ diff --git a/public/emoji/unicode/1f44f.png b/public/emoji/unicode/1f44f.png new file mode 100644 index 00000000..d01c982a Binary files /dev/null and b/public/emoji/unicode/1f44f.png differ diff --git a/public/emoji/unicode/1f450.png b/public/emoji/unicode/1f450.png new file mode 100644 index 00000000..2cc25bd4 Binary files /dev/null and b/public/emoji/unicode/1f450.png differ diff --git a/public/emoji/unicode/1f451.png b/public/emoji/unicode/1f451.png new file mode 100644 index 00000000..39da1d52 Binary files /dev/null and b/public/emoji/unicode/1f451.png differ diff --git a/public/emoji/unicode/1f452.png b/public/emoji/unicode/1f452.png new file mode 100644 index 00000000..4cb2e6a6 Binary files /dev/null and b/public/emoji/unicode/1f452.png differ diff --git a/public/emoji/unicode/1f453.png b/public/emoji/unicode/1f453.png new file mode 100644 index 00000000..a3cf75a2 Binary files /dev/null and b/public/emoji/unicode/1f453.png differ diff --git a/public/emoji/unicode/1f454.png b/public/emoji/unicode/1f454.png new file mode 100644 index 00000000..80461c66 Binary files /dev/null and b/public/emoji/unicode/1f454.png differ diff --git a/public/emoji/unicode/1f455.png b/public/emoji/unicode/1f455.png new file mode 100644 index 00000000..297a6d63 Binary files /dev/null and b/public/emoji/unicode/1f455.png differ diff --git a/public/emoji/unicode/1f456.png b/public/emoji/unicode/1f456.png new file mode 100644 index 00000000..d721cea5 Binary files /dev/null and b/public/emoji/unicode/1f456.png differ diff --git a/public/emoji/unicode/1f457.png b/public/emoji/unicode/1f457.png new file mode 100644 index 00000000..6434e2e2 Binary files /dev/null and b/public/emoji/unicode/1f457.png differ diff --git a/public/emoji/unicode/1f458.png b/public/emoji/unicode/1f458.png new file mode 100644 index 00000000..34ffe137 Binary files /dev/null and b/public/emoji/unicode/1f458.png differ diff --git a/public/emoji/unicode/1f459.png b/public/emoji/unicode/1f459.png new file mode 100644 index 00000000..4ff63b40 Binary files /dev/null and b/public/emoji/unicode/1f459.png differ diff --git a/public/emoji/unicode/1f45a.png b/public/emoji/unicode/1f45a.png new file mode 100644 index 00000000..aa297c7b Binary files /dev/null and b/public/emoji/unicode/1f45a.png differ diff --git a/public/emoji/unicode/1f45b.png b/public/emoji/unicode/1f45b.png new file mode 100644 index 00000000..8f06a2b9 Binary files /dev/null and b/public/emoji/unicode/1f45b.png differ diff --git a/public/emoji/unicode/1f45c.png b/public/emoji/unicode/1f45c.png new file mode 100644 index 00000000..d7adf04d Binary files /dev/null and b/public/emoji/unicode/1f45c.png differ diff --git a/public/emoji/unicode/1f45d.png b/public/emoji/unicode/1f45d.png new file mode 100644 index 00000000..0bc5879f Binary files /dev/null and b/public/emoji/unicode/1f45d.png differ diff --git a/public/emoji/unicode/1f45e.png b/public/emoji/unicode/1f45e.png new file mode 100644 index 00000000..ecba9ba7 Binary files /dev/null and b/public/emoji/unicode/1f45e.png differ diff --git a/public/emoji/unicode/1f45f.png b/public/emoji/unicode/1f45f.png new file mode 100644 index 00000000..45b82e61 Binary files /dev/null and b/public/emoji/unicode/1f45f.png differ diff --git a/public/emoji/unicode/1f460.png b/public/emoji/unicode/1f460.png new file mode 100644 index 00000000..525b6a0d Binary files /dev/null and b/public/emoji/unicode/1f460.png differ diff --git a/public/emoji/unicode/1f461.png b/public/emoji/unicode/1f461.png new file mode 100644 index 00000000..aa62cca5 Binary files /dev/null and b/public/emoji/unicode/1f461.png differ diff --git a/public/emoji/unicode/1f462.png b/public/emoji/unicode/1f462.png new file mode 100644 index 00000000..58d0fdbc Binary files /dev/null and b/public/emoji/unicode/1f462.png differ diff --git a/public/emoji/unicode/1f463.png b/public/emoji/unicode/1f463.png new file mode 100644 index 00000000..d7a25614 Binary files /dev/null and b/public/emoji/unicode/1f463.png differ diff --git a/public/emoji/unicode/1f464.png b/public/emoji/unicode/1f464.png new file mode 100644 index 00000000..d1313986 Binary files /dev/null and b/public/emoji/unicode/1f464.png differ diff --git a/public/emoji/unicode/1f465.png b/public/emoji/unicode/1f465.png new file mode 100644 index 00000000..1f3aabcf Binary files /dev/null and b/public/emoji/unicode/1f465.png differ diff --git a/public/emoji/unicode/1f466.png b/public/emoji/unicode/1f466.png new file mode 100644 index 00000000..f79f1f29 Binary files /dev/null and b/public/emoji/unicode/1f466.png differ diff --git a/public/emoji/unicode/1f467.png b/public/emoji/unicode/1f467.png new file mode 100644 index 00000000..ea412694 Binary files /dev/null and b/public/emoji/unicode/1f467.png differ diff --git a/public/emoji/unicode/1f468.png b/public/emoji/unicode/1f468.png new file mode 100644 index 00000000..d9bfa26a Binary files /dev/null and b/public/emoji/unicode/1f468.png differ diff --git a/public/emoji/unicode/1f469.png b/public/emoji/unicode/1f469.png new file mode 100644 index 00000000..6bf0d2b1 Binary files /dev/null and b/public/emoji/unicode/1f469.png differ diff --git a/public/emoji/unicode/1f46a.png b/public/emoji/unicode/1f46a.png new file mode 100644 index 00000000..b4b365f3 Binary files /dev/null and b/public/emoji/unicode/1f46a.png differ diff --git a/public/emoji/unicode/1f46b.png b/public/emoji/unicode/1f46b.png new file mode 100644 index 00000000..9e51f40e Binary files /dev/null and b/public/emoji/unicode/1f46b.png differ diff --git a/public/emoji/unicode/1f46c.png b/public/emoji/unicode/1f46c.png new file mode 100644 index 00000000..d1099f21 Binary files /dev/null and b/public/emoji/unicode/1f46c.png differ diff --git a/public/emoji/unicode/1f46d.png b/public/emoji/unicode/1f46d.png new file mode 100644 index 00000000..619646c4 Binary files /dev/null and b/public/emoji/unicode/1f46d.png differ diff --git a/public/emoji/unicode/1f46e.png b/public/emoji/unicode/1f46e.png new file mode 100644 index 00000000..43a5a84f Binary files /dev/null and b/public/emoji/unicode/1f46e.png differ diff --git a/public/emoji/unicode/1f46f.png b/public/emoji/unicode/1f46f.png new file mode 100644 index 00000000..2dfb451a Binary files /dev/null and b/public/emoji/unicode/1f46f.png differ diff --git a/public/emoji/unicode/1f470.png b/public/emoji/unicode/1f470.png new file mode 100644 index 00000000..dd0b0cfd Binary files /dev/null and b/public/emoji/unicode/1f470.png differ diff --git a/public/emoji/unicode/1f471.png b/public/emoji/unicode/1f471.png new file mode 100644 index 00000000..c144301c Binary files /dev/null and b/public/emoji/unicode/1f471.png differ diff --git a/public/emoji/unicode/1f472.png b/public/emoji/unicode/1f472.png new file mode 100644 index 00000000..7aad74b5 Binary files /dev/null and b/public/emoji/unicode/1f472.png differ diff --git a/public/emoji/unicode/1f473.png b/public/emoji/unicode/1f473.png new file mode 100644 index 00000000..036604ca Binary files /dev/null and b/public/emoji/unicode/1f473.png differ diff --git a/public/emoji/unicode/1f474.png b/public/emoji/unicode/1f474.png new file mode 100644 index 00000000..149f0cfb Binary files /dev/null and b/public/emoji/unicode/1f474.png differ diff --git a/public/emoji/unicode/1f475.png b/public/emoji/unicode/1f475.png new file mode 100644 index 00000000..f839565f Binary files /dev/null and b/public/emoji/unicode/1f475.png differ diff --git a/public/emoji/unicode/1f476.png b/public/emoji/unicode/1f476.png new file mode 100644 index 00000000..3b29da40 Binary files /dev/null and b/public/emoji/unicode/1f476.png differ diff --git a/public/emoji/unicode/1f477.png b/public/emoji/unicode/1f477.png new file mode 100644 index 00000000..4d648604 Binary files /dev/null and b/public/emoji/unicode/1f477.png differ diff --git a/public/emoji/unicode/1f478.png b/public/emoji/unicode/1f478.png new file mode 100644 index 00000000..1ebb2ce9 Binary files /dev/null and b/public/emoji/unicode/1f478.png differ diff --git a/public/emoji/unicode/1f479.png b/public/emoji/unicode/1f479.png new file mode 100644 index 00000000..e9f5471c Binary files /dev/null and b/public/emoji/unicode/1f479.png differ diff --git a/public/emoji/unicode/1f47a.png b/public/emoji/unicode/1f47a.png new file mode 100644 index 00000000..bd21b187 Binary files /dev/null and b/public/emoji/unicode/1f47a.png differ diff --git a/public/emoji/unicode/1f47b.png b/public/emoji/unicode/1f47b.png new file mode 100644 index 00000000..671dd0c9 Binary files /dev/null and b/public/emoji/unicode/1f47b.png differ diff --git a/public/emoji/unicode/1f47c.png b/public/emoji/unicode/1f47c.png new file mode 100644 index 00000000..da52c310 Binary files /dev/null and b/public/emoji/unicode/1f47c.png differ diff --git a/public/emoji/unicode/1f47d.png b/public/emoji/unicode/1f47d.png new file mode 100644 index 00000000..e3fd76a7 Binary files /dev/null and b/public/emoji/unicode/1f47d.png differ diff --git a/public/emoji/unicode/1f47e.png b/public/emoji/unicode/1f47e.png new file mode 100644 index 00000000..38404916 Binary files /dev/null and b/public/emoji/unicode/1f47e.png differ diff --git a/public/emoji/unicode/1f47f.png b/public/emoji/unicode/1f47f.png new file mode 100644 index 00000000..48e57010 Binary files /dev/null and b/public/emoji/unicode/1f47f.png differ diff --git a/public/emoji/unicode/1f480.png b/public/emoji/unicode/1f480.png new file mode 100644 index 00000000..bd4ee382 Binary files /dev/null and b/public/emoji/unicode/1f480.png differ diff --git a/public/emoji/unicode/1f481.png b/public/emoji/unicode/1f481.png new file mode 100644 index 00000000..52c0a50a Binary files /dev/null and b/public/emoji/unicode/1f481.png differ diff --git a/public/emoji/unicode/1f482.png b/public/emoji/unicode/1f482.png new file mode 100644 index 00000000..b67b335d Binary files /dev/null and b/public/emoji/unicode/1f482.png differ diff --git a/public/emoji/unicode/1f483.png b/public/emoji/unicode/1f483.png new file mode 100644 index 00000000..6885a0bc Binary files /dev/null and b/public/emoji/unicode/1f483.png differ diff --git a/public/emoji/unicode/1f484.png b/public/emoji/unicode/1f484.png new file mode 100644 index 00000000..82f990c5 Binary files /dev/null and b/public/emoji/unicode/1f484.png differ diff --git a/public/emoji/unicode/1f485.png b/public/emoji/unicode/1f485.png new file mode 100644 index 00000000..6a66e63d Binary files /dev/null and b/public/emoji/unicode/1f485.png differ diff --git a/public/emoji/unicode/1f486.png b/public/emoji/unicode/1f486.png new file mode 100644 index 00000000..dd30d159 Binary files /dev/null and b/public/emoji/unicode/1f486.png differ diff --git a/public/emoji/unicode/1f487.png b/public/emoji/unicode/1f487.png new file mode 100644 index 00000000..902d273f Binary files /dev/null and b/public/emoji/unicode/1f487.png differ diff --git a/public/emoji/unicode/1f488.png b/public/emoji/unicode/1f488.png new file mode 100644 index 00000000..a10cb232 Binary files /dev/null and b/public/emoji/unicode/1f488.png differ diff --git a/public/emoji/unicode/1f489.png b/public/emoji/unicode/1f489.png new file mode 100644 index 00000000..e7e7ab6e Binary files /dev/null and b/public/emoji/unicode/1f489.png differ diff --git a/public/emoji/unicode/1f48a.png b/public/emoji/unicode/1f48a.png new file mode 100644 index 00000000..cd84a78f Binary files /dev/null and b/public/emoji/unicode/1f48a.png differ diff --git a/public/emoji/unicode/1f48b.png b/public/emoji/unicode/1f48b.png new file mode 100644 index 00000000..4ae2c2b5 Binary files /dev/null and b/public/emoji/unicode/1f48b.png differ diff --git a/public/emoji/unicode/1f48c.png b/public/emoji/unicode/1f48c.png new file mode 100644 index 00000000..e29981f4 Binary files /dev/null and b/public/emoji/unicode/1f48c.png differ diff --git a/public/emoji/unicode/1f48d.png b/public/emoji/unicode/1f48d.png new file mode 100644 index 00000000..8a57fd68 Binary files /dev/null and b/public/emoji/unicode/1f48d.png differ diff --git a/public/emoji/unicode/1f48e.png b/public/emoji/unicode/1f48e.png new file mode 100644 index 00000000..8a5d8dad Binary files /dev/null and b/public/emoji/unicode/1f48e.png differ diff --git a/public/emoji/unicode/1f48f.png b/public/emoji/unicode/1f48f.png new file mode 100644 index 00000000..d0279082 Binary files /dev/null and b/public/emoji/unicode/1f48f.png differ diff --git a/public/emoji/unicode/1f490.png b/public/emoji/unicode/1f490.png new file mode 100644 index 00000000..ce637832 Binary files /dev/null and b/public/emoji/unicode/1f490.png differ diff --git a/public/emoji/unicode/1f491.png b/public/emoji/unicode/1f491.png new file mode 100644 index 00000000..c503f40a Binary files /dev/null and b/public/emoji/unicode/1f491.png differ diff --git a/public/emoji/unicode/1f492.png b/public/emoji/unicode/1f492.png new file mode 100644 index 00000000..ead19d52 Binary files /dev/null and b/public/emoji/unicode/1f492.png differ diff --git a/public/emoji/unicode/1f493.png b/public/emoji/unicode/1f493.png new file mode 100644 index 00000000..b6628f6f Binary files /dev/null and b/public/emoji/unicode/1f493.png differ diff --git a/public/emoji/unicode/1f494.png b/public/emoji/unicode/1f494.png new file mode 100644 index 00000000..a1bc850e Binary files /dev/null and b/public/emoji/unicode/1f494.png differ diff --git a/public/emoji/unicode/1f495.png b/public/emoji/unicode/1f495.png new file mode 100644 index 00000000..b189e9ae Binary files /dev/null and b/public/emoji/unicode/1f495.png differ diff --git a/public/emoji/unicode/1f496.png b/public/emoji/unicode/1f496.png new file mode 100644 index 00000000..0826bbc0 Binary files /dev/null and b/public/emoji/unicode/1f496.png differ diff --git a/public/emoji/unicode/1f497.png b/public/emoji/unicode/1f497.png new file mode 100644 index 00000000..a7491cbe Binary files /dev/null and b/public/emoji/unicode/1f497.png differ diff --git a/public/emoji/unicode/1f498.png b/public/emoji/unicode/1f498.png new file mode 100644 index 00000000..49872847 Binary files /dev/null and b/public/emoji/unicode/1f498.png differ diff --git a/public/emoji/unicode/1f499.png b/public/emoji/unicode/1f499.png new file mode 100644 index 00000000..baa29b31 Binary files /dev/null and b/public/emoji/unicode/1f499.png differ diff --git a/public/emoji/unicode/1f49a.png b/public/emoji/unicode/1f49a.png new file mode 100644 index 00000000..7289cb81 Binary files /dev/null and b/public/emoji/unicode/1f49a.png differ diff --git a/public/emoji/unicode/1f49b.png b/public/emoji/unicode/1f49b.png new file mode 100644 index 00000000..fa41ce78 Binary files /dev/null and b/public/emoji/unicode/1f49b.png differ diff --git a/public/emoji/unicode/1f49c.png b/public/emoji/unicode/1f49c.png new file mode 100644 index 00000000..d5f87504 Binary files /dev/null and b/public/emoji/unicode/1f49c.png differ diff --git a/public/emoji/unicode/1f49d.png b/public/emoji/unicode/1f49d.png new file mode 100644 index 00000000..f31c26a3 Binary files /dev/null and b/public/emoji/unicode/1f49d.png differ diff --git a/public/emoji/unicode/1f49e.png b/public/emoji/unicode/1f49e.png new file mode 100644 index 00000000..ea3317c4 Binary files /dev/null and b/public/emoji/unicode/1f49e.png differ diff --git a/public/emoji/unicode/1f49f.png b/public/emoji/unicode/1f49f.png new file mode 100644 index 00000000..b40a4867 Binary files /dev/null and b/public/emoji/unicode/1f49f.png differ diff --git a/public/emoji/unicode/1f4a0.png b/public/emoji/unicode/1f4a0.png new file mode 100644 index 00000000..dfd1098b Binary files /dev/null and b/public/emoji/unicode/1f4a0.png differ diff --git a/public/emoji/unicode/1f4a1.png b/public/emoji/unicode/1f4a1.png new file mode 100644 index 00000000..23afca1c Binary files /dev/null and b/public/emoji/unicode/1f4a1.png differ diff --git a/public/emoji/unicode/1f4a2.png b/public/emoji/unicode/1f4a2.png new file mode 100644 index 00000000..6fb4dca1 Binary files /dev/null and b/public/emoji/unicode/1f4a2.png differ diff --git a/public/emoji/unicode/1f4a3.png b/public/emoji/unicode/1f4a3.png new file mode 100644 index 00000000..3289787d Binary files /dev/null and b/public/emoji/unicode/1f4a3.png differ diff --git a/public/emoji/unicode/1f4a4.png b/public/emoji/unicode/1f4a4.png new file mode 100644 index 00000000..30be0465 Binary files /dev/null and b/public/emoji/unicode/1f4a4.png differ diff --git a/public/emoji/unicode/1f4a5.png b/public/emoji/unicode/1f4a5.png new file mode 100644 index 00000000..bddeb8f4 Binary files /dev/null and b/public/emoji/unicode/1f4a5.png differ diff --git a/public/emoji/unicode/1f4a6.png b/public/emoji/unicode/1f4a6.png new file mode 100644 index 00000000..a83b3e96 Binary files /dev/null and b/public/emoji/unicode/1f4a6.png differ diff --git a/public/emoji/unicode/1f4a7.png b/public/emoji/unicode/1f4a7.png new file mode 100644 index 00000000..9eff4633 Binary files /dev/null and b/public/emoji/unicode/1f4a7.png differ diff --git a/public/emoji/unicode/1f4a8.png b/public/emoji/unicode/1f4a8.png new file mode 100644 index 00000000..dc2c0a8f Binary files /dev/null and b/public/emoji/unicode/1f4a8.png differ diff --git a/public/emoji/unicode/1f4a9.png b/public/emoji/unicode/1f4a9.png new file mode 100644 index 00000000..73a4dc84 Binary files /dev/null and b/public/emoji/unicode/1f4a9.png differ diff --git a/public/emoji/unicode/1f4aa.png b/public/emoji/unicode/1f4aa.png new file mode 100644 index 00000000..19f92efb Binary files /dev/null and b/public/emoji/unicode/1f4aa.png differ diff --git a/public/emoji/unicode/1f4ab.png b/public/emoji/unicode/1f4ab.png new file mode 100644 index 00000000..55213d2d Binary files /dev/null and b/public/emoji/unicode/1f4ab.png differ diff --git a/public/emoji/unicode/1f4ac.png b/public/emoji/unicode/1f4ac.png new file mode 100644 index 00000000..2896c278 Binary files /dev/null and b/public/emoji/unicode/1f4ac.png differ diff --git a/public/emoji/unicode/1f4ad.png b/public/emoji/unicode/1f4ad.png new file mode 100644 index 00000000..701bdf0f Binary files /dev/null and b/public/emoji/unicode/1f4ad.png differ diff --git a/public/emoji/unicode/1f4ae.png b/public/emoji/unicode/1f4ae.png new file mode 100644 index 00000000..c0929d0d Binary files /dev/null and b/public/emoji/unicode/1f4ae.png differ diff --git a/public/emoji/unicode/1f4af.png b/public/emoji/unicode/1f4af.png new file mode 100644 index 00000000..bce9ab14 Binary files /dev/null and b/public/emoji/unicode/1f4af.png differ diff --git a/public/emoji/unicode/1f4b0.png b/public/emoji/unicode/1f4b0.png new file mode 100644 index 00000000..5546c04b Binary files /dev/null and b/public/emoji/unicode/1f4b0.png differ diff --git a/public/emoji/unicode/1f4b1.png b/public/emoji/unicode/1f4b1.png new file mode 100644 index 00000000..d5ee21fc Binary files /dev/null and b/public/emoji/unicode/1f4b1.png differ diff --git a/public/emoji/unicode/1f4b2.png b/public/emoji/unicode/1f4b2.png new file mode 100644 index 00000000..361e26ae Binary files /dev/null and b/public/emoji/unicode/1f4b2.png differ diff --git a/public/emoji/unicode/1f4b3.png b/public/emoji/unicode/1f4b3.png new file mode 100644 index 00000000..be1c1dd3 Binary files /dev/null and b/public/emoji/unicode/1f4b3.png differ diff --git a/public/emoji/unicode/1f4b4.png b/public/emoji/unicode/1f4b4.png new file mode 100644 index 00000000..139bc936 Binary files /dev/null and b/public/emoji/unicode/1f4b4.png differ diff --git a/public/emoji/unicode/1f4b5.png b/public/emoji/unicode/1f4b5.png new file mode 100644 index 00000000..63de8849 Binary files /dev/null and b/public/emoji/unicode/1f4b5.png differ diff --git a/public/emoji/unicode/1f4b6.png b/public/emoji/unicode/1f4b6.png new file mode 100644 index 00000000..1c5904b7 Binary files /dev/null and b/public/emoji/unicode/1f4b6.png differ diff --git a/public/emoji/unicode/1f4b7.png b/public/emoji/unicode/1f4b7.png new file mode 100644 index 00000000..f8be91d7 Binary files /dev/null and b/public/emoji/unicode/1f4b7.png differ diff --git a/public/emoji/unicode/1f4b8.png b/public/emoji/unicode/1f4b8.png new file mode 100644 index 00000000..135e3981 Binary files /dev/null and b/public/emoji/unicode/1f4b8.png differ diff --git a/public/emoji/unicode/1f4b9.png b/public/emoji/unicode/1f4b9.png new file mode 100644 index 00000000..ac2c4bb0 Binary files /dev/null and b/public/emoji/unicode/1f4b9.png differ diff --git a/public/emoji/unicode/1f4ba.png b/public/emoji/unicode/1f4ba.png new file mode 100644 index 00000000..d1cb864b Binary files /dev/null and b/public/emoji/unicode/1f4ba.png differ diff --git a/public/emoji/unicode/1f4bb.png b/public/emoji/unicode/1f4bb.png new file mode 100644 index 00000000..d4d26876 Binary files /dev/null and b/public/emoji/unicode/1f4bb.png differ diff --git a/public/emoji/unicode/1f4bc.png b/public/emoji/unicode/1f4bc.png new file mode 100644 index 00000000..46e82b00 Binary files /dev/null and b/public/emoji/unicode/1f4bc.png differ diff --git a/public/emoji/unicode/1f4bd.png b/public/emoji/unicode/1f4bd.png new file mode 100644 index 00000000..e19cc5d0 Binary files /dev/null and b/public/emoji/unicode/1f4bd.png differ diff --git a/public/emoji/unicode/1f4be.png b/public/emoji/unicode/1f4be.png new file mode 100644 index 00000000..4ad56315 Binary files /dev/null and b/public/emoji/unicode/1f4be.png differ diff --git a/public/emoji/unicode/1f4bf.png b/public/emoji/unicode/1f4bf.png new file mode 100644 index 00000000..baff835c Binary files /dev/null and b/public/emoji/unicode/1f4bf.png differ diff --git a/public/emoji/unicode/1f4c0.png b/public/emoji/unicode/1f4c0.png new file mode 100644 index 00000000..363c83d0 Binary files /dev/null and b/public/emoji/unicode/1f4c0.png differ diff --git a/public/emoji/unicode/1f4c1.png b/public/emoji/unicode/1f4c1.png new file mode 100644 index 00000000..4d8bebf8 Binary files /dev/null and b/public/emoji/unicode/1f4c1.png differ diff --git a/public/emoji/unicode/1f4c2.png b/public/emoji/unicode/1f4c2.png new file mode 100644 index 00000000..2bbbbf5e Binary files /dev/null and b/public/emoji/unicode/1f4c2.png differ diff --git a/public/emoji/unicode/1f4c3.png b/public/emoji/unicode/1f4c3.png new file mode 100644 index 00000000..bf8f979d Binary files /dev/null and b/public/emoji/unicode/1f4c3.png differ diff --git a/public/emoji/unicode/1f4c4.png b/public/emoji/unicode/1f4c4.png new file mode 100644 index 00000000..64cd2e1b Binary files /dev/null and b/public/emoji/unicode/1f4c4.png differ diff --git a/public/emoji/unicode/1f4c5.png b/public/emoji/unicode/1f4c5.png new file mode 100644 index 00000000..6ad2efa5 Binary files /dev/null and b/public/emoji/unicode/1f4c5.png differ diff --git a/public/emoji/unicode/1f4c6.png b/public/emoji/unicode/1f4c6.png new file mode 100644 index 00000000..900b868b Binary files /dev/null and b/public/emoji/unicode/1f4c6.png differ diff --git a/public/emoji/unicode/1f4c7.png b/public/emoji/unicode/1f4c7.png new file mode 100644 index 00000000..374e94e9 Binary files /dev/null and b/public/emoji/unicode/1f4c7.png differ diff --git a/public/emoji/unicode/1f4c8.png b/public/emoji/unicode/1f4c8.png new file mode 100644 index 00000000..de3e9ba7 Binary files /dev/null and b/public/emoji/unicode/1f4c8.png differ diff --git a/public/emoji/unicode/1f4c9.png b/public/emoji/unicode/1f4c9.png new file mode 100644 index 00000000..65b82f04 Binary files /dev/null and b/public/emoji/unicode/1f4c9.png differ diff --git a/public/emoji/unicode/1f4ca.png b/public/emoji/unicode/1f4ca.png new file mode 100644 index 00000000..7871cc60 Binary files /dev/null and b/public/emoji/unicode/1f4ca.png differ diff --git a/public/emoji/unicode/1f4cb.png b/public/emoji/unicode/1f4cb.png new file mode 100644 index 00000000..e2c74e6d Binary files /dev/null and b/public/emoji/unicode/1f4cb.png differ diff --git a/public/emoji/unicode/1f4cc.png b/public/emoji/unicode/1f4cc.png new file mode 100644 index 00000000..540c4ecb Binary files /dev/null and b/public/emoji/unicode/1f4cc.png differ diff --git a/public/emoji/unicode/1f4cd.png b/public/emoji/unicode/1f4cd.png new file mode 100644 index 00000000..e498e92c Binary files /dev/null and b/public/emoji/unicode/1f4cd.png differ diff --git a/public/emoji/unicode/1f4ce.png b/public/emoji/unicode/1f4ce.png new file mode 100644 index 00000000..774412dc Binary files /dev/null and b/public/emoji/unicode/1f4ce.png differ diff --git a/public/emoji/unicode/1f4cf.png b/public/emoji/unicode/1f4cf.png new file mode 100644 index 00000000..af8cb4bc Binary files /dev/null and b/public/emoji/unicode/1f4cf.png differ diff --git a/public/emoji/unicode/1f4d0.png b/public/emoji/unicode/1f4d0.png new file mode 100644 index 00000000..383677cb Binary files /dev/null and b/public/emoji/unicode/1f4d0.png differ diff --git a/public/emoji/unicode/1f4d1.png b/public/emoji/unicode/1f4d1.png new file mode 100644 index 00000000..0c4e3bf1 Binary files /dev/null and b/public/emoji/unicode/1f4d1.png differ diff --git a/public/emoji/unicode/1f4d2.png b/public/emoji/unicode/1f4d2.png new file mode 100644 index 00000000..e4f72ace Binary files /dev/null and b/public/emoji/unicode/1f4d2.png differ diff --git a/public/emoji/unicode/1f4d3.png b/public/emoji/unicode/1f4d3.png new file mode 100644 index 00000000..07ea6087 Binary files /dev/null and b/public/emoji/unicode/1f4d3.png differ diff --git a/public/emoji/unicode/1f4d4.png b/public/emoji/unicode/1f4d4.png new file mode 100644 index 00000000..4f3b14c8 Binary files /dev/null and b/public/emoji/unicode/1f4d4.png differ diff --git a/public/emoji/unicode/1f4d5.png b/public/emoji/unicode/1f4d5.png new file mode 100644 index 00000000..484029c5 Binary files /dev/null and b/public/emoji/unicode/1f4d5.png differ diff --git a/public/emoji/unicode/1f4d6.png b/public/emoji/unicode/1f4d6.png new file mode 100644 index 00000000..8b698415 Binary files /dev/null and b/public/emoji/unicode/1f4d6.png differ diff --git a/public/emoji/unicode/1f4d7.png b/public/emoji/unicode/1f4d7.png new file mode 100644 index 00000000..e86651e5 Binary files /dev/null and b/public/emoji/unicode/1f4d7.png differ diff --git a/public/emoji/unicode/1f4d8.png b/public/emoji/unicode/1f4d8.png new file mode 100644 index 00000000..e2b9e8c7 Binary files /dev/null and b/public/emoji/unicode/1f4d8.png differ diff --git a/public/emoji/unicode/1f4d9.png b/public/emoji/unicode/1f4d9.png new file mode 100644 index 00000000..49650d59 Binary files /dev/null and b/public/emoji/unicode/1f4d9.png differ diff --git a/public/emoji/unicode/1f4da.png b/public/emoji/unicode/1f4da.png new file mode 100644 index 00000000..dca06a1a Binary files /dev/null and b/public/emoji/unicode/1f4da.png differ diff --git a/public/emoji/unicode/1f4db.png b/public/emoji/unicode/1f4db.png new file mode 100644 index 00000000..2b712dcd Binary files /dev/null and b/public/emoji/unicode/1f4db.png differ diff --git a/public/emoji/unicode/1f4dc.png b/public/emoji/unicode/1f4dc.png new file mode 100644 index 00000000..c5a10e6b Binary files /dev/null and b/public/emoji/unicode/1f4dc.png differ diff --git a/public/emoji/unicode/1f4dd.png b/public/emoji/unicode/1f4dd.png new file mode 100644 index 00000000..fc97ddbc Binary files /dev/null and b/public/emoji/unicode/1f4dd.png differ diff --git a/public/emoji/unicode/1f4de.png b/public/emoji/unicode/1f4de.png new file mode 100644 index 00000000..36e21e01 Binary files /dev/null and b/public/emoji/unicode/1f4de.png differ diff --git a/public/emoji/unicode/1f4df.png b/public/emoji/unicode/1f4df.png new file mode 100644 index 00000000..e3e1fc44 Binary files /dev/null and b/public/emoji/unicode/1f4df.png differ diff --git a/public/emoji/unicode/1f4e0.png b/public/emoji/unicode/1f4e0.png new file mode 100644 index 00000000..62be2c95 Binary files /dev/null and b/public/emoji/unicode/1f4e0.png differ diff --git a/public/emoji/unicode/1f4e1.png b/public/emoji/unicode/1f4e1.png new file mode 100644 index 00000000..3481cc2e Binary files /dev/null and b/public/emoji/unicode/1f4e1.png differ diff --git a/public/emoji/unicode/1f4e2.png b/public/emoji/unicode/1f4e2.png new file mode 100644 index 00000000..752385e5 Binary files /dev/null and b/public/emoji/unicode/1f4e2.png differ diff --git a/public/emoji/unicode/1f4e3.png b/public/emoji/unicode/1f4e3.png new file mode 100644 index 00000000..5d9319e7 Binary files /dev/null and b/public/emoji/unicode/1f4e3.png differ diff --git a/public/emoji/unicode/1f4e4.png b/public/emoji/unicode/1f4e4.png new file mode 100644 index 00000000..7ad15e64 Binary files /dev/null and b/public/emoji/unicode/1f4e4.png differ diff --git a/public/emoji/unicode/1f4e5.png b/public/emoji/unicode/1f4e5.png new file mode 100644 index 00000000..e2df0f89 Binary files /dev/null and b/public/emoji/unicode/1f4e5.png differ diff --git a/public/emoji/unicode/1f4e6.png b/public/emoji/unicode/1f4e6.png new file mode 100644 index 00000000..26602af9 Binary files /dev/null and b/public/emoji/unicode/1f4e6.png differ diff --git a/public/emoji/unicode/1f4e7.png b/public/emoji/unicode/1f4e7.png new file mode 100644 index 00000000..176a8e1e Binary files /dev/null and b/public/emoji/unicode/1f4e7.png differ diff --git a/public/emoji/unicode/1f4e8.png b/public/emoji/unicode/1f4e8.png new file mode 100644 index 00000000..afc82712 Binary files /dev/null and b/public/emoji/unicode/1f4e8.png differ diff --git a/public/emoji/unicode/1f4e9.png b/public/emoji/unicode/1f4e9.png new file mode 100644 index 00000000..efaa1fa8 Binary files /dev/null and b/public/emoji/unicode/1f4e9.png differ diff --git a/public/emoji/unicode/1f4ea.png b/public/emoji/unicode/1f4ea.png new file mode 100644 index 00000000..a5982b69 Binary files /dev/null and b/public/emoji/unicode/1f4ea.png differ diff --git a/public/emoji/unicode/1f4eb.png b/public/emoji/unicode/1f4eb.png new file mode 100644 index 00000000..8351e707 Binary files /dev/null and b/public/emoji/unicode/1f4eb.png differ diff --git a/public/emoji/unicode/1f4ec.png b/public/emoji/unicode/1f4ec.png new file mode 100644 index 00000000..dae34594 Binary files /dev/null and b/public/emoji/unicode/1f4ec.png differ diff --git a/public/emoji/unicode/1f4ed.png b/public/emoji/unicode/1f4ed.png new file mode 100644 index 00000000..59f15c5d Binary files /dev/null and b/public/emoji/unicode/1f4ed.png differ diff --git a/public/emoji/unicode/1f4ee.png b/public/emoji/unicode/1f4ee.png new file mode 100644 index 00000000..ce04b700 Binary files /dev/null and b/public/emoji/unicode/1f4ee.png differ diff --git a/public/emoji/unicode/1f4ef.png b/public/emoji/unicode/1f4ef.png new file mode 100644 index 00000000..e9b713bb Binary files /dev/null and b/public/emoji/unicode/1f4ef.png differ diff --git a/public/emoji/unicode/1f4f0.png b/public/emoji/unicode/1f4f0.png new file mode 100644 index 00000000..d171394e Binary files /dev/null and b/public/emoji/unicode/1f4f0.png differ diff --git a/public/emoji/unicode/1f4f1.png b/public/emoji/unicode/1f4f1.png new file mode 100644 index 00000000..df007103 Binary files /dev/null and b/public/emoji/unicode/1f4f1.png differ diff --git a/public/emoji/unicode/1f4f2.png b/public/emoji/unicode/1f4f2.png new file mode 100644 index 00000000..837897f2 Binary files /dev/null and b/public/emoji/unicode/1f4f2.png differ diff --git a/public/emoji/unicode/1f4f3.png b/public/emoji/unicode/1f4f3.png new file mode 100644 index 00000000..a716e96c Binary files /dev/null and b/public/emoji/unicode/1f4f3.png differ diff --git a/public/emoji/unicode/1f4f4.png b/public/emoji/unicode/1f4f4.png new file mode 100644 index 00000000..fa16c763 Binary files /dev/null and b/public/emoji/unicode/1f4f4.png differ diff --git a/public/emoji/unicode/1f4f5.png b/public/emoji/unicode/1f4f5.png new file mode 100644 index 00000000..41df57cf Binary files /dev/null and b/public/emoji/unicode/1f4f5.png differ diff --git a/public/emoji/unicode/1f4f6.png b/public/emoji/unicode/1f4f6.png new file mode 100644 index 00000000..a4bd23eb Binary files /dev/null and b/public/emoji/unicode/1f4f6.png differ diff --git a/public/emoji/unicode/1f4f7.png b/public/emoji/unicode/1f4f7.png new file mode 100644 index 00000000..397d03b3 Binary files /dev/null and b/public/emoji/unicode/1f4f7.png differ diff --git a/public/emoji/unicode/1f4f9.png b/public/emoji/unicode/1f4f9.png new file mode 100644 index 00000000..274cecdd Binary files /dev/null and b/public/emoji/unicode/1f4f9.png differ diff --git a/public/emoji/unicode/1f4fa.png b/public/emoji/unicode/1f4fa.png new file mode 100644 index 00000000..803dc3d4 Binary files /dev/null and b/public/emoji/unicode/1f4fa.png differ diff --git a/public/emoji/unicode/1f4fb.png b/public/emoji/unicode/1f4fb.png new file mode 100644 index 00000000..ea589efe Binary files /dev/null and b/public/emoji/unicode/1f4fb.png differ diff --git a/public/emoji/unicode/1f4fc.png b/public/emoji/unicode/1f4fc.png new file mode 100644 index 00000000..881081c1 Binary files /dev/null and b/public/emoji/unicode/1f4fc.png differ diff --git a/public/emoji/unicode/1f500.png b/public/emoji/unicode/1f500.png new file mode 100644 index 00000000..25cde18b Binary files /dev/null and b/public/emoji/unicode/1f500.png differ diff --git a/public/emoji/unicode/1f501.png b/public/emoji/unicode/1f501.png new file mode 100644 index 00000000..80113b69 Binary files /dev/null and b/public/emoji/unicode/1f501.png differ diff --git a/public/emoji/unicode/1f502.png b/public/emoji/unicode/1f502.png new file mode 100644 index 00000000..3c47bcc1 Binary files /dev/null and b/public/emoji/unicode/1f502.png differ diff --git a/public/emoji/unicode/1f503.png b/public/emoji/unicode/1f503.png new file mode 100644 index 00000000..5f84d7e7 Binary files /dev/null and b/public/emoji/unicode/1f503.png differ diff --git a/public/emoji/unicode/1f504.png b/public/emoji/unicode/1f504.png new file mode 100644 index 00000000..1933ae18 Binary files /dev/null and b/public/emoji/unicode/1f504.png differ diff --git a/public/emoji/unicode/1f505.png b/public/emoji/unicode/1f505.png new file mode 100644 index 00000000..ea15bde4 Binary files /dev/null and b/public/emoji/unicode/1f505.png differ diff --git a/public/emoji/unicode/1f506.png b/public/emoji/unicode/1f506.png new file mode 100644 index 00000000..ba9de7d4 Binary files /dev/null and b/public/emoji/unicode/1f506.png differ diff --git a/public/emoji/unicode/1f507.png b/public/emoji/unicode/1f507.png new file mode 100644 index 00000000..4cf67c36 Binary files /dev/null and b/public/emoji/unicode/1f507.png differ diff --git a/public/emoji/unicode/1f508.png b/public/emoji/unicode/1f508.png new file mode 100644 index 00000000..c884bd4f Binary files /dev/null and b/public/emoji/unicode/1f508.png differ diff --git a/public/emoji/unicode/1f509.png b/public/emoji/unicode/1f509.png new file mode 100644 index 00000000..6aa4dbff Binary files /dev/null and b/public/emoji/unicode/1f509.png differ diff --git a/public/emoji/unicode/1f50a.png b/public/emoji/unicode/1f50a.png new file mode 100644 index 00000000..f63e8149 Binary files /dev/null and b/public/emoji/unicode/1f50a.png differ diff --git a/public/emoji/unicode/1f50b.png b/public/emoji/unicode/1f50b.png new file mode 100644 index 00000000..aa7eedce Binary files /dev/null and b/public/emoji/unicode/1f50b.png differ diff --git a/public/emoji/unicode/1f50c.png b/public/emoji/unicode/1f50c.png new file mode 100644 index 00000000..7a3d6cee Binary files /dev/null and b/public/emoji/unicode/1f50c.png differ diff --git a/public/emoji/unicode/1f50d.png b/public/emoji/unicode/1f50d.png new file mode 100644 index 00000000..aa5b1d7c Binary files /dev/null and b/public/emoji/unicode/1f50d.png differ diff --git a/public/emoji/unicode/1f50e.png b/public/emoji/unicode/1f50e.png new file mode 100644 index 00000000..6e6cf11e Binary files /dev/null and b/public/emoji/unicode/1f50e.png differ diff --git a/public/emoji/unicode/1f50f.png b/public/emoji/unicode/1f50f.png new file mode 100644 index 00000000..375e67e8 Binary files /dev/null and b/public/emoji/unicode/1f50f.png differ diff --git a/public/emoji/unicode/1f510.png b/public/emoji/unicode/1f510.png new file mode 100644 index 00000000..e6fdf6cb Binary files /dev/null and b/public/emoji/unicode/1f510.png differ diff --git a/public/emoji/unicode/1f511.png b/public/emoji/unicode/1f511.png new file mode 100644 index 00000000..34673213 Binary files /dev/null and b/public/emoji/unicode/1f511.png differ diff --git a/public/emoji/unicode/1f512.png b/public/emoji/unicode/1f512.png new file mode 100644 index 00000000..4892b023 Binary files /dev/null and b/public/emoji/unicode/1f512.png differ diff --git a/public/emoji/unicode/1f513.png b/public/emoji/unicode/1f513.png new file mode 100644 index 00000000..22b429cd Binary files /dev/null and b/public/emoji/unicode/1f513.png differ diff --git a/public/emoji/unicode/1f514.png b/public/emoji/unicode/1f514.png new file mode 100644 index 00000000..69acceb2 Binary files /dev/null and b/public/emoji/unicode/1f514.png differ diff --git a/public/emoji/unicode/1f515.png b/public/emoji/unicode/1f515.png new file mode 100644 index 00000000..613b81cd Binary files /dev/null and b/public/emoji/unicode/1f515.png differ diff --git a/public/emoji/unicode/1f516.png b/public/emoji/unicode/1f516.png new file mode 100644 index 00000000..dbee45c6 Binary files /dev/null and b/public/emoji/unicode/1f516.png differ diff --git a/public/emoji/unicode/1f517.png b/public/emoji/unicode/1f517.png new file mode 100644 index 00000000..ffb8f62c Binary files /dev/null and b/public/emoji/unicode/1f517.png differ diff --git a/public/emoji/unicode/1f518.png b/public/emoji/unicode/1f518.png new file mode 100644 index 00000000..63755eec Binary files /dev/null and b/public/emoji/unicode/1f518.png differ diff --git a/public/emoji/unicode/1f519.png b/public/emoji/unicode/1f519.png new file mode 100644 index 00000000..0cde6287 Binary files /dev/null and b/public/emoji/unicode/1f519.png differ diff --git a/public/emoji/unicode/1f51a.png b/public/emoji/unicode/1f51a.png new file mode 100644 index 00000000..edb0bda2 Binary files /dev/null and b/public/emoji/unicode/1f51a.png differ diff --git a/public/emoji/unicode/1f51b.png b/public/emoji/unicode/1f51b.png new file mode 100644 index 00000000..3595387f Binary files /dev/null and b/public/emoji/unicode/1f51b.png differ diff --git a/public/emoji/unicode/1f51c.png b/public/emoji/unicode/1f51c.png new file mode 100644 index 00000000..9386615a Binary files /dev/null and b/public/emoji/unicode/1f51c.png differ diff --git a/public/emoji/unicode/1f51d.png b/public/emoji/unicode/1f51d.png new file mode 100644 index 00000000..9d9557dd Binary files /dev/null and b/public/emoji/unicode/1f51d.png differ diff --git a/public/emoji/unicode/1f51e.png b/public/emoji/unicode/1f51e.png new file mode 100644 index 00000000..a789b3c6 Binary files /dev/null and b/public/emoji/unicode/1f51e.png differ diff --git a/public/emoji/unicode/1f51f.png b/public/emoji/unicode/1f51f.png new file mode 100644 index 00000000..71dac1c1 Binary files /dev/null and b/public/emoji/unicode/1f51f.png differ diff --git a/public/emoji/unicode/1f520.png b/public/emoji/unicode/1f520.png new file mode 100644 index 00000000..ffc0cba4 Binary files /dev/null and b/public/emoji/unicode/1f520.png differ diff --git a/public/emoji/unicode/1f521.png b/public/emoji/unicode/1f521.png new file mode 100644 index 00000000..5218470b Binary files /dev/null and b/public/emoji/unicode/1f521.png differ diff --git a/public/emoji/unicode/1f522.png b/public/emoji/unicode/1f522.png new file mode 100644 index 00000000..c47c2e1f Binary files /dev/null and b/public/emoji/unicode/1f522.png differ diff --git a/public/emoji/unicode/1f523.png b/public/emoji/unicode/1f523.png new file mode 100644 index 00000000..16bc1da9 Binary files /dev/null and b/public/emoji/unicode/1f523.png differ diff --git a/public/emoji/unicode/1f524.png b/public/emoji/unicode/1f524.png new file mode 100644 index 00000000..505d40a1 Binary files /dev/null and b/public/emoji/unicode/1f524.png differ diff --git a/public/emoji/unicode/1f525.png b/public/emoji/unicode/1f525.png new file mode 100644 index 00000000..f2a3149b Binary files /dev/null and b/public/emoji/unicode/1f525.png differ diff --git a/public/emoji/unicode/1f526.png b/public/emoji/unicode/1f526.png new file mode 100644 index 00000000..215940aa Binary files /dev/null and b/public/emoji/unicode/1f526.png differ diff --git a/public/emoji/unicode/1f527.png b/public/emoji/unicode/1f527.png new file mode 100644 index 00000000..a87072ad Binary files /dev/null and b/public/emoji/unicode/1f527.png differ diff --git a/public/emoji/unicode/1f528.png b/public/emoji/unicode/1f528.png new file mode 100644 index 00000000..88e17f07 Binary files /dev/null and b/public/emoji/unicode/1f528.png differ diff --git a/public/emoji/unicode/1f529.png b/public/emoji/unicode/1f529.png new file mode 100644 index 00000000..bddfa72a Binary files /dev/null and b/public/emoji/unicode/1f529.png differ diff --git a/public/emoji/unicode/1f52a.png b/public/emoji/unicode/1f52a.png new file mode 100644 index 00000000..18eade0a Binary files /dev/null and b/public/emoji/unicode/1f52a.png differ diff --git a/public/emoji/unicode/1f52b.png b/public/emoji/unicode/1f52b.png new file mode 100644 index 00000000..c49dc52c Binary files /dev/null and b/public/emoji/unicode/1f52b.png differ diff --git a/public/emoji/unicode/1f52c.png b/public/emoji/unicode/1f52c.png new file mode 100644 index 00000000..f11d54c0 Binary files /dev/null and b/public/emoji/unicode/1f52c.png differ diff --git a/public/emoji/unicode/1f52d.png b/public/emoji/unicode/1f52d.png new file mode 100644 index 00000000..51fd8a07 Binary files /dev/null and b/public/emoji/unicode/1f52d.png differ diff --git a/public/emoji/unicode/1f52e.png b/public/emoji/unicode/1f52e.png new file mode 100644 index 00000000..6d2c6c42 Binary files /dev/null and b/public/emoji/unicode/1f52e.png differ diff --git a/public/emoji/unicode/1f52f.png b/public/emoji/unicode/1f52f.png new file mode 100644 index 00000000..010f8f5f Binary files /dev/null and b/public/emoji/unicode/1f52f.png differ diff --git a/public/emoji/unicode/1f530.png b/public/emoji/unicode/1f530.png new file mode 100644 index 00000000..1f022d17 Binary files /dev/null and b/public/emoji/unicode/1f530.png differ diff --git a/public/emoji/unicode/1f531.png b/public/emoji/unicode/1f531.png new file mode 100644 index 00000000..d79a7b4c Binary files /dev/null and b/public/emoji/unicode/1f531.png differ diff --git a/public/emoji/unicode/1f532.png b/public/emoji/unicode/1f532.png new file mode 100644 index 00000000..7332e397 Binary files /dev/null and b/public/emoji/unicode/1f532.png differ diff --git a/public/emoji/unicode/1f533.png b/public/emoji/unicode/1f533.png new file mode 100644 index 00000000..63c7a3ef Binary files /dev/null and b/public/emoji/unicode/1f533.png differ diff --git a/public/emoji/unicode/1f534.png b/public/emoji/unicode/1f534.png new file mode 100644 index 00000000..b391289b Binary files /dev/null and b/public/emoji/unicode/1f534.png differ diff --git a/public/emoji/unicode/1f535.png b/public/emoji/unicode/1f535.png new file mode 100644 index 00000000..a5b4ad4a Binary files /dev/null and b/public/emoji/unicode/1f535.png differ diff --git a/public/emoji/unicode/1f536.png b/public/emoji/unicode/1f536.png new file mode 100644 index 00000000..46d52e5c Binary files /dev/null and b/public/emoji/unicode/1f536.png differ diff --git a/public/emoji/unicode/1f537.png b/public/emoji/unicode/1f537.png new file mode 100644 index 00000000..f4598ec0 Binary files /dev/null and b/public/emoji/unicode/1f537.png differ diff --git a/public/emoji/unicode/1f538.png b/public/emoji/unicode/1f538.png new file mode 100644 index 00000000..04941d37 Binary files /dev/null and b/public/emoji/unicode/1f538.png differ diff --git a/public/emoji/unicode/1f539.png b/public/emoji/unicode/1f539.png new file mode 100644 index 00000000..5a7b5d55 Binary files /dev/null and b/public/emoji/unicode/1f539.png differ diff --git a/public/emoji/unicode/1f53a.png b/public/emoji/unicode/1f53a.png new file mode 100644 index 00000000..8c4428da Binary files /dev/null and b/public/emoji/unicode/1f53a.png differ diff --git a/public/emoji/unicode/1f53b.png b/public/emoji/unicode/1f53b.png new file mode 100644 index 00000000..94832f06 Binary files /dev/null and b/public/emoji/unicode/1f53b.png differ diff --git a/public/emoji/unicode/1f53c.png b/public/emoji/unicode/1f53c.png new file mode 100644 index 00000000..12173319 Binary files /dev/null and b/public/emoji/unicode/1f53c.png differ diff --git a/public/emoji/unicode/1f53d.png b/public/emoji/unicode/1f53d.png new file mode 100644 index 00000000..f7f2d510 Binary files /dev/null and b/public/emoji/unicode/1f53d.png differ diff --git a/public/emoji/unicode/1f550.png b/public/emoji/unicode/1f550.png new file mode 100644 index 00000000..ca34e897 Binary files /dev/null and b/public/emoji/unicode/1f550.png differ diff --git a/public/emoji/unicode/1f551.png b/public/emoji/unicode/1f551.png new file mode 100644 index 00000000..1a12524e Binary files /dev/null and b/public/emoji/unicode/1f551.png differ diff --git a/public/emoji/unicode/1f552.png b/public/emoji/unicode/1f552.png new file mode 100644 index 00000000..cd99bb15 Binary files /dev/null and b/public/emoji/unicode/1f552.png differ diff --git a/public/emoji/unicode/1f553.png b/public/emoji/unicode/1f553.png new file mode 100644 index 00000000..7274e8b0 Binary files /dev/null and b/public/emoji/unicode/1f553.png differ diff --git a/public/emoji/unicode/1f554.png b/public/emoji/unicode/1f554.png new file mode 100644 index 00000000..3ed5a81a Binary files /dev/null and b/public/emoji/unicode/1f554.png differ diff --git a/public/emoji/unicode/1f555.png b/public/emoji/unicode/1f555.png new file mode 100644 index 00000000..ac38cb92 Binary files /dev/null and b/public/emoji/unicode/1f555.png differ diff --git a/public/emoji/unicode/1f556.png b/public/emoji/unicode/1f556.png new file mode 100644 index 00000000..6a138dfd Binary files /dev/null and b/public/emoji/unicode/1f556.png differ diff --git a/public/emoji/unicode/1f557.png b/public/emoji/unicode/1f557.png new file mode 100644 index 00000000..6690cd74 Binary files /dev/null and b/public/emoji/unicode/1f557.png differ diff --git a/public/emoji/unicode/1f558.png b/public/emoji/unicode/1f558.png new file mode 100644 index 00000000..c4ad7460 Binary files /dev/null and b/public/emoji/unicode/1f558.png differ diff --git a/public/emoji/unicode/1f559.png b/public/emoji/unicode/1f559.png new file mode 100644 index 00000000..f710bef5 Binary files /dev/null and b/public/emoji/unicode/1f559.png differ diff --git a/public/emoji/unicode/1f55a.png b/public/emoji/unicode/1f55a.png new file mode 100644 index 00000000..fbc165b9 Binary files /dev/null and b/public/emoji/unicode/1f55a.png differ diff --git a/public/emoji/unicode/1f55b.png b/public/emoji/unicode/1f55b.png new file mode 100644 index 00000000..c1ca82f3 Binary files /dev/null and b/public/emoji/unicode/1f55b.png differ diff --git a/public/emoji/unicode/1f55c.png b/public/emoji/unicode/1f55c.png new file mode 100644 index 00000000..df939201 Binary files /dev/null and b/public/emoji/unicode/1f55c.png differ diff --git a/public/emoji/unicode/1f55d.png b/public/emoji/unicode/1f55d.png new file mode 100644 index 00000000..f12c6912 Binary files /dev/null and b/public/emoji/unicode/1f55d.png differ diff --git a/public/emoji/unicode/1f55e.png b/public/emoji/unicode/1f55e.png new file mode 100644 index 00000000..1dc9628e Binary files /dev/null and b/public/emoji/unicode/1f55e.png differ diff --git a/public/emoji/unicode/1f55f.png b/public/emoji/unicode/1f55f.png new file mode 100644 index 00000000..7726aaea Binary files /dev/null and b/public/emoji/unicode/1f55f.png differ diff --git a/public/emoji/unicode/1f560.png b/public/emoji/unicode/1f560.png new file mode 100644 index 00000000..e08d4ad2 Binary files /dev/null and b/public/emoji/unicode/1f560.png differ diff --git a/public/emoji/unicode/1f561.png b/public/emoji/unicode/1f561.png new file mode 100644 index 00000000..46f0681f Binary files /dev/null and b/public/emoji/unicode/1f561.png differ diff --git a/public/emoji/unicode/1f562.png b/public/emoji/unicode/1f562.png new file mode 100644 index 00000000..18aab22f Binary files /dev/null and b/public/emoji/unicode/1f562.png differ diff --git a/public/emoji/unicode/1f563.png b/public/emoji/unicode/1f563.png new file mode 100644 index 00000000..ec3e382d Binary files /dev/null and b/public/emoji/unicode/1f563.png differ diff --git a/public/emoji/unicode/1f564.png b/public/emoji/unicode/1f564.png new file mode 100644 index 00000000..fd352214 Binary files /dev/null and b/public/emoji/unicode/1f564.png differ diff --git a/public/emoji/unicode/1f565.png b/public/emoji/unicode/1f565.png new file mode 100644 index 00000000..84a3bc8f Binary files /dev/null and b/public/emoji/unicode/1f565.png differ diff --git a/public/emoji/unicode/1f566.png b/public/emoji/unicode/1f566.png new file mode 100644 index 00000000..415999ec Binary files /dev/null and b/public/emoji/unicode/1f566.png differ diff --git a/public/emoji/unicode/1f567.png b/public/emoji/unicode/1f567.png new file mode 100644 index 00000000..a6527154 Binary files /dev/null and b/public/emoji/unicode/1f567.png differ diff --git a/public/emoji/unicode/1f5fb.png b/public/emoji/unicode/1f5fb.png new file mode 100644 index 00000000..4c313e58 Binary files /dev/null and b/public/emoji/unicode/1f5fb.png differ diff --git a/public/emoji/unicode/1f5fc.png b/public/emoji/unicode/1f5fc.png new file mode 100644 index 00000000..e1cbd7a3 Binary files /dev/null and b/public/emoji/unicode/1f5fc.png differ diff --git a/public/emoji/unicode/1f5fd.png b/public/emoji/unicode/1f5fd.png new file mode 100644 index 00000000..9ad90280 Binary files /dev/null and b/public/emoji/unicode/1f5fd.png differ diff --git a/public/emoji/unicode/1f5fe.png b/public/emoji/unicode/1f5fe.png new file mode 100644 index 00000000..45932803 Binary files /dev/null and b/public/emoji/unicode/1f5fe.png differ diff --git a/public/emoji/unicode/1f5ff.png b/public/emoji/unicode/1f5ff.png new file mode 100644 index 00000000..61a1a9c2 Binary files /dev/null and b/public/emoji/unicode/1f5ff.png differ diff --git a/public/emoji/unicode/1f600.png b/public/emoji/unicode/1f600.png new file mode 100644 index 00000000..0ef00d79 Binary files /dev/null and b/public/emoji/unicode/1f600.png differ diff --git a/public/emoji/unicode/1f601.png b/public/emoji/unicode/1f601.png new file mode 100644 index 00000000..591cfcef Binary files /dev/null and b/public/emoji/unicode/1f601.png differ diff --git a/public/emoji/unicode/1f602.png b/public/emoji/unicode/1f602.png new file mode 100644 index 00000000..47df693d Binary files /dev/null and b/public/emoji/unicode/1f602.png differ diff --git a/public/emoji/unicode/1f603.png b/public/emoji/unicode/1f603.png new file mode 100644 index 00000000..77b581d6 Binary files /dev/null and b/public/emoji/unicode/1f603.png differ diff --git a/public/emoji/unicode/1f604.png b/public/emoji/unicode/1f604.png new file mode 100644 index 00000000..81a83968 Binary files /dev/null and b/public/emoji/unicode/1f604.png differ diff --git a/public/emoji/unicode/1f605.png b/public/emoji/unicode/1f605.png new file mode 100644 index 00000000..3903f717 Binary files /dev/null and b/public/emoji/unicode/1f605.png differ diff --git a/public/emoji/unicode/1f606.png b/public/emoji/unicode/1f606.png new file mode 100644 index 00000000..11c91eb2 Binary files /dev/null and b/public/emoji/unicode/1f606.png differ diff --git a/public/emoji/unicode/1f607.png b/public/emoji/unicode/1f607.png new file mode 100644 index 00000000..503b614f Binary files /dev/null and b/public/emoji/unicode/1f607.png differ diff --git a/public/emoji/unicode/1f608.png b/public/emoji/unicode/1f608.png new file mode 100644 index 00000000..d9040493 Binary files /dev/null and b/public/emoji/unicode/1f608.png differ diff --git a/public/emoji/unicode/1f609.png b/public/emoji/unicode/1f609.png new file mode 100644 index 00000000..756766dd Binary files /dev/null and b/public/emoji/unicode/1f609.png differ diff --git a/public/emoji/unicode/1f60a.png b/public/emoji/unicode/1f60a.png new file mode 100644 index 00000000..1e9021cb Binary files /dev/null and b/public/emoji/unicode/1f60a.png differ diff --git a/public/emoji/unicode/1f60b.png b/public/emoji/unicode/1f60b.png new file mode 100644 index 00000000..fc39637e Binary files /dev/null and b/public/emoji/unicode/1f60b.png differ diff --git a/public/emoji/unicode/1f60c.png b/public/emoji/unicode/1f60c.png new file mode 100644 index 00000000..820cf315 Binary files /dev/null and b/public/emoji/unicode/1f60c.png differ diff --git a/public/emoji/unicode/1f60d.png b/public/emoji/unicode/1f60d.png new file mode 100644 index 00000000..0e579427 Binary files /dev/null and b/public/emoji/unicode/1f60d.png differ diff --git a/public/emoji/unicode/1f60e.png b/public/emoji/unicode/1f60e.png new file mode 100644 index 00000000..1c468a1c Binary files /dev/null and b/public/emoji/unicode/1f60e.png differ diff --git a/public/emoji/unicode/1f60f.png b/public/emoji/unicode/1f60f.png new file mode 100644 index 00000000..bc6e5082 Binary files /dev/null and b/public/emoji/unicode/1f60f.png differ diff --git a/public/emoji/unicode/1f610.png b/public/emoji/unicode/1f610.png new file mode 100644 index 00000000..682a1ba0 Binary files /dev/null and b/public/emoji/unicode/1f610.png differ diff --git a/public/emoji/unicode/1f611.png b/public/emoji/unicode/1f611.png new file mode 100644 index 00000000..1798f24d Binary files /dev/null and b/public/emoji/unicode/1f611.png differ diff --git a/public/emoji/unicode/1f612.png b/public/emoji/unicode/1f612.png new file mode 100644 index 00000000..3722e6f5 Binary files /dev/null and b/public/emoji/unicode/1f612.png differ diff --git a/public/emoji/unicode/1f613.png b/public/emoji/unicode/1f613.png new file mode 100644 index 00000000..e894b769 Binary files /dev/null and b/public/emoji/unicode/1f613.png differ diff --git a/public/emoji/unicode/1f614.png b/public/emoji/unicode/1f614.png new file mode 100644 index 00000000..2f3bad94 Binary files /dev/null and b/public/emoji/unicode/1f614.png differ diff --git a/public/emoji/unicode/1f615.png b/public/emoji/unicode/1f615.png new file mode 100644 index 00000000..18ff760a Binary files /dev/null and b/public/emoji/unicode/1f615.png differ diff --git a/public/emoji/unicode/1f616.png b/public/emoji/unicode/1f616.png new file mode 100644 index 00000000..a5877a0a Binary files /dev/null and b/public/emoji/unicode/1f616.png differ diff --git a/public/emoji/unicode/1f617.png b/public/emoji/unicode/1f617.png new file mode 100644 index 00000000..eb049c80 Binary files /dev/null and b/public/emoji/unicode/1f617.png differ diff --git a/public/emoji/unicode/1f618.png b/public/emoji/unicode/1f618.png new file mode 100644 index 00000000..af9a80b7 Binary files /dev/null and b/public/emoji/unicode/1f618.png differ diff --git a/public/emoji/unicode/1f619.png b/public/emoji/unicode/1f619.png new file mode 100644 index 00000000..d85706e7 Binary files /dev/null and b/public/emoji/unicode/1f619.png differ diff --git a/public/emoji/unicode/1f61a.png b/public/emoji/unicode/1f61a.png new file mode 100644 index 00000000..449de197 Binary files /dev/null and b/public/emoji/unicode/1f61a.png differ diff --git a/public/emoji/unicode/1f61b.png b/public/emoji/unicode/1f61b.png new file mode 100644 index 00000000..53c41433 Binary files /dev/null and b/public/emoji/unicode/1f61b.png differ diff --git a/public/emoji/unicode/1f61c.png b/public/emoji/unicode/1f61c.png new file mode 100644 index 00000000..6ae9d497 Binary files /dev/null and b/public/emoji/unicode/1f61c.png differ diff --git a/public/emoji/unicode/1f61d.png b/public/emoji/unicode/1f61d.png new file mode 100644 index 00000000..333716ee Binary files /dev/null and b/public/emoji/unicode/1f61d.png differ diff --git a/public/emoji/unicode/1f61e.png b/public/emoji/unicode/1f61e.png new file mode 100644 index 00000000..82552008 Binary files /dev/null and b/public/emoji/unicode/1f61e.png differ diff --git a/public/emoji/unicode/1f61f.png b/public/emoji/unicode/1f61f.png new file mode 100644 index 00000000..afd9283f Binary files /dev/null and b/public/emoji/unicode/1f61f.png differ diff --git a/public/emoji/unicode/1f620.png b/public/emoji/unicode/1f620.png new file mode 100644 index 00000000..34174f5e Binary files /dev/null and b/public/emoji/unicode/1f620.png differ diff --git a/public/emoji/unicode/1f621.png b/public/emoji/unicode/1f621.png new file mode 100644 index 00000000..c65ddff5 Binary files /dev/null and b/public/emoji/unicode/1f621.png differ diff --git a/public/emoji/unicode/1f622.png b/public/emoji/unicode/1f622.png new file mode 100644 index 00000000..6d0d9afd Binary files /dev/null and b/public/emoji/unicode/1f622.png differ diff --git a/public/emoji/unicode/1f623.png b/public/emoji/unicode/1f623.png new file mode 100644 index 00000000..c7e433e8 Binary files /dev/null and b/public/emoji/unicode/1f623.png differ diff --git a/public/emoji/unicode/1f624.png b/public/emoji/unicode/1f624.png new file mode 100644 index 00000000..92f93bd1 Binary files /dev/null and b/public/emoji/unicode/1f624.png differ diff --git a/public/emoji/unicode/1f625.png b/public/emoji/unicode/1f625.png new file mode 100644 index 00000000..fa5f9e7f Binary files /dev/null and b/public/emoji/unicode/1f625.png differ diff --git a/public/emoji/unicode/1f626.png b/public/emoji/unicode/1f626.png new file mode 100644 index 00000000..7f8b6c77 Binary files /dev/null and b/public/emoji/unicode/1f626.png differ diff --git a/public/emoji/unicode/1f627.png b/public/emoji/unicode/1f627.png new file mode 100644 index 00000000..c2edad79 Binary files /dev/null and b/public/emoji/unicode/1f627.png differ diff --git a/public/emoji/unicode/1f628.png b/public/emoji/unicode/1f628.png new file mode 100644 index 00000000..513fce47 Binary files /dev/null and b/public/emoji/unicode/1f628.png differ diff --git a/public/emoji/unicode/1f629.png b/public/emoji/unicode/1f629.png new file mode 100644 index 00000000..0c547541 Binary files /dev/null and b/public/emoji/unicode/1f629.png differ diff --git a/public/emoji/unicode/1f62a.png b/public/emoji/unicode/1f62a.png new file mode 100644 index 00000000..df4f55ef Binary files /dev/null and b/public/emoji/unicode/1f62a.png differ diff --git a/public/emoji/unicode/1f62b.png b/public/emoji/unicode/1f62b.png new file mode 100644 index 00000000..3a8eefe5 Binary files /dev/null and b/public/emoji/unicode/1f62b.png differ diff --git a/public/emoji/unicode/1f62c.png b/public/emoji/unicode/1f62c.png new file mode 100644 index 00000000..f78e9407 Binary files /dev/null and b/public/emoji/unicode/1f62c.png differ diff --git a/public/emoji/unicode/1f62d.png b/public/emoji/unicode/1f62d.png new file mode 100644 index 00000000..7d433183 Binary files /dev/null and b/public/emoji/unicode/1f62d.png differ diff --git a/public/emoji/unicode/1f62e.png b/public/emoji/unicode/1f62e.png new file mode 100644 index 00000000..e5283582 Binary files /dev/null and b/public/emoji/unicode/1f62e.png differ diff --git a/public/emoji/unicode/1f62f.png b/public/emoji/unicode/1f62f.png new file mode 100644 index 00000000..afa3f668 Binary files /dev/null and b/public/emoji/unicode/1f62f.png differ diff --git a/public/emoji/unicode/1f630.png b/public/emoji/unicode/1f630.png new file mode 100644 index 00000000..b9e39bc6 Binary files /dev/null and b/public/emoji/unicode/1f630.png differ diff --git a/public/emoji/unicode/1f631.png b/public/emoji/unicode/1f631.png new file mode 100644 index 00000000..76bfc6b8 Binary files /dev/null and b/public/emoji/unicode/1f631.png differ diff --git a/public/emoji/unicode/1f632.png b/public/emoji/unicode/1f632.png new file mode 100644 index 00000000..858a8348 Binary files /dev/null and b/public/emoji/unicode/1f632.png differ diff --git a/public/emoji/unicode/1f633.png b/public/emoji/unicode/1f633.png new file mode 100644 index 00000000..9b49410c Binary files /dev/null and b/public/emoji/unicode/1f633.png differ diff --git a/public/emoji/unicode/1f634.png b/public/emoji/unicode/1f634.png new file mode 100644 index 00000000..a2f3bf75 Binary files /dev/null and b/public/emoji/unicode/1f634.png differ diff --git a/public/emoji/unicode/1f635.png b/public/emoji/unicode/1f635.png new file mode 100644 index 00000000..8001d6ff Binary files /dev/null and b/public/emoji/unicode/1f635.png differ diff --git a/public/emoji/unicode/1f636.png b/public/emoji/unicode/1f636.png new file mode 100644 index 00000000..d9ec7ca7 Binary files /dev/null and b/public/emoji/unicode/1f636.png differ diff --git a/public/emoji/unicode/1f637.png b/public/emoji/unicode/1f637.png new file mode 100644 index 00000000..05887e99 Binary files /dev/null and b/public/emoji/unicode/1f637.png differ diff --git a/public/emoji/unicode/1f638.png b/public/emoji/unicode/1f638.png new file mode 100644 index 00000000..ad333ba3 Binary files /dev/null and b/public/emoji/unicode/1f638.png differ diff --git a/public/emoji/unicode/1f639.png b/public/emoji/unicode/1f639.png new file mode 100644 index 00000000..6c60cb0e Binary files /dev/null and b/public/emoji/unicode/1f639.png differ diff --git a/public/emoji/unicode/1f63a.png b/public/emoji/unicode/1f63a.png new file mode 100644 index 00000000..dbf1b027 Binary files /dev/null and b/public/emoji/unicode/1f63a.png differ diff --git a/public/emoji/unicode/1f63b.png b/public/emoji/unicode/1f63b.png new file mode 100644 index 00000000..eeba240e Binary files /dev/null and b/public/emoji/unicode/1f63b.png differ diff --git a/public/emoji/unicode/1f63c.png b/public/emoji/unicode/1f63c.png new file mode 100644 index 00000000..351565e2 Binary files /dev/null and b/public/emoji/unicode/1f63c.png differ diff --git a/public/emoji/unicode/1f63d.png b/public/emoji/unicode/1f63d.png new file mode 100644 index 00000000..adc62fbe Binary files /dev/null and b/public/emoji/unicode/1f63d.png differ diff --git a/public/emoji/unicode/1f63e.png b/public/emoji/unicode/1f63e.png new file mode 100644 index 00000000..4325fd48 Binary files /dev/null and b/public/emoji/unicode/1f63e.png differ diff --git a/public/emoji/unicode/1f63f.png b/public/emoji/unicode/1f63f.png new file mode 100644 index 00000000..42d4c27c Binary files /dev/null and b/public/emoji/unicode/1f63f.png differ diff --git a/public/emoji/unicode/1f640.png b/public/emoji/unicode/1f640.png new file mode 100644 index 00000000..d94cd34f Binary files /dev/null and b/public/emoji/unicode/1f640.png differ diff --git a/public/emoji/unicode/1f645.png b/public/emoji/unicode/1f645.png new file mode 100644 index 00000000..d459a35b Binary files /dev/null and b/public/emoji/unicode/1f645.png differ diff --git a/public/emoji/unicode/1f646.png b/public/emoji/unicode/1f646.png new file mode 100644 index 00000000..e8b98194 Binary files /dev/null and b/public/emoji/unicode/1f646.png differ diff --git a/public/emoji/unicode/1f647.png b/public/emoji/unicode/1f647.png new file mode 100644 index 00000000..024cb610 Binary files /dev/null and b/public/emoji/unicode/1f647.png differ diff --git a/public/emoji/unicode/1f648.png b/public/emoji/unicode/1f648.png new file mode 100644 index 00000000..0890a622 Binary files /dev/null and b/public/emoji/unicode/1f648.png differ diff --git a/public/emoji/unicode/1f649.png b/public/emoji/unicode/1f649.png new file mode 100644 index 00000000..f97a1f9a Binary files /dev/null and b/public/emoji/unicode/1f649.png differ diff --git a/public/emoji/unicode/1f64a.png b/public/emoji/unicode/1f64a.png new file mode 100644 index 00000000..87944c4d Binary files /dev/null and b/public/emoji/unicode/1f64a.png differ diff --git a/public/emoji/unicode/1f64b.png b/public/emoji/unicode/1f64b.png new file mode 100644 index 00000000..e1741a40 Binary files /dev/null and b/public/emoji/unicode/1f64b.png differ diff --git a/public/emoji/unicode/1f64c.png b/public/emoji/unicode/1f64c.png new file mode 100644 index 00000000..e03142bd Binary files /dev/null and b/public/emoji/unicode/1f64c.png differ diff --git a/public/emoji/unicode/1f64d.png b/public/emoji/unicode/1f64d.png new file mode 100644 index 00000000..6f34d5e1 Binary files /dev/null and b/public/emoji/unicode/1f64d.png differ diff --git a/public/emoji/unicode/1f64e.png b/public/emoji/unicode/1f64e.png new file mode 100644 index 00000000..c4a95c3b Binary files /dev/null and b/public/emoji/unicode/1f64e.png differ diff --git a/public/emoji/unicode/1f64f.png b/public/emoji/unicode/1f64f.png new file mode 100644 index 00000000..f86c992d Binary files /dev/null and b/public/emoji/unicode/1f64f.png differ diff --git a/public/emoji/unicode/1f680.png b/public/emoji/unicode/1f680.png new file mode 100644 index 00000000..783078d3 Binary files /dev/null and b/public/emoji/unicode/1f680.png differ diff --git a/public/emoji/unicode/1f681.png b/public/emoji/unicode/1f681.png new file mode 100644 index 00000000..8e82a0d5 Binary files /dev/null and b/public/emoji/unicode/1f681.png differ diff --git a/public/emoji/unicode/1f682.png b/public/emoji/unicode/1f682.png new file mode 100644 index 00000000..54950776 Binary files /dev/null and b/public/emoji/unicode/1f682.png differ diff --git a/public/emoji/unicode/1f683.png b/public/emoji/unicode/1f683.png new file mode 100644 index 00000000..22361158 Binary files /dev/null and b/public/emoji/unicode/1f683.png differ diff --git a/public/emoji/unicode/1f684.png b/public/emoji/unicode/1f684.png new file mode 100644 index 00000000..49985a3b Binary files /dev/null and b/public/emoji/unicode/1f684.png differ diff --git a/public/emoji/unicode/1f685.png b/public/emoji/unicode/1f685.png new file mode 100644 index 00000000..61b494ae Binary files /dev/null and b/public/emoji/unicode/1f685.png differ diff --git a/public/emoji/unicode/1f686.png b/public/emoji/unicode/1f686.png new file mode 100644 index 00000000..9c0d3ab6 Binary files /dev/null and b/public/emoji/unicode/1f686.png differ diff --git a/public/emoji/unicode/1f687.png b/public/emoji/unicode/1f687.png new file mode 100644 index 00000000..4890f9bc Binary files /dev/null and b/public/emoji/unicode/1f687.png differ diff --git a/public/emoji/unicode/1f688.png b/public/emoji/unicode/1f688.png new file mode 100644 index 00000000..bcfe801e Binary files /dev/null and b/public/emoji/unicode/1f688.png differ diff --git a/public/emoji/unicode/1f689.png b/public/emoji/unicode/1f689.png new file mode 100644 index 00000000..e77daa8a Binary files /dev/null and b/public/emoji/unicode/1f689.png differ diff --git a/public/emoji/unicode/1f68a.png b/public/emoji/unicode/1f68a.png new file mode 100644 index 00000000..5eb29fb7 Binary files /dev/null and b/public/emoji/unicode/1f68a.png differ diff --git a/public/emoji/unicode/1f68b.png b/public/emoji/unicode/1f68b.png new file mode 100644 index 00000000..0a8ea52b Binary files /dev/null and b/public/emoji/unicode/1f68b.png differ diff --git a/public/emoji/unicode/1f68c.png b/public/emoji/unicode/1f68c.png new file mode 100644 index 00000000..823aa39e Binary files /dev/null and b/public/emoji/unicode/1f68c.png differ diff --git a/public/emoji/unicode/1f68d.png b/public/emoji/unicode/1f68d.png new file mode 100644 index 00000000..3695f762 Binary files /dev/null and b/public/emoji/unicode/1f68d.png differ diff --git a/public/emoji/unicode/1f68e.png b/public/emoji/unicode/1f68e.png new file mode 100644 index 00000000..b9740a53 Binary files /dev/null and b/public/emoji/unicode/1f68e.png differ diff --git a/public/emoji/unicode/1f68f.png b/public/emoji/unicode/1f68f.png new file mode 100644 index 00000000..99af2322 Binary files /dev/null and b/public/emoji/unicode/1f68f.png differ diff --git a/public/emoji/unicode/1f690.png b/public/emoji/unicode/1f690.png new file mode 100644 index 00000000..c52cef23 Binary files /dev/null and b/public/emoji/unicode/1f690.png differ diff --git a/public/emoji/unicode/1f691.png b/public/emoji/unicode/1f691.png new file mode 100644 index 00000000..b740f45d Binary files /dev/null and b/public/emoji/unicode/1f691.png differ diff --git a/public/emoji/unicode/1f692.png b/public/emoji/unicode/1f692.png new file mode 100644 index 00000000..9e6c59c9 Binary files /dev/null and b/public/emoji/unicode/1f692.png differ diff --git a/public/emoji/unicode/1f693.png b/public/emoji/unicode/1f693.png new file mode 100644 index 00000000..b8f17275 Binary files /dev/null and b/public/emoji/unicode/1f693.png differ diff --git a/public/emoji/unicode/1f694.png b/public/emoji/unicode/1f694.png new file mode 100644 index 00000000..af20e7ef Binary files /dev/null and b/public/emoji/unicode/1f694.png differ diff --git a/public/emoji/unicode/1f695.png b/public/emoji/unicode/1f695.png new file mode 100644 index 00000000..60a50d36 Binary files /dev/null and b/public/emoji/unicode/1f695.png differ diff --git a/public/emoji/unicode/1f696.png b/public/emoji/unicode/1f696.png new file mode 100644 index 00000000..f78cf310 Binary files /dev/null and b/public/emoji/unicode/1f696.png differ diff --git a/public/emoji/unicode/1f697.png b/public/emoji/unicode/1f697.png new file mode 100644 index 00000000..d70a2f06 Binary files /dev/null and b/public/emoji/unicode/1f697.png differ diff --git a/public/emoji/unicode/1f698.png b/public/emoji/unicode/1f698.png new file mode 100644 index 00000000..cb46de22 Binary files /dev/null and b/public/emoji/unicode/1f698.png differ diff --git a/public/emoji/unicode/1f699.png b/public/emoji/unicode/1f699.png new file mode 100644 index 00000000..978291e0 Binary files /dev/null and b/public/emoji/unicode/1f699.png differ diff --git a/public/emoji/unicode/1f69a.png b/public/emoji/unicode/1f69a.png new file mode 100644 index 00000000..3f25ba1f Binary files /dev/null and b/public/emoji/unicode/1f69a.png differ diff --git a/public/emoji/unicode/1f69b.png b/public/emoji/unicode/1f69b.png new file mode 100644 index 00000000..81ec1f91 Binary files /dev/null and b/public/emoji/unicode/1f69b.png differ diff --git a/public/emoji/unicode/1f69c.png b/public/emoji/unicode/1f69c.png new file mode 100644 index 00000000..058fd3ed Binary files /dev/null and b/public/emoji/unicode/1f69c.png differ diff --git a/public/emoji/unicode/1f69d.png b/public/emoji/unicode/1f69d.png new file mode 100644 index 00000000..913d3002 Binary files /dev/null and b/public/emoji/unicode/1f69d.png differ diff --git a/public/emoji/unicode/1f69e.png b/public/emoji/unicode/1f69e.png new file mode 100644 index 00000000..1f3d1aab Binary files /dev/null and b/public/emoji/unicode/1f69e.png differ diff --git a/public/emoji/unicode/1f69f.png b/public/emoji/unicode/1f69f.png new file mode 100644 index 00000000..aaa45f61 Binary files /dev/null and b/public/emoji/unicode/1f69f.png differ diff --git a/public/emoji/unicode/1f6a0.png b/public/emoji/unicode/1f6a0.png new file mode 100644 index 00000000..5688bb23 Binary files /dev/null and b/public/emoji/unicode/1f6a0.png differ diff --git a/public/emoji/unicode/1f6a1.png b/public/emoji/unicode/1f6a1.png new file mode 100644 index 00000000..38f6dfe2 Binary files /dev/null and b/public/emoji/unicode/1f6a1.png differ diff --git a/public/emoji/unicode/1f6a2.png b/public/emoji/unicode/1f6a2.png new file mode 100644 index 00000000..5d2d8b60 Binary files /dev/null and b/public/emoji/unicode/1f6a2.png differ diff --git a/public/emoji/unicode/1f6a3.png b/public/emoji/unicode/1f6a3.png new file mode 100644 index 00000000..fe8ae3ec Binary files /dev/null and b/public/emoji/unicode/1f6a3.png differ diff --git a/public/emoji/unicode/1f6a4.png b/public/emoji/unicode/1f6a4.png new file mode 100644 index 00000000..da6689b3 Binary files /dev/null and b/public/emoji/unicode/1f6a4.png differ diff --git a/public/emoji/unicode/1f6a5.png b/public/emoji/unicode/1f6a5.png new file mode 100644 index 00000000..42eaf709 Binary files /dev/null and b/public/emoji/unicode/1f6a5.png differ diff --git a/public/emoji/unicode/1f6a6.png b/public/emoji/unicode/1f6a6.png new file mode 100644 index 00000000..7a5ba35f Binary files /dev/null and b/public/emoji/unicode/1f6a6.png differ diff --git a/public/emoji/unicode/1f6a7.png b/public/emoji/unicode/1f6a7.png new file mode 100644 index 00000000..523e9f10 Binary files /dev/null and b/public/emoji/unicode/1f6a7.png differ diff --git a/public/emoji/unicode/1f6a8.png b/public/emoji/unicode/1f6a8.png new file mode 100644 index 00000000..6cf4a775 Binary files /dev/null and b/public/emoji/unicode/1f6a8.png differ diff --git a/public/emoji/unicode/1f6a9.png b/public/emoji/unicode/1f6a9.png new file mode 100644 index 00000000..f9a3f32d Binary files /dev/null and b/public/emoji/unicode/1f6a9.png differ diff --git a/public/emoji/unicode/1f6aa.png b/public/emoji/unicode/1f6aa.png new file mode 100644 index 00000000..83c819ae Binary files /dev/null and b/public/emoji/unicode/1f6aa.png differ diff --git a/public/emoji/unicode/1f6ab.png b/public/emoji/unicode/1f6ab.png new file mode 100644 index 00000000..a8444d18 Binary files /dev/null and b/public/emoji/unicode/1f6ab.png differ diff --git a/public/emoji/unicode/1f6ac.png b/public/emoji/unicode/1f6ac.png new file mode 100644 index 00000000..4aad6cbd Binary files /dev/null and b/public/emoji/unicode/1f6ac.png differ diff --git a/public/emoji/unicode/1f6ad.png b/public/emoji/unicode/1f6ad.png new file mode 100644 index 00000000..eb11d791 Binary files /dev/null and b/public/emoji/unicode/1f6ad.png differ diff --git a/public/emoji/unicode/1f6ae.png b/public/emoji/unicode/1f6ae.png new file mode 100644 index 00000000..c2e350c2 Binary files /dev/null and b/public/emoji/unicode/1f6ae.png differ diff --git a/public/emoji/unicode/1f6af.png b/public/emoji/unicode/1f6af.png new file mode 100644 index 00000000..38c7ae7a Binary files /dev/null and b/public/emoji/unicode/1f6af.png differ diff --git a/public/emoji/unicode/1f6b0.png b/public/emoji/unicode/1f6b0.png new file mode 100644 index 00000000..e9fd5607 Binary files /dev/null and b/public/emoji/unicode/1f6b0.png differ diff --git a/public/emoji/unicode/1f6b1.png b/public/emoji/unicode/1f6b1.png new file mode 100644 index 00000000..1b29d35b Binary files /dev/null and b/public/emoji/unicode/1f6b1.png differ diff --git a/public/emoji/unicode/1f6b2.png b/public/emoji/unicode/1f6b2.png new file mode 100644 index 00000000..65738602 Binary files /dev/null and b/public/emoji/unicode/1f6b2.png differ diff --git a/public/emoji/unicode/1f6b3.png b/public/emoji/unicode/1f6b3.png new file mode 100644 index 00000000..4b262166 Binary files /dev/null and b/public/emoji/unicode/1f6b3.png differ diff --git a/public/emoji/unicode/1f6b4.png b/public/emoji/unicode/1f6b4.png new file mode 100644 index 00000000..4e3e0549 Binary files /dev/null and b/public/emoji/unicode/1f6b4.png differ diff --git a/public/emoji/unicode/1f6b5.png b/public/emoji/unicode/1f6b5.png new file mode 100644 index 00000000..b6988975 Binary files /dev/null and b/public/emoji/unicode/1f6b5.png differ diff --git a/public/emoji/unicode/1f6b6.png b/public/emoji/unicode/1f6b6.png new file mode 100644 index 00000000..7a2bfacf Binary files /dev/null and b/public/emoji/unicode/1f6b6.png differ diff --git a/public/emoji/unicode/1f6b7.png b/public/emoji/unicode/1f6b7.png new file mode 100644 index 00000000..c35f530b Binary files /dev/null and b/public/emoji/unicode/1f6b7.png differ diff --git a/public/emoji/unicode/1f6b8.png b/public/emoji/unicode/1f6b8.png new file mode 100644 index 00000000..b0302ae6 Binary files /dev/null and b/public/emoji/unicode/1f6b8.png differ diff --git a/public/emoji/unicode/1f6b9.png b/public/emoji/unicode/1f6b9.png new file mode 100644 index 00000000..abccfc9f Binary files /dev/null and b/public/emoji/unicode/1f6b9.png differ diff --git a/public/emoji/unicode/1f6ba.png b/public/emoji/unicode/1f6ba.png new file mode 100644 index 00000000..518b76a6 Binary files /dev/null and b/public/emoji/unicode/1f6ba.png differ diff --git a/public/emoji/unicode/1f6bb.png b/public/emoji/unicode/1f6bb.png new file mode 100644 index 00000000..312ca3dc Binary files /dev/null and b/public/emoji/unicode/1f6bb.png differ diff --git a/public/emoji/unicode/1f6bc.png b/public/emoji/unicode/1f6bc.png new file mode 100644 index 00000000..2e58725c Binary files /dev/null and b/public/emoji/unicode/1f6bc.png differ diff --git a/public/emoji/unicode/1f6bd.png b/public/emoji/unicode/1f6bd.png new file mode 100644 index 00000000..e5cc4119 Binary files /dev/null and b/public/emoji/unicode/1f6bd.png differ diff --git a/public/emoji/unicode/1f6be.png b/public/emoji/unicode/1f6be.png new file mode 100644 index 00000000..dfe84d2a Binary files /dev/null and b/public/emoji/unicode/1f6be.png differ diff --git a/public/emoji/unicode/1f6bf.png b/public/emoji/unicode/1f6bf.png new file mode 100644 index 00000000..94f82aac Binary files /dev/null and b/public/emoji/unicode/1f6bf.png differ diff --git a/public/emoji/unicode/1f6c0.png b/public/emoji/unicode/1f6c0.png new file mode 100644 index 00000000..8f75d1d2 Binary files /dev/null and b/public/emoji/unicode/1f6c0.png differ diff --git a/public/emoji/unicode/1f6c1.png b/public/emoji/unicode/1f6c1.png new file mode 100644 index 00000000..1c3f844a Binary files /dev/null and b/public/emoji/unicode/1f6c1.png differ diff --git a/public/emoji/unicode/1f6c2.png b/public/emoji/unicode/1f6c2.png new file mode 100644 index 00000000..675b76d3 Binary files /dev/null and b/public/emoji/unicode/1f6c2.png differ diff --git a/public/emoji/unicode/1f6c3.png b/public/emoji/unicode/1f6c3.png new file mode 100644 index 00000000..92691e31 Binary files /dev/null and b/public/emoji/unicode/1f6c3.png differ diff --git a/public/emoji/unicode/1f6c4.png b/public/emoji/unicode/1f6c4.png new file mode 100644 index 00000000..59ae044a Binary files /dev/null and b/public/emoji/unicode/1f6c4.png differ diff --git a/public/emoji/unicode/1f6c5.png b/public/emoji/unicode/1f6c5.png new file mode 100644 index 00000000..1c08b464 Binary files /dev/null and b/public/emoji/unicode/1f6c5.png differ diff --git a/public/emoji/unicode/203c.png b/public/emoji/unicode/203c.png new file mode 100644 index 00000000..7270f0af Binary files /dev/null and b/public/emoji/unicode/203c.png differ diff --git a/public/emoji/unicode/2049.png b/public/emoji/unicode/2049.png new file mode 100644 index 00000000..64304b9f Binary files /dev/null and b/public/emoji/unicode/2049.png differ diff --git a/public/emoji/unicode/2122.png b/public/emoji/unicode/2122.png new file mode 100644 index 00000000..9ba71b75 Binary files /dev/null and b/public/emoji/unicode/2122.png differ diff --git a/public/emoji/unicode/2139.png b/public/emoji/unicode/2139.png new file mode 100644 index 00000000..9cb8b09b Binary files /dev/null and b/public/emoji/unicode/2139.png differ diff --git a/public/emoji/unicode/2194.png b/public/emoji/unicode/2194.png new file mode 100644 index 00000000..b9fd11c5 Binary files /dev/null and b/public/emoji/unicode/2194.png differ diff --git a/public/emoji/unicode/2195.png b/public/emoji/unicode/2195.png new file mode 100644 index 00000000..b718c214 Binary files /dev/null and b/public/emoji/unicode/2195.png differ diff --git a/public/emoji/unicode/2196.png b/public/emoji/unicode/2196.png new file mode 100644 index 00000000..12aebd9a Binary files /dev/null and b/public/emoji/unicode/2196.png differ diff --git a/public/emoji/unicode/2197.png b/public/emoji/unicode/2197.png new file mode 100644 index 00000000..0daf4e94 Binary files /dev/null and b/public/emoji/unicode/2197.png differ diff --git a/public/emoji/unicode/2198.png b/public/emoji/unicode/2198.png new file mode 100644 index 00000000..2a15cc7c Binary files /dev/null and b/public/emoji/unicode/2198.png differ diff --git a/public/emoji/unicode/2199.png b/public/emoji/unicode/2199.png new file mode 100644 index 00000000..a4438cb6 Binary files /dev/null and b/public/emoji/unicode/2199.png differ diff --git a/public/emoji/unicode/21a9.png b/public/emoji/unicode/21a9.png new file mode 100644 index 00000000..bc45dfef Binary files /dev/null and b/public/emoji/unicode/21a9.png differ diff --git a/public/emoji/unicode/21aa.png b/public/emoji/unicode/21aa.png new file mode 100644 index 00000000..8b4ea6e1 Binary files /dev/null and b/public/emoji/unicode/21aa.png differ diff --git a/public/emoji/unicode/231a.png b/public/emoji/unicode/231a.png new file mode 100644 index 00000000..d503bb87 Binary files /dev/null and b/public/emoji/unicode/231a.png differ diff --git a/public/emoji/unicode/231b.png b/public/emoji/unicode/231b.png new file mode 100644 index 00000000..405aab41 Binary files /dev/null and b/public/emoji/unicode/231b.png differ diff --git a/public/emoji/unicode/23e9.png b/public/emoji/unicode/23e9.png new file mode 100644 index 00000000..b94a1172 Binary files /dev/null and b/public/emoji/unicode/23e9.png differ diff --git a/public/emoji/unicode/23ea.png b/public/emoji/unicode/23ea.png new file mode 100644 index 00000000..13ba866a Binary files /dev/null and b/public/emoji/unicode/23ea.png differ diff --git a/public/emoji/unicode/23eb.png b/public/emoji/unicode/23eb.png new file mode 100644 index 00000000..d42979d4 Binary files /dev/null and b/public/emoji/unicode/23eb.png differ diff --git a/public/emoji/unicode/23ec.png b/public/emoji/unicode/23ec.png new file mode 100644 index 00000000..2ecbebcd Binary files /dev/null and b/public/emoji/unicode/23ec.png differ diff --git a/public/emoji/unicode/23f0.png b/public/emoji/unicode/23f0.png new file mode 100644 index 00000000..86ca8c8e Binary files /dev/null and b/public/emoji/unicode/23f0.png differ diff --git a/public/emoji/unicode/23f3.png b/public/emoji/unicode/23f3.png new file mode 100644 index 00000000..52c9eb70 Binary files /dev/null and b/public/emoji/unicode/23f3.png differ diff --git a/public/emoji/unicode/24c2.png b/public/emoji/unicode/24c2.png new file mode 100644 index 00000000..7424665e Binary files /dev/null and b/public/emoji/unicode/24c2.png differ diff --git a/public/emoji/unicode/25aa.png b/public/emoji/unicode/25aa.png new file mode 100644 index 00000000..a247751e Binary files /dev/null and b/public/emoji/unicode/25aa.png differ diff --git a/public/emoji/unicode/25ab.png b/public/emoji/unicode/25ab.png new file mode 100644 index 00000000..24ba879f Binary files /dev/null and b/public/emoji/unicode/25ab.png differ diff --git a/public/emoji/unicode/25b6.png b/public/emoji/unicode/25b6.png new file mode 100644 index 00000000..fbfe711b Binary files /dev/null and b/public/emoji/unicode/25b6.png differ diff --git a/public/emoji/unicode/25c0.png b/public/emoji/unicode/25c0.png new file mode 100644 index 00000000..2be422ba Binary files /dev/null and b/public/emoji/unicode/25c0.png differ diff --git a/public/emoji/unicode/25fb.png b/public/emoji/unicode/25fb.png new file mode 100644 index 00000000..199808bc Binary files /dev/null and b/public/emoji/unicode/25fb.png differ diff --git a/public/emoji/unicode/25fc.png b/public/emoji/unicode/25fc.png new file mode 100644 index 00000000..204cce12 Binary files /dev/null and b/public/emoji/unicode/25fc.png differ diff --git a/public/emoji/unicode/25fd.png b/public/emoji/unicode/25fd.png new file mode 100644 index 00000000..a115cdc4 Binary files /dev/null and b/public/emoji/unicode/25fd.png differ diff --git a/public/emoji/unicode/25fe.png b/public/emoji/unicode/25fe.png new file mode 100644 index 00000000..25bfe9c4 Binary files /dev/null and b/public/emoji/unicode/25fe.png differ diff --git a/public/emoji/unicode/2600.png b/public/emoji/unicode/2600.png new file mode 100644 index 00000000..d23c095e Binary files /dev/null and b/public/emoji/unicode/2600.png differ diff --git a/public/emoji/unicode/2601.png b/public/emoji/unicode/2601.png new file mode 100644 index 00000000..b31c08c0 Binary files /dev/null and b/public/emoji/unicode/2601.png differ diff --git a/public/emoji/unicode/260e.png b/public/emoji/unicode/260e.png new file mode 100644 index 00000000..87d2559b Binary files /dev/null and b/public/emoji/unicode/260e.png differ diff --git a/public/emoji/unicode/2611.png b/public/emoji/unicode/2611.png new file mode 100644 index 00000000..f07a466c Binary files /dev/null and b/public/emoji/unicode/2611.png differ diff --git a/public/emoji/unicode/2614.png b/public/emoji/unicode/2614.png new file mode 100644 index 00000000..1db722fa Binary files /dev/null and b/public/emoji/unicode/2614.png differ diff --git a/public/emoji/unicode/2615.png b/public/emoji/unicode/2615.png new file mode 100644 index 00000000..57e1adcb Binary files /dev/null and b/public/emoji/unicode/2615.png differ diff --git a/public/emoji/unicode/261d.png b/public/emoji/unicode/261d.png new file mode 100644 index 00000000..01896e21 Binary files /dev/null and b/public/emoji/unicode/261d.png differ diff --git a/public/emoji/unicode/263a.png b/public/emoji/unicode/263a.png new file mode 100644 index 00000000..bbab82d3 Binary files /dev/null and b/public/emoji/unicode/263a.png differ diff --git a/public/emoji/unicode/2648.png b/public/emoji/unicode/2648.png new file mode 100644 index 00000000..d676fd39 Binary files /dev/null and b/public/emoji/unicode/2648.png differ diff --git a/public/emoji/unicode/2649.png b/public/emoji/unicode/2649.png new file mode 100644 index 00000000..6af582f6 Binary files /dev/null and b/public/emoji/unicode/2649.png differ diff --git a/public/emoji/unicode/264a.png b/public/emoji/unicode/264a.png new file mode 100644 index 00000000..d926f6e8 Binary files /dev/null and b/public/emoji/unicode/264a.png differ diff --git a/public/emoji/unicode/264b.png b/public/emoji/unicode/264b.png new file mode 100644 index 00000000..ea43a4a2 Binary files /dev/null and b/public/emoji/unicode/264b.png differ diff --git a/public/emoji/unicode/264c.png b/public/emoji/unicode/264c.png new file mode 100644 index 00000000..e025933b Binary files /dev/null and b/public/emoji/unicode/264c.png differ diff --git a/public/emoji/unicode/264d.png b/public/emoji/unicode/264d.png new file mode 100644 index 00000000..72e1763f Binary files /dev/null and b/public/emoji/unicode/264d.png differ diff --git a/public/emoji/unicode/264e.png b/public/emoji/unicode/264e.png new file mode 100644 index 00000000..c9062dd2 Binary files /dev/null and b/public/emoji/unicode/264e.png differ diff --git a/public/emoji/unicode/264f.png b/public/emoji/unicode/264f.png new file mode 100644 index 00000000..67fcea16 Binary files /dev/null and b/public/emoji/unicode/264f.png differ diff --git a/public/emoji/unicode/2650.png b/public/emoji/unicode/2650.png new file mode 100644 index 00000000..8b5435ba Binary files /dev/null and b/public/emoji/unicode/2650.png differ diff --git a/public/emoji/unicode/2651.png b/public/emoji/unicode/2651.png new file mode 100644 index 00000000..f2044e78 Binary files /dev/null and b/public/emoji/unicode/2651.png differ diff --git a/public/emoji/unicode/2652.png b/public/emoji/unicode/2652.png new file mode 100644 index 00000000..cbff66ed Binary files /dev/null and b/public/emoji/unicode/2652.png differ diff --git a/public/emoji/unicode/2653.png b/public/emoji/unicode/2653.png new file mode 100644 index 00000000..5a2da0a0 Binary files /dev/null and b/public/emoji/unicode/2653.png differ diff --git a/public/emoji/unicode/2660.png b/public/emoji/unicode/2660.png new file mode 100644 index 00000000..133a1aba Binary files /dev/null and b/public/emoji/unicode/2660.png differ diff --git a/public/emoji/unicode/2663.png b/public/emoji/unicode/2663.png new file mode 100644 index 00000000..bfab5365 Binary files /dev/null and b/public/emoji/unicode/2663.png differ diff --git a/public/emoji/unicode/2665.png b/public/emoji/unicode/2665.png new file mode 100644 index 00000000..e8947153 Binary files /dev/null and b/public/emoji/unicode/2665.png differ diff --git a/public/emoji/unicode/2666.png b/public/emoji/unicode/2666.png new file mode 100644 index 00000000..fe082775 Binary files /dev/null and b/public/emoji/unicode/2666.png differ diff --git a/public/emoji/unicode/2668.png b/public/emoji/unicode/2668.png new file mode 100644 index 00000000..a0bc9d75 Binary files /dev/null and b/public/emoji/unicode/2668.png differ diff --git a/public/emoji/unicode/267b.png b/public/emoji/unicode/267b.png new file mode 100644 index 00000000..99104c0e Binary files /dev/null and b/public/emoji/unicode/267b.png differ diff --git a/public/emoji/unicode/267f.png b/public/emoji/unicode/267f.png new file mode 100644 index 00000000..eddcdd79 Binary files /dev/null and b/public/emoji/unicode/267f.png differ diff --git a/public/emoji/unicode/2693.png b/public/emoji/unicode/2693.png new file mode 100644 index 00000000..0c5192e6 Binary files /dev/null and b/public/emoji/unicode/2693.png differ diff --git a/public/emoji/unicode/26a0.png b/public/emoji/unicode/26a0.png new file mode 100644 index 00000000..466658d9 Binary files /dev/null and b/public/emoji/unicode/26a0.png differ diff --git a/public/emoji/unicode/26a1.png b/public/emoji/unicode/26a1.png new file mode 100644 index 00000000..260c531b Binary files /dev/null and b/public/emoji/unicode/26a1.png differ diff --git a/public/emoji/unicode/26aa.png b/public/emoji/unicode/26aa.png new file mode 100644 index 00000000..da782ae2 Binary files /dev/null and b/public/emoji/unicode/26aa.png differ diff --git a/public/emoji/unicode/26ab.png b/public/emoji/unicode/26ab.png new file mode 100644 index 00000000..e46f9df6 Binary files /dev/null and b/public/emoji/unicode/26ab.png differ diff --git a/public/emoji/unicode/26bd.png b/public/emoji/unicode/26bd.png new file mode 100644 index 00000000..1e118b5b Binary files /dev/null and b/public/emoji/unicode/26bd.png differ diff --git a/public/emoji/unicode/26be.png b/public/emoji/unicode/26be.png new file mode 100644 index 00000000..da004e2e Binary files /dev/null and b/public/emoji/unicode/26be.png differ diff --git a/public/emoji/unicode/26c4.png b/public/emoji/unicode/26c4.png new file mode 100644 index 00000000..a97902e5 Binary files /dev/null and b/public/emoji/unicode/26c4.png differ diff --git a/public/emoji/unicode/26c5.png b/public/emoji/unicode/26c5.png new file mode 100644 index 00000000..020dd5ff Binary files /dev/null and b/public/emoji/unicode/26c5.png differ diff --git a/public/emoji/unicode/26ce.png b/public/emoji/unicode/26ce.png new file mode 100644 index 00000000..4eef715b Binary files /dev/null and b/public/emoji/unicode/26ce.png differ diff --git a/public/emoji/unicode/26d4.png b/public/emoji/unicode/26d4.png new file mode 100644 index 00000000..cf2086a8 Binary files /dev/null and b/public/emoji/unicode/26d4.png differ diff --git a/public/emoji/unicode/26ea.png b/public/emoji/unicode/26ea.png new file mode 100644 index 00000000..4c07c6b9 Binary files /dev/null and b/public/emoji/unicode/26ea.png differ diff --git a/public/emoji/unicode/26f2.png b/public/emoji/unicode/26f2.png new file mode 100644 index 00000000..da126e64 Binary files /dev/null and b/public/emoji/unicode/26f2.png differ diff --git a/public/emoji/unicode/26f3.png b/public/emoji/unicode/26f3.png new file mode 100644 index 00000000..cba2116a Binary files /dev/null and b/public/emoji/unicode/26f3.png differ diff --git a/public/emoji/unicode/26f5.png b/public/emoji/unicode/26f5.png new file mode 100644 index 00000000..ff656dc6 Binary files /dev/null and b/public/emoji/unicode/26f5.png differ diff --git a/public/emoji/unicode/26fa.png b/public/emoji/unicode/26fa.png new file mode 100644 index 00000000..5c0d20e4 Binary files /dev/null and b/public/emoji/unicode/26fa.png differ diff --git a/public/emoji/unicode/26fd.png b/public/emoji/unicode/26fd.png new file mode 100644 index 00000000..54c29aeb Binary files /dev/null and b/public/emoji/unicode/26fd.png differ diff --git a/public/emoji/unicode/2702.png b/public/emoji/unicode/2702.png new file mode 100644 index 00000000..020e0522 Binary files /dev/null and b/public/emoji/unicode/2702.png differ diff --git a/public/emoji/unicode/2705.png b/public/emoji/unicode/2705.png new file mode 100644 index 00000000..61dc0583 Binary files /dev/null and b/public/emoji/unicode/2705.png differ diff --git a/public/emoji/unicode/2708.png b/public/emoji/unicode/2708.png new file mode 100644 index 00000000..8407cb67 Binary files /dev/null and b/public/emoji/unicode/2708.png differ diff --git a/public/emoji/unicode/2709.png b/public/emoji/unicode/2709.png new file mode 100644 index 00000000..3631861b Binary files /dev/null and b/public/emoji/unicode/2709.png differ diff --git a/public/emoji/unicode/270a.png b/public/emoji/unicode/270a.png new file mode 100644 index 00000000..ecc8874c Binary files /dev/null and b/public/emoji/unicode/270a.png differ diff --git a/public/emoji/unicode/270b.png b/public/emoji/unicode/270b.png new file mode 100644 index 00000000..5e45c25a Binary files /dev/null and b/public/emoji/unicode/270b.png differ diff --git a/public/emoji/unicode/270c.png b/public/emoji/unicode/270c.png new file mode 100644 index 00000000..f61267c2 Binary files /dev/null and b/public/emoji/unicode/270c.png differ diff --git a/public/emoji/unicode/270f.png b/public/emoji/unicode/270f.png new file mode 100644 index 00000000..e624373b Binary files /dev/null and b/public/emoji/unicode/270f.png differ diff --git a/public/emoji/unicode/2712.png b/public/emoji/unicode/2712.png new file mode 100644 index 00000000..29f6994c Binary files /dev/null and b/public/emoji/unicode/2712.png differ diff --git a/public/emoji/unicode/2714.png b/public/emoji/unicode/2714.png new file mode 100644 index 00000000..336d2626 Binary files /dev/null and b/public/emoji/unicode/2714.png differ diff --git a/public/emoji/unicode/2716.png b/public/emoji/unicode/2716.png new file mode 100644 index 00000000..13d66607 Binary files /dev/null and b/public/emoji/unicode/2716.png differ diff --git a/public/emoji/unicode/2728.png b/public/emoji/unicode/2728.png new file mode 100644 index 00000000..92138828 Binary files /dev/null and b/public/emoji/unicode/2728.png differ diff --git a/public/emoji/unicode/2733.png b/public/emoji/unicode/2733.png new file mode 100644 index 00000000..946a2033 Binary files /dev/null and b/public/emoji/unicode/2733.png differ diff --git a/public/emoji/unicode/2734.png b/public/emoji/unicode/2734.png new file mode 100644 index 00000000..73dc6a0c Binary files /dev/null and b/public/emoji/unicode/2734.png differ diff --git a/public/emoji/unicode/2744.png b/public/emoji/unicode/2744.png new file mode 100644 index 00000000..54b68ff4 Binary files /dev/null and b/public/emoji/unicode/2744.png differ diff --git a/public/emoji/unicode/2747.png b/public/emoji/unicode/2747.png new file mode 100644 index 00000000..23a68ceb Binary files /dev/null and b/public/emoji/unicode/2747.png differ diff --git a/public/emoji/unicode/274c.png b/public/emoji/unicode/274c.png new file mode 100644 index 00000000..b84f6355 Binary files /dev/null and b/public/emoji/unicode/274c.png differ diff --git a/public/emoji/unicode/274e.png b/public/emoji/unicode/274e.png new file mode 100644 index 00000000..b47a0cec Binary files /dev/null and b/public/emoji/unicode/274e.png differ diff --git a/public/emoji/unicode/2753.png b/public/emoji/unicode/2753.png new file mode 100644 index 00000000..63fd7f83 Binary files /dev/null and b/public/emoji/unicode/2753.png differ diff --git a/public/emoji/unicode/2754.png b/public/emoji/unicode/2754.png new file mode 100644 index 00000000..57db41ea Binary files /dev/null and b/public/emoji/unicode/2754.png differ diff --git a/public/emoji/unicode/2755.png b/public/emoji/unicode/2755.png new file mode 100644 index 00000000..a50d265e Binary files /dev/null and b/public/emoji/unicode/2755.png differ diff --git a/public/emoji/unicode/2757.png b/public/emoji/unicode/2757.png new file mode 100644 index 00000000..4c560f5e Binary files /dev/null and b/public/emoji/unicode/2757.png differ diff --git a/public/emoji/unicode/2764.png b/public/emoji/unicode/2764.png new file mode 100644 index 00000000..7d7790ce Binary files /dev/null and b/public/emoji/unicode/2764.png differ diff --git a/public/emoji/unicode/2795.png b/public/emoji/unicode/2795.png new file mode 100644 index 00000000..61595387 Binary files /dev/null and b/public/emoji/unicode/2795.png differ diff --git a/public/emoji/unicode/2796.png b/public/emoji/unicode/2796.png new file mode 100644 index 00000000..b8d3d82f Binary files /dev/null and b/public/emoji/unicode/2796.png differ diff --git a/public/emoji/unicode/2797.png b/public/emoji/unicode/2797.png new file mode 100644 index 00000000..ac757a23 Binary files /dev/null and b/public/emoji/unicode/2797.png differ diff --git a/public/emoji/unicode/27a1.png b/public/emoji/unicode/27a1.png new file mode 100644 index 00000000..e5cca853 Binary files /dev/null and b/public/emoji/unicode/27a1.png differ diff --git a/public/emoji/unicode/27b0.png b/public/emoji/unicode/27b0.png new file mode 100644 index 00000000..8f051aca Binary files /dev/null and b/public/emoji/unicode/27b0.png differ diff --git a/public/emoji/unicode/27bf.png b/public/emoji/unicode/27bf.png new file mode 100644 index 00000000..ef34df3a Binary files /dev/null and b/public/emoji/unicode/27bf.png differ diff --git a/public/emoji/unicode/2934.png b/public/emoji/unicode/2934.png new file mode 100644 index 00000000..c8f670a1 Binary files /dev/null and b/public/emoji/unicode/2934.png differ diff --git a/public/emoji/unicode/2935.png b/public/emoji/unicode/2935.png new file mode 100644 index 00000000..56dd3b9d Binary files /dev/null and b/public/emoji/unicode/2935.png differ diff --git a/public/emoji/unicode/2b05.png b/public/emoji/unicode/2b05.png new file mode 100644 index 00000000..9d7d1b56 Binary files /dev/null and b/public/emoji/unicode/2b05.png differ diff --git a/public/emoji/unicode/2b06.png b/public/emoji/unicode/2b06.png new file mode 100644 index 00000000..565ce295 Binary files /dev/null and b/public/emoji/unicode/2b06.png differ diff --git a/public/emoji/unicode/2b07.png b/public/emoji/unicode/2b07.png new file mode 100644 index 00000000..3956eb39 Binary files /dev/null and b/public/emoji/unicode/2b07.png differ diff --git a/public/emoji/unicode/2b1b.png b/public/emoji/unicode/2b1b.png new file mode 100644 index 00000000..71da10de Binary files /dev/null and b/public/emoji/unicode/2b1b.png differ diff --git a/public/emoji/unicode/2b1c.png b/public/emoji/unicode/2b1c.png new file mode 100644 index 00000000..60cb19a1 Binary files /dev/null and b/public/emoji/unicode/2b1c.png differ diff --git a/public/emoji/unicode/2b50.png b/public/emoji/unicode/2b50.png new file mode 100644 index 00000000..1bfddc86 Binary files /dev/null and b/public/emoji/unicode/2b50.png differ diff --git a/public/emoji/unicode/2b55.png b/public/emoji/unicode/2b55.png new file mode 100644 index 00000000..0ededebe Binary files /dev/null and b/public/emoji/unicode/2b55.png differ diff --git a/public/emoji/unicode/3030.png b/public/emoji/unicode/3030.png new file mode 100644 index 00000000..77f626cc Binary files /dev/null and b/public/emoji/unicode/3030.png differ diff --git a/public/emoji/unicode/303d.png b/public/emoji/unicode/303d.png new file mode 100644 index 00000000..45dc9b85 Binary files /dev/null and b/public/emoji/unicode/303d.png differ diff --git a/public/emoji/unicode/3297.png b/public/emoji/unicode/3297.png new file mode 100644 index 00000000..dcbb1d22 Binary files /dev/null and b/public/emoji/unicode/3297.png differ diff --git a/public/emoji/unicode/3299.png b/public/emoji/unicode/3299.png new file mode 100644 index 00000000..82e383a6 Binary files /dev/null and b/public/emoji/unicode/3299.png differ diff --git a/spec/controllers/subscriptions_controller_spec.rb b/spec/controllers/subscriptions_controller_spec.rb index 9d3f7f39..10b53f91 100644 --- a/spec/controllers/subscriptions_controller_spec.rb +++ b/spec/controllers/subscriptions_controller_spec.rb @@ -57,7 +57,21 @@ RSpec.describe SubscriptionsController, type: :controller do allows_core_content: true, allows_extended_content: true, allows_collective_content: true, - allows_collaboration: true + allows_collaboration: true, + bonus_bandwidth_kb: 0 + ) + + @premium_annual_plan = BillingPlan.create( + name: 'Premium (annual)', + stripe_plan_id: 'premium-annual', + monthly_cents: 700, + available: true, + universe_limit: 5, + allows_core_content: true, + allows_extended_content: true, + allows_collective_content: true, + allows_collaboration: true, + bonus_bandwidth_kb: 123155 ) @free_plan = BillingPlan.create( @@ -71,11 +85,12 @@ RSpec.describe SubscriptionsController, type: :controller do allows_core_content: true, allows_extended_content: false, allows_collective_content: false, - allows_collaboration: false + allows_collaboration: false, + bonus_bandwidth_kb: 123155 ) end - describe "User with no plan (fallback to Starter)" do + describe "User with no plan (fallback to Starter) tries to upgrade" do it "redirects to payment method form if they don't have a payment method saved" do expect(@user.active_subscriptions).to eq([]) post :change, {stripe_plan_id: 'premium'} @@ -87,7 +102,7 @@ RSpec.describe SubscriptionsController, type: :controller do before do # Create a Starter subscription for the user @user.active_subscriptions.create(billing_plan: @free_plan, start_date: Time.now - 5.days, end_date: Time.now + 5.days) - expect(@user.active_subscriptions.map(&:billing_plan_id)).to eq([@free_plan.id]) + @user.update(selected_billing_plan_id: @free_plan.id) end it "redirects to payment method form if they don't have a payment method saved" do @@ -114,12 +129,21 @@ RSpec.describe SubscriptionsController, type: :controller do headers: {} ) - #todo - # post :change, {stripe_plan_id: 'premium'} - # expect(@user.active_billing_plans).to eq([@premium_plan]) + expect(@user.selected_billing_plan_id).to eq(@free_plan.id) + expect(@user.active_billing_plans).to eq([@free_plan]) + + post :change, {stripe_plan_id: 'premium'} + + @user.reload + expect(@user.selected_billing_plan_id).to eq(@premium_plan.id) + expect(@user.active_billing_plans).to eq([@premium_plan]) end describe "Starter Permissions" do + before do + @user.update(selected_billing_plan_id: @premium_plan.id) + end + it "allows Starter users to create core content types" do expect(@user.can_create?(Character)).to eq(true) expect(@user.can_create?(Location)).to eq(true) @@ -133,13 +157,12 @@ RSpec.describe SubscriptionsController, type: :controller do expect(@user.can_create?(Group)).to eq(false) expect(@user.can_create?(Magic)).to eq(false) expect(@user.can_create?(Language)).to eq(false) + expect(@user.can_create?(Flora)).to eq(false) end it "doesn't allow Starter users to create collective content types" do expect(@user.can_create?(Scene)).to eq(false) end - - #todo allow editing existing non-core content end end @@ -175,6 +198,7 @@ RSpec.describe SubscriptionsController, type: :controller do expect(@user.can_create?(Group)).to eq(true) expect(@user.can_create?(Magic)).to eq(true) expect(@user.can_create?(Language)).to eq(true) + expect(@user.can_create?(Flora)).to eq(true) end it "allows Premium users to create collective content types" do @@ -182,4 +206,37 @@ RSpec.describe SubscriptionsController, type: :controller do end end end -end \ No newline at end of file + + describe "Upload storage adjustments" do + before do + @user.active_subscriptions.create(billing_plan: @free_plan, start_date: Time.now - 5.days, end_date: Time.now + 5.days) + @user.update(selected_billing_plan_id: @free_plan.id) + end + + it 'grants storage space to a user after upgrading' do + @user.update(upload_bandwidth_kb: 100) + post :change, { stripe_plan_id: 'premium' } + expect(@user.upload_bandwidth_kb).to eq(100 + @premium_plan.bonus_bandwidth_kb) + end + + it 'decreases storage space for a user after downgrading' do + @user.update(upload_bandwidth_kb: 100) + post :change, { stripe_plan_id: 'starter' } + expect(@user.upload_bandwidth_kb).to eq(100 - @premium_plan.bonus_bandwidth_kb) + end + + it 'does not adjust storage space when going premium --> premium' do + @user.update(upload_bandwidth_kb: 101) + @user.update(selected_billing_plan_id: @premium_plan.id) + post :change, { stripe_plan_id: @premium_annual_plan.stripe_plan_id } + expect(@user.upload_bandwidth_kb).to eq(101) + end + + it 'does not adjust storage space if no plan change is made' do + @user.update(upload_bandwidth_kb: 101) + @user.update(selected_billing_plan_id: @premium_annual_plan.stripe_plan_id) + post :change, { stripe_plan_id: @premium_plan.stripe_plan_id } + expect(@user.upload_bandwidth_kb).to eq(101) + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 23ed4b2c..3b085dad 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -12,6 +12,7 @@ FactoryGirl.define do sequence :name do |n| "Universe #{n}" end + user end factory :character do @@ -19,7 +20,6 @@ FactoryGirl.define do "Character #{n}" end user - universe end factory :location do @@ -27,7 +27,6 @@ FactoryGirl.define do "Location #{n}" end user - universe end factory :item do @@ -35,7 +34,6 @@ FactoryGirl.define do "Item #{n}" end user - universe end factory :attribute_category do diff --git a/spec/models/character_spec.rb b/spec/models/character_spec.rb index 65b8b1bc..b0fbc97b 100644 --- a/spec/models/character_spec.rb +++ b/spec/models/character_spec.rb @@ -6,4 +6,25 @@ RSpec.describe Character, type: :model do it_behaves_like 'content with privacy' it_behaves_like 'content with an is_public scope' it { is_expected.to validate_presence_of(:name) } + + context "when character having a sibling is deleted" do + before do + @alice = create(:character, name: "Alice") + @bob = create(:character, name: "Bob") + @alice.siblings << @bob + @alice.destroy() + end + + it "don't delete the sibling" do + expect(Character.exists?(@bob.id)).to be true + end + + it "delete sibling relation" do + expect(@alice.siblings.include?(@bob)).to be false + end + + it "delete reverse sibling relation" do + expect(@bob.siblings.include?(@alice)).to be false + end + end end diff --git a/spec/support/content_controller_example.rb b/spec/support/content_controller_example.rb index 3a0634ff..abdebeb5 100644 --- a/spec/support/content_controller_example.rb +++ b/spec/support/content_controller_example.rb @@ -37,7 +37,8 @@ shared_examples_for 'a controller for a content item' do describe 'POST #create' do before do post :create, @model_name => { - name: model.name + name: model.name, + user: @user } end it { is_expected.to redirect_to(polymorphic_path(assigns(:content))) } @@ -52,7 +53,8 @@ shared_examples_for 'a controller for a content item' do describe 'PUT #update' do before do put :update, id: model.id, @model_name => { - name: model.name + name: model.name, + user: @user } end it { is_expected.to redirect_to(polymorphic_path(model)) } diff --git a/test/controllers/contributors_controller_test.rb b/test/controllers/contributors_controller_test.rb new file mode 100644 index 00000000..ea04f101 --- /dev/null +++ b/test/controllers/contributors_controller_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class ContributorsControllerTest < ActionController::TestCase + test "should get destroy" do + get :destroy + assert_response :success + end + +end diff --git a/test/fixtures/contributors.yml b/test/fixtures/contributors.yml new file mode 100644 index 00000000..bf2efca1 --- /dev/null +++ b/test/fixtures/contributors.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + universe_id: + email: MyString + user_id: + +two: + universe_id: + email: MyString + user_id: diff --git a/test/mailers/collaboration_mailer_test.rb b/test/mailers/collaboration_mailer_test.rb new file mode 100644 index 00000000..3b66ec79 --- /dev/null +++ b/test/mailers/collaboration_mailer_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CollaborationMailerTest < ActionMailer::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/mailers/previews/collaboration_mailer_preview.rb b/test/mailers/previews/collaboration_mailer_preview.rb new file mode 100644 index 00000000..77faab81 --- /dev/null +++ b/test/mailers/previews/collaboration_mailer_preview.rb @@ -0,0 +1,10 @@ +# Preview all emails at http://localhost:3000/rails/mailers/collaboration_mailer +class CollaborationMailerPreview < ActionMailer::Preview + def contributor_invitation + CollaborationMailer.contributor_invitation( + inviter: User.first, + invite_email: 'andrew@indentlabs.com', + universe: Universe.first + ) + end +end diff --git a/test/models/contributor_test.rb b/test/models/contributor_test.rb new file mode 100644 index 00000000..d6fbed7c --- /dev/null +++ b/test/models/contributor_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ContributorTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end