mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +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/phui/behavior-phui-tab-group.js' => '0a0b10e9',
|
||||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||||
'rsrc/js/phuix/PHUIXActionView.js' => 'b3465b9b',
|
'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/PHUIXDropdownMenu.js' => '8018ee50',
|
||||||
'rsrc/js/phuix/PHUIXFormControl.js' => 'bbece68d',
|
'rsrc/js/phuix/PHUIXFormControl.js' => 'bbece68d',
|
||||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||||
|
@ -899,7 +899,7 @@ return array(
|
||||||
'phui-workpanel-view-css' => 'a3a63478',
|
'phui-workpanel-view-css' => 'a3a63478',
|
||||||
'phuix-action-list-view' => 'b5c256b8',
|
'phuix-action-list-view' => 'b5c256b8',
|
||||||
'phuix-action-view' => 'b3465b9b',
|
'phuix-action-view' => 'b3465b9b',
|
||||||
'phuix-autocomplete' => '6d86ce8b',
|
'phuix-autocomplete' => '7c492cd2',
|
||||||
'phuix-dropdown-menu' => '8018ee50',
|
'phuix-dropdown-menu' => '8018ee50',
|
||||||
'phuix-form-control-view' => 'bbece68d',
|
'phuix-form-control-view' => 'bbece68d',
|
||||||
'phuix-icon-view' => 'bff6884b',
|
'phuix-icon-view' => 'bff6884b',
|
||||||
|
@ -1423,12 +1423,6 @@ return array(
|
||||||
'javelin-typeahead',
|
'javelin-typeahead',
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
),
|
),
|
||||||
'6d86ce8b' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
'phuix-icon-view',
|
|
||||||
'phabricator-prefab',
|
|
||||||
),
|
|
||||||
'70baed2f' => array(
|
'70baed2f' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1496,6 +1490,12 @@ return array(
|
||||||
'owners-path-editor',
|
'owners-path-editor',
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
),
|
),
|
||||||
|
'7c492cd2' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
'phuix-icon-view',
|
||||||
|
'phabricator-prefab',
|
||||||
|
),
|
||||||
'7cbe244b' => array(
|
'7cbe244b' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'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',
|
'LiskRawMigrationIterator' => 'infrastructure/storage/lisk/LiskRawMigrationIterator.php',
|
||||||
'MacroConduitAPIMethod' => 'applications/macro/conduit/MacroConduitAPIMethod.php',
|
'MacroConduitAPIMethod' => 'applications/macro/conduit/MacroConduitAPIMethod.php',
|
||||||
'MacroCreateMemeConduitAPIMethod' => 'applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php',
|
'MacroCreateMemeConduitAPIMethod' => 'applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php',
|
||||||
|
'MacroEmojiExample' => 'applications/uiexample/examples/MacroEmojiExample.php',
|
||||||
'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php',
|
'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php',
|
||||||
'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php',
|
'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php',
|
||||||
'ManiphestAssigneeDatasource' => 'applications/maniphest/typeahead/ManiphestAssigneeDatasource.php',
|
'ManiphestAssigneeDatasource' => 'applications/maniphest/typeahead/ManiphestAssigneeDatasource.php',
|
||||||
|
@ -2626,6 +2627,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEmailVarySubjectsSetting' => 'applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php',
|
'PhabricatorEmailVarySubjectsSetting' => 'applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php',
|
||||||
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
|
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
|
||||||
'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php',
|
'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php',
|
||||||
|
'PhabricatorEmojiDatasource' => 'applications/macro/typeahead/PhabricatorEmojiDatasource.php',
|
||||||
'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php',
|
'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php',
|
||||||
'PhabricatorEmojiTranslation' => 'infrastructure/internationalization/translation/PhabricatorEmojiTranslation.php',
|
'PhabricatorEmojiTranslation' => 'infrastructure/internationalization/translation/PhabricatorEmojiTranslation.php',
|
||||||
'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php',
|
'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php',
|
||||||
|
@ -6242,6 +6244,7 @@ phutil_register_library_map(array(
|
||||||
'LiskRawMigrationIterator' => 'PhutilBufferedIterator',
|
'LiskRawMigrationIterator' => 'PhutilBufferedIterator',
|
||||||
'MacroConduitAPIMethod' => 'ConduitAPIMethod',
|
'MacroConduitAPIMethod' => 'ConduitAPIMethod',
|
||||||
'MacroCreateMemeConduitAPIMethod' => 'MacroConduitAPIMethod',
|
'MacroCreateMemeConduitAPIMethod' => 'MacroConduitAPIMethod',
|
||||||
|
'MacroEmojiExample' => 'PhabricatorUIExample',
|
||||||
'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod',
|
'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod',
|
||||||
'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand',
|
'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand',
|
||||||
'ManiphestAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
'ManiphestAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
|
@ -7639,6 +7642,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting',
|
'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting',
|
||||||
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||||
|
'PhabricatorEmojiDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule',
|
'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule',
|
||||||
'PhabricatorEmojiTranslation' => 'PhutilTranslation',
|
'PhabricatorEmojiTranslation' => 'PhutilTranslation',
|
||||||
'PhabricatorEmptyQueryException' => 'Exception',
|
'PhabricatorEmptyQueryException' => 'Exception',
|
||||||
|
|
|
@ -13,6 +13,13 @@ final class PhabricatorEmojiRemarkupRule extends PhutilRemarkupRule {
|
||||||
$text);
|
$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) {
|
public function markupEmoji(array $matches) {
|
||||||
if (!$this->isFlatText($matches[0])) {
|
if (!$this->isFlatText($matches[0])) {
|
||||||
return $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();
|
$root_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
$user_datasource = new PhabricatorPeopleDatasource();
|
$user_datasource = new PhabricatorPeopleDatasource();
|
||||||
|
$emoji_datasource = new PhabricatorEmojiDatasource();
|
||||||
$proj_datasource = id(new PhabricatorProjectDatasource())
|
$proj_datasource = id(new PhabricatorProjectDatasource())
|
||||||
->setParameters(
|
->setParameters(
|
||||||
array(
|
array(
|
||||||
|
@ -91,6 +92,12 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
||||||
'headerText' => pht('Find Project:'),
|
'headerText' => pht('Find Project:'),
|
||||||
'hintText' => $proj_datasource->getPlaceholderText(),
|
'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());
|
Javelin::initBehavior('phabricator-tooltips', array());
|
||||||
|
|
|
@ -118,7 +118,6 @@ JX.install('PHUIXAutocomplete', {
|
||||||
case '|': // Might be a table cell.
|
case '|': // Might be a table cell.
|
||||||
case '>': // Might be a blockquote.
|
case '>': // Might be a blockquote.
|
||||||
case '!': // Might be a blockquote attribution line.
|
case '!': // Might be a blockquote attribution line.
|
||||||
case ':': // Might be a "NOTE:".
|
|
||||||
// We'll let these autocomplete.
|
// We'll let these autocomplete.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in a new issue