suggest group mentions, and always post to correct group even user is member of multiple groups with same nickname

This commit is contained in:
Hannes Mannerheim 2015-08-25 23:27:08 +02:00
parent 4584a03ffe
commit 976be81ce9
12 changed files with 652 additions and 63 deletions

View File

@ -179,6 +179,10 @@ class QvitterPlugin extends Plugin {
array('action' => 'qvitteradminsettings'));
$m->connect('main/qlogin',
array('action' => 'qvitterlogin'));
URLMapperOverwrite::overwrite_variable($m, 'api/statuses/update.:format',
array('action' => 'ApiStatusesUpdate'),
array('format' => '(xml|json)'),
'ApiQvitterStatusesUpdate');
// check if we should reroute UI to qvitter, and which home-stream the user wants (hide-replies or normal)
@ -916,6 +920,42 @@ class QvitterPlugin extends Plugin {
return true;
}
/**
* Correct group mentions
*
* We get the correct group ids in a $_POST var called "post_to_groups", formatted as a string with ids separated by colon, e.g. 4:5
*
* @return boolean hook flag
*/
public function onEndFindMentions($sender, $text, &$mentions) {
// get the correct group profiles
if(isset($_POST['post_to_groups'])) {
$correct_group_mentions = explode(':',$_POST['post_to_groups']);
foreach($correct_group_mentions as $group_id) {
$correct_group_mentions_profiles[] = Profile::getKV('id',$group_id);
}
// loop through the groups guessed by gnu social's common_find_mentions() and correct them
foreach($mentions as $mention_array_id=>$mention) {
foreach($correct_group_mentions_profiles as $correct_group_array_id=>$correct_group_profile) {
if($mention['mentioned'][0]->nickname == $correct_group_profile->nickname
&& !isset($mentions[$mention_array_id]['corrected'])) {
$mentions[$mention_array_id]['mentioned'][0] = $correct_group_profile;
$user_group = User_group::getKV('profile_id',$correct_group_profile->id);
$mentions[$mention_array_id]['url'] = $user_group->permalink();
$mentions[$mention_array_id]['title'] = $user_group->getFancyName();
$mentions[$mention_array_id]['corrected'] = true;
// now we've used this
unset($correct_group_mentions_profiles[$correct_group_array_id]);
}
}
}
}
return true;
}
/**

View File

@ -1,11 +1,11 @@
<?php
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
· ·
· ·
· Everybody I'm following and all groups I'm member of ·
· (to use for auto-suggestions) ·
· ·
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
· (to use for auto-suggestions) ·
· ·
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
· ·
· ·
· Q V I T T E R ·
@ -19,9 +19,9 @@
· (____/ ·
· (o< ·
· o> \\\\_\ ·
· \\) \____) ·
· \\) \____) ·
· ·
· ·
· ·
· Qvitter is free software: you can redistribute it and / or modify it ·
· under the terms of the GNU Affero General Public License as published by ·
· the Free Software Foundation, either version three of the License or (at ·
@ -36,7 +36,7 @@
· along with Qvitter. If not, see <http://www.gnu.org/licenses/>. ·
· ·
· Contact h@nnesmannerhe.im if you have any questions. ·
· ·
· ·
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · */
@ -48,6 +48,7 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
var $profiles = null;
var $users_stripped = null;
var $groups_stripped = null;
/**
* Take arguments for running
@ -71,16 +72,16 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
}
$this->profiles = $this->getProfiles();
$this->groups = $this->getGroups();
// only keep id, name, nickname and avatar URL
// profiles: only keep id, name, nickname and avatar URL
foreach($this->profiles as $p) {
try {
$avatar = Avatar::byProfile($p, AVATAR_STREAM_SIZE);
$avatar = $avatar->url;
$avatar = Avatar::urlByProfile($p, AVATAR_STREAM_SIZE);
} catch (Exception $e) {
$avatar = false;
}
}
$this_user = array($p->fullname,$p->nickname,$avatar);
if(!$p->isLocal()) {
$this_user[3] = $p->getUrl();
@ -91,6 +92,20 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
$this->users_stripped[$p->id] = $this_user;
}
// groups: only keep id, name, nickname, avatar and local aliases
foreach($this->groups as $user_group) {
$p = $user_group->getProfile();
$avatar = $user_group->stream_logo;
$this_group = array($p->fullname,$p->nickname,$avatar);
if(!$user_group->isLocal()) {
$this_group[3] = $p->getUrl();
}
else {
$this_group[3] = false;
}
$this->groups_stripped[$p->id] = $this_group;
}
return true;
}
@ -104,13 +119,13 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
protected function handle()
{
parent::handle();
$this->initDocument('json');
$this->showJsonObjects($this->users_stripped);
$this->showJsonObjects(array('users'=>$this->users_stripped,'groups'=>$this->groups_stripped));
$this->endDocument('json');
}
/**
* Get profiles
*
@ -123,16 +138,10 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
$subs = null;
if (isset($this->tag)) {
$subs = $this->target->getTaggedSubscriptions(
$this->tag, $offset, $limit
);
} else {
$subs = $this->target->getSubscribed(
$offset,
$limit
);
}
$subs = $this->target->getSubscribed(
$offset,
$limit
);
$profiles = array();
@ -143,4 +152,28 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
return $profiles;
}
/**
* Get groups
*
* @return array groups
*/
function getGroups()
{
$groups = array();
$group = $this->target->getGroups(
($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id
);
while ($group->fetch()) {
$groups[] = clone($group);
}
return $groups;
}
}

View File

@ -0,0 +1,356 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Post a notice (update your status) through the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
/* External API usage documentation. Please update when you change how this method works. */
/*! @page statusesupdate statuses/update
@section Description
Updates the authenticating user's status. Requires the status parameter specified below.
Request must be a POST.
@par URL pattern
/api/statuses/update.:format
@par Formats (:format)
xml, json
@par HTTP Method(s)
POST
@par Requires Authentication
Yes
@param status (Required) The URL-encoded text of the status update.
@param source (Optional) The source application name, if using HTTP authentication or an anonymous OAuth consumer.
@param in_reply_to_status_id (Optional) The ID of an existing status that the update is in reply to.
@param lat (Optional) The latitude the status refers to.
@param long (Optional) The longitude the status refers to.
@param media (Optional) a media upload, such as an image or movie file.
@sa @ref authentication
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@li If the @e source parameter is not supplied the source of the status will default to 'api'. When authenticated via a registered OAuth application, the application's registered name and URL will always override the source parameter.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@li Data uploaded via the @e media parameter should be multipart/form-data encoded.
@subsection exampleusage Example usage
@verbatim
curl -u username:password http://example.com/api/statuses/update.xml -d status='Howdy!' -d lat='30.468' -d long='-94.743'
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0" encoding="UTF-8"?>
<status>
<text>Howdy!</text>
<truncated>false</truncated>
<created_at>Tue Mar 30 23:28:05 +0000 2010</created_at>
<in_reply_to_status_id/>
<source>api</source>
<id>26668724</id>
<in_reply_to_user_id/>
<in_reply_to_screen_name/>
<geo xmlns:georss="http://www.georss.org/georss">
<georss:point>30.468 -94.743</georss:point>
</geo>
<favorited>false</favorited>
<user>
<id>25803</id>
<name>Jed Sanders</name>
<screen_name>jedsanders</screen_name>
<location>Hoop and Holler, Texas</location>
<description>I like to think of myself as America's Favorite.</description>
<profile_image_url>http://avatar.example.com/25803-48-20080924200604.png</profile_image_url>
<url>http://jedsanders.net</url>
<protected>false</protected>
<followers_count>5</followers_count>
<profile_background_color/>
<profile_text_color/>
<profile_link_color/>
<profile_sidebar_fill_color/>
<profile_sidebar_border_color/>
<friends_count>2</friends_count>
<created_at>Wed Sep 24 20:04:00 +0000 2008</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url/>
<profile_background_tile>false</profile_background_tile>
<statuses_count>70</statuses_count>
<following>true</following>
<notifications>true</notifications>
</user>
</status>
@endverbatim
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Updates the authenticating user's status (posts a notice).
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiQvitterStatusesUpdateAction extends ApiAuthAction
{
protected $needPost = true;
var $status = null;
var $in_reply_to_status_id = null;
var $lat = null;
var $lon = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->status = $this->trimmed('status');
$this->post_to_groups = $this->trimmed('post_to_groups');
$this->lat = $this->trimmed('lat');
$this->lon = $this->trimmed('long');
$this->in_reply_to_status_id
= intval($this->trimmed('in_reply_to_status_id'));
return true;
}
/**
* Handle the request
*
* Make a new notice for the update, save it, and show it
*
* @return void
*/
protected function handle()
{
parent::handle();
// Workaround for PHP returning empty $_POST and $_FILES when POST
// length > post_max_size in php.ini
if (empty($_FILES)
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
}
if (empty($this->status)) {
// TRANS: Client error displayed when the parameter "status" is missing.
$this->clientError(_('Client must provide a \'status\' parameter with a value.'));
}
if (is_null($this->scoped)) {
// TRANS: Client error displayed when updating a status for a non-existing user.
$this->clientError(_('No such user.'), 404);
}
/* Do not call shortenlinks until the whole notice has been build */
// Check for commands
$inter = new CommandInterpreter();
$cmd = $inter->handle_command($this->auth_user, $this->status);
if ($cmd) {
if ($this->supported($cmd)) {
$cmd->execute(new Channel());
}
// Cmd not supported? Twitter just returns your latest status.
// And, it returns your last status whether the cmd was successful
// or not!
$this->notice = $this->auth_user->getCurrentNotice();
} else {
$reply_to = null;
if (!empty($this->in_reply_to_status_id)) {
// Check whether notice actually exists
$reply = Notice::getKV($this->in_reply_to_status_id);
if ($reply) {
$reply_to = $this->in_reply_to_status_id;
} else {
// TRANS: Client error displayed when replying to a non-existing notice.
$this->clientError(_('Parent notice not found.'), 404);
}
}
$upload = null;
try {
$upload = MediaFile::fromUpload('media', $this->scoped);
$this->status .= ' ' . $upload->shortUrl();
/* Do not call shortenlinks until the whole notice has been build */
} catch (NoUploadedMediaException $e) {
// There was no uploaded media for us today.
}
// in Qvitter we shorten _before_ posting, so disble shortening here
$status_shortened = $this->status;
if (Notice::contentTooLong($status_shortened)) {
if ($upload instanceof MediaFile) {
$upload->delete();
}
// TRANS: Client error displayed exceeding the maximum notice length.
// TRANS: %d is the maximum lenth for a notice.
$msg = _m('Maximum notice size is %d character, including attachment URL.',
'Maximum notice size is %d characters, including attachment URL.',
Notice::maxContent());
/* Use HTTP 413 error code (Request Entity Too Large)
* instead of basic 400 for better understanding
*/
$this->clientError(sprintf($msg, Notice::maxContent()), 413);
}
$content = html_entity_decode($status_shortened, ENT_NOQUOTES, 'UTF-8');
// groups
$group_ids = Array();
if(strlen($this->post_to_groups)>0) {
$groups_profile_ids = explode(':',$this->post_to_groups);
foreach($groups_profile_ids as $group_profile_id) {
$user_group = User_group::getKV('profile_id',$group_profile_id);
$group_ids[] = $user_group->id;
}
}
$options = array('reply_to' => $reply_to, 'groups' => $group_ids);
if ($this->scoped->shareLocation()) {
$locOptions = Notice::locationOptions($this->lat,
$this->lon,
null,
null,
$this->scoped);
$options = array_merge($options, $locOptions);
}
try {
$this->notice = Notice::saveNew(
$this->scoped->id,
$content,
$this->source,
$options
);
} catch (Exception $e) {
$this->clientError($e->getMessage(), $e->getCode());
}
if (isset($upload)) {
$upload->attachToNotice($this->notice);
}
}
$this->showNotice();
}
/**
* Show the resulting notice
*
* @return void
*/
function showNotice()
{
if (!empty($this->notice)) {
if ($this->format == 'xml') {
$this->showSingleXmlStatus($this->notice);
} elseif ($this->format == 'json') {
$this->show_single_json_status($this->notice);
}
}
}
/**
* Is this command supported when doing an update from the API?
*
* @param string $cmd the command to check for
*
* @return boolean true or false
*/
function supported($cmd)
{
static $cmdlist = array('SubCommand', 'UnsubCommand',
'OnCommand', 'OffCommand', 'JoinCommand', 'LeaveCommand');
$supported = null;
if (Event::handle('CommandSupportedAPI', array($cmd, &$supported))) {
$supported = $supported || in_array(get_class($cmd), $cmdlist);
}
return $supported;
}
}

View File

@ -195,6 +195,7 @@ class QvitterAction extends ApiAction
*/
window.defaultAvatarStreamSize = <?php print json_encode(Avatar::defaultImage(AVATAR_STREAM_SIZE)) ?>;
window.defaultAvatarProfileSize = <?php print json_encode(Avatar::defaultImage(AVATAR_PROFILE_SIZE)) ?>;
window.textLimit = <?php print json_encode((int)common_config('site','textlimit')) ?>;
window.registrationsClosed = <?php print json_encode($registrationsclosed) ?>;
window.thisSiteThinksItIsHttpButIsActuallyHttps = <?php

View File

@ -1563,7 +1563,7 @@ background-repeat: no-repeat;
}
.stream-item.expanded:not(.conversation) + .stream-item:not(.conversation)::before {
border-top-left-radius: 9px;
border-top-right-radius: 9px;
border-top-right-radius: 9px;
}
.stream-item.expanded.selected-by-keyboard::before {
height:100%;
@ -1577,6 +1577,7 @@ background-repeat: no-repeat;
line-height: 12px;
position: absolute;
right: 0;
z-index: 100;
}
.show-full-conversation:hover {
text-decoration:underline;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -293,17 +293,19 @@ function APIJoinOrLeaveGroup(joinOrLeave,group_id,this_element,actionOnSuccess)
·
· @param queetText_txt: the text to post
· @param in_reply_to_status_id: the local id for the queet to reply to
· @param postToGroups: post the queet in these groups, string of ids separated by colon expected, e.g. 5:2:4
· @param actionOnSuccess: callback function, false on error, data on success
·
· · · · · · · · · · · · · */
function postQueetToAPI(queetText_txt, in_reply_to_status_id, actionOnSuccess) {
function postQueetToAPI(queetText_txt, in_reply_to_status_id, postToGroups, actionOnSuccess) {
$.ajax({ url: window.apiRoot + 'statuses/update.json?t=' + timeNow(),
type: "POST",
data: {
status: queetText_txt,
source: 'Qvitter',
in_reply_to_status_id: in_reply_to_status_id
in_reply_to_status_id: in_reply_to_status_id,
post_to_groups: postToGroups
},
dataType:"json",
error: function(data){ actionOnSuccess(false); console.log(data); },

View File

@ -446,9 +446,9 @@ function groupProfileCard(groupAlias) {
data.nickname = data.nickname || '';
data.fullname = data.fullname || '';
data.stream_logo = data.stream_logo || window.fullUrlToThisQvitterApp + 'img/default-avatar-stream.png';
data.homepage_logo = data.homepage_logo || window.fullUrlToThisQvitterApp + 'img/default-avatar-profile.png';
data.original_logo = data.original_logo || window.fullUrlToThisQvitterApp + 'img/default-avatar-profile.png';
data.stream_logo = data.stream_logo || window.defaultAvatarStreamSize;
data.homepage_logo = data.homepage_logo || window.defaultAvatarProfileSize;
data.original_logo = data.original_logo || window.defaultAvatarProfileSize;
data.description = data.description || '';
data.homepage = data.homepage || '';
data.url = data.url || '';
@ -1718,7 +1718,7 @@ function addToFeed(feed, after, extraClasses, isReply) {
if($('#stream-item-' + obj.id).length == 0) {
obj.description = obj.description || '';
obj.stream_logo = obj.stream_logo || window.fullUrlToThisQvitterApp + 'img/default-avatar-profile.png';
obj.stream_logo = obj.stream_logo || window.defaultAvatarStreamSize;
// rtl or not
var rtlOrNot = '';
@ -1733,7 +1733,6 @@ function addToFeed(feed, after, extraClasses, isReply) {
}
var memberButton = '';
if(typeof window.loggedIn.screen_name != 'undefined') {
console.log(obj);
var memberButton = '<div class="user-actions"><button data-group-id="' + obj.id + '" type="button" class="member-button ' + memberClass + '"><span class="button-text join-text"><i class="join"></i>' + window.sL.joinGroup + '</span><span class="button-text ismember-text">' + window.sL.isMemberOfGroup + '</span><span class="button-text leave-text">' + window.sL.leaveGroup + '</span></button></div>';
}
var groupAvatar = obj.stream_logo;

File diff suppressed because one or more lines are too long

View File

@ -533,10 +533,14 @@ function doLogin(streamToSet) {
// get all users i'm following for autosuggestion
window.following = new Array();
window.groupMemberships = new Array();
window.groupNicknamesAndLocalAliases = new Array();
getFromAPI('qvitter/allfollowing/' + window.loggedIn.screen_name + '.json',function(data){
if(data) {
if(data.users) {
var i=0;
$.each(data,function(k,v){
$.each(data.users,function(k,v){
if(v[2] === false) { var avatar = window.defaultAvatarStreamSize; }
else { var avatar = v[2]; }
if(v[3]) {
@ -547,20 +551,39 @@ function doLogin(streamToSet) {
window.following[i] = { 'id': k,'name': v[0], 'username': v[1],'avatar': avatar, 'url':v[3] };
i++;
});
}
cacheSyntaxHighlighting(); // do this now not to stall slow computers
if(data.groups) {
var i=0;
$.each(data.groups,function(k,v){
if(v[2] === false || v[2] === null) { var avatar = window.defaultAvatarStreamSize; }
else { var avatar = v[2]; }
if(v[3]) {
// extract server base url
v[3] = v[3].substring(v[3].indexOf('://')+3);
v[3] = v[3].substring(0, v[3].indexOf('/'));
}
v[0] = v[0] || v[1]; // if name is null we go with username there too
window.groupMemberships[i] = { 'id': k,'name': v[0], 'username': v[1],'avatar': avatar, 'url':v[3] };
window.groupNicknamesAndLocalAliases[i] = v[1];
i++;
});
}
// we might have cached text for the queet box
// (we need to get the mentions suggestions and cache the syntax highlighting before doing this)
var cachedQueetBoxData = localStorageObjectCache_GET('queetBoxInput','queet-box');
var cachedQueetBoxDataText = $('<div/>').html(cachedQueetBoxData).text();
if(cachedQueetBoxData) {
queetBox = $('#queet-box');
queetBox.click();
queetBox.html(cachedQueetBoxData);
setSelectionRange(queetBox[0], cachedQueetBoxDataText.length, cachedQueetBoxDataText.length);
queetBox.trigger('input');
}
// do this now not to stall slow computers, also we know of group memberships to highlight now
cacheSyntaxHighlighting();
cacheSyntaxHighlightingGroups();
// we might have cached text for the queet box
// (we need to get the mentions suggestions and cache the syntax highlighting before doing this)
var cachedQueetBoxData = localStorageObjectCache_GET('queetBoxInput','queet-box');
var cachedQueetBoxDataText = $('<div/>').html(cachedQueetBoxData).text();
if(cachedQueetBoxData) {
queetBox = $('#queet-box');
queetBox.click();
queetBox.html(cachedQueetBoxData);
setSelectionRange(queetBox[0], cachedQueetBoxDataText.length, cachedQueetBoxDataText.length);
queetBox.trigger('input');
}
});
@ -1195,9 +1218,9 @@ $('body').on('click','a', function(e) {
data.nickname = data.nickname || '';
data.fullname = data.fullname || '';
data.stream_logo = data.stream_logo || 'http://quitter.se/theme/quitter-theme2/default-avatar-stream.png';
data.homepage_logo = data.homepage_logo || 'http://quitter.se/theme/quitter-theme2/default-avatar-profile.png';
data.original_logo = data.original_logo || 'http://quitter.se/theme/quitter-theme2/default-avatar-profile.png';
data.stream_logo = data.stream_logo || window.defaultAvatarStreamSize;
data.homepage_logo = data.homepage_logo || window.defaultAvatarProfileSize;
data.original_logo = data.original_logo || window.defaultAvatarProfileSize;
data.description = data.description || '';
data.homepage = data.homepage || '';
data.url = data.url || '';
@ -2104,6 +2127,19 @@ $('body').on('click', '.queet-toolbar button',function () {
popUpAction('popup-sending', '',queetHtml.replace('class="stream-item conversation','class="stream-item'),false);
}
// maybe post queet in groups
var postToGroups = '';
var postToGropsArray = new Array();
$.each(queetBox.siblings('.post-to-group'),function(){
postToGropsArray.push($(this).data('group-id'));
});
if(postToGropsArray.length > 0) {
postToGroups = postToGropsArray.join(':');
}
// remove any post-to-group-divs
queetBox.siblings('.post-to-group').remove();
// remove any replying-to classes
$('.stream-item').removeClass('replying-to');
@ -2114,7 +2150,7 @@ $('body').on('click', '.queet-toolbar button',function () {
setTimeout('checkForNewQueets()', 1000);
// post queet
postQueetToAPI(queetText, in_reply_to_status_id, function(data){ if(data) {
postQueetToAPI(queetText, in_reply_to_status_id, postToGroups, function(data){ if(data) {
// show real queet
var new_queet = Array();
@ -2229,6 +2265,11 @@ $('body').on('mousedown','.syntax-two',function () {
});
$('body').on('blur','.queet-box-syntax',function (e) {
// empty the mention suggestions on blur, timeout because we want to capture clicks in .mentions-suggestions
setTimeout(function(){
$(this).siblings('.mentions-suggestions').empty();
},10);
// don't collapse if a toolbar button has been clicked
var clickedToolbarButtons = $(this).siblings('.queet-toolbar').find('button.clicked');
if(clickedToolbarButtons.length>0) {
@ -2408,13 +2449,31 @@ $('body').on('keydown', '.queet-box-syntax', function(e) {
});
function useSelectedMention(queetBox){
// use selected
if(queetBox.siblings('.mentions-suggestions').children('div.selected').length > 0) {
var username = queetBox.siblings('.mentions-suggestions').children('div.selected').children('span').html();
var selectedSuggestion = queetBox.siblings('.mentions-suggestions').children('div.selected');
}
// if none selected, take top suggestion
else {
var username = queetBox.siblings('.mentions-suggestions').children('div').first().children('span').html();
var selectedSuggestion = queetBox.siblings('.mentions-suggestions').children('div').first();
}
var username = selectedSuggestion.children('span').html();
var name = selectedSuggestion.children('strong').html();
// if this is a group, we remember its id, the user might be member of multiple groups with the same username
if(selectedSuggestion.hasClass('group-suggestion')) {
var groupId = selectedSuggestion.data('group-id');
if(queetBox.siblings('.post-to-group[data-group-id="' + groupId + '"]').length < 1) {
if(queetBox.siblings('.post-to-group').length>0) {
var addAfter = queetBox.siblings('.post-to-group').last();
}
else {
var addAfter = queetBox;
}
addAfter.after('<div class="post-to-group" data-group-username="' + username + '" data-group-id="' + groupId + '">' + name + '</div>');
}
}
// replace the halfwritten username with the one we want
@ -2428,7 +2487,18 @@ function useSelectedMention(queetBox){
queetBox.trigger('input'); // avoid some flickering
}
// check for mentions
// check for removed group mentions
$('body').on('keyup', 'div.queet-box-syntax', function(e) {
var groupMentions = $(this).siblings('.post-to-group');
var queetBoxContent = $(this).text();
$.each(groupMentions,function(){
if(queetBoxContent.indexOf('!' + $(this).data('group-username')) == -1) {
$(this).remove();
}
});
});
// check for user mentions
window.lastMention = new Object();
$('body').on('keyup', 'div.queet-box-syntax', function(e) {
@ -2455,7 +2525,7 @@ $('body').on('keyup', 'div.queet-box-syntax', function(e) {
}
if((contents.lastIndexOf('@')+match[0].length) == cursorPos) {
queetBox.siblings('.mentions-suggestions').empty();
queetBox.siblings('.mentions-suggestions').children('.user-suggestion').remove();
queetBox.siblings('.mentions-suggestions').css('top',(queetBox.height()+20) + 'px');
var term = match[0].substring(match[0].lastIndexOf('@')+1, match[0].length).toLowerCase();
window.lastMention.mentionPos = mentionPos;
@ -2487,17 +2557,90 @@ $('body').on('keyup', 'div.queet-box-syntax', function(e) {
if(suggestionsUsernameCount[this.username]>1 && this.url !== false) {
serverHtml = '@' + this.url;
}
queetBox.siblings('.mentions-suggestions').append('<div title="@' + this.username + serverHtml + '"><img height="24" width="24" src="' + this.avatar + '" /><strong>' + this.name + '</strong> @<span>' + this.username + serverHtml + '</span></div>')
queetBox.siblings('.mentions-suggestions').append('<div class="user-suggestion" title="@' + this.username + serverHtml + '"><img height="24" width="24" src="' + this.avatar + '" /><strong>' + this.name + '</strong> @<span>' + this.username + serverHtml + '</span></div>')
});
}
else {
queetBox.siblings('.mentions-suggestions').empty();
queetBox.siblings('.mentions-suggestions').children('.user-suggestion').remove();
}
}
else {
queetBox.siblings('.mentions-suggestions').empty();
queetBox.siblings('.mentions-suggestions').children('.user-suggestion').remove();
}
}
});
// check for group mentions
$('body').on('keyup', 'div.queet-box-syntax', function(e) {
var queetBox = $(this);
var cursorPosArray = getSelectionInElement(queetBox[0]);
var cursorPos = cursorPosArray[0];
// add space before linebreaks (to separate mentions in beginning of new lines when .text():ing later)
if(e.keyCode == '13') {
e.preventDefault();
var range = createRangeFromCharacterIndices(queetBox[0], cursorPos, cursorPos);
range.insertNode(document.createTextNode(" \n"));
}
else if(e.keyCode != '40' && e.keyCode != '38' && e.keyCode != '13' && e.keyCode != '9') {
var contents = queetBox.text().substring(0,cursorPos);
var mentionPos = contents.lastIndexOf('!');
var check_contents = contents.substring(mentionPos - 1, cursorPos);
var regex = /(^|\s|\.|\n)(!)[a-zA-Z0-9]+/;
var match = check_contents.match(regex);
if (contents.indexOf('!') >= 0 && match) {
if(contents.lastIndexOf('!') > 1) {
match[0] = match[0].substring(1,match[0].length);
}
if((contents.lastIndexOf('!')+match[0].length) == cursorPos) {
queetBox.siblings('.mentions-suggestions').children('.group-suggestion').remove();
queetBox.siblings('.mentions-suggestions').css('top',(queetBox.height()+20) + 'px');
var term = match[0].substring(match[0].lastIndexOf('!')+1, match[0].length).toLowerCase();
window.lastMention.mentionPos = mentionPos;
window.lastMention.cursorPos = cursorPos;
// see if any group we're member of matches
var suggestionsToShow = [];
var suggestionsUsernameCount = {};
$.each(window.groupMemberships,function(){
var userregex = new RegExp(term);
if(this.username.toLowerCase().match(userregex) || this.name.toLowerCase().match(userregex)) {
suggestionsToShow.push({id:this.id, avatar:this.avatar, name:this.name, username:this.username,url:this.url});
// count the usernames to see if we need to show the server for any of them
if(typeof suggestionsUsernameCount[this.username] != 'undefined') {
suggestionsUsernameCount[this.username] = suggestionsUsernameCount[this.username] + 1;
}
else {
suggestionsUsernameCount[this.username] = 1;
}
}
});
// show matches
$.each(suggestionsToShow,function(){
var serverHtml = '';
if(suggestionsUsernameCount[this.username]>1 && this.url !== false) {
serverHtml = this.url + '/group/';
}
queetBox.siblings('.mentions-suggestions').append('<div class="group-suggestion" title="' + serverHtml + this.username + '" data-group-id="' + this.id + '"><img height="24" width="24" src="' + this.avatar + '" /><strong>' + this.name + '</strong> !<span>' + this.username + '</span></div>')
});
}
else {
queetBox.siblings('.mentions-suggestions').children('.group-suggestion').remove();
}
}
else {
queetBox.siblings('.mentions-suggestions').children('.group-suggestion').remove();
}
}
});