diff --git a/Gemfile b/Gemfile index e632c80f5..98cb286ba 100644 --- a/Gemfile +++ b/Gemfile @@ -105,6 +105,8 @@ gem 'json-ld' gem 'json-ld-preloaded', '~> 3.1' gem 'rdf-normalize', '~> 0.4' +gem 'redcarpet', "~> 3.4.0" + group :development, :test do gem 'fabrication', '~> 2.21' gem 'fuubar', '~> 2.5' diff --git a/Gemfile.lock b/Gemfile.lock index 0fe84f21e..852133e7e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -480,6 +480,7 @@ GEM link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.4.0) rdf (~> 3.1) + redcarpet (3.4.0) redis (4.2.1) redis-actionpack (5.2.0) actionpack (>= 5, < 7) @@ -755,6 +756,7 @@ DEPENDENCIES rails-i18n (~> 5.1) rails-settings-cached (~> 0.6) rdf-normalize (~> 0.4) + redcarpet (~> 3.4.0) redis (~> 4.2) redis-namespace (~> 1.7) redis-rails (~> 5.0) diff --git a/app/controllers/api/v2/media_controller.rb b/app/controllers/api/v2/media_controller.rb index 0c1baf01d..de2c72313 100644 --- a/app/controllers/api/v2/media_controller.rb +++ b/app/controllers/api/v2/media_controller.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +class Api::V2::MediaController < Api::V1::MediaController +end +__END__ class Api::V2::MediaController < Api::V1::MediaController def create @media_attachment = current_account.media_attachments.create!({ delay_processing: true }.merge(media_attachment_params)) diff --git a/app/javascript/fonts/MaterialIcons-Regular.woff2 b/app/javascript/fonts/MaterialIcons-Regular.woff2 new file mode 100644 index 000000000..9fa211252 Binary files /dev/null and b/app/javascript/fonts/MaterialIcons-Regular.woff2 differ diff --git a/app/javascript/mastodon/actions/UtilBtns.js b/app/javascript/mastodon/actions/UtilBtns.js new file mode 100644 index 000000000..52ccc6877 --- /dev/null +++ b/app/javascript/mastodon/actions/UtilBtns.js @@ -0,0 +1,71 @@ +import { changeCompose } from '../actions/compose'; + + + +export const UTILBTNS_GOJI = 'UTILBTNS_GOJI'; +export const UTILBTNS_HARUKIN = 'UTILBTNS_HARUKIN'; + + + +export function submitGoji (textarea) { + return function (dispatch, getState) { + if (!textarea.value) { + let text = [ + "#ゴジモリィィィィイイ", + ":goji:" + ].join("\r\n"); + + dispatch(submitGojiRequest()); + dispatch(changeCompose(text)); + + textarea.focus(); + } + } +} + +export function submitGojiRequest () { + return { + type: UTILBTNS_GOJI + } +} + +export function submitHarukin (textarea) { + return function (dispatch, getState) { + const HARUKINS = [":harukin: ", ":harukin_old: ", ":harukin_ika: ", ":harukin_tako: "]; + const MAX = 6; + + if (!textarea.value) { + let text = ""; + + let quantity = Math.round(Math.random() * MAX + 1); + let type = Math.round(Math.random() * (HARUKINS.length - 1)); + + let harukin = HARUKINS[type]; + + switch (quantity) { + default: + text = [ + harukin.repeat(quantity), + "🔥 ".repeat(quantity) + ].join("\r\n"); + + break; + + case MAX + 1: + text = `${harukin}💕\r\n`.repeat(6); + break; + } + + dispatch(submitHarukinRequest()); + dispatch(changeCompose(text)); + + textarea.focus(); + } + } +} + +export function submitHarukinRequest () { + return { + type: UTILBTNS_HARUKIN + } +} diff --git a/app/javascript/mastodon/components/announcement_icon_button.js b/app/javascript/mastodon/components/announcement_icon_button.js new file mode 100644 index 000000000..0de58c6f7 --- /dev/null +++ b/app/javascript/mastodon/components/announcement_icon_button.js @@ -0,0 +1,90 @@ +import React from 'react'; +import Motion from 'react-motion/lib/Motion'; +import spring from 'react-motion/lib/spring'; +import PropTypes from 'prop-types'; + +class IconButton extends React.PureComponent { + + static propTypes = { + className: PropTypes.string, + title: PropTypes.string.isRequired, + icon: PropTypes.string.isRequired, + onClick: PropTypes.func, + size: PropTypes.number, + active: PropTypes.bool, + style: PropTypes.object, + activeStyle: PropTypes.object, + disabled: PropTypes.bool, + inverted: PropTypes.bool, + animate: PropTypes.bool, + overlay: PropTypes.bool, + }; + + static defaultProps = { + size: 18, + active: false, + disabled: false, + animate: false, + overlay: false, + }; + + handleClick = (e) => { + e.preventDefault(); + + if (!this.props.disabled) { + this.props.onClick(e); + } + } + + render () { + const style = { + fontSize: `${this.props.size}px`, + width: `${this.props.size * 1.28571429}px`, + height: `${this.props.size * 1.28571429}px`, + lineHeight: `${this.props.size}px`, + ...this.props.style, + ...(this.props.active ? this.props.activeStyle : {}), + }; + + const classes = ['icon-button']; + + if (this.props.active) { + classes.push('active'); + } + + if (this.props.disabled) { + classes.push('disabled'); + } + + if (this.props.inverted) { + classes.push('inverted'); + } + + if (this.props.overlay) { + classes.push('overlayed'); + } + + if (this.props.className) { + classes.push(this.props.className); + } + + return ( + + {({ rotate }) => +