mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-21 22:32:41 +01:00
Add an Emoji Typeahead
Summary: This adds a more complete emoji datasource, with a typeahead and autocomplete. It works by pulling in a raw datasource from EmojiOne (I chose Unicode 8, but they have a Unicode 9 datasource as well) and transforming it for speed/need. If we build more robustness or an actual picker into the Remarkup bar, having the additional keywords, etc, might be important. When Unicode 9 support is more prevalent, we should only need to update the single file. Tossing up as a proof of concept on engineering direction. Also I can't quite get the autocomplete to complete. Test Plan: Test UIExamples, Autocomplete, and TypeaheadSource Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12139 Differential Revision: https://secure.phabricator.com/D17244
This commit is contained in:
parent
61216dc641
commit
f930fd2e00
9 changed files with 1792 additions and 9 deletions
|
@ -540,7 +540,7 @@ return array(
|
|||
'rsrc/js/phui/behavior-phui-tab-group.js' => '0a0b10e9',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => 'b3465b9b',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '6d86ce8b',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '7c492cd2',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '8018ee50',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => 'bbece68d',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
|
@ -899,7 +899,7 @@ return array(
|
|||
'phui-workpanel-view-css' => 'a3a63478',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
'phuix-action-view' => 'b3465b9b',
|
||||
'phuix-autocomplete' => '6d86ce8b',
|
||||
'phuix-autocomplete' => '7c492cd2',
|
||||
'phuix-dropdown-menu' => '8018ee50',
|
||||
'phuix-form-control-view' => 'bbece68d',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
|
@ -1423,12 +1423,6 @@ return array(
|
|||
'javelin-typeahead',
|
||||
'javelin-uri',
|
||||
),
|
||||
'6d86ce8b' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'70baed2f' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1496,6 +1490,12 @@ return array(
|
|||
'owners-path-editor',
|
||||
'javelin-behavior',
|
||||
),
|
||||
'7c492cd2' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'7cbe244b' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
|
1626
resources/emoji/manifest.json
Normal file
1626
resources/emoji/manifest.json
Normal file
File diff suppressed because it is too large
Load diff
48
scripts/celerity/generate_emoji.php
Executable file
48
scripts/celerity/generate_emoji.php
Executable file
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require_once dirname(dirname(__FILE__)).'/__init_script__.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline(pht('regenerate Emoji data sheets'));
|
||||
$args->setSynopsis(<<<EOHELP
|
||||
**emoji**
|
||||
Rebuild Emoji data sheets.
|
||||
|
||||
EOHELP
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
$args->parse(
|
||||
array(
|
||||
array(
|
||||
'name' => 'force',
|
||||
'help' => pht('Force regeneration even if sources have not changed.'),
|
||||
),
|
||||
));
|
||||
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$path = $root.'/webroot/rsrc/externals/emojione/emoji_strategy.json';
|
||||
$export_path = $root.'/webroot/rsrc/emoji/manifest.json';
|
||||
|
||||
if (Filesystem::pathExists($path)) {
|
||||
$json = Filesystem::readFile($path);
|
||||
|
||||
$emojis = phutil_json_decode($json);
|
||||
$data = array();
|
||||
foreach ($emojis as $shortname => $emoji) {
|
||||
$unicode = $emoji['unicode'];
|
||||
$codes = explode('-', $unicode);
|
||||
$hex = '';
|
||||
foreach ($codes as $code) {
|
||||
$hex .= phutil_utf8_encode_codepoint(hexdec($code));
|
||||
}
|
||||
$data[$shortname] = $hex;
|
||||
}
|
||||
|
||||
$json = new PhutilJSON();
|
||||
$data = $json->encodeFormatted($data);
|
||||
Filesystem::writeFile($export_path, $data);
|
||||
echo pht('Done.')."\n";
|
||||
} else {
|
||||
echo pht('Path %s not exist.', $path)."\n";
|
||||
}
|
|
@ -1412,6 +1412,7 @@ phutil_register_library_map(array(
|
|||
'LiskRawMigrationIterator' => 'infrastructure/storage/lisk/LiskRawMigrationIterator.php',
|
||||
'MacroConduitAPIMethod' => 'applications/macro/conduit/MacroConduitAPIMethod.php',
|
||||
'MacroCreateMemeConduitAPIMethod' => 'applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php',
|
||||
'MacroEmojiExample' => 'applications/uiexample/examples/MacroEmojiExample.php',
|
||||
'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php',
|
||||
'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php',
|
||||
'ManiphestAssigneeDatasource' => 'applications/maniphest/typeahead/ManiphestAssigneeDatasource.php',
|
||||
|
@ -2626,6 +2627,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailVarySubjectsSetting' => 'applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php',
|
||||
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
|
||||
'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php',
|
||||
'PhabricatorEmojiDatasource' => 'applications/macro/typeahead/PhabricatorEmojiDatasource.php',
|
||||
'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php',
|
||||
'PhabricatorEmojiTranslation' => 'infrastructure/internationalization/translation/PhabricatorEmojiTranslation.php',
|
||||
'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php',
|
||||
|
@ -6242,6 +6244,7 @@ phutil_register_library_map(array(
|
|||
'LiskRawMigrationIterator' => 'PhutilBufferedIterator',
|
||||
'MacroConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'MacroCreateMemeConduitAPIMethod' => 'MacroConduitAPIMethod',
|
||||
'MacroEmojiExample' => 'PhabricatorUIExample',
|
||||
'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod',
|
||||
'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand',
|
||||
'ManiphestAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
|
@ -7639,6 +7642,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
||||
'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhabricatorEmojiDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule',
|
||||
'PhabricatorEmojiTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorEmptyQueryException' => 'Exception',
|
||||
|
|
|
@ -13,6 +13,13 @@ final class PhabricatorEmojiRemarkupRule extends PhutilRemarkupRule {
|
|||
$text);
|
||||
}
|
||||
|
||||
public function markupEmojiJSON() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$json = Filesystem::readFile(
|
||||
$root.'/resources/emoji/manifest.json');
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function markupEmoji(array $matches) {
|
||||
if (!$this->isFlatText($matches[0])) {
|
||||
return $matches[0];
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEmojiDatasource extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type an emoji name...');
|
||||
}
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Emojis');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorMacroApplication';
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$results = $this->buildResults();
|
||||
return $this->filterResultsAgainstTokens($results);
|
||||
}
|
||||
|
||||
protected function renderSpecialTokens(array $values) {
|
||||
return $this->renderTokensFromResults($this->buildResults(), $values);
|
||||
}
|
||||
|
||||
private function buildResults() {
|
||||
$raw_query = $this->getRawQuery();
|
||||
|
||||
$data = id(new PhabricatorEmojiRemarkupRule())->markupEmojiJSON();
|
||||
$emojis = phutil_json_decode($data);
|
||||
|
||||
$results = array();
|
||||
foreach ($emojis as $shortname => $emoji) {
|
||||
$display_name = $emoji.' '.$shortname;
|
||||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setPHID($shortname)
|
||||
->setName($display_name)
|
||||
->setAutocomplete($emoji);
|
||||
|
||||
$results[$shortname] = $result;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
47
src/applications/uiexample/examples/MacroEmojiExample.php
Normal file
47
src/applications/uiexample/examples/MacroEmojiExample.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
final class MacroEmojiExample extends PhabricatorUIExample {
|
||||
|
||||
public function getName() {
|
||||
return pht('Emoji Support');
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht('Shiny happy people holding hands');
|
||||
}
|
||||
|
||||
public function renderExample() {
|
||||
|
||||
$raw = id(new PhabricatorEmojiRemarkupRule())
|
||||
->markupEmojiJSON();
|
||||
|
||||
$json = phutil_json_decode($raw);
|
||||
|
||||
$content = array();
|
||||
foreach ($json as $shortname => $hex) {
|
||||
|
||||
$display_name = ' '.$hex.' '.$shortname;
|
||||
|
||||
$content[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'ms grouped',
|
||||
'style' => 'width: 240px; height: 24px; float: left;',
|
||||
),
|
||||
$display_name);
|
||||
|
||||
}
|
||||
|
||||
$wrap = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Emojis'))
|
||||
->addClass('grouped')
|
||||
->appendChild($content);
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(),
|
||||
array(
|
||||
$wrap,
|
||||
));
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
$root_id = celerity_generate_unique_node_id();
|
||||
|
||||
$user_datasource = new PhabricatorPeopleDatasource();
|
||||
$emoji_datasource = new PhabricatorEmojiDatasource();
|
||||
$proj_datasource = id(new PhabricatorProjectDatasource())
|
||||
->setParameters(
|
||||
array(
|
||||
|
@ -91,6 +92,12 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
'headerText' => pht('Find Project:'),
|
||||
'hintText' => $proj_datasource->getPlaceholderText(),
|
||||
),
|
||||
58 => array( // ":"
|
||||
'datasourceURI' => $emoji_datasource->getDatasourceURI(),
|
||||
'headerIcon' => 'fa-smile-o',
|
||||
'headerText' => pht('Find Emoji:'),
|
||||
'hintText' => $emoji_datasource->getPlaceholderText(),
|
||||
),
|
||||
),
|
||||
));
|
||||
Javelin::initBehavior('phabricator-tooltips', array());
|
||||
|
|
|
@ -118,7 +118,6 @@ JX.install('PHUIXAutocomplete', {
|
|||
case '|': // Might be a table cell.
|
||||
case '>': // Might be a blockquote.
|
||||
case '!': // Might be a blockquote attribution line.
|
||||
case ':': // Might be a "NOTE:".
|
||||
// We'll let these autocomplete.
|
||||
break;
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue