View for polls

This commit is contained in:
Thomas 2020-05-01 14:01:51 +02:00
parent 84743c9890
commit 0f0621162b
13 changed files with 427 additions and 100 deletions

View file

@ -15,6 +15,7 @@
"symfony/dotenv": "4.3.*", "symfony/dotenv": "4.3.*",
"symfony/flex": "^1.3.1", "symfony/flex": "^1.3.1",
"symfony/framework-bundle": "4.3.*", "symfony/framework-bundle": "4.3.*",
"symfony/polyfill-intl-messageformatter": "^1.15",
"symfony/security-bundle": "4.3.*", "symfony/security-bundle": "4.3.*",
"symfony/translation": "4.3.*", "symfony/translation": "4.3.*",
"symfony/twig-bundle": "4.3.*", "symfony/twig-bundle": "4.3.*",

79
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "cc98799ce9bc12d4405acb67e867d8b7", "content-hash": "421672a1e764c0f375493f05beb754dd",
"packages": [ "packages": [
{ {
"name": "craue/formflow-bundle", "name": "craue/formflow-bundle",
@ -2104,6 +2104,83 @@
], ],
"time": "2020-03-09T19:04:49+00:00" "time": "2020-03-09T19:04:49+00:00"
}, },
{
"name": "symfony/polyfill-intl-messageformatter",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-messageformatter.git",
"reference": "3326c736f61bbb2d030622ff128a590b41ed46b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-messageformatter/zipball/3326c736f61bbb2d030622ff128a590b41ed46b5",
"reference": "3326c736f61bbb2d030622ff128a590b41ed46b5",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\MessageFormatter\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's MessageFormatter class and related functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"intl",
"messageformatter",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-27T09:26:54+00:00"
},
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.15.0", "version": "v1.15.0",

View file

@ -33,3 +33,6 @@ services:
mastodon.api: mastodon.api:
class: App\Services\Mastodon_api class: App\Services\Mastodon_api
public: true public: true
app.form:
class: App\Form\ComposeType
arguments: ['@security.helper','@translator.default']

View file

@ -24,3 +24,10 @@ html, body {
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.switch_width{
width: 50px;
}
ul.options {
list-style: none;
}

View file

@ -1463,7 +1463,7 @@ document = window.document || {};
if (typeof value.acct == 'undefined') { if (typeof value.acct == 'undefined') {
return shortnameTo(value, self.emojiTemplate); return shortnameTo(value, self.emojiTemplate);
}else{ }else{
return "@"+value.acct; return "@"+value.acct+ " ";
} }
}, },
cache: true, cache: true,

View file

@ -10,33 +10,37 @@ namespace App\Form;
use App\SocialEntity\Compose; use App\SocialEntity\Compose;
use App\SocialEntity\MastodonAccount;
use DateTime;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TimezoneType; use Symfony\Component\Form\Extension\Core\Type\TimezoneType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\Translator;
class ComposeType extends AbstractType { class ComposeType extends AbstractType {
private $securityContext; private $securityContext;
private $translator;
public function __construct(Security $securityContext) public function __construct(Security $securityContext, Translator $translator)
{ {
$this->securityContext = $securityContext; $this->securityContext = $securityContext;
$this->translator = $translator;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
/**@var $user \App\SocialEntity\MastodonAccount**/ /**@var $user MastodonAccount*/
$user = $options['user']; $user = $options['user'];
if( $user->getDefaultSensitivity()) { if( $user->getDefaultSensitivity()) {
@ -75,9 +79,9 @@ class ComposeType extends AbstractType {
'label' => 'page.schedule.form.timeZone', 'label' => 'page.schedule.form.timeZone',
'translation_domain' => 'fediplan']); 'translation_domain' => 'fediplan']);
$builder->add('sensitive', CheckboxType::class, $checkbox); $builder->add('sensitive', CheckboxType::class, $checkbox);
$builder->add('scheduled_at', \Symfony\Component\Form\Extension\Core\Type\DateTimeType::class,[ $builder->add('scheduled_at', DateTimeType::class,[
'widget' => 'single_text', 'widget' => 'single_text',
"data" => new \DateTime(), "data" => new DateTime(),
'label' => 'page.schedule.form.scheduled_at', 'label' => 'page.schedule.form.scheduled_at',
'translation_domain' => 'fediplan']); 'translation_domain' => 'fediplan']);
$builder->add('Send', SubmitType::class, $builder->add('Send', SubmitType::class,
@ -85,6 +89,34 @@ class ComposeType extends AbstractType {
'label' => 'page.schedule.form.send', 'label' => 'page.schedule.form.send',
'translation_domain' => 'fediplan']); 'translation_domain' => 'fediplan']);
$builder->add('poll_option_1', TextType::class, ['required' => false]);
$builder->add('poll_option_2', TextType::class, ['required' => false]);
$builder->add('poll_options', CollectionType::class,
[
'entry_type' => PollOptionType::class,
'allow_add' => true,
'prototype' => true,
'allow_delete' => true,
'required' => false,
]);
$builder->add('poll_multiple', CheckboxType::class,
['required' => false, 'label' => 'page.schedule.form.multiple',
'translation_domain' => 'fediplan']);
$builder->add('poll_expires_at', ChoiceType::class,
[
'choices' => [
$this->translator->trans('poll.duration_m', ['minutes' => 5], 'fediplan') => 5*60,
$this->translator->trans('poll.duration_m', ['minutes' => 30], 'fediplan') => 30*60,
$this->translator->trans('poll.duration_h', ['hours' => 1], 'fediplan') => 60*60,
$this->translator->trans('poll.duration_h', ['hours' => 6], 'fediplan') => 6*60*60,
$this->translator->trans('poll.duration_d', ['days' => 1], 'fediplan') => 24*60*60,
$this->translator->trans('poll.duration_d', ['days' => 3], 'fediplan') => 3*24*60*60,
$this->translator->trans('poll.duration_d', ['days' => 7], 'fediplan') => 7*24*60*60,
],
'required' => false,
'label' => 'page.schedule.form.end_in',
'translation_domain' => 'fediplan']);
} }

View file

@ -0,0 +1,45 @@
<?php
/**
* Created by fediplan.
* User: tom79
*/
namespace App\Form;
use App\SocialEntity\PollOption;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
class PollOptionType extends AbstractType {
private $securityContext;
public function __construct(Security $securityContext)
{
$this->securityContext = $securityContext;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('option', TextType::class,
[
'required' => false,
'attr'=> ['class'=>'form-control']
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => PollOption::class,
'translation_domain' => 'fediplan'
]);
}
}

View file

@ -249,7 +249,7 @@ class Mastodon_api {
if (strpos($value, 'Link: ') !== false) { if (strpos($value, 'Link: ') !== false) {
preg_match( preg_match(
"/min_id=([0-9a-zA-Z]{1,})/", "/min_id=([0-9a-zA-Z]+)/",
$value, $value,
$matches $matches
); );
@ -259,7 +259,7 @@ class Mastodon_api {
} }
if (strpos($value, 'Link: ') !== false) { if (strpos($value, 'Link: ') !== false) {
preg_match( preg_match(
"/max_id=([0-9a-zA-Z]{1,})/", "/max_id=([0-9a-zA-Z]+)/",
$value, $value,
$matches $matches
); );

View file

@ -30,8 +30,16 @@ class Compose
private $timeZone; private $timeZone;
/** @var Poll */ /** @var PollOption[] */
private $poll; private $poll_options;
/** @var int */
private $poll_expires_at;
/** @var bool */
private $poll_multiple;
/** @var PollOption */
private $poll_option_1;
/** @var PollOption */
private $poll_option_2;
public function __construct() public function __construct()
{ {
@ -159,19 +167,83 @@ class Compose
} }
/** /**
* @return Poll * @return PollOption[]
*/ */
public function getPoll(): Poll public function getPollOptions(): ?array
{ {
return $this->poll; return $this->poll_options;
} }
/** /**
* @param Poll $poll * @param PollOption[] $poll_options
*/ */
public function setPoll(Poll $poll): void public function setPollOptions(?array $poll_options): void
{ {
$this->poll = $poll; $this->poll_options = $poll_options;
}
/**
* @return int
*/
public function getPollExpiresAt(): ?int
{
return $this->poll_expires_at;
}
/**
* @param int $poll_expires_at
*/
public function setPollExpiresAt(?int $poll_expires_at): void
{
$this->poll_expires_at = $poll_expires_at;
}
/**
* @return bool
*/
public function isPollMultiple(): ?bool
{
return $this->poll_multiple;
}
/**
* @param bool $poll_multiple
*/
public function setPollMultiple(?bool $poll_multiple): void
{
$this->poll_multiple = $poll_multiple;
}
/**
* @return PollOption
*/
public function getPollOption1(): ?PollOption
{
return $this->poll_option_1;
}
/**
* @param PollOption $poll_option_1
*/
public function setPollOption1(?PollOption $poll_option_1): void
{
$this->poll_option_1 = $poll_option_1;
}
/**
* @return PollOption
*/
public function getPollOption2(): ?PollOption
{
return $this->poll_option_2;
}
/**
* @param PollOption $poll_option_2
*/
public function setPollOption2(?PollOption $poll_option_2): void
{
$this->poll_option_2 = $poll_option_2;
} }

View file

@ -160,6 +160,9 @@
"symfony/polyfill-intl-idn": { "symfony/polyfill-intl-idn": {
"version": "v1.12.0" "version": "v1.12.0"
}, },
"symfony/polyfill-intl-messageformatter": {
"version": "v1.15.0"
},
"symfony/polyfill-mbstring": { "symfony/polyfill-mbstring": {
"version": "v1.12.0" "version": "v1.12.0"
}, },

View file

@ -4,7 +4,7 @@
{% block content %} {% block content %}
{% include 'nav.html.twig' %} {% include 'nav.html.twig' %}
<h1>Schedule</h1> <h3>Schedule for <i><img class="" width="30" src="{{ app.user.avatar }}" alt="{{ app.user.avatar }}"/> {{ convertAccountEmoji(app.user, app.user.displayName) | raw }} (@{{ app.user.acct}}@{{ instance }})</i></h3>
{% for type, messages in app.session.flashbag.all() %} {% for type, messages in app.session.flashbag.all() %}
{% for message in messages %} {% for message in messages %}
@ -25,51 +25,59 @@
{% endfor %} {% endfor %}
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-horizontal" style=" display: flex;flex: 1 1 auto;">
<div class="img-square-wrapper">
<img class="" width="100" src="{{ app.user.avatar }}" >
</div>
<div class="card-body">
<h4 class="card-title">{{ convertAccountEmoji(app.user, app.user.displayName) | raw }}</h4>
<p class="card-text">@{{ app.user.acct }}</p>
</div>
</div>
<div class="card-footer">
<small class="text-muted">{{ app.user.note }}</small>
</div>
</div>
</div>
</div>
{{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }} {{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }}
<div class="row" style="margin-top: 30px;"> <div class="row" style="margin-top: 30px;">
<div class="col-md-12"> <div class="col-md-12">
<div class="row"> <div class="form-group has-feedback">
<div class=" col-md-6"> {{ form_label(form.content_warning) }}
<div class="form-group has-feedback"> {{ form_widget(form.content_warning, {'attr': {'class': 'form-control', 'data-emojiable':'true'}}) }}
{{ form_label(form.content_warning) }} {% if not form.content_warning.vars.errors is empty %}
{{ form_widget(form.content_warning, {'attr': {'class': 'form-control', 'data-emojiable':'true'}}) }} <span class="badge badge-danger">
{% if not form.content_warning.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.content_warning.vars.errors %} {% for errorItem in form.content_warning.vars.errors %}
{{ errorItem.message }} {{ errorItem.message }}
{% endfor %} {% endfor %}
</span> </span>
{% endif %} {% endif %}
</div> </div>
</div>
<div class="form-group has-feedback">
{{ form_label(form.content) }}
{{ form_widget(form.content, {'attr': {'class': 'form-control','id':'composer_content','data-emojiable':'true'}}) }}
{% if not form.content.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.content.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div> </div>
<div class="row"> <div class="row">
<div class=" col-md-6"> <div class=" col-md-3" style="margin-top: 20px;">
<div class="form-group has-feedback"> <div class="form-inline has-feedback">
{{ form_label(form.content) }} <label for="count">{{ 'common.counter'|trans }}</label>&nbsp;&nbsp;<span id="count" class="form-control">0</span>
{{ form_widget(form.content, {'attr': {'class': 'form-control','id':'composer_content','data-emojiable':'true'}}) }} </div>
{% if not form.content.vars.errors is empty %} </div>
<div class=" col-md-4" style="margin-top: 20px;">
<div class="form-inline has-feedback">
{{ form_label(form.visibility) }}&nbsp;&nbsp;
{{ form_widget(form.visibility, {'attr': {'class': 'form-control'}}) }}
{% if not form.visibility.vars.errors is empty %}
<span class="badge badge-danger"> <span class="badge badge-danger">
{% for errorItem in form.content.vars.errors %} {% for errorItem in form.visibility.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div>
</div>
<div class=" col-md-4" style="margin-top: 20px;">
<div class="form-inline has-feedback">
{{ form_label(form.sensitive) }}&nbsp;&nbsp;
{{ form_widget(form.sensitive, {'attr': {'class': 'form-control','data-toggle':'toggle', 'data-on': 'common.yes'|trans , 'data-off':'common.no'|trans}}) }}
{% if not form.sensitive.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.sensitive.vars.errors %}
{{ errorItem.message }} {{ errorItem.message }}
{% endfor %} {% endfor %}
</span> </span>
@ -77,49 +85,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row" style="margin-top: 20px;">
<div class="row"> <div class=" col-md-5">
<div class=" col-md-6">
<div class="form-group has-feedback">
{{ 'common.counter'|trans }}: <span id="count">0</span>
</div>
</div>
</div>
<div class="row">
<div class=" col-md-3">
<div class="form-group has-feedback">
{{ form_label(form.visibility) }}
{{ form_widget(form.visibility, {'attr': {'class': 'form-control'}}) }}
{% if not form.visibility.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.visibility.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class=" col-md-3">
<div class="form-group has-feedback">
{{ form_label(form.sensitive) }}&nbsp;&nbsp;
{{ form_widget(form.sensitive, {'attr': {'class': 'form-control','data-toggle':'toggle', 'data-on': 'common.yes'|trans , 'data-off':'common.no'|trans}}) }}
{% if not form.sensitive.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.sensitive.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class=" col-md-4">
<div class="form-group has-feedback"> <div class="form-group has-feedback">
{{ form_label(form.scheduled_at) }} {{ form_label(form.scheduled_at) }}
{{ form_widget(form.scheduled_at, {'attr': {'class': 'form-control'}}) }} {{ form_widget(form.scheduled_at, {'attr': {'class': 'form-control'}}) }}
@ -132,7 +99,7 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class=" col-md-4"> <div class=" col-md-5">
<div class="form-group has-feedback"> <div class="form-group has-feedback">
{{ form_label(form.timeZone) }} {{ form_label(form.timeZone) }}
{{ form_widget(form.timeZone, {'attr': {'class': 'form-control'}}) }} {{ form_widget(form.timeZone, {'attr': {'class': 'form-control'}}) }}
@ -147,10 +114,70 @@
</div> </div>
<div class=" col-md-3"></div> <div class=" col-md-3"></div>
</div> </div>
<ul class="options"
data-prototype="{{ form_widget(form.poll_options.vars.prototype.option)|e('html_attr') }}">
<div class="form-group has-feedback">
{{ form_label(form.poll_option_1) }}
{{ form_widget(form.poll_option_1, {'attr': {'class': 'form-control'}}) }}
{% if not form.poll_option_1.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.poll_option_1.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div>
<div class="form-group has-feedback">
{{ form_label(form.poll_option_2) }}
{{ form_widget(form.poll_option_2, {'attr': {'class': 'form-control'}}) }}
{% if not form.poll_option_2.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.poll_option_2.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div>
</ul>
<div class="row">
<div class=" col-md-12">
<div class="row">
<div class=" col-md-4">
{{ form_label(form.poll_multiple) }}
{{ form_widget(form.poll_multiple, {'attr': {'class': 'form-control','data-toggle':'toggle', 'data-on': 'common.yes'|trans , 'data-off':'common.no'|trans}}) }}
{% if not form.poll_multiple.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.poll_multiple.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div>
<div class=" col-md-4">
<div class="form-inline has-feedback">
{{ form_label(form.poll_expires_at) }}&nbsp;&nbsp;&nbsp;
{{ form_widget(form.poll_expires_at, {'attr': {'class': 'form-control'}}) }}
{% if not form.poll_expires_at.vars.errors is empty %}
<span class="badge badge-danger">
{% for errorItem in form.poll_expires_at.vars.errors %}
{{ errorItem.message }}
{% endfor %}
</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div> </div>
<div class="container" style="margin-bottom: 30px;" id="media_container"></div> <div class="container" style="margin-bottom: 30px;" id="media_container"></div>
</div> </div>
{{ form_end(form) }} {{ form_end(form) }}
<div class="row" style="margin-top: 20px;"> <div class="row" style="margin-top: 20px;">
@ -189,7 +216,7 @@
> >
<div <div
class="progress-bar progress-bar-success" class="progress-bar progress-bar-success"
style="width:0%;" style="width:0;"
></div> ></div>
</div> </div>
<!-- The extended global progress state --> <!-- The extended global progress state -->
@ -449,6 +476,42 @@
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function() { $(document).ready(function() {
var $collectionHolder;
// setup an "add a tag" link
var $addTagButton = $('<button type="button" style="margin-top:20px;" class="add_tag_link btn btn-secondary">Add an option</button>');
var $newLinkLi = $('<li></li>').append($addTagButton);
jQuery(document).ready(function() {
$collectionHolder = $('ul.options');
$collectionHolder.append($newLinkLi);
$collectionHolder.data('index', $collectionHolder.find('input').length);
$addTagButton.on('click', function(e) {
addOptionPoll($collectionHolder, $newLinkLi);
});
});
function addOptionPoll($collectionHolder, $newLinkLi) {
var prototype = $collectionHolder.data('prototype');
var index = $collectionHolder.data('index');
var newForm = prototype;
newForm = newForm.replace(/__name__/g, index);
$collectionHolder.data('index', index + 1);
var $newFormLi = $('<li></li>').append(newForm);
$newLinkLi.before($newFormLi);
addOptionFormDeleteLink($newFormLi);
}
function addOptionFormDeleteLink($tagFormLi) {
var $removeFormButton = $('<button type="button" style="margin-top: 5px;" class="btn btn-danger btn-sm">Delete this option</button>');
$tagFormLi.append($removeFormButton);
$removeFormButton.on('click', function(e) {
$tagFormLi.remove();
});
}
$("#compose_content").emojioneArea({ $("#compose_content").emojioneArea({
pickerPosition: "bottom", pickerPosition: "bottom",
filtersPosition: "bottom", filtersPosition: "bottom",
@ -458,7 +521,11 @@
autocomplete : "on", autocomplete : "on",
} }
}); });
$("#compose_content_warning").emojioneArea(); $("#compose_content_warning").emojioneArea({
pickerPosition: "bottom",
filtersPosition: "bottom",
searchPosition: "bottom",
});
var timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; var timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
$('#compose_timeZone').val(timezone); $('#compose_timeZone').val(timezone);
$(document).on('click', '.delete_media', function () { $(document).on('click', '.delete_media', function () {

View file

@ -0,0 +1,19 @@
poll:
duration_m: >-
{minutes, plural,
=0 {zero minutes}
one {one minute}
other {# minutes}
}
duration_h: >-
{hours, plural,
=0 {zero hours}
one {one hour}
other {# hours}
}
duration_d: >-
{days, plural,
=0 {zero days}
one {one day}
other {# days}
}

View file

@ -65,4 +65,5 @@ page:
scheduled_at: Scheduled at scheduled_at: Scheduled at
send: Send send: Send
add_files: Add files... add_files: Add files...
multiple: Multiple
end_in: End in