diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99d2859be..8f4eb35e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ v0.18.1 (trunk)
* Order Communities in Servers by last published
* Rename Communities page in Explore
* Add WebM and H264 video embedding support in the Chat
+ * Add Tenor support for GIF/videos search and integration in Chat
v0.18
---------------------------
diff --git a/app/Configuration.php b/app/Configuration.php
index 2643bddcd..9404cb03b 100644
--- a/app/Configuration.php
+++ b/app/Configuration.php
@@ -14,6 +14,7 @@ class Configuration extends Model
'description',
'info',
'unregister',
+ 'gifapikey',
'restrictsuggestions',
'locale',
'loglevel',
diff --git a/app/widgets/AdminMain/adminmain.tpl b/app/widgets/AdminMain/adminmain.tpl
index 3ac3764cd..adf393bd3 100644
--- a/app/widgets/AdminMain/adminmain.tpl
+++ b/app/widgets/AdminMain/adminmain.tpl
@@ -162,6 +162,25 @@
+
{$c->__('tenor.title')}
+
+
+
+ {$c->__('tenor.label')}
+
+
+
+
{$c->__('credentials.title')}
diff --git a/app/widgets/AdminMain/locales.ini b/app/widgets/AdminMain/locales.ini
index dc6db997f..5da9ee51d 100644
--- a/app/widgets/AdminMain/locales.ini
+++ b/app/widgets/AdminMain/locales.ini
@@ -41,6 +41,12 @@ description = Main XMPP server description
country = Main XMPP server country
country_pick= Pick a country in the list
+[tenor]
+title = Tenor integration
+label = Yout Tenor API Key
+info1 = Movim integrates the Tenor API to allow GIF search and publication in Chat
+info2 = Access Tenor and get your API key
+
[log]
empty = Empty
syslog = Syslog
diff --git a/app/widgets/Chat/chat.js b/app/widgets/Chat/chat.js
index d4d59df0f..1c2ff8374 100644
--- a/app/widgets/Chat/chat.js
+++ b/app/widgets/Chat/chat.js
@@ -984,9 +984,21 @@ var Chat = {
var video = document.createElement('video');
video.setAttribute('src', file.uri);
video.setAttribute('controls', 'controls');
+ video.setAttribute('loop', 'loop');
+
+ // Tenor implementation
+ if (file.host && file.host == 'media.tenor.com') {
+ video.setAttribute('autoplay', 'autoplay');
+ }
+
div.appendChild(video);
}
+ // Tenor implementation
+ if (file.host && file.host == 'media.tenor.com') {
+ return div;
+ }
+
var a = document.createElement('a');
if (sticker == null) {
diff --git a/app/widgets/Stickers/Stickers.php b/app/widgets/Stickers/Stickers.php
index 5bb9e6724..11a9971b2 100644
--- a/app/widgets/Stickers/Stickers.php
+++ b/app/widgets/Stickers/Stickers.php
@@ -5,8 +5,12 @@ use Moxl\Xec\Action\BOB\Answer;
use Respect\Validation\Validator;
+use App\Configuration;
+
class Stickers extends \Movim\Widget\Base
{
+ private $paginate = 20;
+
public function load()
{
$this->addcss('stickers.css');
@@ -107,11 +111,13 @@ class Stickers extends \Movim\Widget\Base
return;
}
- $packs = $this->getPacks();
+ $configuration = Configuration::get();
+ $isGifEnabled = !empty($configuration->gifapikey);
+ $packs = $this->getPacks();
$pack = isset($pack) ? $pack : current($packs);
- if (in_array($pack, $packs)) {
+ if (!$isGifEnabled) {
$files = scandir(PUBLIC_PATH.'/stickers/'.$pack);
array_shift($files);
@@ -122,10 +128,18 @@ class Stickers extends \Movim\Widget\Base
$view->assign('stickers', $files);
$view->assign('packs', $packs);
$view->assign('pack', $pack);
+ $view->assign('gifEnabled', $isGifEnabled);
$view->assign('info', parse_ini_file(PUBLIC_PATH.'/stickers/'.$pack.'/info.ini'));
$view->assign('path', $this->respath('stickers', false, false, true));
Drawer::fill($view->draw('_stickers'), true);
+ } else {
+ $view = $this->tpl();
+ $view->assign('jid', $to);
+ $view->assign('packs', $packs);
+
+ Drawer::fill($view->draw('_stickers_gifs'), true);
+ $this->rpc('Stickers.setGifsSearchEvent', $to);
}
}
@@ -144,6 +158,46 @@ class Stickers extends \Movim\Widget\Base
$this->rpc('Stickers.setEmojisEvent', $mid);
}
+ /**
+ * @brief Search for gifs
+ */
+ public function ajaxHttpSearchGifs($keyword, int $page = 0)
+ {
+ $configuration = Configuration::get();
+ $apiKey = $configuration->gifapikey;
+
+ if (empty($apiKey)) return;
+
+ $keyword = filter_var($keyword, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH);
+ $results = requestURL(
+ 'https://api.tenor.com/v1/search?q='.$keyword.
+ '&key='.$apiKey.
+ '&limit='.$this->paginate.
+ '&pos='.($page*$this->paginate)
+ );
+
+ $view = $this->tpl();
+
+ if ($results) {
+ $results = \json_decode($results);
+
+ if ($results) {
+ foreach ($results->results as $result) {
+ $gif = [
+ 'url' => $result->media[0]->tinywebm->url,
+ 'preview' => $result->media[0]->tinywebm->preview,
+ 'width' => $result->media[0]->tinywebm->dims[0],
+ 'height' => $result->media[0]->tinywebm->dims[1],
+ ];
+ $view->assign('gif', $gif);
+ $this->rpc('MovimTpl.append', '#gifs .masonry', $view->draw('_stickers_gifs_result'));
+ }
+ }
+ }
+
+ $this->rpc('Stickers.setGifsEvents');
+ }
+
/**
* @brief Get the path of an emoji
*/
diff --git a/app/widgets/Stickers/_stickers.tpl b/app/widgets/Stickers/_stickers.tpl
index d7bc0bc6f..235c8ae41 100644
--- a/app/widgets/Stickers/_stickers.tpl
+++ b/app/widgets/Stickers/_stickers.tpl
@@ -28,6 +28,11 @@
+ {if="$gifEnabled"}
+
+ gif
+
+ {/if}
{loop="$packs"}
diff --git a/app/widgets/Stickers/_stickers_gifs.tpl b/app/widgets/Stickers/_stickers_gifs.tpl
new file mode 100644
index 000000000..e9167c138
--- /dev/null
+++ b/app/widgets/Stickers/_stickers_gifs.tpl
@@ -0,0 +1,35 @@
+
+
+
+
+
+ gif
+
+ {loop="$packs"}
+
+
+
+ {/loop}
+
+
diff --git a/app/widgets/Stickers/_stickers_gifs_result.tpl b/app/widgets/Stickers/_stickers_gifs_result.tpl
new file mode 100644
index 000000000..7b8db9ec0
--- /dev/null
+++ b/app/widgets/Stickers/_stickers_gifs_result.tpl
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/app/widgets/Stickers/_stickers_smiley.tpl b/app/widgets/Stickers/_stickers_smiley.tpl
deleted file mode 100644
index 893d50c36..000000000
--- a/app/widgets/Stickers/_stickers_smiley.tpl
+++ /dev/null
@@ -1,14 +0,0 @@
-
- {autoescape="off"}
- {$emojis}
- {/autoescape}
-
-
-
- {loop="$packs"}
-
-
-
- {/loop}
-
-
diff --git a/app/widgets/Stickers/locales.ini b/app/widgets/Stickers/locales.ini
index b61c1bebf..87fb9515e 100644
--- a/app/widgets/Stickers/locales.ini
+++ b/app/widgets/Stickers/locales.ini
@@ -1,3 +1,6 @@
[sticker]
-title = Stickers
-sent = A sticker has been sent using Movim
+title = Stickers
+sent = A sticker has been sent using Movim
+keyword = Type a keyword
+gif_title = Find a GIF
+gif_text = Powered by Tenor
\ No newline at end of file
diff --git a/app/widgets/Stickers/stickers.css b/app/widgets/Stickers/stickers.css
index b458afa20..2c1163553 100644
--- a/app/widgets/Stickers/stickers.css
+++ b/app/widgets/Stickers/stickers.css
@@ -22,3 +22,40 @@
transform: scale(1.3);
z-index: 2;
}
+
+#gifssearchbar {
+ margin-bottom: 1rem;
+}
+
+#gifs {
+ height: calc(100% - 6rem);
+ overflow-x: hidden;
+}
+
+#gifs .masonry {
+ display: flex;
+ flex-flow: row wrap;
+ width: calc(100% - 1.25rem);
+ margin-left: 1.5rem;
+ margin-top: 1.5rem;
+}
+
+#gifs .masonry:not(:empty) + .placeholder {
+ display: none;
+}
+
+#gifs .masonry .brick {
+ flex: auto;
+ display: block;
+ height: 19rem;
+ min-width: 10rem;
+ max-width: 31.5rem;
+ object-fit: cover;
+ margin: 0 2rem 2rem 0;
+ border-radius: 0.5rem;
+}
+
+#gifs .masonry .brick:hover {
+ cursor: pointer;
+ box-shadow: 0 0 0.5rem rgba(var(--movim-font), 0.7);
+}
\ No newline at end of file
diff --git a/app/widgets/Stickers/stickers.js b/app/widgets/Stickers/stickers.js
index fbf05d0b6..a3b9061a3 100644
--- a/app/widgets/Stickers/stickers.js
+++ b/app/widgets/Stickers/stickers.js
@@ -1,4 +1,7 @@
var Stickers = {
+ timer : null,
+ stickersPage : 0,
+
addSmiley: function(element) {
Chat.insertAtCursor(element.dataset.emoji);
Drawer.clear();
@@ -36,6 +39,57 @@ var Stickers = {
Dialog_ajaxClear();
}
+ i++;
+ }
+ },
+ setGifsSearchEvent() {
+ const search = document.querySelector('#gifssearchbar input');
+ search.focus();
+
+ search.addEventListener('input', e => {
+ clearTimeout(Stickers.timer);
+
+ Stickers.timer = setTimeout(() => {
+ const gifs = document.querySelector('#gifs .masonry');
+ gifs.innerHTML = '';
+ Stickers.stickersPage = 0;
+
+ if (search.value !== '') {
+ document.querySelector('#gifssearchbar span.primary i').innerText = 'autorenew';
+ document.querySelector('#gifssearchbar span.primary').classList.add('spin');
+ Stickers_ajaxHttpSearchGifs(search.value, Stickers.stickersPage);
+ }
+
+ }, 400);
+ });
+ },
+ setGifsEvents() {
+ if (search.value !== '') {
+ document.querySelector('#gifssearchbar span.primary i').innerText = 'search';
+ document.querySelector('#gifssearchbar span.primary').classList.remove('spin');
+ }
+
+ const gifs = document.querySelectorAll('#gifs video');
+
+ let i = 0;
+
+ while (i < gifs.length) {
+ gifs[i].addEventListener('mouseover', e => {
+ e.target.play();
+ });
+ gifs[i].addEventListener('mouseout', e => {
+ e.target.pause();
+ });
+ gifs[i].addEventListener('click', e => {
+ Chat_ajaxHttpDaemonSendMessage(
+ Chat.getTextarea().dataset.jid,
+ e.target.src,
+ Boolean(Chat.getTextarea().dataset.muc)
+ );
+
+ Drawer.clear();
+ });
+
i++;
}
}
diff --git a/database/migrations/20201203221412_add_gifapikey_to_configuration_table.php b/database/migrations/20201203221412_add_gifapikey_to_configuration_table.php
new file mode 100644
index 000000000..8f5de4569
--- /dev/null
+++ b/database/migrations/20201203221412_add_gifapikey_to_configuration_table.php
@@ -0,0 +1,21 @@
+schema->table('configuration', function (Blueprint $table) {
+ $table->string('gifapikey')->nullable();
+ });
+ }
+
+ public function down()
+ {
+ $this->schema->table('configuration', function (Blueprint $table) {
+ $table->dropColumn('gifapikey');
+ });
+ }
+}