site notice, notifications, etc

This commit is contained in:
Hannes Mannerheim 2014-09-24 23:20:35 +02:00
parent 146231dbe1
commit cc3eb5fdda
19 changed files with 3340 additions and 1756 deletions

View File

@ -74,6 +74,16 @@ class QvitterPlugin extends Plugin {
return $settings[$setting]; return $settings[$setting];
} }
// make sure we have a notifications table
function onCheckSchema()
{
$schema = Schema::get();
$schema->ensureTable('qvitternotification', QvitterNotification::schemaDef());
return true;
}
// route/reroute urls
public function onRouterInitialized($m) public function onRouterInitialized($m)
{ {
@ -102,8 +112,18 @@ class QvitterPlugin extends Plugin {
'id' => Nickname::INPUT_FMT)); 'id' => Nickname::INPUT_FMT));
$m->connect('api/qvitter/statuses/mentions.:format', $m->connect('api/qvitter/statuses/mentions.:format',
array('action' => 'apiqvittermentions')); array('action' => 'apiqvittermentions'));
$m->connect('api/qvitter/statuses/notifications.json',
array('action' => 'apiqvitternotifications'));
$m->connect(':nickname/notifications',
array('action' => 'qvitter',
'nickname' => Nickname::INPUT_FMT));
$m->connect('api/qvitter/newnotifications.json',
array('action' => 'ApiNewNotifications'));
$m->connect('settings/qvitter', $m->connect('settings/qvitter',
array('action' => 'qvittersettings')); array('action' => 'qvittersettings'));
$m->connect('panel/qvitter',
array('action' => 'qvitteradminsettings'));
common_config_append('admin', 'panels', 'qvitteradm');
$m->connect('main/qlogin', $m->connect('main/qlogin',
array('action' => 'qvitterlogin')); array('action' => 'qvitterlogin'));
@ -181,7 +201,11 @@ class QvitterPlugin extends Plugin {
URLMapperOverwrite::overwrite_variable($m, 'tag/:tag', URLMapperOverwrite::overwrite_variable($m, 'tag/:tag',
array('action' => 'showstream'), array('action' => 'showstream'),
array('tag' => Router::REGEX_TAG), array('tag' => Router::REGEX_TAG),
'qvitter'); 'qvitter');
URLMapperOverwrite::overwrite_variable($m, 'notice/:notice',
array('action' => 'shownotice'),
array('notice' => '[0-9]+'),
'qvitter');
} }
} }
@ -207,6 +231,31 @@ class QvitterPlugin extends Plugin {
return true; return true;
} }
/**
* Menu item for admin panel
*
* @param Action $action action being executed
*
* @return boolean hook return
*/
function onEndAdminPanelNav($action)
{
$action_name = $action->trimmed('action');
$action->out->menuItem(common_local_url('qvitteradminsettings'),
// TRANS: Poll plugin menu item on user settings page.
_m('MENU', 'Qvitter'),
// TRANS: Poll plugin tooltip for user settings menu item.
_m('Qvitter Sidebar Notice'),
$action_name === 'qvitteradminsettings');
return true;
}
/** /**
@ -249,6 +298,163 @@ class QvitterPlugin extends Plugin {
return true; return true;
} }
/**
* Insert into notification table
*/
function insertNotification($to_profile_id, $from_profile_id, $ntype, $notice_id=false)
{
// never notify myself
if($to_profile_id != $from_profile_id) {
// insert
$notif = new QvitterNotification();
$notif->to_profile_id = $to_profile_id;
$notif->from_profile_id = $from_profile_id;
$notif->ntype = $ntype;
$notif->notice_id = $notice_id;
$notif->created = common_sql_now();
if (!$notif->insert()) {
common_log_db_error($notif, 'INSERT', __FILE__);
return false;
}
}
return true;
}
/**
* Insert likes in notification table
*/
public function onEndFavorNotice($profile, $notice)
{
// don't notify people favoriting their own notices
if($notice->profile_id != $profile->id) {
$this->insertNotification($notice->profile_id, $profile->id, 'like', $notice->id, $notice->id);
}
}
/**
* Remove likes in notification table on dislike
*/
public function onEndDisfavorNotice($profile, $notice)
{
$notif = new QvitterNotification();
$notif->from_profile_id = $profile->id;
$notif->notice_id = $notice->id;
$notif->ntype = 'like';
$notif->delete();
return true;
}
/**
* Insert notifications for replies, mentions and repeats
*
* @return boolean hook flag
*/
function onStartNoticeDistribute($notice) {
// repeats
if ($notice->isRepeat()) {
$repeated_notice = Notice::getKV('id', $notice->repeat_of);
if ($repeated_notice instanceof Notice) {
$this->insertNotification($repeated_notice->profile_id, $notice->profile_id, 'repeat', $repeated_notice->id);
}
}
// replies and mentions (no notifications for these if this is a repeat)
else {
// check for reply to insert in notifications
if($notice->reply_to) {
$replyparent = $notice->getParent();
$replyauthor = $replyparent->getProfile();
if ($replyauthor instanceof Profile) {
$reply_notification_to = $replyauthor->id;
$this->insertNotification($replyauthor->id, $notice->profile_id, 'reply', $notice->id);
}
}
// check for mentions to insert in notifications
$mentions = common_find_mentions($notice->content, $notice);
$sender = Profile::getKV($notice->profile_id);
foreach ($mentions as $mention) {
foreach ($mention['mentioned'] as $mentioned) {
// Not from blocked profile
$mentioned_user = User::getKV('id', $mentioned->id);
if ($mentioned_user instanceof User && $mentioned_user->hasBlocked($sender)) {
continue;
}
// only notify if mentioned user is not already notified for reply
if($reply_notification_to != $mentioned->id) {
$this->insertNotification($mentioned->id, $notice->profile_id, 'mention', $notice->id);
}
}
}
}
return true;
}
/**
* Delete any notifications tied to deleted notices and un-repeats
*
* @return boolean hook flag
*/
public function onNoticeDeleteRelated($notice)
{
$notif = new QvitterNotification();
// unrepeats
if ($notice->isRepeat()) {
$repeated_notice = Notice::getKV('id', $notice->repeat_of);
$notif->notice_id = $repeated_notice->id;
$notif->from_profile_id = $notice->profile_id;
}
// notices
else {
$notif->notice_id = $notice->id;
}
$notif->delete();
return true;
}
/**
* Add notification on subscription, remove on unsubscribe
*
* @return boolean hook flag
*/
public function onEndSubscribe($subscriber, $other)
{
if(Subscription::exists($subscriber, $other)) {
$this->insertNotification($other->id, $subscriber->id, 'follow');
}
return true;
}
public function onEndUnsubscribe($subscriber, $other)
{
if(!Subscription::exists($subscriber, $other)) {
$notif = new QvitterNotification();
$notif->to_profile_id = $other->id;
$notif->from_profile_id = $subscriber->id;
$notif->ntype = 'follow';
$notif->delete();
}
return true;
}
} }

View File

@ -45,14 +45,24 @@ TODO
1. "following you" badge on other peoples profiles 1. "following you" badge on other peoples profiles
1. better user popup, with e.g. latest queets
1. Faq
1. DM's
1. lists
1. user actions-cog wheel in users lists, with block, list etc
1. proxy to non-https for getting conversations the instance doesn't have via jsonp
1. preview different types of attachments, not just images. e.g. mp3's, torrents etc etc
1. Join _new_ external groups and follow _new_ external users ("New" meaning users/groups that the server don't know yet) 1. Join _new_ external groups and follow _new_ external users ("New" meaning users/groups that the server don't know yet)
2. Creating groups, make admin, block user 2. Creating groups, make admin, block user
3. Background image uploading/editing
6. Auto url-shortening setting under queet box
7. Settings (e.g. don't show replies to people I don't follow) 7. Settings (e.g. don't show replies to people I don't follow)
9. Image/file upload 9. Image/file upload
@ -65,14 +75,8 @@ TODO
14. More languages, maybe make proper po/mo-files 14. More languages, maybe make proper po/mo-files
15. Notice-page
16. Admin-interface 16. Admin-interface
16. New api for serving _number_ of new items in several streams (to show number of new items in menu/history)
17. Notifications-page with likes and repeats
17. New "expand queet" api for getting conversation, retweets, favs and attachment in the same request 17. New "expand queet" api for getting conversation, retweets, favs and attachment in the same request
19. Node.js long polling server and an new api that serve aggregate of all polling users requests in one go 19. Node.js long polling server and an new api that serve aggregate of all polling users requests in one go

View File

@ -0,0 +1,99 @@
<?php
/**
*
* Qvitter: Unread notifications
*
* 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 GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class ApiNewNotificationsAction extends ApiAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
return true;
}
/**
* Handle the request
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$user_id = Profile::current()->id;
$new_notifications = array();
if($user_id) {
$notification = new QvitterNotification();
$notification->selectAdd();
$notification->selectAdd('ntype');
$notification->selectAdd('count(id) as count');
$notification->whereAdd("(to_profile_id = '".$user_id."')");
$notification->groupBy('ntype');
$notification->whereAdd("(is_seen = '0')");
$notification->find();
while ($notification->fetch()) {
$new_notifications[$notification->ntype] = $notification->count;
}
}
else {
$new_notifications = 'You must be logged in.';
}
$this->initDocument('json');
$this->showJsonObjects($new_notifications);
$this->endDocument('json');
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,198 @@
<?php
/* · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
· ·
· ·
· Q V I T T E R ·
· ·
· http://github.com/hannesmannerheim/qvitter ·
· ·
· ·
· <o) ·
· /_//// ·
· (____/ ·
· (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 ·
· your option) any later version. ·
· ·
· Qvitter is distributed in hope that it will be useful but WITHOUT ANY ·
· WARRANTY; without even the implied warranty of MERCHANTABILTY 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 Qvitter. If not, see <http://www.gnu.org/licenses/>. ·
· ·
· Contact h@nnesmannerhe.im if you have any questions. ·
· ·
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · */
if (!defined('STATUSNET')) {
exit(1);
}
class ApiQvitterNotificationsAction extends ApiPrivateAuthAction
{
var $notifications = null;
var $notices = null;
var $profiles = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
$this->notifications = $this->getNotifications();
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showTimeline();
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
$notice = null;
$notifications_populated = array();
foreach($this->notifications as $notification) {
// all but follow has an notice
if($notification->ntype != 'follow') {
$notice = self::twitterSimpleStatusArray(Notice::getKV($notification->notice_id));
}
$notifications_populated[] = array(
'id'=> $notification->id,
'from_profile'=> self::twitterUserArray(Profile::getKV($notification->from_profile_id)),
'ntype'=> $notification->ntype,
'notice'=> $notice,
'created_at'=>self::dateTwitter($notification->created),
'is_seen'=>$notification->is_seen
);
}
// mark as seen
foreach($this->notifications as $notification) {
if($notification->is_seen == 0) {
$notification->is_seen = 1;
$notification->update();
}
}
$this->initDocument('json');
$this->showJsonObjects($notifications_populated);
$this->endDocument('json');
}
/**
* Get notices
*
* @return array notices
*/
function getNotifications()
{
$notices = array();
$profile = ($this->auth_user) ? $this->auth_user->getProfile() : null;
$stream = new NotificationStream($profile);
$notifications = $stream->getNotifications(($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id);
$notifications = $notifications->fetchAll();
return $notifications;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notifications) && (count($this->notifications) > 0)) {
return strtotime($this->notifications[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notifications) && (count($this->notifications) > 0)) {
$last = count($this->notifications) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
strtotime($this->notifications[0]->created),
strtotime($this->notifications[$last]->created))
)
. '"';
}
return null;
}
}

View File

@ -1,38 +1,37 @@
<?php <?php
/** /* · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
* StatusNet, the distributed open-source microblogging tool · ·
* · ·
* Show the public timeline · Q V I T T E R ·
* · ·
* PHP version 5 · http://github.com/hannesmannerheim/qvitter ·
* · ·
* 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 · <o) ·
* the Free Software Foundation, either version 3 of the License, or · /_//// ·
* (at your option) any later version. · (____/ ·
* · (o< ·
* This program is distributed in the hope that it will be useful, · o> \\\\_\ ·
* 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 · Qvitter is free software: you can redistribute it and / or modify it ·
* along with this program. If not, see <http://www.gnu.org/licenses/>. · 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 ·
* @category API · your option) any later version. ·
* @package StatusNet · ·
* @author Craig Andrews <candrews@integralblue.com> · Qvitter is distributed in hope that it will be useful but WITHOUT ANY ·
* @author Evan Prodromou <evan@status.net> · WARRANTY; without even the implied warranty of MERCHANTABILTY or FITNESS ·
* @author Jeffery To <jeffery.to@gmail.com> · FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for ·
* @author mac65 <mac65@mac65.com> · more details. ·
* @author Mike Cochrane <mikec@mikenz.geek.nz> · ·
* @author Robin Millette <robin@millette.info> · You should have received a copy of the GNU Affero General Public License ·
* @author Zach Copley <zach@status.net> · along with Qvitter. If not, see <http://www.gnu.org/licenses/>. ·
* @copyright 2009 StatusNet, Inc. · ·
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org · Contact h@nnesmannerhe.im if you have any questions. ·
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 · ·
* @link http://status.net/ · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · */
*/
if (!defined('STATUSNET')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
@ -54,94 +53,6 @@ if (!defined('STATUSNET')) {
* @link http://status.net/ * @link http://status.net/
*/ */
/* External API usage documentation. Please update when you change how this method works. */
/*! @page publictimeline statuses/public_timeline
@section Description
Returns the 20 most recent notices from users throughout the system who have
uploaded their own avatars. Depending on configuration, it may or may not
not include notices from automatic posting services.
@par URL patterns
@li /api/statuses/public_timeline.:format
@par Formats (:format)
xml, json, rss, atom
@par HTTP Method(s)
GET
@par Requires Authentication
No
@param since_id (Optional) Returns only statuses with an ID greater
than (that is, more recent than) the specified ID.
@param max_id (Optional) Returns only statuses with an ID less than
(that is, older than) or equal to the specified ID.
@param count (Optional) Specifies the number of statuses to retrieve.
@param page (Optional) Specifies the page of results to retrieve.
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@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>).
@subsection exampleusage Example usage
@verbatim
curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
<text>@skwashd oh, commbank reenabled me super quick both times. but disconcerting when you don't expect it though</text>
<truncated>false</truncated>
<created_at>Sat Apr 17 00:49:12 +0000 2010</created_at>
<in_reply_to_status_id>28838393</in_reply_to_status_id>
<source>xmpp</source>
<id>28838456</id>
<in_reply_to_user_id>39303</in_reply_to_user_id>
<in_reply_to_screen_name>skwashd</in_reply_to_screen_name>
<geo></geo>
<favorited>false</favorited>
<user>
<id>44517</id>
<name>joshua may</name>
<screen_name>notjosh</screen_name>
<location></location>
<description></description>
<profile_image_url>http://avatar.identi.ca/44517-48-20090321004106.jpeg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>17</followers_count>
<profile_background_color></profile_background_color>
<profile_text_color></profile_text_color>
<profile_link_color></profile_link_color>
<profile_sidebar_fill_color></profile_sidebar_fill_color>
<profile_sidebar_border_color></profile_sidebar_border_color>
<friends_count>20</friends_count>
<created_at>Sat Mar 21 00:40:25 +0000 2009</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url></profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>100</statuses_count>
<following>false</following>
<notifications>false</notifications>
</user>
</status>
[....]
</statuses>
@endverbatim
*/
class ApiTimelinePublicAndExternalAction extends ApiPrivateAuthAction class ApiTimelinePublicAndExternalAction extends ApiPrivateAuthAction
{ {
var $notices = null; var $notices = null;

View File

@ -189,7 +189,9 @@ class QvitterAction extends ApiAction
color:#0084B4;/*COLOREND*/ color:#0084B4;/*COLOREND*/
} }
.topbar .global-nav, .topbar .global-nav,
.menu-container { .menu-container,
#unseen-notifications,
.stream-item.notification .not-seen {
background-color:#0084B4;/*BACKGROUNDCOLOREND*/ background-color:#0084B4;/*BACKGROUNDCOLOREND*/
} }
</style> </style>
@ -278,7 +280,14 @@ class QvitterAction extends ApiAction
</div> </div>
</div> </div>
<div id="page-container"> <div id="page-container">
<div class="front-welcome-text"> <?php
$site_notice = common_config('site', 'notice');
if(!empty($site_notice)) {
print '<div id="site-notice">'.common_config('site', 'notice').'</div>';
}
?><div class="front-welcome-text">
<h1></h1> <h1></h1>
<p></p> <p></p>
</div> </div>
@ -343,6 +352,7 @@ class QvitterAction extends ApiAction
</div> </div>
<div class="menu-container"> <div class="menu-container">
<a class="stream-selection friends-timeline" data-stream-header="" data-stream-name="qvitter/statuses/friends_timeline.json"><i class="chev-right"></i></a> <a class="stream-selection friends-timeline" data-stream-header="" data-stream-name="qvitter/statuses/friends_timeline.json"><i class="chev-right"></i></a>
<a class="stream-selection notifications" data-stream-header="" data-stream-name="qvitter/statuses/notifications.json"><span id="unseen-notifications"></span><i class="chev-right"></i></a>
<a class="stream-selection mentions" data-stream-header="" data-stream-name="qvitter/statuses/mentions.json"><i class="chev-right"></i></a> <a class="stream-selection mentions" data-stream-header="" data-stream-name="qvitter/statuses/mentions.json"><i class="chev-right"></i></a>
<a class="stream-selection my-timeline" data-stream-header="@statuses/user_timeline.json" data-stream-name="statuses/user_timeline.json"><i class="chev-right"></i></a> <a class="stream-selection my-timeline" data-stream-header="@statuses/user_timeline.json" data-stream-name="statuses/user_timeline.json"><i class="chev-right"></i></a>
<a class="stream-selection favorites" data-stream-header="" data-stream-name="favorites.json"><i class="chev-right"></i></a> <a class="stream-selection favorites" data-stream-header="" data-stream-name="favorites.json"><i class="chev-right"></i></a>
@ -350,6 +360,7 @@ class QvitterAction extends ApiAction
<a href="<?php print $instanceurl ?>main/all" class="stream-selection public-and-external-timeline" data-stream-header="" data-stream-name="statuses/public_and_external_timeline.json"><i class="chev-right"></i></a> <a href="<?php print $instanceurl ?>main/all" class="stream-selection public-and-external-timeline" data-stream-header="" data-stream-name="statuses/public_and_external_timeline.json"><i class="chev-right"></i></a>
</div> </div>
<div class="menu-container" id="history-container"></div> <div class="menu-container" id="history-container"></div>
<div id="qvitter-notice"><?php print common_config('site', 'qvitternotice'); ?></div>
</div> </div>
<div id="feed"> <div id="feed">
<div id="feed-header"> <div id="feed-header">
@ -364,7 +375,7 @@ class QvitterAction extends ApiAction
<div id="footer"></div> <div id="footer"></div>
</div> </div>
<script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery-2.0.2.min.js"></script> <script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery-ui-1.10.3.min.js"></script> <script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery-ui-1.10.3.min.js"></script>
<script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery.easing.1.3.js"></script> <script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery.easing.1.3.js"></script>
<script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery.minicolors.min.js"></script> <script type="text/javascript" src="<?php print $qvitterpath; ?>js/lib/jquery.minicolors.min.js"></script>

View File

@ -0,0 +1,203 @@
<?php
/* · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
· ·
· ·
· Q V I T T E R ·
· ·
· http://github.com/hannesmannerheim/qvitter ·
· ·
· ·
· <o) ·
· /_//// ·
· (____/ ·
· (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 ·
· your option) any later version. ·
· ·
· Qvitter is distributed in hope that it will be useful but WITHOUT ANY ·
· WARRANTY; without even the implied warranty of MERCHANTABILTY 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 Qvitter. If not, see <http://www.gnu.org/licenses/>. ·
· ·
· Contact h@nnesmannerhe.im if you have any questions. ·
· ·
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · */
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
class QvitterAdminSettingsAction extends AdminPanelAction
{
/**
* Returns the page title
*
* @return string page title
*/
function title()
{
// TRANS: Page title for site-wide notice tab in admin panel.
return _('Qvitter Sidebar Notice');
}
/**
* Instructions for using this form.
*
* @return string instructions
*/
function getInstructions()
{
// TRANS: Instructions for site-wide notice tab in admin panel.
return _('Edit notice in Qvitter\'s sidebar');
}
/**
* Show the site notice admin panel form
*
* @return void
*/
function showForm()
{
$form = new QvitterNoticeAdminPanelForm($this);
$form->show();
return;
}
/**
* Save settings from the form
*
* @return void
*/
function saveSettings()
{
$qvitterNotice = $this->trimmed('qvitter-notice');
// assert(all values are valid);
// This throws an exception on validation errors
$this->validate($qvitterNotice);
$config = new Config();
$result = Config::save('site', 'qvitternotice', $qvitterNotice);
if (!$result) {
// TRANS: Server error displayed when saving a sidebar notice was impossible.
$this->ServerError(_('Unable to save qvitter sidebar notice.'));
}
}
function validate(&$qvitterNotice)
{
// Validate notice text
if (mb_strlen($qvitterNotice) > 255) {
$this->clientError(
// TRANS: Client error displayed when a sidebar notice was longer than allowed.
_('Maximum length for the sidebar notice is 255 characters.')
);
}
// scrub HTML input
$config = array(
'safe' => 1,
'deny_attribute' => 'on*'
);
$qvitterNotice = htmLawed($qvitterNotice, $config);
}
}
class QvitterNoticeAdminPanelForm extends AdminForm
{
/**
* ID of the form
*
* @return int ID of the form
*/
function id()
{
return 'form_qvitter_notice_admin_panel';
}
/**
* class of the form
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
return common_local_url('qvitteradminsettings');
}
/**
* Data elements of the form
*
* @return void
*/
function formData()
{
$this->out->elementStart('ul', 'form_data');
$this->out->elementStart('li');
$this->out->textarea(
'qvitter-notice',
// TRANS: Label for sidebar notice text field in admin panel.
_('Qvitter sidebar notice text'),
common_config('site', 'qvitternotice'),
// TRANS: Tooltip for sidebar notice text field in admin panel.
_('Qvitter\'s sidebar notice text (255 characters maximum; HTML allowed)')
);
$this->out->elementEnd('li');
$this->out->elementEnd('ul');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
$this->out->submit(
'submit',
// TRANS: Button text for saving sidebar notice in admin panel.
_m('BUTTON','Save'),
'submit',
null,
// TRANS: Button title to save sidebar notice in admin panel.
_('Save sidebar notice.')
);
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
*
* Notification stream
*
*/
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
class NotificationStream
{
protected $target = null;
/**
* Constructor
*
* @param Profile $target Profile to get a stream for
*/
function __construct(Profile $target)
{
$this->target = $target;
}
/**
* Get IDs in a range
*
* @param int $offset Offset from start
* @param int $limit Limit of number to get
* @param int $since_id Since this notice
* @param int $max_id Before this notice
*
* @return Array IDs found
*/
function getNotificationIds($offset, $limit, $since_id, $max_id)
{
$notification = new QvitterNotification();
$notification->selectAdd();
$notification->selectAdd('id');
$notification->whereAdd(sprintf('qvitternotification.to_profile_id = "%s"', $notification->escape($this->target->id)));
$notification->whereAdd(sprintf('qvitternotification.created > "%s"', $notification->escape($this->target->created)));
$notification->limit($offset, $limit);
$notification->orderBy('qvitternotification.created DESC');
QvitterNotification::addWhereSinceId($notification, $since_id);
QvitterNotification::addWhereMaxId($notification, $max_id);
if (!$notification->find()) {
return array();
}
$ids = $notification->fetchAll('id');
return $ids;
}
function getNotifications($offset, $limit, $sinceId, $maxId)
{
$all = array();
do {
$ids = $this->getNotificationIds($offset, $limit, $sinceId, $maxId);
$notifications = QvitterNotification::pivotGet('id', $ids);
// By default, takes out false values
$notifications = array_filter($notifications);
$all = array_merge($all, $notifications);
if (count($notifications < count($ids))) {
$offset += $limit;
$limit -= count($notifications);
}
} while (count($notifications) < count($ids) && count($ids) > 0);
return new ArrayWrapper($all);
}
}

View File

@ -0,0 +1,152 @@
<?php
/**
* Table Definition for reply
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class QvitterNotification extends Managed_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'qvitternotification'; // table name
public $id; // int(4) primary_key not_null
public $to_profile_id; // int(4) primary_key not_null
public $from_profile_id; // int(4) primary_key not_null
public $type; // varchar(7)
public $notice_id; // int(4) primary_key not_null
public $date; // datetime multiple_key not_null default_0000-00-00%2000%3A00%3A00
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
public static function schemaDef()
{
return array(
'fields' => array(
'id' => array('type' => 'serial', 'not null' => true),
'to_profile_id' => array('type' => 'int', 'description' => 'the profile being notified'),
'from_profile_id' => array('type' => 'int', 'description' => 'the profile that is notifying'),
'ntype' => array('type' => 'varchar', 'length' => 7, 'description' => 'reply, like, mention or follow'),
'notice_id' => array('type' => 'int', 'description' => 'id for the reply or mention or notice being faved'),
'is_seen' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'if the notification has been seen'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created')
),
'primary key' => array('id')
);
}
/**
* Wrapper for record insertion to update related caches
*/
function insert()
{
$result = parent::insert();
if ($result) {
self::blow('qvitternotification:stream:%d', $this->to_profile_id);
}
return $result;
}
/**
* Look up the creation timestamp for a given notice ID, even
* if it's been deleted.
*
* @param int $id
* @return mixed string recorded creation timestamp, or false if can't be found
*/
public static function getAsTimestamp($id)
{
if (!$id) {
return false;
}
$notice = QvitterNotification::getKV('id', $id);
if ($notice) {
return $notice->created;
}
return false;
}
/**
* Build an SQL 'where' fragment for timestamp-based sorting from a since_id
* parameter, matching notices posted after the given one (exclusive).
*
* If the referenced notice can't be found, will return false.
*
* @param int $id
* @param string $idField
* @param string $createdField
* @return mixed string or false if no match
*/
public static function whereSinceId($id, $idField='id', $createdField='created')
{
$since = QvitterNotification::getAsTimestamp($id);
if ($since) {
return sprintf("($createdField = '%s' and $idField > %d) or ($createdField > '%s')", $since, $id, $since);
}
return false;
}
/**
* Build an SQL 'where' fragment for timestamp-based sorting from a since_id
* parameter, matching notices posted after the given one (exclusive), and
* if necessary add it to the data object's query.
*
* @param DB_DataObject $obj
* @param int $id
* @param string $idField
* @param string $createdField
* @return mixed string or false if no match
*/
public static function addWhereSinceId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
{
$since = self::whereSinceId($id, $idField, $createdField);
if ($since) {
$obj->whereAdd($since);
}
}
/**
* Build an SQL 'where' fragment for timestamp-based sorting from a max_id
* parameter, matching notices posted before the given one (inclusive).
*
* If the referenced notice can't be found, will return false.
*
* @param int $id
* @param string $idField
* @param string $createdField
* @return mixed string or false if no match
*/
public static function whereMaxId($id, $idField='id', $createdField='created')
{
$max = QvitterNotification::getAsTimestamp($id);
if ($max) {
return sprintf("($createdField < '%s') or ($createdField = '%s' and $idField <= %d)", $max, $max, $id);
}
return false;
}
/**
* Build an SQL 'where' fragment for timestamp-based sorting from a max_id
* parameter, matching notices posted before the given one (inclusive), and
* if necessary add it to the data object's query.
*
* @param DB_DataObject $obj
* @param int $id
* @param string $idField
* @param string $createdField
* @return mixed string or false if no match
*/
public static function addWhereMaxId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
{
$max = self::whereMaxId($id, $idField, $createdField);
if ($max) {
$obj->whereAdd($max);
}
}
}

View File

@ -409,7 +409,7 @@ body.rtl .dropdown-menu li:not(.dropdown-caret) {
.dropdown-menu.quitter-settings li.language { .dropdown-menu.quitter-settings li.language {
display:none; display:none;
} }
#birds-top { #birds-top {
background-position: -193px -3px; background-position: -193px -3px;
display: block; display: block;
@ -436,7 +436,8 @@ body.rtl .dropdown-menu li:not(.dropdown-caret) {
unicode-bidi: bidi-override; unicode-bidi: bidi-override;
} }
.front-welcome-text { .front-welcome-text,
#site-notice {
color: #EEEEEE; color: #EEEEEE;
font-size: 30px; font-size: 30px;
font-weight: 300; font-weight: 300;
@ -453,6 +454,26 @@ body.rtl .front-welcome-text {
text-align:right; text-align:right;
font-family:Tahoma,Arial,sans-serif; font-family:Tahoma,Arial,sans-serif;
} }
#site-notice{
display:block;
}
#qvitter-notice {
text-shadow:none;
color:#fff;
font-size:11px;
padding:10px;
display:none;
}
#qvitter-notice a {
color:#fff;
opacity:0.5;
line-height:17px;
display:inline-block;
}
#qvitter-notice a:hover {
opacity:0.8;
}
.front-welcome-text h1 { .front-welcome-text h1 {
color: #FFFFFF; color: #FFFFFF;
font-size: 30px; font-size: 30px;
@ -992,7 +1013,32 @@ button#submit-login:hover {
} }
#history-container a:hover .chev-right:hover { #history-container a:hover .chev-right:hover {
background-position:-40px -508px; background-position:-40px -508px;
} }
.menu-container a.notifications {
line-height:32px;
padding:0 12px;
position:relative;
}
#unseen-notifications {
display:block;
position:absolute;
right:0px;
top:0;
width:24px;
height:24px;
line-height:24px;
border-radius:12px;
color:#fff;
text-shadow:none;
margin:4px 5px;
z-index:10;
display:none;
text-align:center;
font-weight:normal;
}
#history-container { #history-container {
display:none; display:none;
} }
@ -1204,6 +1250,7 @@ button#submit-login:hover {
-o-transition: opacity 0.1s ease, height 0s linear, margin 0.1s ease; -o-transition: opacity 0.1s ease, height 0s linear, margin 0.1s ease;
transition: opacity 0.1s ease, height 0s linear, margin 0.1s ease; transition: opacity 0.1s ease, height 0s linear, margin 0.1s ease;
height:auto; height:auto;
/* position:relative; */
} }
.stream-item.hidden { .stream-item.hidden {
display:none; display:none;
@ -1230,6 +1277,70 @@ button#submit-login:hover {
.stream-item.conversation .queet:hover { .stream-item.conversation .queet:hover {
background-color:#F6F6F6; background-color:#F6F6F6;
} }
.stream-item.notification .not-seen {
position:absolute;
left:-5px;
top:50%;
margin-top:-5px;
width:10px;
height:10px;
border-radius:5px;
z-index:10;
}
.stream-item.notification.repeat .dogear,
.stream-item.notification.like .dogear {
display:block;
left: 36px;
top: 7px;
z-index: 10;
width:24px;
height:24px;
}
.stream-item.notification.repeat .dogear {
background-position:-70px -1072px;
}
.stream-item.notification.like .dogear {
background-position:-115px -1075px;
}
.stream-item.notification.follow .queet,
.stream-item.notification.repeat .queet,
.stream-item.notification.like .queet {
min-height:37px;
cursor:default;
}
.stream-item.notification.follow .queet .queet-content,
.stream-item.notification.repeat .queet .queet-content,
.stream-item.notification.like .queet .queet-content {
cursor:default;
}
.stream-item.notification.follow .queet:hover,
.stream-item.notification.repeat .queet:hover,
.stream-item.notification.like .queet:hover {
background-color:transparent;
}
.stream-item.notification.follow a.account-group img.avatar,
.stream-item.notification.repeat a.account-group img.avatar,
.stream-item.notification.like a.account-group img.avatar {
width:24px;
height:24px;
left:36px;
top:4px;
}
.stream-item.notification .small-grey-notice,
.stream-item.notification .small-grey-notice a {
color:#999;
}
.stream-item.notification .small-grey-notice a:hover {
color:#333;
text-decoration:underline;
}
.stream-item.notification .stream-item-header {
color:#333;
}
.show-full-conversation { .show-full-conversation {
float:right; float:right;
font-style:italic; font-style:italic;
@ -1281,6 +1392,14 @@ button#submit-login:hover {
border-radius:0 0 6px 6px; border-radius:0 0 6px 6px;
} }
.expanded-content {
-webkit-transition: height 0s linear;
-moz-transition: height 0s linear;
-o-transition: height 0s linear;
transition: height 0s linear;
overflow:hidden;
}
body.rtl .queet.rtl .expanded-content { body.rtl .queet.rtl .expanded-content {
direction:rtl; direction:rtl;
} }
@ -3718,6 +3837,12 @@ body.rtl #feed-header-inner h2 {
@media (max-width: 866px) { @media (max-width: 866px) {
#site-notice,
#qvitter-notice {
display:none !important;
}
#new-queets-bar-container { #new-queets-bar-container {
height:77px; height:77px;
} }
@ -4060,6 +4185,7 @@ body.rtl #feed-header-inner h2 {
display:block; display:block;
} }
.menu-container a.favorites, .menu-container a.favorites,
.menu-container a.mentions,
.menu-container a.public-and-external-timeline { .menu-container a.public-and-external-timeline {
display:none; display:none;
} }
@ -4071,7 +4197,7 @@ body.rtl #feed-header-inner h2 {
font-size:0; font-size:0;
color:transparent; color:transparent;
height:55px; height:55px;
padding:0; padding:0 !important;
margin:0; margin:0;
border-radius:0 0 0 0 !important; border-radius:0 0 0 0 !important;
background-image: url("../img/sprite.png?v=2"); background-image: url("../img/sprite.png?v=2");
@ -4098,7 +4224,7 @@ body.rtl #feed-header-inner h2 {
} }
.stream-selection.friends-timeline:after, .stream-selection.friends-timeline:after,
.stream-selection.mentions:after, .stream-selection.notifications:after,
.stream-selection.my-timeline:after, .stream-selection.my-timeline:after,
.stream-selection.public-timeline:after { .stream-selection.public-timeline:after {
content: ""; content: "";
@ -4115,7 +4241,7 @@ body.rtl #feed-header-inner h2 {
.stream-selection.friends-timeline:after { .stream-selection.friends-timeline:after {
background-position:0px -861px ; background-position:0px -861px ;
} }
.stream-selection.mentions:after { .stream-selection.notifications:after {
background-position:-78px -861px ; background-position:-78px -861px ;
} }
.stream-selection.my-timeline:after { .stream-selection.my-timeline:after {
@ -4249,8 +4375,12 @@ body.rtl #feed-header-inner h2 {
border-top: 1px solid #E8E8E8; border-top: 1px solid #E8E8E8;
border-bottom: 1px solid #E8E8E8; border-bottom: 1px solid #E8E8E8;
height:45px; height:45px;
margin-top:20px; margin-top:9px;
} }
.queet-content {
margin-bottom: -1px;
padding-bottom:0;
}
/* .stream-item.collapsing > .queet ul.queet-actions { /* .stream-item.collapsing > .queet ul.queet-actions {
display:none !important; display:none !important;
} */ } */
@ -4289,9 +4419,6 @@ body.rtl #feed-header-inner h2 {
} }
.queet ul.stats { .queet ul.stats {
border-top: 0 none;
margin-left:-63px;
margin-top:0;
} }
.queet ul.stats li.rq-count a, .queet ul.stats li.rq-count a,
@ -4302,7 +4429,11 @@ body.rtl #feed-header-inner h2 {
.queet ul.stats li:first-child a { .queet ul.stats li:first-child a {
padding-left:0 !important; padding-left:0 !important;
} }
.client-and-actions { .expanded-content {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
margin-left:-63px; margin-left:-63px;
} }
.client-and-actions .permalink-link { .client-and-actions .permalink-link {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because one or more lines are too long

View File

@ -326,4 +326,53 @@ function getFavsOrRequeetsForQueet(apiaction,qid,actionOnSuccess) {
} }
}); });
} }
/* ·
·
· Check for unseen notifications
·
· @param actionOnSuccess: callback function
·
· · · · · · · · · */
function checkForNewNotifications() {
$.ajax({ url: window.apiRoot + "qvitter/newnotifications.json",
type: "GET",
dataType: 'json',
success: function(data) {
if(data.length == 0) {
$('#unseen-notifications').hide();
document.title = window.siteTitle;
}
else {
var totNotif = 0;
$.each(data,function(k,v){
totNotif = totNotif + parseInt(v,10);
});
if(totNotif>0) {
$('#unseen-notifications').html(totNotif);
document.title = window.siteTitle + ' (' + totNotif + ')'; // update html page title
$('#unseen-notifications').show();
}
else {
$('#unseen-notifications').hide();
document.title = window.siteTitle;
}
}
},
error: function(data) {
$('#unseen-notifications').hide();
document.title = window.siteTitle;
remove_spinner();
console.log(data);
}
});
}

View File

@ -302,6 +302,7 @@ function setNewCurrentStream(stream,actionOnSuccess,setLocation) {
// halt interval that checks for new queets // halt interval that checks for new queets
window.clearInterval(checkForNewQueetsInterval); window.clearInterval(checkForNewQueetsInterval);
window.clearInterval(checkForNewNotificationsInterval);
display_spinner(); display_spinner();
@ -335,6 +336,7 @@ function setNewCurrentStream(stream,actionOnSuccess,setLocation) {
// if this is one of the default streams, get header from DOM // if this is one of the default streams, get header from DOM
else if(stream == 'qvitter/statuses/friends_timeline.json' else if(stream == 'qvitter/statuses/friends_timeline.json'
|| stream == 'qvitter/statuses/mentions.json' || stream == 'qvitter/statuses/mentions.json'
|| stream == 'qvitter/statuses/notifications.json'
|| stream == 'favorites.json' || stream == 'favorites.json'
|| stream == 'statuses/public_timeline.json' || stream == 'statuses/public_timeline.json'
|| stream == 'statuses/public_and_external_timeline.json') { || stream == 'statuses/public_and_external_timeline.json') {
@ -354,6 +356,11 @@ function setNewCurrentStream(stream,actionOnSuccess,setLocation) {
var defaultStreamName = stream; var defaultStreamName = stream;
var streamHeader = '#' + stream.substring(stream.indexOf('/timeline/')+10,stream.indexOf('.json')); var streamHeader = '#' + stream.substring(stream.indexOf('/timeline/')+10,stream.indexOf('.json'));
} }
// if this is a notice stream
else if(stream.substring(0,14) == 'statuses/show/') {
var defaultStreamName = stream;
var streamHeader = 'notice/' + stream.substring(stream.indexOf('/show/')+6,stream.indexOf('.json'));
}
// if this is a search stream // if this is a search stream
else if(stream.substring(0,11) == 'search.json') { else if(stream.substring(0,11) == 'search.json') {
var defaultStreamName = stream; var defaultStreamName = stream;
@ -362,10 +369,10 @@ function setNewCurrentStream(stream,actionOnSuccess,setLocation) {
// set the h2 header in the feed // set the h2 header in the feed
if(stream.substring(0,23) == 'statuses/followers.json') { if(stream.substring(0,23) == 'statuses/followers.json') {
var h2FeedHeader = window.sL.followers; var h2FeedHeader = '<div class="queet-streams"><a class="queet-stream following">' + window.sL.following + '</a> / </div>' + window.sL.followers;
} }
else if(stream.substring(0,21) == 'statuses/friends.json') { else if(stream.substring(0,21) == 'statuses/friends.json') {
var h2FeedHeader = window.sL.following; var h2FeedHeader = window.sL.following + '<div class="queet-streams">/ <a class="queet-stream followers">' + window.sL.followers + '</a></div>';
} }
else if(stream.substring(0,40) == 'statuses/user_timeline.json?screen_name=') { else if(stream.substring(0,40) == 'statuses/user_timeline.json?screen_name=') {
var h2FeedHeader = window.sL.notices + '<div class="queet-streams">/ <a class="queet-stream mentions">' + window.sL.mentions + '</a> / <a class="queet-stream favorites">' + window.sL.favoritesNoun +'</a></div>'; var h2FeedHeader = window.sL.notices + '<div class="queet-streams">/ <a class="queet-stream mentions">' + window.sL.mentions + '</a> / <a class="queet-stream favorites">' + window.sL.favoritesNoun +'</a></div>';
@ -428,6 +435,7 @@ function setNewCurrentStream(stream,actionOnSuccess,setLocation) {
$('.stream-selection').removeClass('current'); $('.stream-selection').removeClass('current');
$('.stream-selection[data-stream-header="' + streamHeader + '"]').addClass('current'); $('.stream-selection[data-stream-header="' + streamHeader + '"]').addClass('current');
// if this is user's user feed, i.e. followers etc, we want a profile card, which we need to get from user_timeline since the users/show api action is broken (auth doesn't work) // if this is user's user feed, i.e. followers etc, we want a profile card, which we need to get from user_timeline since the users/show api action is broken (auth doesn't work)
if(stream.substring(0,23) == 'statuses/followers.json' if(stream.substring(0,23) == 'statuses/followers.json'
|| stream.substring(0,21) == 'statuses/friends.json' || stream.substring(0,21) == 'statuses/friends.json'
@ -454,7 +462,14 @@ function setNewCurrentStream(stream,actionOnSuccess,setLocation) {
} }
profileCardFromFirstObject(profile_data,thisUsersScreenName); // show profile card profileCardFromFirstObject(profile_data,thisUsersScreenName); // show profile card
checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling); // start interval again
// start checking for new queets again
window.clearInterval(checkForNewQueetsInterval);
window.clearInterval(checkForNewNotificationsInterval);
checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling);
checkForNewNotificationsInterval=window.setInterval(function(){checkForNewNotifications()},window.timeBetweenPolling);
checkForNewNotifications();
remove_spinner(); remove_spinner();
$('#feed-body').html(''); // empty feed only now so the scrollers don't flicker on and off $('#feed-body').html(''); // empty feed only now so the scrollers don't flicker on and off
$('#new-queets-bar').parent().addClass('hidden'); document.title = window.siteTitle; // hide new queets bar if it's visible there $('#new-queets-bar').parent().addClass('hidden'); document.title = window.siteTitle; // hide new queets bar if it's visible there
@ -493,7 +508,19 @@ function setNewCurrentStream(stream,actionOnSuccess,setLocation) {
groupProfileCard(thisGroupAlias); groupProfileCard(thisGroupAlias);
} }
checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling); // start interval again // start checking for new queets again
window.clearInterval(checkForNewQueetsInterval);
window.clearInterval(checkForNewNotificationsInterval);
checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling);
if(window.currentStream != 'qvitter/statuses/notifications.json') { // don't check for notifications if this is the notifications page
checkForNewNotifications();
checkForNewNotificationsInterval=window.setInterval(function(){checkForNewNotifications()},window.timeBetweenPolling);
}
else {
$('#unseen-notifications').hide();
document.title = window.siteTitle;
}
remove_spinner(); remove_spinner();
$('#feed-body').html(''); // empty feed only now so the scrollers don't flicker on and off $('#feed-body').html(''); // empty feed only now so the scrollers don't flicker on and off
$('#new-queets-bar').parent().addClass('hidden'); document.title = window.siteTitle; // hide new queets bar if it's visible there $('#new-queets-bar').parent().addClass('hidden'); document.title = window.siteTitle; // hide new queets bar if it's visible there
@ -547,6 +574,9 @@ function convertStreamToPath(stream) {
else if(stream == 'qvitter/statuses/mentions.json') { else if(stream == 'qvitter/statuses/mentions.json') {
return window.loggedIn.screen_name + '/replies'; return window.loggedIn.screen_name + '/replies';
} }
else if(stream == 'qvitter/statuses/notifications.json') {
return window.loggedIn.screen_name + '/notifications';
}
else if(stream == 'favorites.json') { else if(stream == 'favorites.json') {
return window.loggedIn.screen_name + '/favorites'; return window.loggedIn.screen_name + '/favorites';
} }
@ -564,9 +594,6 @@ function convertStreamToPath(stream) {
var screenName = stream.substring(stream.indexOf('=')+1); var screenName = stream.substring(stream.indexOf('=')+1);
return screenName + '/all'; return screenName + '/all';
} }
else if (stream == 'qvitter/statuses/mentions.json') {
return window.loggedIn.screen_name + '/replies';
}
else if(stream == 'statuses/public_timeline.json') { else if(stream == 'statuses/public_timeline.json') {
return ''; return '';
} }
@ -589,6 +616,10 @@ function convertStreamToPath(stream) {
var tagName = stream.substring(stream.indexOf('/timeline/')+10,stream.indexOf('.json')); var tagName = stream.substring(stream.indexOf('/timeline/')+10,stream.indexOf('.json'));
return 'tag/' + tagName; return 'tag/' + tagName;
} }
else if(stream.substring(0,14) == 'statuses/show/') {
var noticeId = stream.substring(stream.indexOf('/show/')+6,stream.indexOf('.json'));
return 'notice/' + noticeId;
}
else if(stream.substring(0,11) == 'search.json') { else if(stream.substring(0,11) == 'search.json') {
var searchTerms = stream.substring(stream.indexOf('?q=')+3); var searchTerms = stream.substring(stream.indexOf('?q=')+3);
return 'search/notice?q=' + searchTerms; return 'search/notice?q=' + searchTerms;
@ -662,6 +693,21 @@ function getStreamFromUrl() {
} }
} }
} }
// {domain}/{screen_name}/notifications
else if ((/^[a-zA-Z0-9]+$/.test(loc.replace('/','').replace('/notifications','')))) {
var userToStream = loc.replace('/','').replace('/notifications','');
if(userToStream.length>0) {
if(window.loggedIn.screen_name == userToStream) {
streamToSet = 'qvitter/statuses/notifications.json';
}
else {
// not allowed for others than logged in user
window.location.replace(window.siteInstanceURL);
}
}
}
// {domain}/{screen_name}/favorites // {domain}/{screen_name}/favorites
else if ((/^[a-zA-Z0-9]+$/.test(loc.replace('/','').replace('/favorites','')))) { else if ((/^[a-zA-Z0-9]+$/.test(loc.replace('/','').replace('/favorites','')))) {
@ -723,6 +769,15 @@ function getStreamFromUrl() {
} }
} }
// {domain}/notice/{notide_id}
else if (loc.indexOf('/notice/')>-1) {
var noticeToStream = loc.replace('/notice/','');
if(noticeToStream.length>0) {
streamToSet = 'statuses/show/' + noticeToStream + '.json';
}
}
// {domain}/group/{group}/members, {domain}/group/{group}/admins or {domain}/group/{group} // {domain}/group/{group}/members, {domain}/group/{group}/admins or {domain}/group/{group}
else if (loc.indexOf('/group/')>-1) { else if (loc.indexOf('/group/')>-1) {
@ -776,6 +831,10 @@ function expand_queet(q,doScrolling) {
var sel = getSelection().toString(); var sel = getSelection().toString();
if(!sel && !q.find('.queet-button').children('button').hasClass('enabled')) { // don't collapse if text is selected, or if queet has an active queet button if(!sel && !q.find('.queet-button').children('button').hasClass('enabled')) { // don't collapse if text is selected, or if queet has an active queet button
// remove some things right away
q.find('.inline-reply-caret').remove();
q.addClass('collapsing'); q.addClass('collapsing');
q.css('overflow','hidden'); q.css('overflow','hidden');
q.find('.stream-item-expand').html(window.sL.expand); q.find('.stream-item-expand').html(window.sL.expand);
@ -786,18 +845,19 @@ function expand_queet(q,doScrolling) {
q.find('.view-more-container-bottom').remove(); q.find('.view-more-container-bottom').remove();
q.find('.stream-item.conversation').remove(); q.find('.stream-item.conversation').remove();
q.find('.inline-reply-queetbox').remove(); q.find('.inline-reply-queetbox').remove();
q.find('.expanded-content').remove(); q.find('.expanded-content').remove();
} }
else { else {
rememberMyScrollPos(q.children('.queet'),qid,0); rememberMyScrollPos(q.children('.queet'),qid,0);
// give stream item a height // give stream item a height
q.css('height',q.height() + 'px'); q.css('height',q.height() + 'px');
q.children('.queet').find('.expanded-content').css('height',q.find('.expanded-content').height() + 'px');
q.children('div').not('.queet').children('a').css('opacity','0.5'); q.children('div').not('.queet').children('a').css('opacity','0.5');
q.children('div').not('.queet').children().children().css('opacity','0.5'); q.children('div').not('.queet').children().children().css('opacity','0.5');
var collapseTime = 150 + q.find('.stream-item.conversation:not(.hidden-conversation)').length*50; var collapseTime = 350 + q.find('.stream-item.conversation:not(.hidden-conversation)').length*50;
// set transition time (needs to be delayed, otherwise webkit animates the height-setting above) // set transition time (needs to be delayed, otherwise webkit animates the height-setting above)
setTimeout(function() { setTimeout(function() {
@ -809,10 +869,15 @@ function expand_queet(q,doScrolling) {
q.css('-o-transition-duration',Math.round( collapseTime / 1000 * 10) / 10 + 's'); q.css('-o-transition-duration',Math.round( collapseTime / 1000 * 10) / 10 + 's');
q.css('-webkit-transition-duration',Math.round( collapseTime * 1000 * 10) / 10 + 's'); q.css('-webkit-transition-duration',Math.round( collapseTime * 1000 * 10) / 10 + 's');
q.css('transition-duration',Math.round( collapseTime / 1000 * 10) / 10 + 's'); q.css('transition-duration',Math.round( collapseTime / 1000 * 10) / 10 + 's');
q.find('.expanded-content').css('-moz-transition-duration',Math.round( collapseTime / 1000 * 10) / 10 + 's');
q.find('.expanded-content').css('-o-transition-duration',Math.round( collapseTime / 1000 * 10) / 10 + 's');
q.find('.expanded-content').css('-webkit-transition-duration',Math.round( collapseTime * 1000 * 10) / 10 + 's');
q.find('.expanded-content').css('transition-duration',Math.round( collapseTime / 1000 * 10) / 10 + 's');
// set new heights and margins to animate to // set new heights and margins to animate to
q.css('height',(q.children('.queet').find('.queet-content').outerHeight() - q.children('.queet').find('.expanded-content').outerHeight() - 10) + 'px'); q.css('height',(q.children('.queet').find('.queet-content').outerHeight() - q.children('.queet').find('.expanded-content').outerHeight()) + 'px');
q.children('.queet').css('margin-top', '-' + (q.children('.queet').offset().top - q.offset().top) + 'px'); q.children('.queet').css('margin-top', '-' + (q.children('.queet').offset().top - q.offset().top) + 'px');
q.children('.queet').find('.expanded-content').css('height','0');
}, 50); }, 50);
@ -839,12 +904,12 @@ function expand_queet(q,doScrolling) {
} }
} }
} }
else { else if(!q.hasClass('collapsing')) {
rememberMyScrollPos(q,qid,-8); rememberMyScrollPos(q,qid,-8);
// not for acitivity // not for acitivity or notifications
if(!q.hasClass('activity')) { if(!q.hasClass('activity') && !q.hasClass('repeat') && !q.hasClass('like') && !q.hasClass('follow')) {
q.addClass('expanded'); q.addClass('expanded');
q.find('.stream-item-expand').html(window.sL.collapse); q.find('.stream-item-expand').html(window.sL.collapse);
@ -869,7 +934,7 @@ function expand_queet(q,doScrolling) {
var metadata = '<span class="longdate" title="' + longdate + '">' + longdate + ' · ' + unescape(q.attr('data-source')) + '</span> · <a href="' + qurl + '" class="permalink-link">' + window.sL.details + '</a>'; var metadata = '<span class="longdate" title="' + longdate + '">' + longdate + ' · ' + unescape(q.attr('data-source')) + '</span> · <a href="' + qurl + '" class="permalink-link">' + window.sL.details + '</a>';
// show expanded content // show expanded content
q.find('.stream-item-footer').after('<div class="expanded-content"><div class="queet-stats-container"></div><div class="client-and-actions"><span class="metadata">' + metadata + '</span></div></div>'); q.find('.stream-item-footer').before('<div class="expanded-content"><div class="queet-stats-container"></div><div class="client-and-actions"><span class="metadata">' + metadata + '</span></div></div>');
// maybe show images or videos // maybe show images or videos
@ -1327,13 +1392,68 @@ function checkForHiddenConversationQueets(qid) {
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
function addToFeed(feed, after, extraClasses) { function addToFeed(feed, after, extraClasses) {
// some streams, e.g. /statuses/show/1234.json is not enclosed in an array, make sure it is
if(!$.isArray(feed)) {
feed = [feed];
}
$.each(feed.reverse(), function (key,obj) { $.each(feed.reverse(), function (key,obj) {
var extraClassesThisRun = extraClasses; var extraClassesThisRun = extraClasses;
// if this is the notifications feed
if(window.currentStream.substring(0,35) == 'qvitter/statuses/notifications.json') {
// only if this notification isn't already in stream
if($('#stream-item-' + obj.id).length == 0) {
obj.from_profile.description = obj.from_profile.description || '';
var notificationTime = parseTwitterDate(obj.created_at);
if(obj.is_seen == '0') {
extraClassesThisRun = extraClassesThisRun + ' not-seen'
}
if(obj.ntype == 'like') {
var noticeTime = parseTwitterDate(obj.notice.created_at);
obj.notice = convertNewGNUSocialURItoURL(obj.notice);
var notificationHtml = '<div data-quitter-id-in-stream="' + obj.id + '" id="stream-item-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' notification like"><div class="queet"><div class="queet-content"><div class="stream-item-header"><small class="created-at" data-created-at="' + obj.created_at + '" title="' + obj.created_at + '">' + notificationTime + '</small><a class="account-group" href="' + obj.from_profile.statusnet_profile_url + '"><span class="dogear"></span><img class="avatar" src="' + obj.from_profile.profile_image_url + '" /><strong class="name" data-user-id="' + obj.from_profile.id + '" title="@' + obj.from_profile.screen_name + '">' + obj.from_profile.name + '</strong></a> ' + window.sL.xFavedYourQueet + '</div><div class="small-grey-notice"><a href="' + obj.notice.uri + '">' + noticeTime + '</a>: ' + $.trim(obj.notice.statusnet_html) + '</div></div></div></div>';
}
else if(obj.ntype == 'repeat') {
obj.notice = convertNewGNUSocialURItoURL(obj.notice);
var noticeTime = parseTwitterDate(obj.notice.created_at);
var notificationHtml = '<div data-quitter-id-in-stream="' + obj.id + '" id="stream-item-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' notification repeat"><div class="queet"><div class="queet-content"><div class="stream-item-header"><small class="created-at" data-created-at="' + obj.created_at + '" title="' + obj.created_at + '">' + notificationTime + '</small><a class="account-group" href="' + obj.from_profile.statusnet_profile_url + '"><span class="dogear"></span><img class="avatar" src="' + obj.from_profile.profile_image_url + '" /><strong class="name" data-user-id="' + obj.from_profile.id + '" title="@' + obj.from_profile.screen_name + '">' + obj.from_profile.name + '</strong></a> ' + window.sL.xRepeatedYourQueet + '</div><div class="small-grey-notice"><a href="' + obj.notice.uri + '">' + noticeTime + '</a>: ' + $.trim(obj.notice.statusnet_html) + '</div></div></div></div>';
}
else if(obj.ntype == 'mention') {
var notificationHtml = buildQueetHtml(obj.notice, obj.id, extraClassesThisRun + ' notification mention');
}
else if(obj.ntype == 'reply') {
var notificationHtml = buildQueetHtml(obj.notice, obj.id, extraClassesThisRun + ' notification reply');
}
else if(obj.ntype == 'follow') {
var notificationHtml = '<div data-quitter-id-in-stream="' + obj.id + '" id="stream-item-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' notification follow"><div class="queet"><div class="queet-content"><div class="stream-item-header"><small class="created-at" data-created-at="' + obj.created_at + '" title="' + obj.created_at + '">' + notificationTime + '</small><a class="account-group" href="' + obj.from_profile.statusnet_profile_url + '"><img class="avatar" src="' + obj.from_profile.profile_image_url + '" /><strong class="name" data-user-id="' + obj.from_profile.id + '" title="@' + obj.from_profile.screen_name + '">' + obj.from_profile.name + '</strong></a> ' + window.sL.xStartedFollowingYou + '</div></div></div></div>';
}
if(after) {
$('#' + after).after(notificationHtml);
}
else {
$('#feed-body').prepend(notificationHtml);
}
// add not seen notification circle
$.each($('.notification.not-seen .queet'),function(){
if($(this).children('.not-seen').length<1) {
$(this).prepend('<div class="not-seen"></div>');
}
});
}
}
// if this is a user feed // if this is a user feed
if(window.currentStream.substring(0,21) == 'statuses/friends.json' else if(window.currentStream.substring(0,21) == 'statuses/friends.json'
|| window.currentStream.substring(0,18) == 'statuses/followers' || window.currentStream.substring(0,18) == 'statuses/followers'
|| window.currentStream.substring(0,28) == 'statusnet/groups/membership/' || window.currentStream.substring(0,28) == 'statusnet/groups/membership/'
|| window.currentStream.substring(0,24) == 'statusnet/groups/admins/') { || window.currentStream.substring(0,24) == 'statusnet/groups/admins/') {
@ -1426,73 +1546,7 @@ function addToFeed(feed, after, extraClasses) {
// retweeted object don't exist in feed // retweeted object don't exist in feed
else { else {
// we don't want to print 'null', someone might have that username! var queetHtml = buildQueetHtml(obj.retweeted_status, obj.id, extraClassesThisRun, obj);
var in_reply_to_screen_name = '';
if(obj.retweeted_status.in_reply_to_screen_name != null) {
in_reply_to_screen_name = obj.retweeted_status.in_reply_to_screen_name;
}
// requeet html
var requeetedClass = '';
if(obj.retweeted_status.user.id == window.myUserID) {
var requeetHtml = '<li class="action-del-container"><a class="with-icn"><span class="icon sm-trash"></span> <b>' + window.sL.deleteVerb + '</b></a></li></i>';
}
else if(obj.retweeted_status.repeated) {
var requeetHtml = '<li class="action-rt-container"><a class="with-icn done"><span class="icon sm-rt"></span> <b>' + window.sL.requeetedVerb + '</b></a></i>';
requeetedClass = 'requeeted';
}
else {
var requeetHtml = '<li class="action-rt-container"><a class="with-icn"><span class="icon sm-rt"></span> <b>' + window.sL.requeetVerb + '</b></a></i>';
}
// favorite html
var favoritedClass = '';
if(obj.retweeted_status.favorited) {
var favoriteHtml = '<a class="with-icn done"><span class="icon sm-fav"></span> <b>' + window.sL.favoritedVerb + '</b></a>';
favoritedClass = 'favorited';
}
else {
var favoriteHtml = '<a class="with-icn"><span class="icon sm-fav"></span> <b>' + window.sL.favoriteVerb + '</b></a>';
}
// actions only for logged in users
var queetActions = '';
if(typeof window.loggedIn.screen_name != 'undefined') {
queetActions = '<ul class="queet-actions"><li class="action-reply-container"><a class="with-icn"><span class="icon sm-reply"></span> <b>' + window.sL.replyVerb + '</b></a></li>' + requeetHtml + '<li class="action-fav-container">' + favoriteHtml + '</li></ul>';
}
// reply-to html
var reply_to_html = '';
if(obj.retweeted_status.in_reply_to_screen_name != null && obj.retweeted_status.in_reply_to_screen_name != obj.retweeted_status.user.screen_name) {
reply_to_html = '<span class="reply-to">@' + obj.retweeted_status.in_reply_to_screen_name + '</span> ';
}
// in-groups html
var in_groups_html = '';
if(obj.retweeted_status.statusnet_in_groups !== false && typeof obj.retweeted_status.statusnet_in_groups != 'undefined') {
in_groups_html = '<span class="in-groups">' + obj.retweeted_status.statusnet_in_groups + '</span>';
}
// image attachment thumbnails
var attachment_html = '';
if(typeof obj.retweeted_status.attachments != "undefined") {
$.each(obj.retweeted_status.attachments, function(){
if(this.thumb_url != null) {
attachment_html = attachment_html + '<a href="' + this.url + '"><img src="' + this.thumb_url + '"/></a>';
}
});
}
if(attachment_html.length>0) {
attachment_html = '<div class="attachments">' + attachment_html + '</div>';
}
obj.retweeted_status = convertNewGNUSocialURItoURL(obj.retweeted_status);
var queetTime = parseTwitterDate(obj.retweeted_status.created_at);
var queetHtml = '<div id="stream-item-' + obj.retweeted_status.id + '" class="stream-item ' + extraClassesThisRun + ' ' + requeetedClass + ' ' + favoritedClass + '" data-source="' + escape(obj.retweeted_status.source) + '" data-quitter-id="' + obj.retweeted_status.id + '" data-conversation-id="' + obj.retweeted_status.statusnet_conversation_id + '" data-quitter-id-in-stream="' + obj.id + '" data-in-reply-to-screen-name="' + in_reply_to_screen_name + '" data-in-reply-to-status-id="' + obj.retweeted_status.in_reply_to_status_id + '"><div class="queet" id="q-' + obj.retweeted_status.id + '">' + attachment_html + '<span class="dogear"></span><div class="queet-content"><div class="stream-item-header"><a class="account-group" href="' + obj.retweeted_status.user.statusnet_profile_url + '"><img class="avatar" src="' + obj.retweeted_status.user.profile_image_url_profile_size + '" /><strong class="name" data-user-id="' + obj.retweeted_status.user.id + '">' + obj.retweeted_status.user.name + '</strong> <span class="screen-name">@' + obj.retweeted_status.user.screen_name + '</span></a><i class="addressees">' + reply_to_html + in_groups_html + '</i><small class="created-at" data-created-at="' + obj.retweeted_status.created_at + '"><a href="' + obj.retweeted_status.uri + '">' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.retweeted_status.statusnet_html) + '</div><div class="stream-item-footer">' + queetActions + '<div class="context" id="requeet-' + obj.id + '"><span class="with-icn"><i class="badge-requeeted"></i><span class="requeet-text"> ' + window.sL.requeetedBy + '<a href="' + obj.user.statusnet_profile_url + '"> <b>' + obj.user.name + '</b></a></span></span></div><span class="stream-item-expand">' + window.sL.expand + '</span></div></div></div></div>';
// detect rtl
queetHtml = detectRTL(queetHtml);
if(after) { if(after) {
$('#' + after).after(queetHtml); $('#' + after).after(queetHtml);
@ -1549,74 +1603,7 @@ function addToFeed(feed, after, extraClasses) {
} }
} }
// we don't want to print 'null' in in_reply_to_screen_name-attribute, someone might have that username! var queetHtml = buildQueetHtml(obj, obj.id, extraClassesThisRun);
var in_reply_to_screen_name = '';
if(obj.in_reply_to_screen_name != null) {
in_reply_to_screen_name = obj.in_reply_to_screen_name;
}
// requeet html
var requeetedClass = '';
if(obj.user.id == window.myUserID) {
var requeetHtml = '<li class="action-del-container"><a class="with-icn"><span class="icon sm-trash"></span> <b>' + window.sL.deleteVerb + '</b></a></li></i></li>';
}
else if(obj.repeated) {
var requeetHtml = '<li class="action-rt-container"><a class="with-icn done"><span class="icon sm-rt"></span> <b>' + window.sL.requeetedVerb + '</b></a></li>';
var requeetedClass = 'requeeted';
}
else {
var requeetHtml = '<li class="action-rt-container"><a class="with-icn"><span class="icon sm-rt"></span> <b>' + window.sL.requeetVerb + '</b></a></li>';
}
// favorite html
var favoritedClass = '';
if(obj.favorited) {
var favoriteHtml = '<a class="with-icn done"><span class="icon sm-fav"></span> <b>' + window.sL.favoritedVerb + '</b></a>';
favoritedClass = 'favorited';
}
else {
var favoriteHtml = '<a class="with-icn"><span class="icon sm-fav"></span> <b>' + window.sL.favoriteVerb + '</b></a>';
}
// actions only for logged in users
var queetActions = '';
if(typeof window.loggedIn.screen_name != 'undefined') {
queetActions = '<ul class="queet-actions"><li class="action-reply-container"><a class="with-icn"><span class="icon sm-reply"></span> <b>' + window.sL.replyVerb + '</b></a></li>' + requeetHtml + '<li class="action-fav-container">' + favoriteHtml + '</li></ul>';
}
// reply-to html
var reply_to_html = '';
if(obj.in_reply_to_screen_name != null && obj.in_reply_to_screen_name != obj.user.screen_name) {
reply_to_html = '<span class="reply-to">@' + obj.in_reply_to_screen_name + '</span> ';
}
// in-groups html
var in_groups_html = '';
if(obj.statusnet_in_groups !== false && typeof obj.statusnet_in_groups != 'undefined') {
in_groups_html = '<span class="in-groups">' + obj.statusnet_in_groups + '</span>';
}
// image attachment thumbnails
var attachment_html = '';
if(typeof obj.attachments != "undefined") {
$.each(obj.attachments, function(){
if(this.thumb_url != null) {
attachment_html = attachment_html + '<a href="' + this.url + '"><img src="' + this.thumb_url + '"/></a>';
}
});
}
if(attachment_html.length>0) {
attachment_html = '<div class="attachments">' + attachment_html + '</div>';
}
obj = convertNewGNUSocialURItoURL(obj);
var queetTime = parseTwitterDate(obj.created_at);
var queetHtml = '<div id="stream-item-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' ' + requeetedClass + ' ' + favoritedClass + '" data-source="' + escape(obj.source) + '" data-quitter-id="' + obj.id + '" data-conversation-id="' + obj.statusnet_conversation_id + '" data-quitter-id-in-stream="' + obj.id + '" data-in-reply-to-screen-name="' + in_reply_to_screen_name + '" data-in-reply-to-status-id="' + obj.in_reply_to_status_id + '"><div class="queet" id="q-' + obj.id + '">' + attachment_html + '<span class="dogear"></span><div class="queet-content"><div class="stream-item-header"><a class="account-group" href="' + obj.user.statusnet_profile_url + '"><img class="avatar" src="' + obj.user.profile_image_url_profile_size + '" /><strong class="name" data-user-id="' + obj.user.id + '">' + obj.user.name + '</strong> <span class="screen-name">@' + obj.user.screen_name + '</span></a><i class="addressees">' + reply_to_html + in_groups_html + '</i><small class="created-at" data-created-at="' + obj.created_at + '"><a href="' + obj.uri + '">' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.statusnet_html) + '</div><div class="stream-item-footer">' + queetActions + '<span class="stream-item-expand">' + window.sL.expand + '</span></div></div></div></div>';
// detect rtl
queetHtml = detectRTL(queetHtml);
if(after) { if(after) {
if($('#' + after).hasClass('conversation')) { // if this is a reply, give stream item some conversation formatting if($('#' + after).hasClass('conversation')) { // if this is a reply, give stream item some conversation formatting
@ -1630,7 +1617,13 @@ function addToFeed(feed, after, extraClasses) {
} }
} }
else { else {
$('#feed-body').prepend(queetHtml); $('#feed-body').prepend(queetHtml);
// if this is a single notice, we expand it
if(window.currentStream.substring(0,14) == 'statuses/show/') {
expand_queet($('#stream-item-' + obj.id));
}
} }
} }
@ -1642,61 +1635,125 @@ function addToFeed(feed, after, extraClasses) {
$('.stream-selection').removeAttr('data-current-user-stream-name'); // don't remeber user feeds $('.stream-selection').removeAttr('data-current-user-stream-name'); // don't remeber user feeds
} }
/* · /* ·
· ·
· View threaded converation · Build HTML for a queet from an object
· ·
· @param id: the stream item id · @param obj: a queet object
· · @param requeeted_by: if this is a requeet
· (experimental, not finished, commented out...)
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
// $('body').on('click','.longdate',function(){ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by) {
// threadedConversation($(this).closest('.stream-item:not(.conversation)').attr('data-quitter-id'));
// }) // we don't want to print 'null' in in_reply_to_screen_name-attribute, someone might have that username!
// function threadedConversation(id){ var in_reply_to_screen_name = '';
// $('body').prepend('<div id="threaded-' + id + '" class="modal-container"><div class="thread-container" style="margin-left:0;"><div></div></div></div>'); if(obj.in_reply_to_screen_name != null) {
// var scrollTop = $(window).scrollTop(); in_reply_to_screen_name = obj.in_reply_to_screen_name;
// var containerStreamItem = $('#stream-item-' + id); }
// if(containerStreamItem.children('div:first-child').hasClass('.queet')) {
// var firstStreamItemId = id; // requeet html
// } var requeetedClass = '';
// else { if(obj.user.id == window.myUserID) {
// var firstStreamItemId = containerStreamItem.children('div:first-child').attr('data-quitter-id'); var requeetHtml = '<li class="action-del-container"><a class="with-icn"><span class="icon sm-trash"></span> <b>' + window.sL.deleteVerb + '</b></a></li></i></li>';
// } }
// getThreadedReply(id,firstStreamItemId,$('#threaded-' + id + ' .thread-container div')); else if(obj.repeated) {
// } var requeetHtml = '<li class="action-rt-container"><a class="with-icn done"><span class="icon sm-rt"></span> <b>' + window.sL.requeetedVerb + '</b></a></li>';
// var requeetedClass = 'requeeted';
// function getThreadedReply(containerStreamId,this_id,appendToObj) { }
// else {
// var $this_item = $('<div/>').append($('.stream-item[data-quitter-id="' + this_id + '"]').outerHTML()); var requeetHtml = '<li class="action-rt-container"><a class="with-icn"><span class="icon sm-rt"></span> <b>' + window.sL.requeetVerb + '</b></a></li>';
// $this_item.children().children().remove('.stream-item.conversation'); }
// $this_item.children('.stream-item').css('margin-left',parseInt(appendToObj.css('margin-left'),10)+20 + 'px'); // favorite html
// $this_item.children('.stream-item').removeClass('hidden-conversation'); var favoritedClass = '';
// $this_item.children('.stream-item').removeClass('expanded'); if(obj.favorited) {
// $this_item.children('.stream-item').removeClass('activity'); var favoriteHtml = '<a class="with-icn done"><span class="icon sm-fav"></span> <b>' + window.sL.favoritedVerb + '</b></a>';
// $this_item.children('.stream-item').removeClass('conversation'); favoritedClass = 'favorited';
// $this_item.children('.stream-item').removeClass('visible'); }
// $this_item.children('.stream-item').children('div:not(.queet)').remove(); else {
// $this_item.children('.stream-item').find('.inline-reply-queetbox').remove(); var favoriteHtml = '<a class="with-icn"><span class="icon sm-fav"></span> <b>' + window.sL.favoriteVerb + '</b></a>';
// $this_item.children('.stream-item').find('.expanded-content').remove(); }
// $this_item.children('.stream-item').find('.stream-item-expand').remove();
// $this_item.children('.stream-item').css('opacity','1');
// appendToObj.after($this_item.html()); // actions only for logged in users
// var queetActions = '';
// $.each($('.stream-item[data-quitter-id="' + containerStreamId + '"]').children().get().reverse(),function(){ if(typeof window.loggedIn.screen_name != 'undefined') {
// if($(this).hasClass('queet')) { queetActions = '<ul class="queet-actions"><li class="action-reply-container"><a class="with-icn"><span class="icon sm-reply"></span> <b>' + window.sL.replyVerb + '</b></a></li>' + requeetHtml + '<li class="action-fav-container">' + favoriteHtml + '</li></ul>';
// var this_reply_to = $(this).parent().attr('data-in-reply-to-status-id'); }
// var childs_id = $(this).parent().attr('data-quitter-id');
// } // reply-to html
// else { var reply_to_html = '';
// var this_reply_to = $(this).attr('data-in-reply-to-status-id'); if(obj.in_reply_to_screen_name != null && obj.in_reply_to_screen_name != obj.user.screen_name) {
// var childs_id = $(this).attr('data-quitter-id'); reply_to_html = '<span class="reply-to">@' + obj.in_reply_to_screen_name + '</span> ';
// } }
// if(this_id == this_reply_to) {
// getThreadedReply(containerStreamId,childs_id,$('#threaded-' + containerStreamId + ' .stream-item[data-quitter-id="' + this_id + '"]')); // in-groups html
// } var in_groups_html = '';
// }); if(obj.statusnet_in_groups !== false && typeof obj.statusnet_in_groups != 'undefined') {
// } in_groups_html = '<span class="in-groups">' + obj.statusnet_in_groups + '</span>';
}
// image attachment thumbnails
var attachment_html = '';
if(typeof obj.attachments != "undefined") {
$.each(obj.attachments, function(){
if(this.thumb_url != null) {
attachment_html = attachment_html + '<a href="' + this.url + '"><img src="' + this.thumb_url + '"/></a>';
}
});
}
if(attachment_html.length>0) {
attachment_html = '<div class="attachments">' + attachment_html + '</div>';
}
// requeets
var requeetHtml = '';
if(typeof requeeted_by != 'undefined') {
requeetHtml = '<div class="context" id="requeet-' + requeeted_by.id + '"><span class="with-icn"><i class="badge-requeeted"></i><span class="requeet-text"> ' + window.sL.requeetedBy + '<a href="' + requeeted_by.user.statusnet_profile_url + '"> <b>' + requeeted_by.user.name + '</b></a></span></span></div>';
}
obj = convertNewGNUSocialURItoURL(obj);
var queetTime = parseTwitterDate(obj.created_at);
var queetHtml = '<div \
id="stream-item-' + obj.id + '" \
class="stream-item ' + extraClassesThisRun + ' ' + requeetedClass + ' ' + favoritedClass + '" \
data-source="' + escape(obj.source) + '" \
data-quitter-id="' + obj.id + '" \
data-conversation-id="' + obj.statusnet_conversation_id + '" \
data-quitter-id-in-stream="' + idInStream + '" \
data-in-reply-to-screen-name="' + in_reply_to_screen_name + '" \
data-in-reply-to-status-id="' + obj.in_reply_to_status_id + '">\
<div class="queet" id="q-' + obj.id + '">\
' + attachment_html + '\
<span class="dogear"></span>\
<div class="queet-content">\
<div class="stream-item-header">\
<a class="account-group" href="' + obj.user.statusnet_profile_url + '">\
<img class="avatar" src="' + obj.user.profile_image_url_profile_size + '" />\
<strong class="name" data-user-id="' + obj.user.id + '">' + obj.user.name + '</strong> \
<span class="screen-name">@' + obj.user.screen_name + '</span>\
</a>\
<i class="addressees">' + reply_to_html + in_groups_html + '</i>\
<small class="created-at" data-created-at="' + obj.created_at + '">\
<a href="' + obj.uri + '">' + queetTime + '</a>\
</small>\
</div>\
<div class="queet-text">' + $.trim(obj.statusnet_html) + '</div>\
<div class="stream-item-footer">\
' + queetActions + '\
' + requeetHtml + '\
<span class="stream-item-expand">' + window.sL.expand + '</span>\
</div>\
</div>\
</div>\
</div>';
// detect rtl
queetHtml = detectRTL(queetHtml);
return queetHtml;
}

View File

@ -146,6 +146,11 @@ window.l.es.registerRepeatPassword = 'Verificar contraseña';
window.l.es.moreSettings = 'Más configuraciones'; window.l.es.moreSettings = 'Más configuraciones';
window.l.es.otherServers = 'De manera alternativa, puedes crear una cuenta en otro servidor de la red GNUsocial. <a href="http://federation.skilledtests.com/select_your_server.html">Comparativa</a>'; window.l.es.otherServers = 'De manera alternativa, puedes crear una cuenta en otro servidor de la red GNUsocial. <a href="http://federation.skilledtests.com/select_your_server.html">Comparativa</a>';
window.l.es.editMyProfile = 'Editar perfil'; window.l.es.editMyProfile = 'Editar perfil';
window.l.es.notifications = 'Notificaciones';
window.l.es.xFavedYourQueet = 'marcó tu Queet como favorito';
window.l.es.xRepeatedYourQueet = 'te ha requitteado ';
window.l.es.xStartedFollowingYou = 'te ha seguido';
// Português-Brasil // Português-Brasil
@ -257,6 +262,13 @@ window.l.pt_br.registerRepeatPassword = 'Repetir senha';
window.l.pt_br.moreSettings = 'Mais configurações'; window.l.pt_br.moreSettings = 'Mais configurações';
window.l.pt_br.otherServers = 'De manera alternativa, você pode criar uma conta em outro servidor da rede GNUsocial. <a href="http://federation.skilledtests.com/select_your_server.html">Comparativo</a>'; window.l.pt_br.otherServers = 'De manera alternativa, você pode criar uma conta em outro servidor da rede GNUsocial. <a href="http://federation.skilledtests.com/select_your_server.html">Comparativo</a>';
window.l.pt_br.editMyProfile = 'Editar perfil'; window.l.pt_br.editMyProfile = 'Editar perfil';
window.l.pt_br.notifications = 'Notificações';
window.l.pt_br.xFavedYourQueet = 'curtiu seu Queet';
window.l.pt_br.xRepeatedYourQueet = 'requeetou você';
window.l.pt_br.xStartedFollowingYou = 'seguiu você';
// galician // galician
window.l.gl = new Object(); window.l.gl = new Object();
@ -367,6 +379,10 @@ window.l.gl.registerRepeatPassword = 'Verificar contrasinal';
window.l.gl.moreSettings = 'máis opcións'; window.l.gl.moreSettings = 'máis opcións';
window.l.gl.otherServers = 'De maneira alternativa, podes crear unha conta noutro servidor da rede GNUsocial. <a href="http://federation.skilledtests.com/select_your_server.html">Comparativa</a>'; window.l.gl.otherServers = 'De maneira alternativa, podes crear unha conta noutro servidor da rede GNUsocial. <a href="http://federation.skilledtests.com/select_your_server.html">Comparativa</a>';
window.l.gl.editMyProfile = 'Editar o perfil'; window.l.gl.editMyProfile = 'Editar o perfil';
window.l.gl.notifications = 'Notificacións';
window.l.gl.xFavedYourQueet = 'marcou como favorito o teu chío';
window.l.gl.xRepeatedYourQueet = 'rechouchioute';
window.l.gl.xStartedFollowingYou = 'está a seguirte';
// basque // basque
@ -478,6 +494,11 @@ window.l.eu.registerRepeatPassword = 'Errepikatu pasahitza';
window.l.eu.moreSettings = 'Aukera gehiago'; window.l.eu.moreSettings = 'Aukera gehiago';
window.l.eu.otherServers = 'GNU social sareko beste nodo batean sor dezakezu kontu bat nahi izanez gero. <a href="http://federation.skilledtests.com/select_your_server.html">Alderatu</a>'; window.l.eu.otherServers = 'GNU social sareko beste nodo batean sor dezakezu kontu bat nahi izanez gero. <a href="http://federation.skilledtests.com/select_your_server.html">Alderatu</a>';
window.l.eu.editMyProfile = 'Editatu profila'; window.l.eu.editMyProfile = 'Editatu profila';
window.l.eu.notifications = 'Jakinarazpenak';
window.l.eu.xFavedYourQueet = 'erabiltzaileak gustuko du zure Txioa';
window.l.eu.xRepeatedYourQueet = 'erabiltzaileak bertxiotu zaitu';
window.l.eu.xStartedFollowingYou = 'jarraitzen hasi zaizu';
// french // french
window.l.fr = new Object(); window.l.fr = new Object();
@ -588,6 +609,10 @@ window.l.fr.registerRepeatPassword = 'Vérifiez votre mot de passe';
window.l.fr.moreSettings = 'Plus de paramètres'; window.l.fr.moreSettings = 'Plus de paramètres';
window.l.fr.otherServers = ''; window.l.fr.otherServers = '';
window.l.fr.editMyProfile = 'Éditer le profil'; window.l.fr.editMyProfile = 'Éditer le profil';
window.l.fr.notifications = 'Notifications';
window.l.fr.xFavedYourQueet = 'a ajouté votre Queet à ses favoris ';
window.l.fr.xRepeatedYourQueet = 'vous a requeeté ';
window.l.fr.xStartedFollowingYou = 'vous a suivi';
// deutsch // deutsch
@ -711,6 +736,10 @@ window.l.de.registerRepeatPassword = 'Passwort bestätigen';
window.l.de.moreSettings = 'Weitere Einstellungen'; window.l.de.moreSettings = 'Weitere Einstellungen';
window.l.de.otherServers = 'Du kannst Dir auch gerne ein Konto auf einem anderen Server des GNUsocial-Netzwerks einrichten. <a href="http://federation.skilledtests.com/select_your_server.html">Übersicht</a>'; window.l.de.otherServers = 'Du kannst Dir auch gerne ein Konto auf einem anderen Server des GNUsocial-Netzwerks einrichten. <a href="http://federation.skilledtests.com/select_your_server.html">Übersicht</a>';
window.l.de.editMyProfile = 'Profil bearbeiten'; window.l.de.editMyProfile = 'Profil bearbeiten';
window.l.de.notifications = 'Mitteilungen';
window.l.de.xFavedYourQueet = 'favorisierte Deinen Queet';
window.l.de.xRepeatedYourQueet = 'hat Dich requeetet';
window.l.de.xStartedFollowingYou = 'folgt Dir jetzt';
// english // english
@ -830,6 +859,12 @@ window.l.en.registerRepeatPassword = 'Repeat password';
window.l.en.moreSettings = 'More settings'; window.l.en.moreSettings = 'More settings';
window.l.en.otherServers = 'Alternatively you can create an account on another server of the GNU social network. <a href="http://federation.skilledtests.com/select_your_server.html">Comparison</a>'; window.l.en.otherServers = 'Alternatively you can create an account on another server of the GNU social network. <a href="http://federation.skilledtests.com/select_your_server.html">Comparison</a>';
window.l.en.editMyProfile = 'Edit profile'; window.l.en.editMyProfile = 'Edit profile';
window.l.en.notifications = 'Notifications';
window.l.en.xFavedYourQueet = 'favorited your Queet';
window.l.en.xRepeatedYourQueet = 'requeeted you';
window.l.en.xStartedFollowingYou = 'followed you';
// simplified chinese // simplified chinese
window.l.zh_cn = new Object(); window.l.zh_cn = new Object();
@ -940,6 +975,12 @@ window.l.zh_cn.registerRepeatPassword = '重复密码';
window.l.zh_cn.moreSettings = '更多的设置'; window.l.zh_cn.moreSettings = '更多的设置';
window.l.zh_cn.otherServers = '其他服务器: <a href="http://federation.skilledtests.com/select_your_server.html">对照</a>'; window.l.zh_cn.otherServers = '其他服务器: <a href="http://federation.skilledtests.com/select_your_server.html">对照</a>';
window.l.zh_cn.editMyProfile = '编辑个人资料'; window.l.zh_cn.editMyProfile = '编辑个人资料';
window.l.zh_cn.notifications = '通知';
window.l.zh_cn.xFavedYourQueet = '收藏了你的推文';
window.l.zh_cn.xRepeatedYourQueet = '转推了你的推文';
window.l.zh_cn.xStartedFollowingYou = '关注了你';
// traditional chinese // traditional chinese
window.l.zh_tw = new Object(); window.l.zh_tw = new Object();
@ -1050,6 +1091,12 @@ window.l.zh_tw.registerRepeatPassword = '重複密碼';
window.l.zh_tw.moreSettings = '更多的設置'; window.l.zh_tw.moreSettings = '更多的設置';
window.l.zh_tw.otherServers = '其他服務器: <a href="http://federation.skilledtests.com/select_your_server.html">對照</a>'; window.l.zh_tw.otherServers = '其他服務器: <a href="http://federation.skilledtests.com/select_your_server.html">對照</a>';
window.l.zh_tw.editMyProfile = '編輯個人檔案'; window.l.zh_tw.editMyProfile = '編輯個人檔案';
window.l.zh_tw.notifications = '通知';
window.l.zh_tw.xFavedYourQueet = '已收藏你的推文';
window.l.zh_tw.xRepeatedYourQueet = '已轉推你';
window.l.zh_tw.xStartedFollowingYou = '已跟隨你';
// svenska // svenska
window.l.sv = new Object(); window.l.sv = new Object();
@ -1160,7 +1207,10 @@ window.l.sv.registerRepeatPassword = 'Upprepa lösenord';
window.l.sv.moreSettings = 'Fler inställningar'; window.l.sv.moreSettings = 'Fler inställningar';
window.l.sv.otherServers = 'Men du kan lika gärna skapa ett konto på en annan server som är del av GNU social-nätverket. <a href="http://federation.skilledtests.com/select_your_server.html">Här är en jämförelse.</a>'; window.l.sv.otherServers = 'Men du kan lika gärna skapa ett konto på en annan server som är del av GNU social-nätverket. <a href="http://federation.skilledtests.com/select_your_server.html">Här är en jämförelse.</a>';
window.l.sv.editMyProfile = 'Redigera profil'; window.l.sv.editMyProfile = 'Redigera profil';
window.l.sv.notifications = 'Notiser';
window.l.sv.xFavedYourQueet = 'favoritmarkerade ditt qvitter';
window.l.sv.xRepeatedYourQueet = 'requeetade dig';
window.l.sv.xStartedFollowingYou = 'följde dig';
// farsi/persian // farsi/persian
window.l.fa = new Object(); window.l.fa = new Object();
@ -1271,6 +1321,10 @@ window.l.fa.registerRepeatPassword = 'تایید گذرواژه';
window.l.fa.moreSettings = 'تنظیمات بیشتر'; window.l.fa.moreSettings = 'تنظیمات بیشتر';
window.l.fa.otherServers = ''; window.l.fa.otherServers = '';
window.l.fa.editMyProfile = 'ویرایش نمایه'; window.l.fa.editMyProfile = 'ویرایش نمایه';
window.l.fa.notifications = 'آگاه‌سازی‌ها';
window.l.fa.xFavedYourQueet = 'موارد دلخواه خود را صدای جیر جیر';
window.l.fa.xRepeatedYourQueet = 'توییت شما را باز توییت کرد.';
window.l.fa.xStartedFollowingYou = 'شما را دنبال کرد';
// arabic // arabic
window.l.ar = new Object(); window.l.ar = new Object();
@ -1381,6 +1435,10 @@ window.l.ar.registerRepeatPassword = 'تأكيد كلمة المرور';
window.l.ar.moreSettings = 'مزيد من الإعدادات'; window.l.ar.moreSettings = 'مزيد من الإعدادات';
window.l.ar.otherServers = ''; window.l.ar.otherServers = '';
window.l.ar.editMyProfile = 'تعديل الملف الشخصي'; window.l.ar.editMyProfile = 'تعديل الملف الشخصي';
window.l.ar.notifications = 'التنبيهات';
window.l.ar.xFavedYourQueet = 'بتفضيل تغريدتك';
window.l.ar.xRepeatedYourQueet = 'بإعادة تغريد';
window.l.ar.xStartedFollowingYou = 'بمتابعتك';
// esperanto // esperanto
window.l.eo = new Object(); window.l.eo = new Object();
@ -1500,7 +1558,10 @@ window.l.eo.registerRepeatPassword = 'Ripeti pasvorton';
window.l.eo.moreSettings = 'Pli agordoj'; window.l.eo.moreSettings = 'Pli agordoj';
window.l.eo.otherServers = ''; window.l.eo.otherServers = '';
window.l.eo.editMyProfile = 'Redaktu profilon'; window.l.eo.editMyProfile = 'Redaktu profilon';
window.l.eo.notifications = 'Sciigoj';
window.l.eo.xFavedYourQueet = 'ŝatataj via avizo';
window.l.eo.xRepeatedYourQueet = 'ripetis vin';
window.l.eo.xStartedFollowingYou = 'sekvas vin';
// italian // italian
window.l.it = new Object(); window.l.it = new Object();
@ -1620,6 +1681,10 @@ window.l.it.registerRepeatPassword = 'Ripetere la password';
window.l.it.moreSettings = 'Altre opzioni'; window.l.it.moreSettings = 'Altre opzioni';
window.l.it.otherServers = 'In alternativa puoi creare un account su un altro server della rete GNU Social. <a href="http://federation.skilledtests.com/select_your_server.html">Confronto</a>'; window.l.it.otherServers = 'In alternativa puoi creare un account su un altro server della rete GNU Social. <a href="http://federation.skilledtests.com/select_your_server.html">Confronto</a>';
window.l.it.editMyProfile = 'Modifica profilo'; window.l.it.editMyProfile = 'Modifica profilo';
window.l.it.notifications = 'Notifiche';
window.l.it.xFavedYourQueet = 'ha aggiunto ai preferiti il tuo Queet';
window.l.it.xRepeatedYourQueet = 'ti ha requittato';
window.l.it.xStartedFollowingYou = 'ha iniziato a seguirti';
// Norwegian // Norwegian
window.l.no = new Object(); window.l.no = new Object();
@ -1730,6 +1795,10 @@ window.l.no.registerRepeatPassword = 'Gjenta passord';
window.l.no.moreSettings = 'Flere instillinger'; window.l.no.moreSettings = 'Flere instillinger';
window.l.no.otherServers = 'Det finnes flere instanser i GNUsocial nettverket som alternativt kan benyttes. <a href="http://gnu.io/social/try/">Følger du denne lenken finner du noen anbefalinger.</a>'; window.l.no.otherServers = 'Det finnes flere instanser i GNUsocial nettverket som alternativt kan benyttes. <a href="http://gnu.io/social/try/">Følger du denne lenken finner du noen anbefalinger.</a>';
window.l.no.editMyProfile = 'Rediger profil'; window.l.no.editMyProfile = 'Rediger profil';
window.l.no.notifications = 'Varsler';
window.l.no.xFavedYourQueet = 'favorittmarkerte queeten din';
window.l.no.xRepeatedYourQueet = 'requeetet deg';
window.l.no.xStartedFollowingYou = 'følger deg';
// set language, from local storage, else browser language, else english (english also if no localstorage availible) // set language, from local storage, else browser language, else english (english also if no localstorage availible)
@ -1797,6 +1866,8 @@ $('.stream-selection[data-stream-name="qvitter/statuses/friends_timeline.json"]'
$('.stream-selection[data-stream-name="qvitter/statuses/friends_timeline.json"]').attr('data-stream-header',window.sL.timeline); $('.stream-selection[data-stream-name="qvitter/statuses/friends_timeline.json"]').attr('data-stream-header',window.sL.timeline);
$('.stream-selection[data-stream-name="qvitter/statuses/mentions.json"]').prepend(window.sL.mentions); $('.stream-selection[data-stream-name="qvitter/statuses/mentions.json"]').prepend(window.sL.mentions);
$('.stream-selection[data-stream-name="qvitter/statuses/mentions.json"]').attr('data-stream-header',window.sL.mentions); $('.stream-selection[data-stream-name="qvitter/statuses/mentions.json"]').attr('data-stream-header',window.sL.mentions);
$('.stream-selection[data-stream-name="qvitter/statuses/notifications.json"]').prepend(window.sL.notifications);
$('.stream-selection[data-stream-name="qvitter/statuses/notifications.json"]').attr('data-stream-header',window.sL.notifications);
$('.stream-selection[data-stream-name="favorites.json"]').prepend(window.sL.favoritesNoun); $('.stream-selection[data-stream-name="favorites.json"]').prepend(window.sL.favoritesNoun);
$('.stream-selection[data-stream-name="favorites.json"]').attr('data-stream-header',window.sL.favoritesNoun); $('.stream-selection[data-stream-name="favorites.json"]').attr('data-stream-header',window.sL.favoritesNoun);
$('.stream-selection[data-stream-name="statuses/public_timeline.json"]').prepend(window.sL.publicTimeline); $('.stream-selection[data-stream-name="statuses/public_timeline.json"]').prepend(window.sL.publicTimeline);

File diff suppressed because one or more lines are too long

4
js/lib/jquery-2.1.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -458,25 +458,27 @@ function findUrls(text) {
· · · · · · · · · · · · */ · · · · · · · · · · · · */
function display_spinner() { function display_spinner() {
$('body').prepend('\ if($('.loader').length<1) {
<div class="loader">\ $('body').prepend('\
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\ <div class="loader">\
width="40px" height="40px" viewBox="0 0 40 40" enable-background="new 0 0 40 40" xml:space="preserve">\ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\
<path opacity="0.2" enable-background="new " d="M20.201,8.503c-6.413,0-11.612,5.199-11.612,11.612s5.199,11.611,11.612,11.611\ width="40px" height="40px" viewBox="0 0 40 40" enable-background="new 0 0 40 40" xml:space="preserve">\
c6.412,0,11.611-5.198,11.611-11.611S26.613,8.503,20.201,8.503z M20.201,29.153c-4.992,0-9.039-4.046-9.039-9.038\ <path opacity="0.2" enable-background="new " d="M20.201,8.503c-6.413,0-11.612,5.199-11.612,11.612s5.199,11.611,11.612,11.611\
s4.047-9.039,9.039-9.039c4.991,0,9.038,4.047,9.038,9.039S25.192,29.153,20.201,29.153z"/>\ c6.412,0,11.611-5.198,11.611-11.611S26.613,8.503,20.201,8.503z M20.201,29.153c-4.992,0-9.039-4.046-9.039-9.038\
<path d="M24.717,12.293l1.285-2.227c-1.708-0.988-3.686-1.563-5.801-1.563l0,0v2.573l0,0C21.848,11.076,23.386,11.524,24.717,12.293 z">\ s4.047-9.039,9.039-9.039c4.991,0,9.038,4.047,9.038,9.039S25.192,29.153,20.201,29.153z"/>\
<animateTransform attributeType="xml"\ <path d="M24.717,12.293l1.285-2.227c-1.708-0.988-3.686-1.563-5.801-1.563l0,0v2.573l0,0C21.848,11.076,23.386,11.524,24.717,12.293 z">\
attributeName="transform"\ <animateTransform attributeType="xml"\
type="rotate"\ attributeName="transform"\
from="0 20 20"\ type="rotate"\
to="360 20 20"\ from="0 20 20"\
dur="1s"\ to="360 20 20"\
repeatCount="indefinite"/>\ dur="1s"\
</path>\ repeatCount="indefinite"/>\
</svg>\ </path>\
</div>\ </svg>\
'); </div>\
');
}
} }
function remove_spinner() { function remove_spinner() {
$('.loader').remove(); $('.loader').remove();

View File

@ -59,12 +59,12 @@ window.onpopstate = function(event) {
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
window.loginContentStartPos = $('.front-welcome-text').height()+45;
$(window).scroll(function(e){ $(window).scroll(function(e){
if ($(this).scrollTop() > window.loginContentStartPos && $('#login-content').css('position') != 'fixed'){ // console.log($('#feed').offset().top);
if ($(this).scrollTop() > ($('#feed').offset().top-50) && $('#login-content').css('position') != 'fixed'){
$('#login-content, .front-signup').not('#popup-signup').css({'position': 'fixed', 'top': '50px'}); $('#login-content, .front-signup').not('#popup-signup').css({'position': 'fixed', 'top': '50px'});
} }
else if ($(this).scrollTop() < window.loginContentStartPos && $('#login-content').css('position') != 'absolute'){ else if ($(this).scrollTop() < ($('#feed').offset().top-50) && $('#login-content').css('position') != 'absolute'){
$('#login-content, .front-signup').not('#popup-signup').css({'position': 'absolute', 'top': 'auto'}); $('#login-content, .front-signup').not('#popup-signup').css({'position': 'absolute', 'top': 'auto'});
} }
}); });
@ -123,9 +123,9 @@ if(!window.registrationsClosed) {
$('#signup-user-nickname-step2').after('<div class="spinner-wrap"><div class="spinner"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></div>'); $('#signup-user-nickname-step2').after('<div class="spinner-wrap"><div class="spinner"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></div>');
} }
window.checkNicknameTimeout = setTimeout(function(){ window.checkNicknameTimeout = setTimeout(function(){
getFromAPI('check_nickname.json?nickname=' + encodeURIComponent($('#signup-user-nickname-step2').val()),function(data){ $.get(window.apiRoot + 'check_nickname.json?nickname=' + encodeURIComponent($('#signup-user-nickname-step2').val()),function(data){
$('.spinner-wrap').remove(); $('.spinner-wrap').remove();
if(data=='taken') { if(data==0) {
$('#signup-user-password2-step2').trigger('keyup'); // revalidates $('#signup-user-password2-step2').trigger('keyup'); // revalidates
} }
else { else {
@ -288,6 +288,7 @@ function doLogin(streamToSet) {
// add user data to DOM, show search form, remeber user id, show the feed // add user data to DOM, show search form, remeber user id, show the feed
$('#user-container').css('z-index','1000'); $('#user-container').css('z-index','1000');
$('#top-compose').removeClass('hidden'); $('#top-compose').removeClass('hidden');
$('#qvitter-notice').show();
$('#user-avatar').attr('src', window.loggedIn.profile_image_url_profile_size); $('#user-avatar').attr('src', window.loggedIn.profile_image_url_profile_size);
$('#user-name').append(window.loggedIn.name); $('#user-name').append(window.loggedIn.name);
$('#user-screen-name').append(window.loggedIn.screen_name); $('#user-screen-name').append(window.loggedIn.screen_name);
@ -298,6 +299,7 @@ function doLogin(streamToSet) {
$('#user-groups strong').html(window.loggedIn.groups_count); $('#user-groups strong').html(window.loggedIn.groups_count);
$('.stream-selection.friends-timeline').attr('href', window.loggedIn.statusnet_profile_url + '/all'); $('.stream-selection.friends-timeline').attr('href', window.loggedIn.statusnet_profile_url + '/all');
$('.stream-selection.mentions').attr('href', window.loggedIn.statusnet_profile_url + '/replies'); $('.stream-selection.mentions').attr('href', window.loggedIn.statusnet_profile_url + '/replies');
$('.stream-selection.notifications').attr('href', window.loggedIn.statusnet_profile_url + '/notifications');
$('.stream-selection.my-timeline').attr('href', window.loggedIn.statusnet_profile_url); $('.stream-selection.my-timeline').attr('href', window.loggedIn.statusnet_profile_url);
$('.stream-selection.favorites').attr('href', window.loggedIn.statusnet_profile_url + '/favorites'); $('.stream-selection.favorites').attr('href', window.loggedIn.statusnet_profile_url + '/favorites');
window.myUserID = window.loggedIn.id; window.myUserID = window.loggedIn.id;
@ -668,6 +670,12 @@ $('body').on('click','.profile-banner-footer .stats li a, .queet-stream',functio
else if($(this).hasClass('favorites')) { else if($(this).hasClass('favorites')) {
setNewCurrentStream('favorites.json?screen_name=' + screenName,function(){},true); setNewCurrentStream('favorites.json?screen_name=' + screenName,function(){},true);
} }
else if($(this).hasClass('following')) {
setNewCurrentStream('statuses/friends.json?count=20',function(){},true);
}
else if($(this).hasClass('followers')) {
setNewCurrentStream('statuses/followers.json?count=20',function(){},true);
}
else if($(this).hasClass('member-stats')) { else if($(this).hasClass('member-stats')) {
setNewCurrentStream('statusnet/groups/membership/' + screenName + '.json?count=20',function(){},true); setNewCurrentStream('statusnet/groups/membership/' + screenName + '.json?count=20',function(){},true);
} }
@ -737,6 +745,10 @@ $(document).on('click','a', function(e) {
e.preventDefault(); e.preventDefault();
setNewCurrentStream('qvitter/statuses/mentions.json',function(){},true); setNewCurrentStream('qvitter/statuses/mentions.json',function(){},true);
} }
else if ($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/' + window.loggedIn.screen_name,'') == '/notifications') {
e.preventDefault();
setNewCurrentStream('qvitter/statuses/notifications.json',function(){},true);
}
else if ($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/' + window.loggedIn.screen_name,'') == '/favorites') { else if ($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/' + window.loggedIn.screen_name,'') == '/favorites') {
e.preventDefault(); e.preventDefault();
setNewCurrentStream('favorites.json',function(){},true); setNewCurrentStream('favorites.json',function(){},true);
@ -747,9 +759,6 @@ $(document).on('click','a', function(e) {
if($(this).parent().attr('id') == 'user-profile-link') { // logged in user if($(this).parent().attr('id') == 'user-profile-link') { // logged in user
setNewCurrentStream('statuses/user_timeline.json?screen_name=' + window.loggedIn.screen_name,function(){},true); setNewCurrentStream('statuses/user_timeline.json?screen_name=' + window.loggedIn.screen_name,function(){},true);
} }
else if($(this).hasClass('account-group')) { // any user
setNewCurrentStream('statuses/user_timeline.json?screen_name=' + $(this).find('.screen-name').text().substring(1).toLowerCase(),function(){},true);
}
else { // any user else { // any user
setNewCurrentStream('statuses/user_timeline.json?screen_name=' + $(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/',''),function(){},true); setNewCurrentStream('statuses/user_timeline.json?screen_name=' + $(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/',''),function(){},true);
} }
@ -763,6 +772,11 @@ $(document).on('click','a', function(e) {
e.preventDefault(); e.preventDefault();
setNewCurrentStream('statusnet/tags/timeline/' + $(this).text().toLowerCase().replace('#','') + '.json',function(){},true); setNewCurrentStream('statusnet/tags/timeline/' + $(this).text().toLowerCase().replace('#','') + '.json',function(){},true);
} }
// notices
else if ($(this).attr('href').indexOf(window.siteRootDomain + '/notice/')>-1) {
e.preventDefault();
setNewCurrentStream('statuses/show/' + $(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/notice/','') + '.json',function(){},true);
}
// groups // groups
else if (/^[0-9]+$/.test($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/group/','').replace('/id',''))) { else if (/^[0-9]+$/.test($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/group/','').replace('/id',''))) {
e.preventDefault(); e.preventDefault();
@ -1005,10 +1019,12 @@ var updateTimesInterval=self.setInterval(function(){
/* · /* ·
· ·
· Check for new queets · Check for new queets and notifications
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
var checkForNewNotificationsInterval=window.setInterval(function(){checkForNewNotifications()},window.timeBetweenPolling);
checkForNewNotifications();
var checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling); var checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling);
function checkForNewQueets() { function checkForNewQueets() {
@ -1033,7 +1049,12 @@ function checkForNewQueets() {
// if we have hidden items, show new-queets-bar // if we have hidden items, show new-queets-bar
if($('#feed-body').find('.stream-item.hidden').length > 0) { if($('#feed-body').find('.stream-item.hidden').length > 0) {
var new_queets_num = $('#feed-body').find('.stream-item.hidden').length; var new_queets_num = $('#feed-body').find('.stream-item.hidden').length;
document.title = window.siteTitle + ' (' + new_queets_num + ')';
// if this is notifications page, update site title with hidden notification count
if(window.currentStream == 'qvitter/statuses/notifications.json') {
document.title = window.siteTitle + ' (' + new_queets_num + ')';
}
$('#new-queets-bar').parent().removeClass('hidden'); $('#new-queets-bar').parent().removeClass('hidden');
// text plural // text plural
@ -1050,8 +1071,6 @@ function checkForNewQueets() {
} }
/* · /* ·
· ·
· Show hidden queets when user clicks on new-queets-bar · Show hidden queets when user clicks on new-queets-bar
@ -1059,7 +1078,9 @@ function checkForNewQueets() {
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
$('body').on('click','#new-queets-bar',function(){ $('body').on('click','#new-queets-bar',function(){
document.title = window.siteTitle; if(window.currentStream == 'qvitter/statuses/notifications.json') {
document.title = window.siteTitle;
}
$('.stream-item.hidden').css('opacity','0') $('.stream-item.hidden').css('opacity','0')
$('.stream-item.hidden').animate({opacity:'1'}, 200); $('.stream-item.hidden').animate({opacity:'1'}, 200);
$('.stream-item.hidden').removeClass('hidden'); $('.stream-item.hidden').removeClass('hidden');
@ -1819,6 +1840,7 @@ $('body').on('click','.edit-profile-button',function(){
</div>\ </div>\
</div>\ </div>\
</div>'); </div>');
$('#edit-profile-popup .profile-card').css('top',$('#page-container .profile-card').offset().top-53 + 'px'); // position exactly over
} }
else { else {
abortEditProfile(); abortEditProfile();